dSPACE COM tools - MsBuild Tasks
This is an unstable prerelease. Anything may change at any time!
dscom generates a type library that describes the types defined in a common language runtime assembly and is a replacement for tlbexp.exe and TypeLibConverter.ConvertAssemblyToTypeLib.
The tool consists of a library and a command line tool. The library can be used in net5+ or in net48 projects.
Fortunately, .NET still supports COM, but there is no support for generating TLBs.
From the Microsoft documentation:
Unlike in .NET Framework, there is no support in .NET Core or .NET 5+ for generating a COM Type Library (TLB) from a .NET assembly.
One main goal is to make dscom behave like tlbexp.exe.
Happy IUnknowing and IDispatching ;-)
The command-line interface (CLI) tool dscom is a replacement for tlbexp.exe and OleView (View TypeLib).
It supports the following features:
YAML fileThe installation is quite simple. You can use dotnet tool to install the dscom binary if you want to create a 64Bit TLB.
dotnet tool install --global dscomHere you can find all available versions:
https://www.nuget.org/packages/dscom/
Alternatively you can download dscom.exe from the relase page.
https://github.com/dspace-group/dscom/releases
dscom installed by dotnet tool install can only handle AnyCPU or 64Bit assemblies and can only generate a 64bit TLB.
Depending on whether you want to process 32bit or 64bit assemblies, you need to download different executables from the release page.
Warning!
If your assembly is an AnyCPU assembly, then an yourassemblyname.comhost.dll is created as a 64 bit dll.
Therefore after calling regserv32.exe a 64 bit dll is registred.
To prevent this it is recommended that the assembly is compiled as a 32 bit assembly and not as an AnyCPU assembly.
see: https://github.com/dotnet/runtime/issues/32493
Use dscom --help to get further information.
c:\> dscom --help
Description:
dSPACE COM tools
Usage:
dscom [command] [options]
Options:
--version Show version information
-?, -h, --help Show help and usage information
Commands:
tlbexport <Assembly> Export the assembly to the specified type library
tlbdump <TypeLibrary> Dump a type library
tlbregister <TypeLibrary> Register a type library
tlbunregister <TypeLibrary> Unregister a type libraryThe dSPACE.Runtime.InteropServices.BuildTasks assembly and NuGet package provide the ability to create type libraries for a certain assembly at runtime.
For details on the implementation refer to the documentation section of the repository.
To create a type library at compile time, simply add a reference to the nuget package, e.g. by using the command line.
dotnet add package dSPACE.Runtime.InteropServices.BuildTasksThe result should be a line as follows in your .csproj file:
<PackageReference Include="dSPACE.Runtime.InteropServices.BuildTasks" Version="0.17.0" NoWarn="NU1701">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>Note: The extra attribute NoWarn="NU1701" is only required, if neither .NET 4.8 nor .NET 6.0 are targeted, since dotnet pack will currently not create a .NETStandard 2.0 compliant NuGet Package.
The native build task is automatically selected, if a .NET 4.8 or .NET 6.0 assembly for Windows is being build using an x64 platform.
The CLI task is automatically selected, if a .NET Standard 2.0 assembly is build. It is also chosen, if the target platform is set to x86.
It might be necessary to select the CLI based task. To do so, add the following property to your .csproj file:
<_DsComForceToolUsage>true</_DsComForceToolUsage>This will enforce the usage of the DsCom as a command-line tool. Please note, that verbose logging will no longer be working.
The build tasks puts a warning to the build log, if the desired type library has not been created, even if the backend has reported a success.
This warning is issued with the warning code DSCOM001, which can be collected in the WarningsAsErrors array:
<WarningsAsErrors>$(WarningsAsErrors);DSCOM001</WarningsAsErrors>This way the build stops, if the type library is not exported.
The build task can be parameterized with the following properties:
| Name | Description | Default |
|---|---|---|
| _DsComTlbExt | Extension of the resulting type library. | .tlb |
| _DsComForceToolUsage | Use DsCom Exe files to create the TLB | false |
| DsComTypeLibraryUniqueId | Overwrite the library UUID | Empty Guid |
| DsComRegisterTypeLibrariesAfterBuild | Use regasm call after the build to register type library after the build | false |
| DsComTlbExportAutoAddReferences | Add referenced assemblies automatically to type libraries | true |
| DsComTlbExportIncludeReferencesWithoutHintPath | If a Reference assembly does not provide a HintPath Metadata, the item spec shall be task. | false |
| _DsComExportTypeLibraryTargetFile | Path to the resulting file. | $(TargetDir)\$(TargetName)$(_DsComTlbExt) * |
| _DsComExportTypeLibraryAssemblyFile | Path to the source assembly file. | $(TargetPath) * |
*) This value cannot be overridden.
The build task consumes the following items:
| Name | Description |
|---|---|
| DsComTlbExportTlbReferences | Referenced type library files. |
| DsComTlbExportReferencePaths | Directories containing type libraries to use for export. |
| DsComTlbExportAssemblyPaths | Assemblies to add for the export. |
Usage:
dotnet add package dSPACE.Runtime.InteropServicesdSPACE.Runtime.InteropServices supports the following methods and classes:
If you miss the TypeLibConverter class and the ConvertAssemblyToTypeLib method in .NET, then the dSPACE.Runtime.InteropServices might help you.
This method should behave compatible to the .NET Framework method.
public object? ConvertAssemblyToTypeLib(
Assembly assembly,
string tlbFilePath,
ITypeLibExporterNotifySink? notifySink)Example:
using dSPACE.Runtime.InteropServices;
// The assembly to convert
var assembly = typeof(Program).Assembly;
// Convert to assembly
var typeLibConverter = new TypeLibConverter();
var callback = new TypeLibConverterCallback();
var result = typeLibConverter.ConvertAssemblyToTypeLib(assembly, "MyTypeLib.tlb", callback);
// Get the name of the type library
var typeLib2 = result as System.Runtime.InteropServices.ComTypes.ITypeLib2;
if (typeLib2 != null)
{
typeLib2.GetDocumentation(-1, out string name, out _, out _, out _);
Console.WriteLine($"TypeLib name: {name}");
}
// The callback to load additional type libraries, if necessary
public class TypeLibConverterCallback : ITypeLibExporterNotifySink
{
public void ReportEvent(ExporterEventKind eventKind, int eventCode, string eventMsg)
{
Console.WriteLine($"{eventCode}: {eventMsg}");
}
public object? ResolveRef(System.Reflection.Assembly assembly)
{
// Returns additional type libraries
return null;
}
}The dSPACE.Runtime.InteropServices.RegistrationServices provides a set of services for registering and unregistering managed assemblies for use from COM.
This method is equivalent to calling CoRegisterClassObject in COM.
You can register a .NET class so that other applications can connect to it (For example as INPROC_SERVER or as a LOCAL_SERVER).
A outproc demo application is available here: examples\outproc
Example:
using dSPACE.Runtime.InteropServices;
var registration = new RegistrationServices();
var cookie = registration.RegisterTypeForComClients(typeof(Server.Common.Greeter),
RegistrationClassContext.LocalServer,
RegistrationConnectionType.MultipleUse);
Console.WriteLine($"Press enter to stop the server");
Console.ReadLine();
registration.UnregisterTypeForComClients(cookie);Both assemblies are ComVisible=false but lot of .NET Framework types are ComVisible=true.
But this is not the case for .NET (.NET Core and .NET >= 5).
Unlike mscorelib (the good old .NET Framework), no tlb is shipped for .NET.
As example the System.Exception class:
In case of mscorelib the System.Exception class is ComVisible=true:
[Serializable]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(_Exception))]
[ComVisible(true)]
public class Exception : ISerializable, _ExceptionIn case of System.Private.CoreLib (.NET Core and .NET >=5), the Exception class is ComVisible=false
[Serializable]
[TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public class Exception : ISerializable
{The _Exception class interface (default interface in this case) is not available in .NET (.NET Core and .NET >=5).
The magic is in TypeForwardedFromAttribute.
If you try to load an .NET Framework assembly inside a .NET (.NET Core and .NET >=5) application, the runtime will forward the original type to
a type defined in the System.Private.CoreLib assembly.
classextern forwarder System.Exception
{
.assemblyextern System.Private.CoreLib
}Therefore you should make sure that you do not use any types from the mscorelib typelib in your .NET Framework project if you plan to migrate to .NET 5+
mscorelib typelib (all types are VT_UNKNOWN)
TypeLibExporterFlags is not supportedITypeLibExporterNotifySink is not COM visibleTypeLibConverter is not COM visibleAutoDual is not supported
IEnumVARIANT (stdole)GUID (stdole)OLE_COLOR(stdole)UnmanagedType.CustomMarshalerAssemblyMetadataAttribute value ".NETFrameworkAssembly"