2011-11-17 10 views
12

Estoy terminando una DLL de extensión MFC (MFCXDLL_2) para hacer que su funcionalidad esté disponible para los programadores de C#.C# .NET User Control dentro de la aplicación nativa. Problemas de la cadena de recursos

El contenedor es un "archivo DLL estándar que utiliza DLL MFC compartido" con "Common Language Runtime Support (/ clr)". (Modo mezclado).

Las clases en MFCXDLL_2 que estarán disponibles están decoradas en MFCXDLL_3.

El escenario que estoy experimentando es el caso donde MFCXDLL_2 se usa desde un control de usuario C# .NET ejecutándose en una aplicación nativa.

Otra DLL de extensión MFC -MFCXDLL_1-dentro de la aplicación nativa también usa el MFCXDLL_2 y esto causa problemas.

Cuando inicio la aplicación nativa cargará implícitamente el MFCXDLL_2.

Cuando cargo el control de usuario .NET, el mismo MFCXDLL_2 se carga de nuevo explícitamente de acuerdo con el consejo en http://msdn.microsoft.com/en-us/library/ksa99t88.aspx, "Utilizando DLL de extensión de bases de datos, OLE y Sockets en DLL regulares".

Tanto el código nativo como el control de usuario .NET instancian el mismo tipo de clase y llaman al mismo método en el MFCXDLL_2.

El método deserializa los datos (recibidos en la memoria compartida) y devuelve los datos deserializados a la persona que llama. Esto funciona muy bien desde código nativo hasta que cargue el control de usuario .NET.

Después de cargar el control de usuario de .NET, la deserialización deja de funcionar desde el código nativo, pero funciona muy bien cuando se llama desde el control de usuario .NET.

Adjunté WinDbg a la versión de depuración de la aplicación nativa y ejecuté mi escenario. WinDbg encontró lo siguiente durante la deserialización:

"Advertencia: No se puede cargar desde el archivo. Clase no definida. Excepción de CArchive: badClass. "

Creo que hay algunos problemas de recursos aquí, así que ejecuto la versión de lanzamiento de la aplicación nativa cargando la versión de lanzamiento de MFCXDLL_2. Luego cargo la versión de depuración del control de usuario .NET, que nuevamente carga la versión de depuración de MFCXDLL_2 en la aplicación nativa.

Entonces todo funciona muy bien. Una versión de lanzamiento de MFCXDLL_2 cargada por el código nativo y una versión de depuración de MFCXDLL_2 cargada por el control de usuario .NET, todo se ejecuta dentro de la aplicación nativa.

¿Qué está pasando? ¿No es posible acceder al mismo MFCXDLL de, p. Ej., una extensión DLL y una DLL regular al mismo tiempo en la misma aplicación?
¿Se ha destruido la cadena de recursos de alguna manera? ¿Cuáles son las posibles soluciones?

Aquí hay un código que muestra cómo se carga el archivo DLL MFCXDLL_2
Cuando la aplicación nativa comienza MFCXDLL_2 DLLMain se llama:

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 
} 

Cuando el.control de usuario NET está cargado, el MFCXDLL_2 DLL se carga de nuevo:

//============================================================== 
// 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); 
} 

El código deserializar dentro 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(); 

La imagen muestra la relación entre los DLL.

enter image description here

Respuesta

1

creo que es posible que tenga alguna confusión en cuanto a lo que su envoltura está haciendo.

Puede llamar a las DLL de C++ no administradas desde el código .NET mediante las sentencias de DLLImport.

Sugeriría que cree un proyecto de biblioteca de clase C# que será la DLL contenedora para su DLL no administrada, MFCXDLL.

Probablemente no podrá agregar la DLL como un recurso al que se hace referencia, pero debe crear una carpeta de proyecto en la que la almacene y añada como archivo de proyecto, establezca Copy Local True para cuando la biblioteca de la clase NET sea construido. También querrá colocar cualquier archivo DLL que las referencias MFCXDLL dentro de la misma carpeta también establezcan en Copiar local.

A continuación, hace referencia a su DLL NET desde todos los códigos NET.

Here es un ejemplo del proceso de envoltura.

edición

que he tenido un cheque y sí he utilizado un archivo DLL no administrado C++ que utiliza MFC como una biblioteca compartida. Aquí hay una versión reducida del código que utilicé (algunos nombres de clases han sido cambiados debido a un acuerdo de confidencialidad)

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

    } 
+0

No puedo ver cómo una envoltura C# resolvería el problema. ¿Cómo soluciona el problema? Estoy siguiendo el patrón dado en el artículo http://www.codeguru.com/cpp/cpp/cpp_managed/interop/article.php/c6867. El puente real entre el mundo gestionado y el no gestionado es el DLL de modo mixto. El contenedor solo decora las clases que quiero que sean visibles para el control de usuario de .NET. – kjella

+0

Tienes razón. La imagen fue incorrecta. Es, por supuesto, el "DLL regular que usa DLL MFC compartido", que es el contenedor. He cambiado la imagen ahora. El MFCXDLL_3 decora algunas de las clases en MFCXDLL_2 y hace que la funcionalidad necesaria esté disponible para el control del usuario. – kjella

+0

El patrón CodeGuru que está viendo es desde 2004. Estoy bastante seguro de que ya no se aplica (al menos para NET 3.5 en adelante). Puedo verificar con el código que tengo en otra máquina y lo publicaré más tarde. – ChrisBD

Cuestiones relacionadas