Accessing COM Interface from C++ COM Server that d

2019-08-05 07:50发布

问题:

I've been asked to integrate with an already-shipped product that is a COM server. Along with this product was provided a .tlb file. I've used tlbimp to export the tlb to primary assembly and I've included that in my C# project. When I attempt to execute my COM client I receive "No interface found" errors, because the application never registered its typelib in the registry. I've attempted to run "server.exe /RegServer" per the MSDN docs. It executes but doesn't seem to update or add anything in the registry.

I was able to get past this problem by forcing the typelib to register via regtlibv12.exe, but I've read on this site that that executable is inconsistent and may be deprecated.

That link also says, in regards to regtlibv12.exe:

it is a workaround for COM servers that don't register their type library. Similar as above, but registering type libraries is optional and typically only required on your dev machine, not on the machine that runs the server.

If server registration is option, then I can't seem to find a way to get around it. I've tried manually defined the interface in C# classes via System.Runtime.InteropServices, but it still doesn't work unless the library items from the TLB are defined in the registry.

So, is there a way to get around a COM Server that doesn't register (and for which I have no code) in C#, without executing some application to force the TLB to be registered?

EDIT: Here is the IDL I exported from the TLB via oleview. The Root UUID is found in the typelib section of the registry as a Primary Interop Assembly (I assume due to the tlbimp call), but the library items themselves doesn't have any references anywhere. Searching for the Concrete Class or Interface UUID in the registry comes back blank.

        [
      uuid(C8CF03E5-FD1F-11D3-8C03-0080C8D3C5D3),
      version(1.0),
      custom(DE77BA64-517C-11D1-A2DA-0000F8773CE9, 100663657),
      custom(DE77BA63-517C-11D1-A2DA-0000F8773CE9, 1153317527),
      custom(DE77BA65-517C-11D1-A2DA-0000F8773CE9, "Created by MIDL version 6.00.0361 at Wed Jul 19 09:58:45 2006
    ")

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

        // Forward declare all types defined in this typelib
        dispinterface IEFilm;

        [
          uuid(C8CF03E6-FD1F-11D3-8C03-0080C8D3C5D3)
        ]
        dispinterface IEFilm {
            properties:
            methods:
                [id(0x00000001)]
                VARIANT_BOOL oleOpenStudy(
                                BSTR strPatientID, 
                                BSTR strAccessionNo, 
                                VARIANT_BOOL bCloseCurWindow, 
                                VARIANT_BOOL bAddToWindow, 
                                short nSeriesRows, 
                                short nSeriesCols, 
                                short nImageRows, 
                                short nImageCols, 
                                VARIANT_BOOL bAutoSeriesFormat, 
                                VARIANT_BOOL bAutoImageFormat);
                [id(0x00000002)]
                VARIANT_BOOL oleShowMainWindow(long nCmdShow);
                [id(0x00000003)]
                VARIANT_BOOL oleShowSearchWindow(long nCmdShow);
                [id(0x00000004)]
                VARIANT_BOOL olePositionMainWindow(
                                short left, 
                                short top, 
                                short right, 
                                short bottom);
                [id(0x00000005)]
                VARIANT_BOOL oleSetForegroundWindow();
                [id(0x00000006)]
                VARIANT_BOOL oleOpenSearchRemote(
                                BSTR strPatientID, 
                                BSTR strAccessionNo);
                [id(0x00000007)]
                VARIANT_BOOL oleOpenStudy2(
                                BSTR strPatientID, 
                                BSTR strAccessionNo, 
                                VARIANT_BOOL bCloseCurWindow, 
                                VARIANT_BOOL bAddToWindow, 
                                short nSeriesRows, 
                                short nSeriesCols, 
                                short nImageRows, 
                                short nImageCols, 
                                VARIANT_BOOL bAutoSeriesFormat, 
                                VARIANT_BOOL bAutoImageFormat, 
                                BSTR strImageSource);
                [id(0x00000008)]
                VARIANT_BOOL oleCloseAllWindows();
                [id(0x00000009)]
                VARIANT_BOOL oleCloseCurrentWindow();
                [id(0x0000000a)]
                VARIANT_BOOL oleOpenStudy3(
                                BSTR strPatientID, 
                                BSTR strAccessionNo, 
                                BSTR strStudyInstanceUID, 
                                VARIANT_BOOL bCloseCurWindow, 
                                VARIANT_BOOL bAddToWindow, 
                                short nSeriesRows, 
                                short nSeriesCols, 
                                short nImageRows, 
                                short nImageCols, 
                                VARIANT_BOOL bAutoSeriesFormat, 
                                VARIANT_BOOL bAutoImageFormat, 
                                BSTR strImageSource);
                [id(0x0000000b)]
                VARIANT_BOOL oleSearch(
                                BSTR searchParams, 
                                VARIANT* searchResults, 
                                BSTR lpszImageSource);
                [id(0x0000000c)]
                VARIANT_BOOL oleOpenSearch(
                                BSTR strPatientID, 
                                BSTR strAccessionNo, 
                                BSTR lpszImageSource);
                [id(0x0000000d)]
                VARIANT_BOOL oleLoginViaDomain(
                                BSTR strUsername, 
                                BSTR strPassword, 
                                BSTR strDomainName);
                [id(0x0000000e)]
                VARIANT_BOOL oleLoginViaFusion(
                                BSTR strUsername, 
                                BSTR strPassword, 
                                BSTR strWSDLFile);
                [id(0x0000000f)]
                VARIANT_BOOL oleLoginViaFusionWithToken(
                                BSTR strUsername, 
                                BSTR strToken, 
                                BSTR strWSDLFile);
                [id(0x00000010)]
                VARIANT_BOOL oleSaveCurrentUserProfile();
                [id(0x00000011), helpstring("method oleOpenStudy4")]
                VARIANT_BOOL oleOpenStudy4(
                                BSTR strOpenStudyInfoXML, 
                                VARIANT_BOOL bCloseCurWindow, 
                                VARIANT_BOOL bFindRelatedStudies, 
                                unsigned long nNumPriors, 
                                BSTR strProtocolListXML);
                [id(0x00000012), helpstring("method oleExportAsBitmap")]
                VARIANT_BOOL oleExportAsBitmap(
                                BSTR destinationDirectory, 
                                short bitmapFormat);
                [id(0x00000013), helpstring("method oleLogout")]
                VARIANT_BOOL oleLogout();
                [id(0x00000014), helpstring("method oleOpenStudy5")]
                VARIANT_BOOL oleOpenStudy5(
                                BSTR strOpenStudyInfoXML, 
                                short nRows, 
                                short nCols, 
                                short nImageRows, 
                                short nImageColumns, 
                                VARIANT_BOOL bShowStudyManager, 
                                VARIANT_BOOL bCloseCurWindow, 
                                VARIANT_BOOL bFindRelatedStudies, 
                                unsigned long nNumPriors, 
                                VARIANT_BOOL bApplyHP, 
                                BSTR strProtocolListXML);
                [id(0x00000015), helpstring("method oleLock")]
                VARIANT_BOOL oleLock();
                [id(0x00000016), helpstring("method oleUnlock")]
                VARIANT_BOOL oleUnlock();
                [id(0x00000017), helpstring("method oleIsLocked")]
                VARIANT_BOOL oleIsLocked();
                [id(0x00000018), helpstring("method oleSelectServers")]
                VARIANT_BOOL oleSelectServers(
                                BSTR strImageSourceGUID, 
                                BSTR strServerList);
        };

        [
          uuid(C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3)
        ]
        coclass Document {
            [default] dispinterface IEFilm;
        };
    };

EDIT2:

I've loaded up a VM and tracked the changes. During initial server self-registration, the only registration that happens is this:

    [HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}]
