2009-05-05 7 views
0

** MAJOR UPDATE ** J'ai fait une erreur mineure mais je suis toujours curieux de savoir exactement ce qui se passe.Pouvez-vous PInvoke ANSI multibyte à un varargs? Qu'est-ce que je fais mal?

La fonction que je fais appel est en fait « fooV », une fonction avec cette signature:

foo(const char *, const char *, EnumType, va_list) 

Cela efface les AccessViolationExceptions que je recevais, mais n'explique pas pourquoi paramètres params travaillent pour tous les autres. Type NET sauf pour les chaînes qui devront être converties en caractères ANSI multi-octets. Je vais obtenir le développeur de la DLL pour exposer la version qui utilise réellement ... dans la liste des paramètres, des conseils pour PInvoking si va_list est le paramètre?

ancien poste ** **

Ceci est lié, mais différent d'un recent question I asked.

j'utiliser PInvoke pour appeler une fonction de bibliothèque de C qui a la signature suivante:

foo(const char *, const char *, EnumType, ...) 

La fonction configure une ressource d'une manière qui varie en fonction du StructType; le cas qui m'intéresse configure quelque chose qui s'attend à ce que les varargs soient une seule chaîne multi-octets ANSI. J'ai utilisé cette signature PInvoke pour appeler la fonction:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")] 
    public static extern int Foo(string s1, string s2, EnumType st1, params string[] s3); 

Le params string[] me semblait étrange, mais les signatures précédentes qui ont appelé cette fonction étaient l'utiliser pour d'autres types tels que bool donc j'ai suivi le modèle.

J'enveloppe cela avec une méthode .NET plus conviviale:

public void Foo(string s1, string s2, string s3) 
{ 
    int error = Dll.Foo(s1, s2, EnumType.Foo, s3); 
    // handle errors 
} 

Récemment, je changé la signature d'inclure "BestFitMapping = false, ThrowOnUnmappableChar = true" dans l'attribut dllimport de se conformer aux suggestions de FxCop. C'est un hareng rouge comme je le décrirai plus tard. Ce que j'attends de cette modification est un support limité pour Unicode. Par exemple, sur une machine avec une page de codes en anglais, une exception sera levée si vous passez une chaîne qui contient un caractère japonais. Sur une machine japonaise, je m'attendrais à pouvoir passer une chaîne japonaise à la fonction. Le test en anglais a fonctionné comme prévu, mais le test japonais lève une exception System.Runtime.InteropServices.COMException avec HRESULT 0x8007007A. Cela se produit même sans les paramètres BestFitMapping et ThrowOnUnmappableChar.

Je l'ai fait un peu regardant autour et a vu certains sites qui laissaient entendre que vous pourriez PInvoke varargs en spécifiant simplement les arguments normaux, donc j'ai essayé cette signature:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")] 
public static extern int Foo(string s1, string s2, EnumType st1, string s3); 

Cela jette un AccessViolationException quand je l'utilise sur soit la machine anglaise ou japonaise.

Je ne peux pas utiliser UTF-8 ou un autre codage Unicode car cette bibliothèque C attend et gère uniquement les ANSI multi-octets. J'ai trouvé que la spécification de CharSet.Unicode pour cette fonction fonctionne, mais je suis inquiet que ce soit uniquement par coïncidence et non quelque chose sur lequel je devrais compter. J'ai pensé à convertir la chaîne en un byte [] en utilisant la page de code du système, mais je n'arrive pas à comprendre comment spécifier que je voudrais passer un tableau d'octets au paramètre varargs.

Que se passe-t-il?Sur la machine anglaise, les caractères anglais fonctionnent bien et les charactes japonais lancent une exception ArgumentException comme prévu. Sur la machine japonaise, les caractères anglais fonctionnent correctement et les caractères japonais lancent l'exception COMException. Y at-il un problème avec ma signature PInvoke? J'ai essayé d'utiliser l'attribut MarshalAs pour spécifier un type de LPArray et un sous-type de LPStr, mais cela échoue de la même manière. UnmanagedType.LPStr indique qu'il s'agit d'une chaîne ANSI à un octet; existe-t-il un moyen de spécifier une chaîne ANSI multi-octets?

** MISE À JOUR ** Voici un ajout de données prenant en compte les commentaires actuels.

Si je spécifie une convention d'appel de CDECL et prendre des paramètres normaux comme ceci:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.Cdecl)] 
public static extern int Foo(string s1, string s2, EnumType e1, string s3) 

Lorsque j'utilise cette spécification, je reçois un AccessViolationException. J'ai donc essayé ceci:

Cela fonctionne pour l'anglais et déclenche l'exception COMException lorsque j'utilise des caractères japonais.

La seule chose que j'ai trouvé qui fonctionne est toujours ceci:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", BestFitMapping = false, ThrowOnUnmappableChar = true)] 
public static extern int Foo(string s1, string s2, EnumType e1, params IntPtr[] s3) 

Pour faire ce travail, j'utilise Marshal.StringToHGlobalAnsi() et passer ce pointeur. Cela fonctionne pour l'anglais et le japonais. Je ne suis pas à l'aise de ne pas comprendre pourquoi c'est la solution, mais cela fonctionne.

Répondre

2

Vous avez probablement besoin aussi de spécifier CallingConvention=CallingConvention.Cdecl depuis une fonction varargs utilisera une _cdecl convention d'appel (la valeur par défaut est Winapi qui par défaut à son tour StdCall).

Vous pourriez avoir besoin d'autre chose, mais je suis sûr que vous en aurez besoin au moins.

+0

J'ai essayé ceci, et pour une raison quelconque la fonction n'a aucun effet quand je le fais. (comme dans, c'est le "Set" d'une paire Get/Set et quand j'appelle Set de cette façon, le Get ne renvoie rien.) Je suis à peu près certain que c'est la bonne piste donc je pars de là. – OwenP

+0

En fait, sur la machine japonaise, j'obtiens une exception AccessViolationException lorsque j'utilise Cdecl. Curieuse. – OwenP

Questions connexes