2009-03-26 9 views
2

Toujours en train de travailler sur un problème qui a commencé ici Calling C++ dll function from C#: Of structs, strings and wchar_t arrays., mais avec une approche différente. En suivant l'exemple Calling Managed Code from Unmanaged Code and vice-versa j'ai écrit un wrapper managé en C++ pour accéder à la classe unmanages dans la DLL C++ non managée.Passer des paramètres de type de données C# à DLL écrits en C++?

Il ressemble à ceci:

//in header file 
public __gc class TSSLDllWrapper 
{ 
public: 
TSSLDllWrapper(); 
    //this is the unmanaged class 
CcnOCRsdk * _sdk; 

bool convertHKID_Name(char *code, RECO_DATA *o_data); 
}; 

//in .cpp file 
TSSLDllWrapper::TSSLDllWrapper(void) 
{ 
    _sdk = new CcnOCRsdk(); 
} 

bool TSSLDllWrapper::convertHKID_Name(char *code, RECO_DATA *o_data) 
{ 
return _sdk->convertHKID_Name(code, o_data); 
} 

//C++ RECO_DATA structure definition: 
struct RECO_DATA{ 
wchar_t FirstName[200]; 
wchar_t Surname[200]; 
}; 

Maintenant, j'ai une dll que je peux importer dans mon projet C#.

est le problème cependant: Quand je veux appeler la méthode à partir du fichier dll, comme ceci:

TSSLDllWrapper wrapper = new TSSLDllWrapper(); 
bool res = wrapper.convertHKID_NameSimple(//need to pass parameters here); 

Il attend les paramètres C++ - des pointeurs sur des caractères et RECO_DATA.

Comment puis-je résoudre ce problème et passer des types C++ à partir du code C#?

Répondre

5

Une façon de convertir la plupart des types de données C est d'utiliser PInvoke Interop Assitant. Il créera les types C#/VB.Net appropriés pour la plupart des structures C. Voici la sortie pour RECO_DATA

[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Unicode)] 
public struct RECO_DATA { 

    /// wchar_t[200] 
    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=200)] 
    public string FirstName; 

    /// wchar_t[200] 
    [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=200)] 
    public string Surname; 
} 

Pour le paramètre char *, vous pouvez passer IntPtr.Zero ou utiliser Marshal.StringToCoTaskMemAnsi pour faire le travail.

+0

Merci, j'ai finalement fait en sorte que tout fonctionne ensemble – Evgeny

0

Quelques points que je suis tombé sur tout en créant une enveloppe dll:

  • je devais appeler une fonction de membre de la classe non géré. J'ai pris le temps de découvrir que je ne peux pas le faire en utilisant DllImport directement, mais je dois écrire un 'wrapper' moi-même.
  • Dans l'emballage lui-même, il ne suffit pas non plus d'envelopper uniquement la fonction membre. Je dois être capable de créer un pointeur vers la classe C++, donc je dois exporter le pointeur vers le constructeur (au moins, c'est comme ça que je le comprends, peut-être que ce n'est pas exactement correct.). J'ai essayé juste d'exporter la fonction membre initialement, il a compilé mais a renvoyé "AccessViolationException" à l'exécution. Je suis resté là pendant un moment.

Par conséquent, mon emballage ressemble à ceci (comme le suggère using a class defined in a c++ dll in c# code):

public class __declspec(dllexport) Wrapper 
{ 
public: 
    CcnOCRsdk* SDKCreate() 
    { 
     return new CcnOCRsdk(); 
    } 

    bool CcnOCRsdk_HKID(CcnOCRsdk* pSDK, char *code, RECO_DATA *o_data) 
    { 
     return pSDK->convertHKID_Name(code, o_data); 
    } 

    void SDKDelete(CcnOCRsdk* pSDK) 
    { 
     delete pSDK;  
    } 
}; 

__declspec (dllexport) sur les exportations de niveau de classe tous les membres publics de la classe. SDKCreate() renvoie un pointeur vers cette classe CcnOCRsdk à partir de la DLL non managée quelle fonction de membre je dois appeler. CcnOCRsdk_HKID appelle cette fonction de membre. Notez que le pointeur sur le CcnOCRsdk est passé.

Une fois le code généré dans la DLL, je dois utiliser le DUMPBIN pour savoir quels sont les points d'entrée "mutilés" pour la DLL d'encapsulation que j'ai écrite.

Pour mon emballage, les résultats se présentent comme suit

 1 0 00001240 [email protected]@@[email protected]@@Z = [email protected][email protected]@@[email protected]@@Z ([T2M] public: class TSSL::Wrapper & __thiscall TSSL::Wrapper::operator=(class TSSL::Wrapper const &)) 
     2 1 00001220 [email protected]@[email protected]@[email protected]@[email protected]@@Z = [email protected][email protected]@[email protected]@[email protected]@PAD[email protected]@@Z ([T2M] public: bool __thiscall TSSL::Wrapper::CcnOCRsdk_HKID(class CcnOCRsdk *,char *,struct RECO_DATA *)) 
     3 2 00001200 [email protected]@[email protected]@[email protected]@XZ = [email protected]@[email protected]@[email protected]@XZ (public: class CcnOCRsdk * __thiscall TSSL::Wrapper::SDKCreate(void)) 
     4 3 00001410 [email protected]@[email protected]@[email protected]@@Z = [email protected]@[email protected]@[email protected]@@Z (public: void __thiscall TSSL::Wrapper::SDKDelete(class CcnOCRsdk *)) 

Maintenant, je suis enfin prêt à utiliser mon emballage dans le C#.Lorsque je l'ai fait sans spécifier le point d'entrée exactement comme dans ma sortie de dumpbin, j'ai eu l'erreur 'Impossible de trouver le point d'entrée'.

[DllImport(@"TSSLWrapper.dll", EntryPoint = "[email protected]@[email protected]@[email protected]@XZ")] 
    public static extern IntPtr SDKCreate(); 
    [DllImport(@"TSSLWrapper.dll", EntryPoint = "[email protected]@[email protected]@[email protected]@[email protected]@@Z")] 
    public static extern bool CcnOCRsdk_HKID(IntPtr ptr, string num, out RECO_DATA o_data); 

Le RECO_DATA est défini comme JaredPar suggéré.

Et la dernière étape consiste à apprécier les résultats. D'abord, je dois appeler le constructeur de la classe, et ensuite passer le pointeur à l'appel réel à la fonction

RECO_DATA recoData = new cnOCRsdk.RECO_DATA(); 
    string num = "262125355174"; 

    IntPtr ptr = cnOCRsdk.SDKCreate(); 
    bool res = cnOCRsdk.CcnOCRsdk_HKID(ptr, num, out recoData); 

Mes res renvoie true, et j'obtenir les résultats que j'attendais à recoData.

Questions connexes