1

I wrote a very simple DLL in C#, and it looks like this:

[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
[ComVisible(true)]
public interface IComClass1
{
    void Do();
}

[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F933"),
    ProgId("MyComProgId"),
    ComVisible(true)]
public class ComClass1 : IComClass1
{
    void IComClass1.Do()
    {
        System.Windows.Forms.MessageBox.Show("ComClass1 ^^^^^ Test");
    }
}

And my client side looks like this:

[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
public interface IComClass1
{
    void Do();
}

class Program
{
    static void Main(string[] args)
    {
        var type = Type.GetTypeFromProgID("MyComProgId");
        var com = (IComClass1)Activator.CreateInstance(type); // ERROR: System.InvalidCastException
        com.Do();
    }
}

For some reason there is an error here: InvalidCastException.

Why does the error occur? Am I wrong? And how do I fix it?

8
  • A COM interface requires [InterfaceType(ComInterfaceType.InterfaceIsXXX)] attribute. Commented Oct 27, 2020 at 19:40
  • @SimonMourier Where to add it? Commented Oct 27, 2020 at 20:04
  • As an attribute on both interfaces declarations Commented Oct 28, 2020 at 6:50
  • @SimonMourier not helping. Commented Oct 28, 2020 at 7:16
  • It does help as .NET COM doesn't work w/o this. But since you're doing .NET to .NET, it stays .NET calls and COM is out of the loop. Define IComClass1 interface in a common .NET library or in a TLB that you reference in both projects. Or don't define IComClass1 in Program and use dynamic C# keyword to declare the type of com (no intellisense any more). Commented Oct 28, 2020 at 7:49

1 Answer 1

1

Since .NET sees the COM object is a .NET object, it cannot make an equivalence between the two definitions of the IComClass1 interface, hence the invalid cast exception. One solution is to declare the common COM interfaces in a separate .tlb file (type library) from an .idl file.

To create a .TLB you could imagine to export the .NET component as a TLB using regasm, unfortunately it works, but the resulting .TLB cannot be referenced by a .NET project (again, no .NET->COM->.NET).

However it's still a good start as you can have a look at the resulting .TLB with OleView from the Windows SDK and basically remove all "custom" IDL attributes that tell it's a .NET exported .tlb.

So, let's say I have this definition in C#:

namespace ClassLibrary2
{
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
    [ComVisible(true)]
    public interface IComClass1
    {
        void Do();
    }
}

I compile and run this for example (or use TlbExp):

c:\MyProject\ClassLibrary2\bin\Debug>tlbexp ClassLibrary2.dll
Microsoft (R) .NET Framework Assembly to Type Library Converter 4.8.3928.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Assembly exported to 'c:\MyProject\ClassLibrary2\bin\Debug\ClassLibrary2.tlb'

Here is what it look like in OleView:

enter image description here

If you try to reference this from a .NET project it will say:

---------------------------
Microsoft Visual Studio
---------------------------
A reference to 'c:\MyProject\ClassLibrary2\bin\Debug\ClassLibrary2.tlb' could not be added. Please make sure that the file is accessible, and that it is a valid assembly or COM component.
---------------------------
OK   
---------------------------

This is really dumb IMHO. Anyway, so, you have to modify the .tlb. Copy paste the OleView result in some test.idl text file, remove all those "custom" idl attributes, something like this:

[
  uuid("your tlb guid here"),
  version(1.0),

]
library ClassLibrary2 // some name or keep ClassLibrary2
{
    importlib("mscorlib.tlb");
    importlib("stdole2.tlb");

    [
      odl,
      uuid(EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F), // your interface id
      version(1.0),
      dual,
      oleautomation,

    ]
    interface IComClass1 : IDispatch { // if the interface is IDispatch
        [id(0x60020000)]
        HRESULT Do();
    };
};

Now run the MIDL compiler on the test.idl file:

c:\MyProject\ClassLibrary2\ClassLibrary1>midl test.idl
Microsoft (R) 32b/64b MIDL Compiler Version 8.01.0622
Copyright (c) Microsoft Corporation. All rights reserved.
Processing .\test.idl
test.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um\oaidl.idl
oaidl.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um\objidl.idl
objidl.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um\unknwn.idl
unknwn.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared\wtypes.idl
wtypes.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared\wtypesbase.idl
wtypesbase.idl
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared\basetsd.h
basetsd.h
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared\guiddef.h
guiddef.h
Processing C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um\oaidl.acf
oaidl.acf

This will create a .TLB then you can now reference in both your projects using standard .NET tooling. Note the types it contains should be automatically embedded in your assemblies, no need to ship the .tlb or any other binary.

Sign up to request clarification or add additional context in comments.

6 Comments

In the client I get error: Could not resolve COM reference "cca3d0bd-33f0-4ffb-8d67-a24d1e596e2e" version 1.0. The type library importer encountered an error during type verification. Try importing without class members.
Please check the new .tld generated by midl in oleview, It still contains a custom idl attributes.
Not all custom attributes are problematic "DE77BA64-517C-11D1-A2DA-0000F8773CE9" is fine (from MIDL). Only .NET attributes must be removed (like "0F21F359-AB84-41E8-9A78-36D110E6D2F9", or GUID_ManagedName). I tested and works fine for me. Sometimes, the VS add reference UI doesn't work. Try tlbimp test2.tlb to create an interop .dll and reference that instead.
Unfortunately, I made a lot of different attempts and did not succeed.
Can you post your idl somewhere
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.