如何解决处理嵌入在 Visual C++ (MFC) 应用程序中的 .NET-ActiveX-Control
我正在尝试将带有 WinForms 控件的第三方 .NET Framework 3.5 DLL 添加到我的非托管 Visual C++ MFC 应用程序中。因此,我构建了一个 C# com-interop-wrapper DLL,它注册为 ActiveX 控件。
运行良好,但每次退出 MFC 容器应用程序都会导致访问异常。
在 MFCApplication2.exe 中的 0x7B7E13C7 (mscorwks.dll) 处抛出异常:0xC0000005:访问冲突读取位置 0xddddDDE5。
该错误仅发生在为事件添加接口时,即添加属性 ComSourceInterface 时。下面的示例在没有行 [ComSourceInterfaces(typeof(IUserControlEvents))]
的情况下也能正常工作。
这是最小的例子:
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Reflection;
namespace WindowsFormsControlLibrary1
{
[InterfaceType(ComInterfaceType.InterfaceIsIdispatch)]
[ComVisible(true)]
[Guid("F80C042C-ABEA-458D-96E0-F1A4DD620A72")]
public interface IUserControl
{
[dispId(1)]
void testMethod();
}
[InterfaceType(ComInterfaceType.InterfaceIsIdispatch)]
[ComVisible(true)]
[Guid("CFF6DA90-2B8A-4D57-A4B8-581A47BA5226")]
public interface IUserControlEvents
{
[dispId(2)]
void click();
}
[ProgId("WindowsFormsControlLibrary1.UserControl1")]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IUserControl))]
[ComSourceInterfaces(typeof(IUserControlEvents))]
[ComVisible(true)]
[Guid("E5A1E243-AFD2-4443-8A54-D5FE9A8633C8")]
public partial class UserControl1: UserControl,IUserControl
{
[ComVisible(true)]
public delegate void UserControlClick();
public event UserControlClick click;
[ComVisible(true)]
public void testMethod()
{
MessageBox.Show("hallo");
}
public UserControl1()
{
InitializeComponent();
}
private void button1_Click(object sender,EventArgs e)
{
if (click != null)
click();
}
[ComregisterFunction()]
public static void RegisterClass(string i_Key)
{
i_Key = i_Key.Replace(@"HKEY_CLASSES_ROOT\","");
// open the CLSID\{guid} key for write access
using (RegistryKey clsidRegisterKey = Registry.ClassesRoot.CreateSubKey(i_Key))
{
// and create the 'Control' key - this allows it to show up in
// the ActiveX control container
clsidRegisterKey.CreateSubKey("Control").Close();
// next create the CodeBase entry - needed if not string named and GACced.
using (RegistryKey inprocServer32 = clsidRegisterKey.CreateSubKey("InprocServer32"))
{
inprocServer32.SetValue("CodeBase",Assembly.GetExecutingAssembly().CodeBase);
}
using (RegistryKey miscStatus = clsidRegisterKey.CreateSubKey("MiscStatus"))
{
//??????
miscStatus.SetValue("",0x20191);//0x00000801
}
using (RegistryKey typeLib = clsidRegisterKey.CreateSubKey("TypeLib"))
{
Guid libid = Marshal.GetTypeLibGuidForAssembly(typeof(UserControl1).Assembly);
typeLib.SetValue("",libid.ToString("B"));
}
using (RegistryKey version = clsidRegisterKey.CreateSubKey("Version"))
{
Marshal.GetTypeLibVersionForAssembly(typeof(UserControl1).Assembly,out int major,out int minor);
version.SetValue("",$"{major}.{minor}");
}
}
using (RegistryKey interfaceRegisterKey = Registry.ClassesRoot.CreateSubKey(@"Interface\" + typeof(IUserControl).GUID.ToString("B")))
{
interfaceRegisterKey.SetValue("","IUserControl");
using (RegistryKey subkey = interfaceRegisterKey.CreateSubKey("TypeLib"))
{
Guid libid = Marshal.GetTypeLibGuidForAssembly(typeof(IUserControl).Assembly);
subkey.SetValue("",libid.ToString("B"));
}
using (RegistryKey subkey = interfaceRegisterKey.CreateSubKey("ProxyStubClsid32"))
{
subkey.SetValue("","{00020420-0000-0000-C000-000000000046}");
}
}
using (RegistryKey interfaceRegisterKey = Registry.ClassesRoot.CreateSubKey(@"Interface\" + typeof(IUserControlEvents).GUID.ToString("B")))
{
interfaceRegisterKey.SetValue("","IUserControlEvents");
using (RegistryKey subkey = interfaceRegisterKey.CreateSubKey("TypeLib"))
{
Guid libid = Marshal.GetTypeLibGuidForAssembly(typeof(IUserControlEvents).Assembly);
subkey.SetValue("","{00020420-0000-0000-C000-000000000046}");
}
}
}
[ComUnregisterFunction()]
public static void UnregisterClass(string i_Key)
{
i_Key = i_Key.Replace(@"HKEY_CLASSES_ROOT\","");
try
{
Registry.ClassesRoot.DeleteSubKeyTree(i_Key);
}
catch (ArgumentException e)
{
}
try
{
Registry.ClassesRoot.DeleteSubKeyTree(@"Interface\" + typeof(IUserControl).GUID.ToString("B"));
}
catch (ArgumentException e)
{
}
try
{
Registry.ClassesRoot.DeleteSubKeyTree(@"Interface\" + typeof(IUserControlEvents).GUID.ToString("B"));
}
catch (ArgumentException e)
{
}
}
}
}
在我的 MFC 应用程序中,我将 ActiveX 控件放置在带有“插入 ActiveX 控件”的主对话框中。没有必要使用类向导添加控制变量。只需在编辑器中添加控件,异常就会出现。
大多数时候异常发生在mscorwks.dll的SafeReleaseHelper方法中:
mscorwks.dll!SafeReleaseHelper(struct IUnkNown *,struct RCW *) UnkNown
mscorwks.dll!SafeRelease(struct IUnkNown *,struct RCW *) UnkNown
mscorwks.dll!IUnkEntry::Free(struct RCW *,int) UnkNown
mscorwks.dll!RCW::ReleaseAllInterfaces(void) UnkNown
mscorwks.dll!RCW::ReleaseAllInterfacesCallBack(void *) UnkNown
mscorwks.dll!RCW::Cleanup(void) UnkNown
mscorwks.dll!RCWCleanupList::ReleaseRCWListRaw(struct RCW *) UnkNown
mscorwks.dll!RCWCleanupList::ReleaseRCWListInCorrectCtx(void *) UnkNown
mscorwks.dll!CtxEntry::EnterContextCallback(struct tagComCallData *) UnkNown
combase.dll!CRemoteUnkNown::DoCallback(tagXAptCallback * pCallbackData) Line 1882 C++
rpcrt4.dll!_Invoke@12() UnkNown
rpcrt4.dll!_NdrStubCall2@16() UnkNown
combase.dll!CStdStubBuffer_Invoke(IRpcStubBuffer * This,tagRPCOLEMESSAGE * prpcmsg,IRpcChannelBuffer * pRpcChannelBuffer) Line 1531 C++
[Inline Frame] combase.dll!InvokeStubWithExceptionPolicyAndTracing::__l6::<lambda_ee1df801181086a03fa4f8f75bd5617f>::operator()() Line 1279 C++
combase.dll!ObjectMethodExceptionHandlingAction<<lambda_ee1df801181086a03fa4f8f75bd5617f>>(InvokeStubWithExceptionPolicyAndTracing::__l6::<lambda_ee1df801181086a03fa4f8f75bd5617f> action,ObjectMethodExceptionHandlingInfo * pExceptionHandlingInfo,ExceptionHandlingResult * pExceptionHandlingResult,void *) Line 87 C++
[Inline Frame] combase.dll!InvokeStubWithExceptionPolicyAndTracing(IRpcStubBuffer * pMsg,tagRPCOLEMESSAGE *) Line 1277 C++
combase.dll!DefaultStubInvoke(bool bIsAsyncBeginMethod,IServerCall * pServerCall,IRpcChannelBuffer * pChannel,IRpcStubBuffer * pStub,unsigned long * pdwFault) Line 1346 C++
[Inline Frame] combase.dll!SyncStubCall::Invoke(IServerCall *) Line 1403 C++
[Inline Frame] combase.dll!SyncServerCall::StubInvoke(IRpcChannelBuffer *) Line 780 C++
[Inline Frame] combase.dll!StubInvoke(tagRPCOLEMESSAGE * pMsg,CStdIdentity * pStdID,IRpcStubBuffer *) Line 1628 C++
combase.dll!ServerCall::ContextInvoke(tagRPCOLEMESSAGE * pMessage,CServerChannel * pChannel,tagIPIDEntry * pIPIDEntry,unsigned long * pdwFault) Line 1423 C++
[Inline Frame] combase.dll!CServerChannel::ContextInvoke(tagRPCOLEMESSAGE *) Line 1332 C++
[Inline Frame] combase.dll!DefaultInvokeInApartment(tagRPCOLEMESSAGE *) Line 3297 C++
combase.dll!ReentrantSTAInvokeInApartment(tagRPCOLEMESSAGE * pMsg,unsigned long dwCallCat,bool bIsTouchedASTACall,CServerChannel * pChnl,unsigned long * pdwFault) Line 113 C++
[Inline Frame] combase.dll!AppInvoke(ServerCall * pStub,CServerChannel *) Line 1122 C++
combase.dll!ComInvokeWithLockAndIPID(ServerCall * pServerCall,bool * pbCallerResponsibleForRequestMessageCleanup) Line 2210 C++
[Inline Frame] combase.dll!ComInvoke(ServerCall *) Line 1697 C++
[Inline Frame] combase.dll!Threaddispatch(ServerCall *) Line 414 C++
combase.dll!ThreadWndProc(HWND__ * window,unsigned int message,unsigned int wparam,long params) Line 740 C++
user32.dll!__InternalCallWinProc@20() UnkNown
user32.dll!UserCallWinProcCheckWow() UnkNown
user32.dll!dispatchMessageWorker() UnkNown
user32.dll!_dispatchMessageW@4() UnkNown
[Inline Frame] combase.dll!CclimodalLoop::MydispatchMessage(tagMSG *) Line 2989 C++
combase.dll!CclimodalLoop::PeekRPCAndDDEMessage() Line 2616 C++
combase.dll!CclimodalLoop::FindMessage(unsigned long dwStatus) Line 2706 C++
combase.dll!CclimodalLoop::HandleWakeForMsg() Line 2302 C++
combase.dll!CclimodalLoop::BlockFn(void * * ahEvent,unsigned long cEvents,unsigned long * lpdwSignaled) Line 2239 C++
combase.dll!ClassicSTAThreadWaitForHandles(unsigned long dwFlags,unsigned long dwTimeout,unsigned long cHandles,void * * pHandles,unsigned long * pdwIndex) Line 51 C++
combase.dll!CoWaitForMultipleHandles(unsigned long dwFlags,unsigned long * lpdwindex) Line 122 C++
mscorwks.dll!NT5WaitRoutine(int,unsigned long,int,void * *,int) UnkNown
mscorwks.dll!MsgWaitHelper(int,int) UnkNown
mscorwks.dll!Thread::DoAppropriateAptStateWait(int,enum WaitMode) UnkNown
mscorwks.dll!Thread::DoAppropriateWaitWorker(int,enum WaitMode) UnkNown
mscorwks.dll!Thread::DoAppropriateWait(int,enum WaitMode,struct PendingSync *) UnkNown
mscorwks.dll!CLREvent::WaitEx(unsigned long,struct PendingSync *) UnkNown
mscorwks.dll!CLREvent::Wait(unsigned long,struct PendingSync *) UnkNown
mscorwks.dll!_CorExitProcess@4() UnkNown
mscorwks.dll!WaitForEndOfShutdown(void) UnkNown
mscorwks.dll!EEShutDown(int) UnkNown
mscorwks.dll!disableRuntime(void) UnkNown
mscorwks.dll!_CorExitProcess@4() UnkNown
mscoreei.dll!RuntimeDesc::ShutdownAllActiveRuntimes(unsigned int,class RuntimeDesc *,enum RuntimeDesc::ShutdownCompatMode) UnkNown
mscoreei.dll!_CorExitProcess@4() UnkNown
mscoree.dll!_ShellShim_CorExitProcess@4() UnkNown
ucrtbased.dll!try_cor_exit_process(const unsigned int return_code) Line 98 C++
ucrtbased.dll!exit_or_terminate_process(const unsigned int return_code) Line 139 C++
ucrtbased.dll!common_exit(const int return_code,const _crt_exit_cleanup_mode cleanup_mode,const _crt_exit_return_mode return_mode) Line 280 C++
ucrtbased.dll!exit(int return_code) Line 293 C++
> MFCApplication2.exe!__scrt_common_main_seh() Line 297 C++
MFCApplication2.exe!__scrt_common_main() Line 331 C++
MFCApplication2.exe!wWinMainCRTStartup(void * __formal) Line 17 C++
kernel32.dll!@BaseThreadInitThunk@12() UnkNown
ntdll.dll!__RtlUserThreadStart() UnkNown
ntdll.dll!__RtlUserThreadStart@8() UnkNown
解决方法
发生的情况是 .NET 持有对某些本机 COM 指针(由 MFC 提供)的引用,因为已建立双向连接(事件)。
如果 .NET 引用的 MFC 对象首先被删除,当 .NET 想要释放其引用时(当发生不确定性的垃圾回收时),为时已晚,它会在流氓指针上调用 IUnknown->Release()。>
解决方案是调用 .NET 提供的原生方法:CoEEShutDownCOM 但如何调用它取决于 .NET Framework 版本。这是处理这两种情况的辅助方法:
#include "MetaHost.h"
HRESULT CoEEShutDownCOM()
{
typedef void(WINAPI* CoEEShutDownCOMfn)();
typedef HRESULT(WINAPI* CLRCreateInstanceFn)(REFCLSID,REFIID,LPVOID*);
HMODULE mscoree = GetModuleHandleW(L"mscoree.dll");
if (!mscoree)
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
CLRCreateInstanceFn createInstance = (CLRCreateInstanceFn)GetProcAddress(mscoree,"CLRCreateInstance");
if (createInstance)
{
// .NET 4+
ICLRMetaHost* host;
HRESULT hr = createInstance(CLSID_CLRMetaHost,IID_PPV_ARGS(&host));
if (FAILED(hr))
return hr;
IEnumUnknown* enumunk;
hr = host->EnumerateLoadedRuntimes(GetCurrentProcess(),&enumunk);
if (FAILED(hr))
{
host->Release();
return hr;
}
ICLRRuntimeInfo* info;
while (S_OK == enumunk->Next(1,(IUnknown**)&info,NULL))
{
CoEEShutDownCOMfn shutdown = NULL;
info->GetProcAddress("CoEEShutDownCOM",(LPVOID*)&shutdown);
if (shutdown)
{
shutdown();
}
info->Release();
}
enumunk->Release();
host->Release();
}
else
{
// other .NET
CoEEShutDownCOMfn shutdown = (CoEEShutDownCOMfn)GetProcAddress(mscoree,"CoEEShutDownCOM");
if (shutdown)
{
shutdown();
}
}
FreeLibrary(mscoree);
return S_OK;
}
必须在 MFC 应用程序销毁 ActiveX 控件之前调用它,例如在销毁对话框时:
class CMFCApplication3Dlg : public CDialogEx
{
...
protected:
afx_msg void OnDestroy();
};
BEGIN_MESSAGE_MAP(CMFCApplication3Dlg,CDialogEx)
...
ON_WM_DESTROY()
END_MESSAGE_MAP()
void CMFCApplication3Dlg::OnDestroy()
{
CoEEShutDownCOM();
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。