@="EFilm Document"

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\AuxUserType]
@=""

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\AuxUserType\2]
@="EFilm"

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\AuxUserType\3]
@="eFilm"

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\DefaultIcon]
@="C:\\PROGRA~1\\MERGEH~1\\eFilm\\eFilm.exe,1"

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\InprocHandler32]
@="ole32.dll"

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\Insertable]
@=""

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\LocalServer32]
@="C:\\PROGRA~1\\MERGEH~1\\eFilm\\eFilm.exe"

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\MiscStatus]
@="32"

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\ProgId]
@="EFilm.Document"

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\verb]
@=""

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\verb\0]
@="&Edit,0,2"

[HKEY_CLASSES_ROOT\CLSID\{C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3}\verb\1]
@="&Open,0,2"

When I execute regtlibv12.exe it adds the following registry entries:

    [HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{C8CF03E5-FD1F-11D3-8C03-0080C8D3C5D3}\1.0]
@="EFilm"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{C8CF03E5-FD1F-11D3-8C03-0080C8D3C5D3}\1.0\FLAGS]
@="0"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{C8CF03E5-FD1F-11D3-8C03-0080C8D3C5D3}\1.0\0\win32]
@="C:\\eFilm\\eFilm.tlb"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{C8CF03E5-FD1F-11D3-8C03-0080C8D3C5D3}\1.0\HELPDIR]
@="C:\\eFilm"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{C8CF03E6-FD1F-11D3-8C03-0080C8D3C5D3}]
@="IEFilm"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{C8CF03E6-FD1F-11D3-8C03-0080C8D3C5D3}\ProxyStubClsid]
@="{00020420-0000-0000-C000-000000000046}"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{C8CF03E6-FD1F-11D3-8C03-0080C8D3C5D3}\ProxyStubClsid32]
@="{00020420-0000-0000-C000-000000000046}"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{C8CF03E6-FD1F-11D3-8C03-0080C8D3C5D3}\TypeLib]
@="{C8CF03E5-FD1F-11D3-8C03-0080C8D3C5D3}"
"Version"="1.0"

Without those my client does not run. Technically it's just the HKLM\software\classes\interface lines that are required. Even if I don't run regtlibv12.exe and manually enter these 4 lines it will start functioning. So, is there a way I can make this work without adding these lines to the registry?

回答1:

So, is there a way I can make this work without adding these lines to the registry?

No, an out-of-process server always requires the Interface keys to be present. Necessary so that Windows can figure out how to serialize the arguments of a method call from the client process to the server process.

A COM server that was written in C++ typically does not depend on type library marshaling, it normally uses a dedicated proxy/stub DLL. Which is easy to generate from the IDL, the midl tool auto-generates the source code for the DLL. You'll have to find that DLL and use Regsvr32.exe to get it to register itself and write the Interface keys. There are few breadcrumbs to give you to find that DLL, other than that it will be small and maybe has "ps" in the filename.

Using regtlibv12.exe or writing these keys yourself is otherwise a valid workaround. The interface that the COM server exposes is in fact compatible with type library marshaling. It is a simple late-bound interface based on IDispatch that strictly uses Automation compatible types so you should not have any problem. Using a dedicated proxy/stub is faster but that just doesn't matter anymore when you use slow late-binding.

Be careful taking a dependency on this server, it is very old. Pretty unlikely you can still get support for it if you have a problem. Like this one.