Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,9 @@ var settings = new TypeLibEmbedderSettings
TypeLibEmbedder.EmbedTypeLib(settings);
```

IMPORTANT: Embedding the type library will alter the assembly, which may cause issues with signing the assembly. Therefore, the scenario of signing the assembly with a certificate or a strong name is not tested. If it is required that the assembly be signed, it is recommended that a build script be used to ensure proper sequence of steps is executed.
IMPORTANT: Embedding the type library will alter the assembly, which may cause issues with signing the assembly. Therefore, the scenario of signing the assembly with a certificate or a strong name is not tested. If it is required that the assembly be signed, it is recommended that a build script be used to ensure proper sequence of steps is executed.

IMPORTANT: In order to embed the type library into the built assembly, the process must unload the assembly. As dotnet restricts the usage of unloadable assemblies via AssemblyLoadContext to pure CLR assemblies, embedding TLBs into an assembly may only work, if none of the assemblies loaded via `--asmpath` may be a mixed mode C++/CLI assembly. In this case, please use to different calls to `dscom.exe` / `dscom32.exe` or the separate dscom build tasks. For more information refer to issue [#292](https://github.com/dspace-group/dscom/issues/292).

### RegistrationServices.RegisterTypeForComClients

Expand Down
7 changes: 5 additions & 2 deletions src/dscom.client/AssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal sealed class AssemblyResolver : AssemblyLoadContext, IDisposable
{
private bool _disposedValue;

internal AssemblyResolver(TypeLibConverterOptions options) : base("dscom", isCollectible: true)
internal AssemblyResolver(TypeLibConverterOptions options) : base("dscom", isCollectible: options.ShouldEmbed())
{
Options = options;
Resolving += Context_Resolving;
Expand Down Expand Up @@ -72,7 +72,10 @@ private void Dispose(bool disposing)
if (disposing)
{
Resolving -= Context_Resolving;
Unload();
if (IsCollectible)
{
Unload();
}
}

_disposedValue = true;
Expand Down
65 changes: 34 additions & 31 deletions src/dscom.client/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,6 @@ namespace dSPACE.Runtime.InteropServices;

public static class ConsoleApp
{
/// <summary>
/// Use a null character, which is an illegal character and therefore impossible
/// to input via a command line arguments to make it distinct from nulls that
/// gets passed when the option is specified but no argument is given.
/// </summary>
private const string NotSpecifiedViaCommandLineArgumentsDefault = "\0";

public static int Main(string[] args)
{
var tlbexportCommand = new Command("tlbexport", "Export the assembly to the specified type library") {
Expand All @@ -46,7 +39,7 @@ public static int Main(string[] args)
new Option<string>(new[] { "--overridename", "/overridename"}, description: "Overwrites the library name"),
new Option<Guid>(new[] {"--overridetlbid", "/overridetlbid"}, description: "Overwrites the library id"),
new Option<bool?>(new[] {"--createmissingdependenttlbs", "/createmissingdependenttlbs"}, description: "Generate missing type libraries for referenced assemblies. (default true)"),
new Option<string?>(new[] { "--embed", "/embed"}, () => NotSpecifiedViaCommandLineArgumentsDefault, description: "Embeds type library into the assembly. (default: false)") { Arity = ArgumentArity.ZeroOrOne },
new Option<string?>(new[] { "--embed", "/embed"}, () => TypeLibConverterOptions.NotSpecifiedViaCommandLineArgumentsDefault, description: "Embeds type library into the assembly. (default: false)") { Arity = ArgumentArity.ZeroOrOne },
new Option<ushort>(new[] {"--index", "/index"}, () => 1, description: "If the switch --embed is specified, the index indicates the resource ID to be used for the embedded type library. Must be a number between 1 and 65535. Ignored if --embed not present. (default 1)")
};

Expand Down Expand Up @@ -179,30 +172,13 @@ private static void ConfigureTLBExportHandler(Command tlbexportCommand)

ExportTypeLibraryImpl(options, out var weakRef);

if (options.Embed != NotSpecifiedViaCommandLineArgumentsDefault)
if (options.ShouldEmbed())
{
for (var i = 0; weakRef.IsAlive && (i < 10); i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}

if (weakRef.IsAlive)
{
throw new ApplicationException("Unable to embed type library as the assembly is still locked by other processes.");
}

var assemblyPath = string.IsNullOrWhiteSpace(options.Embed) ? options.Assembly : options.Embed;
Console.WriteLine($"Embedding type library '{options.Out}' into assembly '{assemblyPath}'...");
var settings = new TypeLibEmbedderSettings
{
SourceTypeLibrary = options.Out,
TargetAssembly = assemblyPath,
Index = options.Index
};
TypeLibEmbedder.EmbedTypeLib(settings);
ExportTypeLibraryAfterExport(options, weakRef);
}

_ = RemoveWeakReference(weakRef);

return 0;
}
catch (Exception e)
Expand Down Expand Up @@ -231,8 +207,6 @@ private static void ExportTypeLibraryImpl(TypeLibConverterOptions options, out W
{
createTypeLib2.SaveAllChanges().ThrowIfFailed($"Failed to save type library {options.Out}.");
}

assemblyResolver.Dispose();
}

private static void RegisterTypeLib(string typeLibFilePath, bool forUser = false)
Expand Down Expand Up @@ -313,4 +287,33 @@ private static int HandleException(Exception e, string errorText)

return 1;
}

private static void ExportTypeLibraryAfterExport(TypeLibConverterOptions options, WeakReference weakRef)
{
if (!RemoveWeakReference(weakRef))
{
throw new ApplicationException("Unable to embed type library as the assembly is still locked by other processes.");
}

var assemblyPath = string.IsNullOrWhiteSpace(options.Embed) ? options.Assembly : options.Embed;
Console.WriteLine($"Embedding type library '{options.Out}' into assembly '{assemblyPath}'...");
var settings = new TypeLibEmbedderSettings
{
SourceTypeLibrary = options.Out,
TargetAssembly = assemblyPath,
Index = options.Index
};
TypeLibEmbedder.EmbedTypeLib(settings);
}

private static bool RemoveWeakReference(WeakReference weakRef)
{
for (var i = 0; weakRef.IsAlive && (i < 10); i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}

return !weakRef.IsAlive;
}
}
16 changes: 16 additions & 0 deletions src/dscom.client/TypeLibConverterOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ namespace dSPACE.Runtime.InteropServices;
/// </summary>
public class TypeLibConverterOptions : TypeLibConverterSettings
{
/// <summary>
/// Use a null character, which is an illegal character and therefore impossible
/// to input via a command line arguments to make it distinct from nulls that
/// gets passed when the option is specified but no argument is given.
/// </summary>
internal const string NotSpecifiedViaCommandLineArgumentsDefault = "\0";

/// <summary>
/// Gets or sets a value indicating whether the output is silent.
/// </summary>
Expand Down Expand Up @@ -51,4 +58,13 @@ public class TypeLibConverterOptions : TypeLibConverterSettings
/// is set to <c>true</c>.
/// </summary>
public ushort Index { get; set; }

/// <summary>
/// Centralized check for using a meaningful value for <see cref="Embed"/>.
/// </summary>
/// <returns><c>true</c>, if the tlb should be embedded into the resulting assembly; <c>false</c> otherwise.</returns>
internal bool ShouldEmbed()
{
return Embed is null || !StringComparer.OrdinalIgnoreCase.Equals(Embed, NotSpecifiedViaCommandLineArgumentsDefault);
}
}
Loading