Code Monkey home page Code Monkey logo

dscom's Introduction

dSPACE COM tools

Nuget:Cli
Nuget:Lib
Nuget:LibBuildTask

Release License dSPACE

Unit Tests Example Tests Code Style Check

The command line client dscom is a replacement for tlbexp.exe and creates and registers TLBs from .NET assemblies.
The dSPACE.Runtime.InteropServices library contains various classes and methods for COM.
It can be used in net5+ or in net48 projects. With the library you can register assemblies and classes for COM and programmatically generate TLBs at runtime.
The dSPACE.Runtime.InteropServices.BuildTasks library provides build tasks which can be used to automatically generate TLBs at compile time.

Introducing

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.

https://docs.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com

One main goal is to make dscom behave like tlbexp.exe.

Also, some classes are missing in .NET 5+ that were available in the full framework. This is where dSPACE.Runtime.InteropServices may be able to help.

Command Line Client

The command-line interface (CLI) tool dscom is a replacement for tlbexp.exe and OleView (View TypeLib).

It supports the following features:

  • Convert an assembly to a type library
  • Convert a type library to YAML file
  • Register a type library
  • Unregister a type library

Installation

The 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 dscom

Here 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

Usage

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 library

Library

Usage:

dotnet add package dSPACE.Runtime.InteropServices

dSPACE.Runtime.InteropServices supports the following methods and classes:

  • TypeLibConverter
    • ConvertAssemblyToTypeLib
  • RegistrationServices
    • RegisterTypeForComClients
    • UnregisterTypeForComClients
    • RegisterAssembly
    • UnregisterAssembly

TypeLibConverter.ConvertAssemblyToTypeLib

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)

https://www.nuget.org/packages/dSPACE.Runtime.InteropServices/

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;
    }
}

RegistrationServices.RegisterTypeForComClients

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);

RegistrationServices.RegisterAssembly

Registers the classes in a managed assembly to enable creation from COM.

using dSPACE.Runtime.InteropServices;

var registration = new RegistrationServices();

// Register MyAssembly
registration.RegisterAssembly(typeof(MyAssembly), true);

// Unregister MyAssembly
registration.UnregisterAssembly(typeof(MyAssembly), true);

Build Tasks

The dSPACE.Runtime.InteropServices.BuildTasks assembly and NuGet package provide the ability to create type libraries for a certain assembly at compile time.

Build task usage

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.BuildTasks

The 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.

Using the native build task

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.

Using the CLI based task

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.

Enforcing the usage of the CLI

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.

Enforcing to stop the build, if an error occurs

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.

Parameters

The build task can be parameterized with the following properties:

Name Description
_DsComTlbExt Extension of the resulting type library.
Default Value: .tlb
_DsComForceToolUsage Use DsCom Exe files to create the TLB
Default value: false
DsComTypeLibraryUniqueId Overwrite the library UUID
Default Value: Empty Guid
DsComOverideLibraryName Overwrite the IDL name of the library.
Default Value: Empty string
DsComRegisterTypeLibrariesAfterBuild Use regasm call after the build to register type library after the build
Default value: false
DsComTlbExportAutoAddReferences Add referenced assemblies automatically to type libraries
Default value: true
DsComTlbExportIncludeReferencesWithoutHintPath If a Reference assembly does not provide a HintPath Metadata, the item spec shall be task.
Default value: false
_DsComExportTypeLibraryTargetFile Path to the resulting file.
Default value: $(TargetDir)\$(TargetName)$(_DsComTlbExt) *
_DsComExportTypeLibraryAssemblyFile Path to the source assembly file.
Default value: $(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.
DsComTlbAliasNames Names to use as aliases for types with aliases.

Example

<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
      <DsComTypeLibraryUniqueId>f74a0889-2959-4e7b-9a1b-f41c54f31d74</DsComTypeLibraryUniqueId>
   </PropertyGroup>
   <ItemGroup>
    <DsComTlbExportTlbReferences Include="C:\Path\To\My.tlb" />
    <DsComTlbExportAssemblyPaths Include="C:\Path\To\Assemblies\Dependency.dll" />
    <DsComTlbExportReferencePaths Include="C:\Path\To\Additional\TypeLibraries\" />
   </ItemGroup>
</Project>

32Bit support

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.

  • dscom.exe to create a 64Bit TLB from a AnyCPU or a 64Bit assembly
  • dscom32.exe to create a 32Bit TLB from a AnyCPU or a 32Bit assembly

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: dotnet/runtime#32493

Migration notes (mscorelib vs System.Private.CoreLib)

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, _Exception

In 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).

