I am trying to add a third-party .NET Framework 3.5 DLL with a WinForms control to my unmanaged Visual C++ MFC application. Therefore, I have built a C# com-interop-wrapper DLL, which is registered as an ActiveX control.
It is working well, but exiting the MFC container application leads to an access exception every time.
Exception thrown at 0x7B7E13C7 (mscorwks.dll) in MFCApplication2.exe: 0xC0000005: Access violation reading location 0xDDDDDDE5.
The error occurs only, if I add an interface for events, i.e. if I add the attribute ComSourceInterface. The underneath example would work well without the line [ComSourceInterfaces(typeof(IUserControlEvents))]
.
Here is the minimal example:
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("", libid.ToString("B"));
}
using (RegistryKey subkey = interfaceRegisterKey.CreateSubKey("ProxyStubClsid32"))
{
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)
{
}
}
}
}
In my MFC application, I have placed the ActiveX control in the main dialog with "Insert ActiveX control". It is not necessary to add a control variable with class wizard. Just add the control in the editor, and the exception will already appear.
Most of the time the exception occurs in the method SafeReleaseHelper of mscorwks.dll:
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, IRpcStubBuffer * pStub, 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, IRpcStubBuffer * pStub, CServerChannel * pChnl, tagIPIDEntry * pIPIDEntry, unsigned long * pdwFault) Line 113 C++
[Inline Frame] combase.dll!AppInvoke(ServerCall * pStub, CServerChannel *) Line 1122 C++
combase.dll!ComInvokeWithLockAndIPID(ServerCall * pServerCall, tagIPIDEntry * pIPIDEntry, 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 dwTimeout, unsigned long cHandles, void * * pHandles, unsigned long * lpdwindex) Line 122 C++
mscorwks.dll!NT5WaitRoutine(int,unsigned long,int,void * *,int) Unknown
mscorwks.dll!MsgWaitHelper(int,void * *,int,unsigned long,int) Unknown
mscorwks.dll!Thread::DoAppropriateAptStateWait(int,void * *,int,unsigned long,enum WaitMode) Unknown
mscorwks.dll!Thread::DoAppropriateWaitWorker(int,void * *,int,unsigned long,enum WaitMode) Unknown
mscorwks.dll!Thread::DoAppropriateWait(int,void * *,int,unsigned long,enum WaitMode,struct PendingSync *) Unknown
mscorwks.dll!CLREvent::WaitEx(unsigned long,enum WaitMode,struct PendingSync *) Unknown
mscorwks.dll!CLREvent::Wait(unsigned long,int,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
lpUnk->Release()
isSystem.Runtime.InteropServices.Marshal.ReleasComObject(some_managed_COM_object);
In your code you probably do other things like subscribe to events for the control. You could unsubscribe events. Before shutdown, you could try to make sure all your runtime objects that are COM are null and force a garbage collection cycle. – Joseph Willcoxson