2010-07-19 19 views
4

Je suis nouveau en C++. J'appelle une fonction C++ de C# en utilisant un PINVOKE et je veux récupérer une chaîne en tant que paramètre out. Cependant, je viens de récupérer une chaîne vide. Le paramètre int out fonctionne correctement.Comment marshall C++ char * en chaîne C# en utilisant P/INVOKE

Importation:

[DllImport (@"UnamanagedAssembly.dll", CharSet = CharSet.Ansi)] 
public static extern int Activate(ref int numActivated, StringBuilder eventsActivated); 

extern "C" __declspec(dllexport) int Activate(int *p_NumActivated, char *p_EventsActivated) {return Activation::GetInstance()->Activate(p_NumActivated, p_EventsActivated);} 

Appel ma fonction C++ de C#:

int numActivated = 0; 
StringBuilder eventsActivated = new StringBuilder(); 
int status = Activate(ref numActivated, eventsActivated); 

La fonction C++:

int Activation::Activate(int *p_NumActivated, char *&p_EventsActivated) 
{ 
    char *pTemp = "Hello"; 
    p_EventsActivated = pTemp; 

    *p_NumActivated = 1; 

    return 0; 
} 
+0

ne vous ont pas seulement besoin nécessaire d'ajouter soit l'arbitre ou sur mot-clé pour vous la définition de la méthode de la chaîne? – pmcilreavy

Répondre

2
[DllImport (@"UnamanagedAssembly.dll", CharSet = CharSet.Ansi)] 
public static extern int Activate(
    ref int numActivated, 
    [MarshalAs(UnmanagedType.LPStr)]StringBuilder eventsActivated); 
+0

J'ai essayé d'ajouter cela, mais il y a encore quelque chose qui ne va pas. Salut! – TerryB

+1

Il s'agit d'une référence à un pointeur vers un char, pas un simple pointeur sur char.Ce que vous avez publié présente un comportement indéfini d'environ 10 façons différentes. –

+0

En y regardant de plus près, je ne suis pas sûr de ce que signifie 'char * & p_EventsActivated' dans votre signature de fonction C++. Aviez-vous l'intention de lire 'char * p_EventsActivated'? edit: ah, je vois - référence au pointeur. pas une construction que j'ai jamais utilisée, alors ignorez-moi entièrement. – allonym

0

Pardon ma réponse si elle est fausse, mais:

Je ne pense qu'un StringBuilder est compatible avec un char * &

Êtes-vous en train de confondre un StringBuilder avec un caractère **? * Vous pouvez créer une nouvelle chaîne, ou un constructeur de chaîne basé sur le retour d'Activate, plutôt que d'essayer de passer Activer un emplacement pour placer les données. (Comme vous semblez essayer de le faire?)

(Activer alloue de l'espace pour "Bonjour", puis renvoie le pointeur à son emplacement).

Généralement, lors de la mise en correspondance d'objets C++ ou C, lors de l'envoi de chaînes, il est préférable d'envoyer la longueur de la chaîne, afin de garantir que les chaînes de longueur appropriées sont transmises entre les appelants.

1

P/Invoke n'est pris en charge que pour les interfaces C. YMMV si vous essayez de l'amener à parler à des constructions C++. Dans ce cas, vous devriez savoir précisément à quoi le compilateur C++ traduisait la référence, car les compilateurs C++ sont libres de l'implémenter de la manière qu'ils souhaitent.

Si vous voulez être en mesure de P/Invoke, mais ne veulent pas avoir à utiliser la sémantique de pointeur sur le site d'appel, vous pouvez faire quelque chose comme:

int Activation::Activate(int *p_NumActivated, char **p_EventsActivated) 
{ 
    return Activate(p_NumActivated, *p_EventsActivated); 
} 

int Activation::Activate(int *p_NumActivated, char *&p_EventsActivated) 
{ 
    char *pTemp = "Hello"; 
    p_EventsActivated = pTemp; 

    *p_NumActivated = 1; 

    return 0; 
} 

qui expose un pointeur simple vous devriez pouvoir plus simplement P/Invoke. Notez toutefois que la chaîne en question ne sera probablement pas modifiable par la fonction en question. Cela est dû au fait que les chaînes .NET sont des chaînes UTF-16, tandis que la fonction C++ utilise des chaînes ASCII qui ne sont pas complètement compatibles entre elles. Vous devrez probablement convertir la chaîne .NET en ASCII, la déplacer vers l'arrière, la restaurer et la reconvertir en UTF-16.

Oh, une dernière chose: Comme écrit, vous devrez passer une référence à un objet Activation pour que cette fonction fonctionne. La position de cet argument va être dépendant du compilateur C++.

+0

Cheers. Je vais essayer. Quel est le moyen le plus simple de passer une chaîne de longueur arbitraire (ou dans une certaine longueur maximale de toute façon) de C++ à C#. On dirait que je fais quelque chose qui n'est pas aussi facile que je le pensais :) – TerryB

+1

@TerryB: Cela peut être simple - mais c'est beaucoup plus facile si le code C++ utilise aussi UTF-16 - c'est-à-dire , 'wchar_t' et amis au lieu de' char'. C# est un environnement Unicode. C++ n'est pas sauf si vous travaillez à le faire ainsi. –

+0

Notez que wchar_t et les amis n'impliquent pas automatiquement unicode. Il pourrait utiliser UCS2. – Arafangion

3
StringBuilder eventsActivated = new StringBuilder(5); 

au lieu de

StringBuilder eventsActivated = new StringBuilder();