dspace-group/dscom

Error with a COM project containing WPF/WinForms

bclothier opened this issue · 6 comments

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

Hello bclothier,

the problem here is that dscom does not use the GAC.
To create a TLB from a .NET 4.x project, you need to specify the dependent DLLs.

This is how it should work:

dscom tlbexport C:\ProgramData\Rubberduck\Rubberduck.dll --asmpath C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089

Thanks for the guidance. Adding the reference assemblies (it appears that I needed 2), I still get the duplicate GUID error:

dscom tlbexport .\Rubberduck.Deployment\bin\Rubberduck.dll --out .\Rubberduck.Deployment\bin\Test.tlb --asmpath 'C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089' --asmpath 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\PublicAssemblies'
Failed to export type library. Failed to set GUID for DOMDocument. Duplicate ID in inheritance hierarchy. (0x800288C6 (TYPE_E_DUPLICATEID))

We have several GUIDs defined so it's hard to tell which one is causing the problem.

FWIW, this seems to work:

dscom.exe tlbexport .\Rubberduck.Deployment\bin\Rubberduck.dll --out .\Rubberduck.Deployment\bin\Test.tlb --asmpath 'C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089' --tlbreference 'C:\Program Files (x86)\Common Files\Designer\MSADDNDR.dll' --verbose

The duplicate ID is from the Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime assembly. It looks like error is with GUID {2933bf81-7b36-11d2-b20e-00c04f983e60} for the type named DOMDocument. This was not a type we wanted to export, and apparently was not a problem with the System version. The above tlbreference allows us to bypass the exports of the types defined in the interop assembly. Oddly, trying it without the assembly references to System.Windows.Forms still fails even after removing any actual references. The original type library was leaking a UserControl type from the System.Windows.Forms assembly. I edited the interface to use object rather than the actual type. After the changes, I verified via the OLEVIEW that it now has the following importlib:

    // TLib : mscorlib.dll : {BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}	
    importlib("mscorlib.tlb");	
    // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}	
    importlib("stdole2.tlb");	
    // TLib : Microsoft Add-In Designer : {AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}	
    importlib("MSADDNDR.dll");

Using the working example, I get only the last two importlibs. I'm not yet sure if it'll work without the importlib to the mscorlib.lib and will have to test this out. I think it's because the System version decorate the exports with _Object interfaces which requires the reference. The dSPACE version does not decorate the exports.

Now that I have a working command-line example, I will need to translate it to the C# code accessing the TypeLibConverter object rather than via the command line.

Good to hear that you were able to generate a tlb.
In case you migrate to .NET 5+, then note that you will have problems with mscorelib types anyway.

See: https://github.com/dspace-group/dscom#migration-notes-mscorelib-vs-systemprivatecorelib

When I perform an export, the result looks like this:

dscom tlbexport C:\ProgramData\Rubberduck\Rubberduck.dll --asmpath C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0__b77a5c561934e089
// Generated .IDL file (by the OLE/COM Object Viewer)
// 
// typelib filename: Rubberduck.x32.tlb

[
  uuid(E07C841C-43F0-3B33-B105-9B8188A6F040),
  version(2.5),
  helpstring("Rubberduck AddIn"),
  custom(90883F05-3D28-11D2-8F17-00A0C9A6186D, "Rubberduck, Version=2.5.2.5906, Culture=neutral, PublicKeyToken=null")

]
library Rubberduck
{
    // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface IDockableWindowHost;
    interface IAssert;
    interface IFake;
    interface IFakesProvider;
    interface IStub;
    interface IVerify;

System.Windows.Forms is only needed because Rubberduck.dll references it and dscom needs it for Reflection.

Hi @bclothier,
can I close the issue?

Apologies. I meant to circle back because I did have a follow up question.

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.