Why can I load a .NET Framework library into a .NET application?

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+

Limitations

RegisterAssembly

  • InProc Registration only will take place, if a comhost assembly is present.
  • No CustomRegisterFunction Or CustomUnRegisterFunction Attribute support
  • No PrimaryInteropAssembly Support

dscom's People

Contributors

bclothier avatar brimerland avatar carstencodes avatar dependabot[bot] avatar eo3-kopf avatar marklechtermann avatar matthiasnissen avatar mgaffigan avatar mlessmann avatar sandoli avatar sosterbrink avatar spkl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

dscom's Issues

Error: Could not load file or assembly 'PresentationFramework' on tlbexport

When using dscom to export a library referencing WPF or another unavailable library, exceptions are thrown from ComAliasNameResolver. We don't really want to "fix" those reference problems, just to have tlbexport ignore them. LibraryWriter already has the logic to turn missing references into warnings, but ComAliasNameWriter is missing them.

Related to #120, PR incoming

LibraryWriter does not write TLB files when hosted in MsBuild

Description

In some cases, the TlbExport task fails with DSCOM0001, i.e. the requested library does not exist, even though the Conversion method did not return null. This was observed when adding #102.

The reason is yet unkown.

As a workaround, by default the usage of the CLI during the build is enforced.

Severity

Major

Workaround exists

How to reproduce

Run the acceptance criteria when _DsComForceToolUsage is set to false.

Acceptance criteria

The build does not issue warning DSCOM0001

Build task fails

After adding the nuget package to my project the build returns an error:

Fehler MSB4113 Die angegebene Bedingung @(DsComTlbAliasNames->Count() > 0) wird zu @(DsComTlbAliasNames->Count() > 0) statt zu einem booleschen Wert ausgewertet.

According to the message the error originates in dSPACE.Runtime.InteropServices.BuildTasks.Tools.targets, line 47. It seems there is a typo in the condition phrase of _DsComAliasNames (comparing it to the surrounding ones). I assume it should be Condition="@(DsComTlbAliasNames->Count()) &gt; 0" instead of Condition="@(DsComTlbAliasNames->Count() &gt; 0)" (wrong position of the closing paranthesis).

Overriding library name during build task

This is an idea for enhancement:

If I got it right, dscom picks AssemblyName as name of the library in the TLB. Maybe in some cases it might come in handy to override this behaviour and define a name manually.

Therefore I suggest adding a new item which the build task consumes also. Something like DsComTlbLibraryName

Honor [ComVisible(false)] method atrtibutes in advance

I have simple example:

public interface IMyClass {
    [ComVisible(false)] 
    public System.Security.Cryptography.X509Certificates.X509RevocationMode RevocationMode { get; set; }
}
public class MyClass: IMyClass {
    public System.Security.Cryptography.X509Certificates.X509RevocationMode RevocationMode { get; set; }
}

If I run tlbexport with --createmissingdependenttlbs false there will be warnings in output.

dscom : warning TX00000000 : The referenced library System.Security.Cryptography does not have a type library and auto generation of dependent type libs is disabled

Option to bypass imported types

Hello,
First of all, many thanks for this extremely useful tool. We use it on many projects and it saves us a lot of time.
I'd like to suggest a small improvement, unless the functionality already exists but I've missed it: our assemblies contain imported interop types and, from time to time, these imported types appear several times in the hierarchy when the tlb is exported. This generates a TYPE_E_DUPLICATEID error when the SetGuid method is called, preventing the command from executing correctly.
It seems to me that it would be fairly easy to get around this problem by adding an option (e.g. --includeimportedtypes) and a test on type.IsImport in the LibraryWriter.CollectAllTypes method so that imported types can be ignored.
What do you think?

