Вызов функции C DLL из C # - преобразование параметра char в строку - PullRequest
0 голосов
/ 04 августа 2009

Я пытаюсь вызвать метод из DLL, написанной на C, из моего приложения на C #. Вот как я реализовал вызов:

-> Подпись метода C: (от сторонней компании)

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 *));

-> Определение типов 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

-> C # DLL Объявление метода:

[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);

Вызов метода:

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;
        }
    }

И затем я получаю Win32Exception: 1008 - Была сделана попытка сослаться на токен, который не существует

Я знаю, что проблема должна заключаться в преобразовании CHAR в STRING между неуправляемым (C) и управляемым (C #) кодами. Если я изменю параметры Binding или LogPath на тип SByte , это не даст никаких ошибок. Но так как метод ожидает текст (строку), я не знаю, как я могу передать текст методу, так как он ожидает переменную SByte ...

Я знаю, что мне, возможно, придется использовать что-то вроде MarshalAs , но я пробовал несколько вариантов и не имел никакого успеха.

Может кто-нибудь сказать мне, что я делаю не так?

Большое спасибо !!


Вот определение обратного вызова:

 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);

Дело в том, что метод C DLL ожидает параметр "REF". Вызов SS_Initialize присоединяет выполнение к функции обратного вызова ProcessMessage . В этой функции мне нужно иметь возможность получить измененные параметры (ref) из SS_Initialize ...

Не могли бы вы подсказать, как, по вашему мнению, должен быть структурирован код?

Спасибо !!

Ответы [ 3 ]

0 голосов
/ 04 августа 2009

Вам не нужно использовать ref для Binding и LogPath; они являются постоянными символами * и не изменятся.

Этот ResponseMessage в обратном вызове вернет массив строк, (char **), а не просто одну строку. Размер ответа, вероятно, будет указывать на глубину массива. Попробуйте [MarshalAs (UnamangedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string [] вместо этого.

0 голосов
/ 04 августа 2009

Спасибо Ravadre и R Уббен , ваша помощь в сочетании сделала свое дело !!

В конце концов, речь шла о правильном управлении предложением REF в параметрах и использовании тега MarshalAs. Параметры, которые являются «const char *» в части C, действительно не нуждаются в теге REF. Также я использовал [MarshalAs (UnmanagedType.LPStr)] для параметров строки. Вот и все!

ps: Stackoverflow.com потрясающе! Я уже нашел много ответов на другие проблемы здесь просто путем поиска. Это мой первый пост, и меня удивляют быстрые ответы, которые я получил на этот пост. Поздравляем всех сотрудников команды и участников! ;)

0 голосов
/ 04 августа 2009

Для сортировки строк вы не передаете ref string, просто string. Кроме того, вы должны украсить каждую строку с помощью [MarshalAs( UnmanagedType.LPStr)], чтобы указать, что вы передаете строку, которая содержит символы ascii, и она завершена нулем.
Изменить: Не могли бы вы также дать определение этой процедуры обратного вызова, как вы могли бы сделать аналогичные ошибки.

...