Appelez la fonction DLL C à partir de C # - convertissez le paramètre char en chaîne.

StackOverflow https://stackoverflow.com/questions/1227644

  •  22-07-2019
  •  | 
  •  

Question

J'essaie d'appeler une méthode à partir d'une DLL écrite en C, à partir de mon application C #. Voici comment j'ai mis en œuvre l'appel:

- > Signature de la méthode C: (d'une société tierce)

Int16 SSEXPORT SS_Initialize(Uint16 ServerIds[], Uint16 ServerQty, const char PTR *Binding, const char PTR *LogPath, Uint16 LogDays, Int16 LogLevel,
Uint16 MaxThreads, Uint16 ThreadTTL, Int16 (CALLBACK *ProcessMessage)(Uint16, const char PTR *, Uint32, char PTR **, Uint32 PTR *, Int16 PTR *));

- > Définition des types C:

#ifndef _CSISERVERTYPES_
#define _CSISERVERTYPES_

#if defined(OS_NT)

#define CALLBACK __stdcall 
#define SSEXPORT __stdcall
#define PTR      

typedef char                Int8;
typedef unsigned char       Uint8;
typedef short               Int16;
typedef unsigned short      Uint16;
typedef long                Int32;
typedef unsigned long       Uint32;
typedef unsigned char       Byte;
typedef unsigned short      Word;
typedef unsigned long       Dword;
typedef short               Bool;

#endif

typedef Int16 (CALLBACK *PM_Function)
        (Uint16,const char PTR *,Uint32,char PTR **,Uint32 PTR *,Int16 PTR *);

#define SS_OK               0
#define SS_NOT_INIT         -1
#define SS_PROC_ERR         -2
#define SS_ERR_TCP_SRV      -3
#define SS_ERR_OUT_MEM      -4

#ifndef NULL
#define NULL                0
#endif

#endif

- > Déclaration de méthode DLL C #:

[DllImport("CSITCPServer.dll", SetLastError = true)]
    static extern Int16 SS_Initialize(
        UInt16[] ServerIds,
        UInt16 ServerQty,
        ref string Binding,
        ref string LogPath,
        UInt16 LogDays,
        Int16 LogLevel,
        UInt16 MaxThreads,
        UInt16 ThreadTTL,
        ProcessMessageCallback callback);

Appel de méthode:

public static void Call_SS_Initialize()
    {   
        Int16 ret;
        UInt16[] serverIds = new UInt16[] { 2020, 2021 };
        string binding = "";
        string logPath = "";

        try
        {
            ret = SS_Initialize(
                serverIds,
                Convert.ToUInt16(serverIds.ToList().Count),
                ref binding,
                ref logPath,
                10,
                0,
                256,
                300,
                ProcessMessage);

            Console.WriteLine("Call_SS_Initialize() -> Result of SS_Initialize:  {0}", ret.ToString());
        }
        catch (Exception ex)
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
            //throw;
        }
    }

Ensuite, je reçois Win32Exception: 1008 - Une tentative de référence à un jeton inexistant

a été effectuée.

Je sais que le problème doit être lié à la conversion de CHAR à STRING entre les codes non gérés (C) et les codes gérés (C #). Si je modifie les paramètres Liaison ou LogPath pour saisir SByte , cela ne génère aucune erreur. Mais comme la méthode attend un texte (chaîne), je ne sais pas comment puis-je lui transmettre le texte car elle attend une variable SByte ...

Je suis conscient que je devrais utiliser quelque chose comme MarshalAs , mais j'ai essayé quelques options et je n'ai pas réussi.

Quelqu'un peut-il me dire ce que je fais mal ??

Merci beaucoup !!

Voici la définition du rappel:

 public delegate Int16 ProcessMessageCallback(
        UInt16 ServerId,
        [MarshalAs(UnmanagedType.LPStr)] ref string RequestMsg,
        UInt32 RequestSize,
        [MarshalAs(UnmanagedType.LPStr)] ref string ResponseMsg,
        ref UInt32 ResponseSize,
        ref Int16 AppErrCode);

Le fait est que la méthode de la DLL C attend un "REF". paramètre. L'appel à SS_Initialize associe l'exécution à la fonction de rappel ProcessMessage . Dans cette fonction, je dois pouvoir obtenir les paramètres modifiés (ref) de SS_Initialize ...

Pourriez-vous suggérer comment le code devrait être structuré?

Merci !!

Était-ce utile?

La solution

Vous n'avez pas besoin d'utiliser ref pour Binding et LogPath; ils sont const char * et ne changeront pas.

Ce ResponseMessage dans le rappel retournera un tableau de chaînes, (char **) et non pas une seule chaîne. La taille de la réponse indiquera probablement la profondeur du tableau. Essayez plutôt [MarshalAs (UnamangedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string [].

Autres conseils

Pour regrouper des chaînes, vous ne transmettez pas chaîne de référence , mais chaîne . En outre, vous devez décorer chaque chaîne avec [MarshalAs (UnmanagedType.LPStr)] pour indiquer que vous transmettez une chaîne contenant des caractères ascii et qu'elle est terminée par la valeur null.
Edit: Pourriez-vous également donner votre définition de cette procédure de rappel, car vous pourriez commettre des erreurs similaires.

Merci Ravadre et R Ubben , votre aide combinée a fait l'affaire!

En fin de compte, il s’agissait de contrôler correctement la clause REF dans les paramètres et d’utiliser la balise MarshalAs. Les paramètres en tant que " const char * " dans la partie C n'a vraiment pas besoin de la balise REF. De plus, j'ai utilisé [MarshalAs (UnmanagedType.LPStr)] pour les paramètres de chaîne. C'est ça!

ps: Stackoverflow.com est incroyable! J'ai déjà trouvé beaucoup de réponses à d'autres problèmes ici simplement en cherchant. Ceci est mon premier post et je suis surpris des réponses rapides que j'ai pour celui-ci. Félicitations à toute l'équipe et aux membres du personnel! ;)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top