The DsComTlbExportTlbReferences and DsComTlbExportAssemblyPaths options are ignored

I worked out the logic for using the TypeLibConverter by using the sink's ResolveRef to mirror the argument line commands for the command-line tools. However, I did want to use the build task instead of running code. The documentation implies that the equivalent should be:

<DsComTlbExportTlbReferences>C:\Program Files\Common Files\DESIGNER\MSADDNDR.OLB</DsComTlbExportTlbReferences>

As a property in my .csproj. However, it does not appear that it get passed as an input to the command line tool during the build. I tried with DsComTlbExportAssemblyPaths but got the same result; no changes to the command line generated by the build task.

In both cases, the error is as following:

The command "C:\Users\User\.nuget\packages\dspace.runtime.interopservices.buildtasks\0.18.0\build\..\tools\x64\dscom.exe      tlbexport    C:\GitHub\Rubberduck\Rubberduck.Main\bin\Debug\net48\Rubberduck.dll --out C:\GitHub\Rubberduck\Rubberduck.Main\bin\Debug\net48\\Rubberduck.tlb" exited with code 1.

with additional warnings:

TX00000000	Type library exporter encountered an error while processing 'Microsoft.VisualStudio.Interop'. Error: Could not load file or assembly 'Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
TX00000000	Type library exporter encountered an error while processing 'Rubberduck'. Error: Could not load file or assembly 'System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. The system cannot find the file specified.

I should note that in the TypeLibConverter + sink's ResolveRef approach, I basically check if it's trying to resolve the Microsoft.VisualStudio.Interop in which case I pass back C:\Program Files\Common Files\DESIGNER\MSADDNDR.OLB which has the needed type information without all the junk. This works correctly but not sure how to replicate this as a build task.

Originally posted by @bclothier in #120 (comment)

Mixed assembly support

Hi!

Is it possible to return mixed assembly support?
Latest version is unable to perform tlbexport for mixed assembly or assembly with mixed assembly dependency.
I have digged a bit and found that AssemblyLoadContext is constructed with unload feature which is not supported for mixed assemblies.

Error with a COM project containing WPF/WinForms

It appears that if a solution contains WPF & WinForms, there are errors within the library that prevents it from exporting correctly where the same DLL will export fine with the System.Runtime.InteropServices.

To reproduce, use the Rubberduck.dll which can be obtained from here and run the command line tool:

dscom tlbexport .\Rubberduck.Deployment\bin\Rubberduck.dll --out .\Rubberduck.Deployment\bin\Test.tlb

I get the following output:

dscom : warning TX00000000 : Type library exporter encountered an error while processing 'Rubberduck'. Error: Could not load file or assembly 'System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. The system cannot find the file specified.
dscom : warning TX00000000 : Type library exporter encountered an error while processing 'Microsoft.VisualStudio.Interop'. Error: Could not load file or assembly 'Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
Failed to export type library. Failed to set GUID for DOMDocument. Duplicate ID in inheritance hierarchy. (0x800288C6 (TYPE_E_DUPLICATEID))

DefaultParameterValue null does not create the expected TLB

With the following code example:

    public void MyMethodName(int param1, [DefaultParameterValue(1)] int param2);
    public void MyMethodName2(IDemoInterface param1, [DefaultParameterValue(null)] IDemoInterface? param2);
    public void MyMethodName3(IDemoInterface param1, IDemoInterface? param2 = null);
 

The first and third options to set a default value work as expected.
But the second option will ignore the default value.

image

This happens only if the provided default value is not optional and null

IntPtr is mapped to Int64 in TLBs generated by dscom32

Hi there,

I'm attempting to export TLBs from a 32-bit .NET class library which contains several interfaces and classes.
Some of the methods defined in these interfaces require IntPtr arguments, and when I import the TLB into unmanaged C++, the resulting .TLH requires int64 * arguments in place of the IntPtr ones, when I would've expected them to require long *.

