C# .NET用户控件嵌入本地应用程序。资源链问题。

12
我正在封装一个MFC扩展DLL(MFCXDLL_2),以使其功能可供C#程序员使用。该包装器是“使用共享MFC DLL的常规DLL”,具有“公共语言运行时支持(/clr)”(混合模式)。在MFCXDLL_3中修饰了应该可用的MFCXDLL_2中的类。我遇到的情况是,在本地应用程序中运行的C#.NET用户控件使用MFCXDLL_2的情况。本地应用程序中的另一个MFC扩展DLL-MFCXDLL_1-也使用MFCXDLL_2,这会导致问题。当我启动本地应用程序时,它会隐式加载MFCXDLL_2。当我加载.NET用户控件时,根据http://msdn.microsoft.com/en-us/library/ksa99t88.aspx中的建议,将再次显式加载相同的MFCXDLL_2,“在常规DLL中使用数据库、OLE和套接字扩展DLL”。本机代码和.NET用户控件都实例化相同的类类型并调用MFCXDLL_2中的相同方法。该方法对数据进行反序列化(通过共享内存接收)并将反序列化数据返回给调用者。从本机代码中很好地工作,直到我加载.NET用户控件。在加载.NET用户控件后,从本机代码中停止反序列化工作,但从.NET用户控件中调用时效果很好。

我将WinDbg附加到本地应用程序的调试版本上并运行我的场景。在反序列化期间,WinDbg发现如下:

"警告:无法从档案中加载。未定义类。CArchive异常:badClass."

我认为这里存在一些资源问题,因此我运行本地应用程序的发布版本,加载MFCXDLL_2的发布版本。然后,我将.NET用户控件的调试版本(再次加载MFCXDLL_2的调试版本)加载到本地应用程序中。

然后一切都很顺利。本地代码加载了一个MFCXDLL_2的发布版本,.NET用户控件加载了一个MFCXDLL_2的调试版本,所有这些都在本地应用程序内部运行。

那么到底发生了什么?是否不可能同时从扩展DLL和常规DLL访问同一个MFCXDLL?
资源链是否以某种方式被破坏? 有哪些可能的解决方案?

以下是显示如何加载MFCXDLL_2 DLL的一些代码
当本地应用程序启动时,将调用MFCXDLL_2 DLLMain:

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
}

当.NET用户控件被加载时,MFCXDLL_2 DLL会再次被加载:
//==============================================================
// 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);
}

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();
此图展示了dll之间的关系。

enter image description here

1个回答

1

我认为您对包装程序的功能存在一些困惑。

您可以使用 DLLImport 语句在 .NET 代码中调用未管理的 C++ DLL。

我建议您创建一个 C# 类库项目,该项目将是未管理的 DLL MFCXDLL 的包装 DLL。

您可能无法将 DLL 添加为引用资源,但应创建一个项目文件夹,在其中存储它,并将其添加为项目文件,设置为在构建 NET 类库时复制本地 true。您还需要将任何 MFCXDLL 引用的 DLL 放置在同一文件夹中,并设置为复制本地。

然后,您可以从所有基于 NET 的代码中引用 NET DLL。

这里 提供了一个包装器流程的示例。

编辑

我检查过了,我确实使用了一个使用 MFC 作为共享库的未管理的 C++ DLL。这是我使用的代码的简化版本(由于保密协议,某些类名已更改)。

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

   }

我看不出C#包装器如何解决问题。 它是如何解决问题的? 我正在遵循文章http://www.codeguru.com/cpp/cpp/cpp_managed/interop/article.php/c6867中给出的模式。 托管和非托管世界之间的实际桥梁是混合模式DLL。包装器仅装饰我希望对.NET用户控件可见的类。 - kjella
你说得对。图片是错误的。当然应该是“使用共享MFC DLL的常规dll”作为包装器。我已经更改了图片。MFCXDLL_3修饰了MFCXDLL_2中的一些类,并使必要的功能可用于用户控件。 - kjella
你正在查看的CodeGuru模式来自2004年。我非常确定它不再适用(至少对于NET 3.5及以上版本)。我可以在另一台机器上检查代码,稍后会回复。 - ChrisBD
是的,我知道它很老,但那是目前我找到的最好、最简单的模式。我的混合模式DLL支持MFC而不是库。我没有详细使用这个模式,因为它已经过时了。在VS2008中,封送非常简单。我的托管类型和非托管类型都可以轻松转换。顺便说一下,如果我提供以反序列化方法结尾的调用堆栈,会更容易理解吗? - kjella
你确定这是最新的做法吗?我觉得我的方法更简单、更强大。问题只出现在使用相同MFC资源的本地应用程序中。我真的需要一个解释,说明出了什么问题以及原因是什么。 - kjella

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接