C# .NET User Control inside native app. Resource chain problems
Asked Answered
C

1

12

I am wrapping up an MFC extension DLL (MFCXDLL_2) to make its functionality available for C# programmers.

The wrapper is a “Regular DLL using shared MFC DLL” with “Common Language Runtime Support (/clr)”. (Mixed mode).

The classes in MFCXDLL_2 that shall be available are decorated in MFCXDLL_3.

The scenario I am experiencing is the case where MFCXDLL_2 is used from a C# .NET user control running in a native application.

Another MFC Extension DLL -MFCXDLL_1-inside the native application also uses the MFCXDLL_2 and this cause trouble.

When I start the native application it will implicitly load the MFCXDLL_2.

When I load the .NET user control, the same MFCXDLL_2 is loaded again explicitly according to the advice in http://msdn.microsoft.com/en-us/library/ksa99t88.aspx, “Using Database, OLE, and Sockets Extension DLLs in Regular DLLs”.

Both the native code and the .NET user control instantiate the same class type and call the same method in the MFCXDLL_2.

The method de-serializes data (received over shared memory) and returns the de-serialized data to the caller. This works great from native code until I load the .NET user control.

After loading the .NET user control, de-serializing stops working from native code but it works great when called from the .NET user control.

I attached WinDbg to the debug version of the native application and run my scenario. WinDbg found as follows during de-serialization:

“Warning: Cannot load from archive. Class not defined. CArchive exception: badClass.”

I think there are some resource issues here so I run the release version of the native application loading the release version of MFCXDLL_2. Then I load the debug version of the .NET user control –which again loads the debug version of MFCXDLL_2- into the native application.

Then everything just works great. One release version of the MFCXDLL_2 loaded by the native code and one debug version of MFCXDLL_2 loaded by the .NET user control –all running inside the native application.

So what is happening? Is it not possible to access the same MFCXDLL from e.g. an extension DLL and a regular DLL at the same time in the same application?
Is the resource chain destroyed in some way? What are the possible solutions?

Here is some code showing how the MFCXDLL_2 DLL is loaded
When the native application starts MFCXDLL_2 DLLMain is called:

static AFX_EXTENSION_MODULE MFCXDLL_2 = { NULL, NULL };
static CDynLinkLibrary* gpDynLinkLibrary = NULL;

extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID )
{
  if (dwReason == DLL_PROCESS_ATTACH)
  {
        // Extension DLL one-time initialization
        AfxInitExtensionModule(MFCXDLL_2, hInstance);

        // Insert this DLL into the resource chain
        gpDynLinkLibrary = new CDynLinkLibrary(MFCXDLL_2);
  }
  else if (dwReason == DLL_PROCESS_DETACH)
  {
        if (gpDynLinkLibrary)
        {
              delete gpDynLinkLibrary;
              gpDynLinkLibrary = NULL;
        }
        // Terminate the library before destructors are called
        AfxTermExtensionModule(MFCXDLL_2);
  }
  return 1;   // ok
}

When the .NET user control is loaded, the MFCXDLL_2 DLL is loaded again:

//==============================================================
// Exported DLL initialization to run in context of Regular DLL.
// Must be called in InitInstance
// BOOL CYourRegularDLLApp::InitInstance()
//==============================================================
extern "C" _declspec(dllexport) CDynLinkLibrary* WINAPI InitMFCXDLL_2FromRegularDLL()
{
    if (gpDynLinkLibrary)
    {
        delete gpDynLinkLibrary;
        gpDynLinkLibrary = NULL;
     }
     // Create a new CDynLinkLibrary for this Regular DLL
     return new CDynLinkLibrary(MFCXDLL_2);
}

The deserialize code inside MFCXDLL_2

    CMyClass* pMyclass = NULL; //CObject derived serializeable class
    BYTE *pBuf      = pGlobalCom->GetBuffer(); //Buffer with serialized CMyClass
    int nBufSize    = pGlobalCom->GetSize();   //Size of buffer

    CMemFile mf;
    mf.Attach(pBuf,nBufSize);

    CArchive ar(&mf, CArchive::load); //“Warning: Cannot load CMyClass from archive.  Class not defined.CArchive exception: badClass.”

    ar >> pMyclass; //CArchive exception thrown
    ar.Close();
    mf.Detach();

The image show the relationship between the dlls.

enter image description here

Clinker answered 17/11, 2011 at 14:35 Comment(0)
G
1

I think that you may have some confusion here as to what your wrapper is doing.

You can call unmanaged C++ DLLs from within .NET code using DLLImport statements.

I would suggest that you create a C# class library project that will be the wrapper DLL for your unmanaged DLL, MFCXDLL.

You probably wont be able to add the DLL as a referenced resource, but you should create a project folder in which you store it and add it as a project file, set to Copy Local True for when the NET class library is built. You will also want to place any DLLs that MFCXDLL references within the same folder also set to Copy Local.

You then reference your NET DLL from all NET based code.

Here's an example of the wrapper process.

edit

I've had a check and yes I have used an unmanaged C++ DLL that used MFC as a shared library. Here's a cut down version of the code that I used.(Some class names have been changed due to confidentiality agreement.)

    using System.Collections.Generic;
    using System.Runtime.InteropServices;

    public class WrapperClass
    {
        [DllImport("some.dll", EntryPoint = "WriteDataCard", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.VariantBool)]
        public static extern Boolean WriteDataCard(Byte nComPort, bool bInitialize, bool bCardFeeder, [In] Byte[] bytesData, Byte dataSize, bool bTestKey);

        [DllImport("some.dll", EntryPoint = "ReadDataCard", SetLastError=true)]
        [return: MarshalAs(UnmanagedType.VariantBool)]
        public static extern Boolean ReadDataCard(Byte nComPort, Boolean bInitialize, Boolean bCardFeeder,  [Out] Byte[] bytesData, Byte dataSize);

   }
Glyoxaline answered 23/11, 2011 at 11:2 Comment(5)
I can nto see how a C# Wrapper would solve the problem. How does it solve the problem? I am following the pattern given in article codeguru.com/cpp/cpp/cpp_managed/interop/article.php/c6867. The actual bridge between the managed and unmanaged world is the mixed mode DLL. The wrapper only decorate the classes I want to be visible for the .NET user control.Clinker
You are right. The picture was wrong. It's of course the "Regular dll using shared MFC DLL" which is the wrapper. I have changed the picture now. The MFCXDLL_3 decorates a few of the classes in MFCXDLL_2 and make necessary functionallity avaliable for the user control.Clinker
The CodeGuru pattern that you are looking at is from 2004. I'm pretty sure that it no longer applies (at least for NET 3.5 onwards). I can double check with code that I have on another machine and will post back later.Glyoxaline
Yes I know its old, but that is the best and easiest pattern I found so far. I have a mixed mode dll with mfc support instead of library. I am not using the pattern in detail. Its old keywords that has changed. In VS2008 the marshaling is very simple. Both my managed types and unmanaged types are easily converted. By the way will it be more understandable if I provide the callstack ending up in the deserialize method?Clinker
Are you sure that this is the up to date way of doing it? The way I am doing it seem to be much more easy and powerful. The problem is only inside a native application using the same MFC resources. I really need an explanation on whats going wrong and why.Clinker

© 2022 - 2024 — McMap. All rights reserved.