I've verified the contents of the TLB using OleView, so it would seem that dscom32 is not differentiating IntPtr into long* and int64*.
I'm wondering is this expected behaviour or a bug?

FileLoadException during tlbexport

dscom tlbexport may throw a FileLoadException when trying to load a dependent assembly if that assembly was previously loaded from a different path. This has occured for the WindowsBase.dll assembly, which is part of the .NET Windows Runtime.

The issue may be fixed by using Assembly.LoadFile() rather than Assembly.LoadFrom() when loading dependent assemblies from paths specified by --asmpath.

Global Config update

The global.json requires .NET 6.0.100 because 6.0.200 contained a bug but currently 6.0.300 is available where the bug was fixed. The configurations should be updated

Handling assemblies without namespace properties

          I faced strange error.

dscom fails to export tlb if assembly references System.Security.Cryptography.
Interop+BCrypt+BCRYPT_DSA_KEY_BLOB_V2+<Count>e__FixedBuffer, System.Security.Cryptography, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
I think it's related to .NET 8.

Originally posted by @gureedo in #196 (comment)

combase error - class not registered

This may be an error on my part, but I thought I'd bring it up here in case there's a bug to fix, or even a clarification that can be made on the main page. Consistently when I run my CPP code, that imports a tlb and attempts to point to a class, I'm getting this error:
onecore\com\combase\dcomrem\resolver.cxx(2299)\combase.dll!77A61A49: (caller: 77ADABBC) ReturnHr(1) tid(3a1c) 80040154 Class not registered

The error occurs when I call "PSP_CSLibraryDLLClassPtr p(__uuidof(PSP_CSLibrary));" - I'm guessing that it's PSP_CSLibrary which is the unregistered class.

Note that this error does not appear if I use an old tlb made with tlbexp.exe
I already made sure the tlb was created with --win32 (from AnyCpu), which matched my C++ architecture, and then I registered it with "dscom tlbregister".
Is there anything more I have to do? Register the dll somehow? Do something extra to register the class? The class itself has a unique GUID in my C# file as well. Not sure what to do, or where this error is coming from.

Enhancement: Usage of .NET SourceLink

Using .NET Sourcelink, nuget packages can be published that will reference not only the project on github, but also the commit that was used for the creation of the package.

For details, please refer to the package and the repository.

It is really easy to introduce: Just add the package Microsoft.SourceLink.Github to the development dependencies of the project to be published.

According to the project website, the following new properties should be added to the project:

    <!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
 
    <!-- Optional: Embed source files that are not tracked by the source control manager in the PDB -->
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
  
    <!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
    <IncludeSymbols>true</IncludeSymbols>
    <SymbolPackageFormat>snupkg</SymbolPackageFormat>

The first one should already be setup for dscom.
The second parameter will include generated code to the pdb. This is optional.
The third and fourth parameter can be used to create a symbol nuget package (snupkg), which will remove the necessity to use a symbol server.

NU1701 when adding build task to .NETStandard 2.0 project

Description

