如何在dotnet中使用免注册的COM dll

本文关键字:注册 COM dll dotnet | 更新日期: 2023-09-27 18:24:05

在我的一个项目中,我有一个使用c++DLL的c#应用程序。目前,在客户端PC上,我们正在注册表中的COM组件上注册C++DLL,以便在C#中使用它们。

我在NET上了解到,微软提供了一个Reg Free解决方案,链接为http://msdn.microsoft.com/en-us/library/ms973913.aspx

但在阅读后,我并没有得到太多线索,因为我的应用程序架构与以下不同

  1. 我有两个C++dll,让我们说CPForms.dll和Rules.dll
  2. Rule.dll包含在CPForms.dll中
  3. 我有一个C#dll,比方说ConsumeForm.dll,它正在使用CPForms.dll
  4. 我有另一个C#Exe,它正在使用ConsumeForm.dll

我的客户端只打开C#Exe,它反过来调用ConsumerForm.dll,后者进一步调用显示C++窗体(UI)的CPForms.dll,当用户使用C++Rules.dll在内部单击该按钮时,会有按钮验证。目前我正在注册表中注册两个C++dll。

现在客户端只希望Rule.dll被引用为RegFree安装,因为Rule.dll经常更改,客户端不想使用Admin帐户一次又一次地注销和注册。

除此之外,该客户端可以注册CPForms.dll。

我的问题是如何生成清单文件?在我的场景中,它将如何工作?

如何在dotnet中使用免注册的COM dll

注意:考虑使用此处答案中的清单来获得更好的解决方案:

  • 如何在不注册的情况下在.NET(C#)中使用AutoItX

如何在没有清单的情况下进行操作

COM注册表是一个服务,对于inproc服务器来说,这是可选的

同样:您不必注册对象即可创建它。如果它未注册(并且不在清单中),则无法使用CoCreateInstance创建它,因为注册表(或清单)是告诉CoCreateInstance加载哪个DLL的。

但是,您可以使用LoadLibraryGetProcAddressDllGetClassObjectIClassFactory::CreateInstance创建它。

(请注意,您将无法获得任何COM+服务,也无法以这种方式创建进程外对象,或线程模型与创建线程不兼容的对象。如果您不要求COM为您做这件事,这些事情就会成为您的问题)。

CoCreateInstance提供的服务是为LoadLibrary的调用定位正确的DLL,并只为您调用其他函数。(检查线程模型是否兼容,在适当的线程上创建,如果不兼容,则使用CoMarshalInterthreadInterfaceInStream/CoUnmarshalInterfaceAndReleaseStream对接口进行封送。这听起来工作量很大,但如果你只有一个STA therad,你几乎可以忽略所有问题。)

像这样的东西应该可以做到:

// Really you should break this up int GetClassFactoryFromDLL, then reuse the class factory.
// But this is all from memory...
// Load CLSID_MyID, from DLL pszMyDllPath
typedef HRESULT __stdcall (*_PFNDLLGETCLASSOBJECT)(
  __in   REFCLSID rclsid,
  __in   REFIID riid,
  __out  LPVOID *ppv
) PFNDLLGETCLASSOBJECT;
HRESULT CreateInstanceFromDll(LPCTSTR pszMyDllPath, CLSID clsidMyClass, IUknown** ppunkRet)
{
    // Handle bad callers
    *ppunkRet = NULL;
    HANDLE hDLL = LoadLibrary(pszMyDllPath);
    if(hDLL == NULL)
    {
        // Failed to load
        return HRESULT_FROM_NTSTATUS(GetLastError());
    }
    PFNDLLGETCLASSOBJECT pfnDllGetClassObject = GetProcAddress(hDLL);
    if(pfnDllGetClassObject == NULL)
    {
        // Not a COM dll
        HRESULT hrRet = HRESULT_FROM_NTSTATUS(GetLastError());
        FreeLibrary(hDLL);hDLL = NULL;
        return hrRet;
    }
    IClassFactory* pClassFactory = NULL;
    HRESULT hr = pfnDllGetClassObject(clsidMyClass, IID_IClassFactory, &pClassFactory);
    if(FAILED(hr)){
        FreeLibrary(hDLL);hDLL = NULL;
        return hr;
    }
    hr = pClassFactory->CreateInstance(NULL, IID_IUnknown, &ppunkRet);
    pClassFactory->Release();
    if(FAILED(hr))
    {
        *ppunkRet = NULL;
        FreeLibrary(hDLL);
        return hr;
    }
    return hr;
}

注意:这将允许您创建对象。但是,如果您调用的对象本身依赖于注册,它将无法正常工作。

特别是,许多IDispatch实现依赖于类型库。如果特定对象是这种情况,那么GetIDsOfNamesGetTypeInfo将失败,并且您将无法对该对象使用后期绑定方法。这包括在C#中使用dynamic,以及Python等动态语言。

然而,即使IDispatch方法不起作用,其他方法(如双接口方法和不继承自IDispatch的接口)也可以很好地工作。

一句话:为了让免注册COM工作,被实例化的对象不能依赖于它自己的注册。

对于那些在搜索Registration free COM dll in dot net时会跳到这个问题的人,我能够在这个答案中从C#以Reg Free的方式使用AutoItX COM对象https://stackoverflow.com/a/19996424/173073.创建和使用其他Reg Free对象也是类似的。