recevant les chaînes par Interop
-
19-09-2019 - |
Question
Je vais avoir du mal à obtenir une chaîne de retour de code C je l'ai écrit.
D'abord quelques informations d'arrière-plan général unrealated: Je souhaite recevoir l'utilisateur chaîne lisible pour un TSP TAPI de l'API TAPI. J'ai mis en place une solution TAPI semi-réaliste reposant sur l'adéquation entre les noms des pilotes à des chaînes stockées, mais je voudrais changer cela fonctionne sur ID de la place la ligne permanant, comme l'un de nos clients a un PBX (Alcatel) qui refuse de travailler d'une autre manière .
En C, je définit la fonction dans mon fichier d'en-tête comme:
__declspec(dllexport) void GetDeviceName(long DeviceId, wchar_t* DeviceName);
La fonction est écrite ainsi:
__declspec(dllexport) void GetDeviceName(long DeviceId, wchar_t* DeviceName)
{
//tapi code here...
//copy the string to DeviceName
wcscpy(DeviceName, (wchar_t*)((char *)devCaps + devCaps->dwLineNameOffset));
}
Comme indiqué plus haut, ce qui finira par faire quelque chose d'utile, mais pour l'instant je serai heureux si abc est placé dans mon wchar_t * / StringBuilder et je peux voir que dans C #.
En C # Je définit la fonction comme:
[DllImport("SBW.Tapi.TapiInterop.dll", CharSet = CharSet.Auto)]
static extern void GetDeviceName(long DeviceId, StringBuilder DeviceName);
Je définis DeviceName comme StringBuilder parce qu'une chaîne est immuable pour et je veux DeviceName à régler en C ( Ceci est recommandé par MS ). Je mets également le type de retour à void
sur le mince espoir que cela affecte aussi quelque chose (voir cette article mono semi-utiles pour une explication scientifique pseudo partielle).
Et l'appeler ainsi:
StringBuilder name = new StringBuilder();
name.EnsureCapacity(100);
long n = 0;
GetDeviceName(n, name);
Je joins mon débogueur au processus en cours d'exécution, définissez un point d'arrêt, et l'avis dans le code C qui est en quelque sorte le StringBuilder perverti et est fourni au code non managé comme un pointeur NULL.
Un AccessViolationException est ensuite jeté en C #.
Qu'est-ce qui a mal tourné?
suppression du paramètre à long aide. Je suis en mesure d'ajouter « abc » à mon paramètre DeviceName en C. Je veux cette variable longtemps cependant! Qu'est-ce que je fais mal ou dérangeant pour forcer un accident en ayant là ce paramètre longtemps?
La solution
Sur les plates-formes 32 bits long
est de 8 octets (64 bits) de large dans .NET et 4 octets (32 bits) de large dans le code natif. Vous devez changer votre méthode P / Invoke comme ceci:
[DllImport("SBW.Tapi.TapiInterop.dll", CharSet = CharSet.Auto)]
static extern void GetDeviceName(int DeviceId, StringBuilder DeviceName);
De cette façon, C # int
et C long
ont la même largeur sur les plates-formes 32 bits.
Autres conseils
Je veux cette variable longtemps cependant
Ensuite, vous devez définir le paramètre en C comme long long
. Comme souligné Lucero, une longue en C ++ est de 32 bits, alors qu'une longue en C # est de 64 bits. Donc, si vous passez une valeur de 64 bits où la fonction C attend une valeur 32 bits, la fonction lit les 32 bits supplémentaires comme second paramètre, ce qui entraîne évidemment une mauvaise adresse pour le tampon ...
c ++ long
est de 32 bits, le C # long
est de 64 bits, non? Par conséquent, vous aurez besoin d'utiliser un int
pour le premier paramètre.