在我的一个项目中,我有一个使用c++DLL的c#应用程序。目前,在客户端PC上,我们正在注册表中的COM组件上注册C++DLL,以便在C#中使用它们。
我在NET上了解到,微软提供了一个Reg Free解决方案,链接为http://msdn.microsoft.com/en-us/library/ms973913.aspx
但在阅读后,我并没有得到太多线索,因为我的应用程序架构与以下不同
- 我有两个C++dll,让我们说CPForms.dll和Rules.dll
- Rule.dll包含在CPForms.dll中
- 我有一个C#dll,比方说ConsumeForm.dll,它正在使用CPForms.dll
- 我有另一个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。
我的问题是如何生成清单文件?在我的场景中,它将如何工作?
注意:考虑使用此处答案中的清单来获得更好的解决方案:
- 如何在不注册的情况下在.NET(C#)中使用AutoItX
如何在没有清单的情况下进行操作
COM注册表是一个服务,对于inproc服务器来说,这是可选的。
同样:您不必注册对象即可创建它。如果它未注册(并且不在清单中),则无法使用CoCreateInstance
创建它,因为注册表(或清单)是告诉CoCreateInstance
加载哪个DLL的。
但是,您可以使用LoadLibrary
、GetProcAddress
、DllGetClassObject
和IClassFactory::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
实现依赖于类型库。如果特定对象是这种情况,那么GetIDsOfNames
和GetTypeInfo
将失败,并且您将无法对该对象使用后期绑定方法。这包括在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对象也是类似的。