Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 637
kjella Avatar asked Nov 17 '11 14:11

kjella


1 Answers

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);

   }
like image 66
ChrisBD Avatar answered Oct 27 '22 04:10

ChrisBD