Adding the package dSPACE.Runtime.InteropServices.BuildTasks package (added in #102) to projects targetting .NET Standard 2.0 causes NuGet restore to issue a NU1701 warning.
This is due to the fact, that no group for .NET Standard 2.0 is created in the resulting NuGet package.

Accepted workaround is currently, that the package must be added with ItemMetadata NoWarn="1702".

Severity

Major

(.NET Standard 2.0 is losing ground, Workaround exists)

How to reproduce

Add the build task to a project targeting .NET Standard 2.0. Run the restore.

Acceptance criteria

The NU1701 warning must not appear.

Component Category Registation

I think this section need some more explanation as I have my doubts about this:

using var componentCategoryKey = Registry.ClassesRoot.CreateSubKey(RegistryKeys.ComponentCategories);
using var managedCategoryKey = componentCategoryKey.CreateSubKey(RegistryKeys.ManagedCategoryGuid);
var key0 = Convert.ToString(0, CultureInfo.InvariantCulture);
var value = managedCategoryKey.GetValue(key0);
if (value is not null && value.GetType() != typeof(string))
{
managedCategoryKey.DeleteValue(key0, false);
managedCategoryKey.SetValue(key0, RegistryValues.ManagedCategoryDescription);
}
else if (value is not null)
{
var keyValue = (string)value;
if (!StringComparer.InvariantCulture.Equals(keyValue, RegistryValues.ManagedCategoryDescription))
{
managedCategoryKey.SetValue(key0, RegistryValues.ManagedCategoryDescription);
}
}
else
{
managedCategoryKey.SetValue(key0, RegistryValues.ManagedCategoryDescription);
}

My concerns are as follows:

  • This is a registry key that's not ours to write because that is Microsoft's responsibility to write the component category key as part of the .NET installation. I would think it's more appropriate to check if the key exists and if it doesn't, to exit with an error.

This method can only be called by the owner of a category, usually as part of the installation or de-installation of the operating system or application.
Reference

  • This is adding a locale of 0 (e.g. LOCALE_NEUTRAL). When I look at all other component categories within the HKCR\Component Categories\, I do not see any other categories with a neutral locale. In fact, on my computer, all have the same locale of 409 (e.g. en-us locale). I'm not sure how that looks on other computers. I would expect that as part of the .NET installation, it will use the appropriate locales and thus it isn't our responsibility to add it.

From my testing, ICatRegister::RegisterCategory will not register the category unless running in an elevated context, which fits the expectation because those are described in HKLM exclusively, never in HKCU, and the ICatRegister will write to the HKLM only. Indeed, when running non-elevated, it won't even throw an error but silently gives S_OK even though no registry keys were written. I cannot find any documentation suggesting whether a component category can be registered non-elevated and even though HCKR will reflect HKCU (which normally does not have the Component Categories key and must be manually added), I don't think anyone has used components on a per-user level, only at per-machine level. That makes sense because the component categories are normally used to support tasks such as displaying an dialog to select from a list of controls that can be inserted into a container.

Can you provide a counterexample as to why this code is necessary? The code to register a component as implementing a certain category certainly is appropriate but I'm not so sure we ought to be registering category that aren't ours.

Addendum:

After posting above, I found this article which does list component categories as one of elements that's merged in the HKCR which mirror my manual test even though I did not have any per-user categories registered. The only problem is that for older software that's incorrectly using HKLM rather than HKCR; those will not work correctly if the data is in HKCU even though it will also appear in HKCR. If you are aware of such examples where HKLM is essential we ought to consider this. In the example of registering a neutral locale key, that could be written to HKCU instead of HKCR which would then remove the need to elevate the privileges for registering the assembly.

Support for 32Bit assemblies

Currently it is a known limitation that only 64 bit libraries are supported. As shown by #58 and #47 there is a demand for 32 bit libraries to be supported.

It must be evaluated if the changes from #58 will be sufficient and if further changes are required.

Runtime.InteropServices - StrongNameSigning

We are trying to migrate to Net6 and want to consume this NugetPackage instead of the original System.Runtime.InteropServices.
Since we need to generate code and register this code at runtime, we need to add the .dll to our deliverables.
Since we are StrongName-Signing our product, we can only deliver packages, that are strongName-Signed as well.
==> We want a strongName-Signed version of the dSPACE.Runtime.InteropServices Package.

Build uses wrong version of CLI

If I set PlatformTarget to x86 the CLI version ist used for creating the tlb-file. So far so good. However the wrong version is used. The build task returns an error, which points out that the x64-version ist used.

Fehler MSB3073 Der Befehl "D:\Entwicklung.nuget\packages\dspace.runtime.interopservices.buildtasks\0.21.0\build..\tools\x64\dscom.exe tlbexport D:\Entwicklung<PathTo>\My.dll --out D:\Entwicklung<PathTo>\My.tlb" wurde mit dem Code 1 beendet.

According to the message the error originates in dSPACE.Runtime.InteropServices.BuildTasks.Tools.targets, line 62. However I think it originates in the lines 23 resp. 24. In Line 23 _DsComToolsFileDir is set without condition. Line 24 provides condition for x86. To me it seems the condition ist wrong. If I comment out line 23 $(_DsComToolsFileDir) becomes empty (instead of pointing to $(MsBuildThisFileDirectory)..\tools\x86\). Therefore I think something with the condition in line 24 is wrong.

Could not load file or assembly

Hi. i have create a small test project and try your project but it thow an error, can you help me please?

PS C:\Users\user1\Documents\GitHub\TestComExport\TestComExport\bin\x86\Debug\net6.0> dscom tlbexport .\TestComExport.dll
Failed to export type library. Could not load file or assembly 'TestComExport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <EnableComHosting>true</EnableComHosting>
    <Platforms>x86</Platforms>
  </PropertyGroup>

</Project>
using System.Runtime.InteropServices;

namespace TestComExport
{
    [ComVisible(true), Guid("FA63E193-BF1E-41A2-95B6-D0AEDC825CFB")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IFlyNumbers
    {
        [DispId(1)]
        int GetDay();

        [DispId(2)]
        int GetMonth();

        [DispId(3)]
        int GetYear();

        [DispId(4)]
        int DayOfYear();
    }

    [ComVisible(true), Guid("F9AFBEBF-401F-4074-BEA8-9C49FF3EA1EB")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("FlyTest")]
    public class FlyNumbers : IFlyNumbers
    {
        public FlyNumbers() { }

        public int GetDay()
        {
            return DateTime.Today.Day;
        }

        public int GetMonth()
        {
            return DateTime.Today.Month;
        }

        public int GetYear()
        {
            return DateTime.Today.Year;
        }

        public int DayOfYear()
        {
            return DateTime.Now.DayOfYear;
        }
    }
}

RegisterTypeForComClients fails when Type does not have a GuidAttribute

Using the method fails when the provided Type element has no CustomAttribute GuidAttribute.

RegisterTypeForComClients(Type type, ComTypes.RegistrationClassContext classContext, ComTypes.RegistrationConnectionType flags) 

An empty sequence exception is thrown by this line.

var guid = new Guid(type.GetCustomAttributes<GuidAttribute>().First().Value);

Using the RegistrationServices from System.Runtime.InteropServices the method works, the passed type object is the same and does also not contain a CustomAttribute GuidAttribute.

I did observer that the Type object has an assembly property. Looking into it the provided assembly has a GuidAttribute. Adjusting the code as such:

var guid = new Guid(type.Assembly.GetCustomAttributes<GuidAttribute>().First().Value);

The code works, and cross referencing with the source code the GUID is also correct.

The code should also check the Assembly for the GUID.
A more fitting Exception would be helpful if the GUID cannot be retrieved at all.

Support for NETCoreSdkRuntimeIdentifier missing

I use the property NETCoreSdkRuntimeIdentifier in some of my projects to specify if a 32 or 64 bit comhost.dll will be generated. However dSPACE.Runtime.InteropServices.BuildTasks.Tools.targets does not recognize this property and uses the 64-bit version of dscom for a 32-bit comhost.dll.

I suggest adding a line like this
<_DsComToolsFileDir Condition="'$(NETCoreSdkRuntimeIdentifier)' == 'win-x86'">$(MsBuildThisFileDirectory)..\tools\x86\</_DsComToolsFileDir>
to dSPACE.Runtime.InteropServices.BuildTasks.Tools.targets. This makes MSBuild to use the right version of dscom if NETCoreSdkRuntimeIdentifier is set to win-x86.

I think you also have to add a line to dSPACE.Runtime.InteropServices.BuildTasks.targets to automatically use the cli version in this case. I assume it will be something like
<_DsComExporterTargetsFile Condition="'$(NETCoreSdkRuntimeIdentifier)' == 'win-x86'">$(MsBuildThisFileDirectory)dSPACE.Runtime.InteropServices.BuildTasks.Tools.targets</_DsComExporterTargetsFile>

warning TX00000000

I'm getting this error after a .Net 6 port. TlbExport (.Net 4.7.2) could generate the files, dscom reports this error on IEnumerable
dscom : warning TX00000000 : ComVisible interface System.Collections.IEnumerable could not be added to source type ....

The c# dll compiles without errors.

Sorting tests and adding missing test cases in the unit tests

Because there is no clear naming schema, it's sometimes hard to see which test cases are missing.
Here we should define an easier to read naming schema and rename the tests accordingly.

There are currently test cases missing in the unit tests. For example:

  • Methods with valid out parameters and valid return values (This likely created problems in #121 )

It's likely that we will identify additional missing test cases during the renaming operation.

`tlbregister` registers with exact path instead of relative path

Hello! Thank you for publishing such a useful project. It was a pleasure to find and easy to get started. As a minor nit, one of the "tripping points" I experienced was that unlike regasm, dscom tlbregister does not expand the path provided to be a full path.

Actual behavior:

PS C:\path\to\project> dscom tlbregister foo.tlb
Type library was registered successfully
PS C:\path\to\project> (Get-ItemProperty 'hklm:Software\Classes\TypeLib\{F50810FE-4FA5-3CFF-9523-530867891B55}\1.0\0\win64')."(default)"
foo.tlb

Expected behavior:

PS C:\path\to\project> dscom tlbregister foo.tlb
Type library was registered successfully
PS C:\path\to\project> (Get-ItemProperty 'hklm:Software\Classes\TypeLib\{F50810FE-4FA5-3CFF-9523-530867891B55}\1.0\0\win64')."(default)"
C:\path\to\project\foo.tlb

If you prefer a PR, let me know.

.NET 7 support

Heya,

I loove your dscom tool as it is the only feasable possibility for my project to get MSAL working on an old C++ app by calling current .NET Code from C++.

.NET 7 is out since yesterday and the released dscom32.exe throws an error when working on a .NET 7 Library.

Do you have any plans to support .NET 7 as quick as possible?

Thanks
Soko

Warning regarding type substitution cannot be avoided by explicitly changing the type with MarshalAs.

If an interface is referenced as a parameter that cannot be resolved by a TLB, a warning comes up, see this simple example:

public interface WarningTestInterface
{
    System.Collections.IList GetList();
}

dscom : warning TX0013116F : Warning: Type library exporter could not find the type library for IList.  IUnknown was substituted for the interface.

If you now want to resolve this warning by an explicit MarshalAs as follows, the warning still appears.

public interface WarningTestInterface
{
    [return: MarshalAs(UnmanagedType.IUnknown)]
    System.Collections.IList GetList();
}

One should be able to avoid the warning by an explicit MarshalAs, since the origin type becomes irrelevant with it.

Too many files in output directory

Using the build tasks adds a lot of (unnecessary) files to the output directory:

  • dSPACE.Runtime.InteropServices.dll
  • Microsoft.Build.Tasks.Core.dll and all its dependencies

All this files have nothing to do with the COM-Server I am building and they are not needed to run it. Is there a way preventing these files to be copied to the output directory?

I assume they are put in the output directory because they are referenced by the dSPACE.Runtime.InteropServices.BuildTasks-package.

Build fails, when project path contains whitespaces

The title already tells it.

$ mkdir C:\test
$ mkdir "C:\test\My Project"
$ mkdir "C:\test\My Project\test"
$ cd "C:\test\My Project\test"
$ dotnet new console
$ dotnet add package dspace.runtime.interopservices.buildtasks
$ dotnet build

Expected: The build works
Result: Calling dscom.exe to export type library
Unrecognized command or argument 'Project\test\bin\Debug\net6.0\test.dll'.

IEnumerable as parameter is generated as VT_UNKOWN

In case of IEnumerable (this interface is ComVisible=true in System.Private.CoreLib) dscom should generate:

type: VT_PTR -> VT_USERDEFINED refType: name: IEnumerable

As example:
void MyMethod(IEnumerable elements);

Code Style check fails after .NET update in github actions

The code style check fails after the used windows image was updated:

Worked with:
https://github.com/dspace-group/dscom/actions/runs/4281683514/jobs/7455010898

Current runner version: '2.301.1'
Operating System
Microsoft Windows Server 2022
10.0.20348
Datacenter
Runner Image
Image: windows-2022
Version: 20230219.1
Included Software: https://github.com/actions/runner-images/blob/win22/20230219.1/images/win/Windows2022-Readme.md
Image Release: https://github.com/actions/runner-images/releases/tag/win22%2F20230219.1

Failing with:
https://github.com/dspace-group/dscom/actions/runs/4363953151/jobs/7630677977

Current runner version: '2.302.1'
Operating System
Microsoft Windows Server 2022
10.0.20348
Datacenter
Runner Image
Image: windows-2022
Version: 20230226.1
Included Software: https://github.com/actions/runner-images/blob/win22/20230226.1/images/win/Windows2022-Readme.md
Image Release: https://github.com/actions/runner-images/releases/tag/win22%2F20230226.1

Evaluate support for COM Source generation

Build tasks do not work for Framework

Trying to install the dscom nuget package in a .NET Framewok 4.8-project results in the following error message:

Das Paket "dscom 0.22.1" weist den Pakettyp "DotnetTool" auf, der vom Projekt ".NETFramework\COMFassade" nicht unterstützt wird.

According to readme.me it is meant to work for .NET Framework too. How?

--overridetlbid leads to errors and warnings during type resolution

Export a TLB for the following code:

[ComVisible(false)]
public interface ComInvisibleInterface
{}

public interface TestInterface
{
    void DoSomething(ComInvisibleInterface param);
}

Using the --createmissingdependenttlbs:false --overridetlbid:12345678-1234-1234-1234-123456789012 parameters, the following warnings will appear:

dscom : warning TX00000000 : The referenced library dSPACE.Runtime.InteropServices.DemoAssembly1 does not have a type library and auto generation of dependent type libs is 
disabled
dscom : warning TX00000000 : The referenced library dSPACE.Runtime.InteropServices.DemoAssembly1 does not have a type library and auto generation of dependent type libs is 
disabled
dscom : warning TX00000000 : The referenced library dSPACE.Runtime.InteropServices.DemoAssembly1 does not have a type library and auto generation of dependent type libs is 
disabled
dscom : warning TX00000000 : The referenced library dSPACE.Runtime.InteropServices.DemoAssembly1 does not have a type library and auto generation of dependent type libs is 
disabled
dscom : warning TX00000000 : The referenced library dSPACE.Runtime.InteropServices.DemoAssembly1 does not have a type library and auto generation of dependent type libs is 
disabled
dscom : warning TX00000000 : The referenced library dSPACE.Runtime.InteropServices.DemoAssembly1 does not have a type library and auto generation of dependent type libs is 
disabled
dscom : warning TX00000000 : The referenced library dSPACE.Runtime.InteropServices.DemoAssembly1 does not have a type library and auto generation of dependent type libs is 
disabled
dscom : warning TX0013116F : Warning: Type library exporter could not find the type library for ComInvisibleInterface.  IUnknown was substituted for the interface.
dscom : warning TX00000000 : The referenced library dSPACE.Runtime.InteropServices.DemoAssembly1 does not have a type library and auto generation of dependent type libs is 
disabled
dscom : warning TX00000000 : The referenced library dSPACE.Runtime.InteropServices.DemoAssembly1 does not have a type library and auto generation of dependent type libs is 
disabled
dscom : warning TX00000000 : The referenced library dSPACE.Runtime.InteropServices.DemoAssembly1 does not have a type library and auto generation of dependent type libs is 
disabled
dscom : warning TX00000000 : The referenced library dSPACE.Runtime.InteropServices.DemoAssembly1 does not have a type library and auto generation of dependent type libs is 
disabled

The warnings are caused by the fact that in the generated TLB with the specified GUID the type "ComInvisibleInterface" cannot be found because it was not created. Therefore dscom looks now whether there is a TLB with the GUID, which dscom would have assigned, here the overwritten GUID is ignored and if necessary tries to generate the TLB again with the automatically generated GUID. The overwritten GUID must be used consistently as identity for the TLB in all places.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.