2010-11-17 19 views
0

J'ai une méthode C++ avec cette signature:Invoke C++ méthode de VB.Net avec chaîne traitée comme tableau de chaînes

STDMETHODIMP ClassName::FunctionName(long number, BSTR* names, long* status) 

Dans la méthode la variable noms est accessible comme un tableau de chaînes, à savoir

char * tempString = NULL; 

for (int n = 0; n < number; n++) 
{ 
    tempString = OLE2T(names[n]); 
... 

Je compile le projet C++, qui génère une DLL, puis j'inscris cette DLL et j'y ajoute une référence dans le projet VB. Lorsque j'ajoute la référence, un ensemble Interop est généré automatiquement, et la signature de la méthode dans l'assemblage Interop est la suivante:

FunctionName (number as Integer, ByRef names as String) As Integer 

De VB.Net j'invoque la méthode comme ceci:

result = FunctionName (number, names(0)) 

Où noms est un tableau de chaînes avec plusieurs éléments, et le nombre et le résultat sont des entiers. Le problème est que lorsque le code C++ tente d'accéder au reste des éléments dans le tableau de noms (noms [1] et à venir) il commence à obtenir "garbage" sur ces champs.

Ma question est, comment puis-je envoyer le tableau de chaînes entières au lieu de seulement la première valeur.

Le code C++ est une bibliothèque que je ne peux pas modifier, donc tout changement que je fais doit être sur le code VB.Net.

Je pensais que peut-être utiliser PInvoke pour appeler la méthode pourrait faire l'affaire (en déclarant une signature correcte), mais j'espérais quelque chose de mieux.

Des idées?

Merci!

Edit:

Je ne suis pas expert en Interop/Marshaling mais j'ai vérifié la définition IDL de la méthode et se présente comme suit:

[id(60), helpstring("method FunctionName")] 
     HRESULT FunctionName(
        [in] long number, 
        [in, size_is(number)] BSTR* names, 
        [out, retval] long* status); 

devrait-il pas size_is indiquer que les noms paramètre est un tableau, et donc, lorsque l'assembly Interop est généré agir en conséquence?

Merci encore

+0

Vous avez besoin de directives IDL spéciales pour indiquer au marshaller COM que 'number' correspond à la taille du tableau de chaînes' names'. Sinon, le marshaller ne le sait pas, et attend simplement des valeurs indépendantes, sans savoir que 'names' est un tableau du tout. Bien sûr, vous pouvez écrire un wrapper C++, qui "connaît" le comportement incorrect de la bibliothèque C++ d'origine, et corrige l'interface. – gimpf

+0

Merci pour votre réponse rapide, je viens d'éditer la question avec ce que je pense est l'information idl requis comme vous le dites. Aucun conseil? – willvv

+0

VB n'a pas p/invoke ou 'MarshalAsAttribute', peut-être que vous vouliez dire VB.net? Et 'names [0]' n'est pas la syntaxe de l'élément de tableau dans VB non plus? Le moyen le plus propre de résoudre votre problème serait avec un encapsuleur C++/CLI qui accepte un .NET 'array ', convertit en un tableau de 'BSTR', puis appelle la fonction C++. –

Répondre

2

Nope, [size_is] est un attribut qui ne midl.exe sait comment utiliser. Il le fait quand il génère le proxy/stub pour l'interface, quelque chose qui est utilisé lorsque vous voulez faire l'appel à travers les limites du processus.

Il n'est pas autrement exprimable dans une bibliothèque de types. L'argument est émis en tant que "pointeur sur BSTR" qui peut soit indiquer un BSTR transmis par référence, soit un tableau de BSTR. Tlbimp.exe ne peut pas faire la différence, il choisit l'ancien. Il doit, il ne peut pas raisonnablement déduire la taille du tableau. Vous obtenez des indésirables au moment de l'exécution car la couche d'interopérabilité CLR ne transmet qu'un seul élément du tableau. Tout client COM a un gros problème en utilisant cette méthode, il n'est pas spécifique à .NET. Le marselleur pinvoke .NET a une solution de contournement pour ce problème, notez la propriété SizeParamIndex de l'attribut [MarshalAs].

Si vous ne pouvez pas modifier le code C++, vous devez modifier manuellement la bibliothèque interop pour injecter l'attribut [MarshalAs]. C'est assez frivole, vous devez décompiler la bibliothèque avec ildasm.exe, éditez le .il et remettez-le avec ilasm.exe. Tu ne veux pas faire ça souvent.

Si vous le pouvez, vous devez utiliser la méthode compatible Automation pour transmettre des tableaux. Utilisez un SAFEARRAY. Ceci est entièrement pris en charge par les bibliothèques de types et la plomberie CLR Interop, aucun travail de votre côté n'est nécessaire du côté géré. Notez également que vous n'avez plus besoin de l'argument numéro, les tableaux sécurisés savent combien de temps ils sont. Pas différent des tableaux gérés.

+0

Super! Merci beaucoup, au cas où quelqu'un serait intéressé, j'ai modifié la déclaration de la méthode dans l'IL à partir de: [in] string & marshal (bstr) names) runtime géré internalcall à ceci: [in] string [] marshal (bstr [0]) noms) runtime géré internalcall. Cela indique que le paramètre sera un tableau de chaînes et que la longueur du tableau sera le premier argument de la méthode. – willvv