1
votes

We have a legacy C++ COM DLL that defines a struct in the IDL.
Simplified version of the IDL contains:

typedef struct 
{
    int num;
} LegacyStruct;

interface ILegacyInterface : IUnknown
{
    HRESULT GetStruct( [in,out] LegacyStruct* pVal );
}

We now need to define a .Net C# COM-visible assembly that implements ILegacyInterface.

In a C# project we add a reference to the legacy COM DLL and define a class that implements this interface:

[ComVisible( true )]
public class CSClass : ILegacyInterface
{
    public void GetStruct( ref LegacyStruct pVal )
    {
        ....
    }
}

The goal is to then utilize this COM-exposed C# assembly class in a C++ COM client program. This program should be able to use both the legacy COM DLL and the new C# assembly classes that implement ILegacyInterface.

When compiling, the following warning is displayed:
Type library exporter warning processing 'CSClass.GetStruct(pVal)'. warning : Non COM visible value type 'LegacyStruct' is being referenced either from the type currently being exported or from one of its base types.

As a result of LegacyStruct being non-COM visible, the resulting assembly's .tlb does not have GetStruct() method exposed (i.e., when viewed using oleview).
Obviously the C++ COM client does not compile:
error C2039: 'GetStruct' : is not a member of 'CSClass'

Is there a way to ensure that LegacyStruct, which is defined in the legacy C++ COM DLL, is properly exposed when utilized in a C# COM-visible .Net assembly's methods?

2
You should really use C++/CLI wrapper class to communicate to the COM.Indy9000
Can you post the non-simplified IDL for that structure?tcarvin
non-simplified version of the structure is pretty simple as well... typedef struct { long days; long msecs; long zone; } LegacyStruct;alexg
The structure does NOT have a uuid associated with it, but I tried with and without a uuid. The results are the same, unfortunately...alexg
Please post C# declaration for LegacyStructSimon Mourier

2 Answers

3
votes

To solve this issue the following 2 items are required:

  1. The legacy COM IDL must include the uuid for the structure being defined. This was mentioned above by tcarvin. In addition to the uuid the structure tag name must be identical to the structure name. Leaving the tag out will not be sufficient, i.e., even if the uuid is present. Here's the new structure definition:

    typedef [uuid(XXX-YYY-ZZZ-AAA-BBB)]
    struct LegacyStruct
    {
    int num;
    } LegacyStruct;

    Not having the uuid associated with the structure will include a copy of its definition with an auto-generated uuid in the resulting .Net assembly. This will obviously be a totally different structure as far as COM is concerned with the same name.

  2. When the legacy COM DLL was added as a reference to the C# project, it is important to set "Embed Interop Types" property to False. This will also ensure that the legacy COM DLL's definitions, e.g., structs, etc., are not included with the resulting .Net assembly.

1
votes

I realize this doesn't strictly answer your question as to why this is happening, but I've run into similar problems with tlbimp and I've learned to just avoid it.

I've generally found that the way .Net projects automatically import TLBs is too restrictive. A way you can work around this is to redeclare your IDL contents in a C# file with all the appropriate ComInterface, Guid, and CoClass attributes.

tlbimp+reflector is also a good way to generate the skeleton for these declarations. If you look at the decompiled results of what tlbimp does you can see what attributes you expect to be on the .Net declarations that are missing, and that may help you figure out what's happening.