2016-02-05 4 views
-2

У меня есть .dll с функцией C++, которая принимает параметры const wchar16_t*. Я пытаюсь импортировать и использовать его в C# со строкой, массивом символов и массивом символов с дополнительным символом '\ 0', но я не получил результата. Когда я проверяю его в исходной программе C++ в режиме отладки, в конце он имеет дополнительный символ «\ 0». Какой именно тип следует использовать?PInvoke вызов функции с параметрами wchar16_t

P.s. Я не уверен на 100%, что проблемы возникают из-за этих параметров. Я бы очень признателен и даю много очков, если кто-то любезно рассмотрит небольшие проекты I attach, иллюстрирующие проблему. Программа на C++ отлично работает (мы получаем ответ на запрос), но в проекте C# OnLoginResponseCallback никогда не срабатывает.

Что делать в C#

[DllImport("ActiveTickServerAPI.dll", CharSet = CharSet.Unicode, EntryPoint = "[email protected]@[email protected]@@[email protected]", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] 
     private static extern ulong ATCreateLoginRequest(ulong sessionId, string user, string pwd, ATLoginResponseCallback onLoginResponse); 

public delegate void ATLoginResponseCallback(ulong hSession, ulong hRequest, ATLOGINRESPONSE response); 
     public delegate void ATRequestTimeoutCallback(ulong origRequest); 
static void Main(string[] args) 
    { 
lastRequest = ATCreateLoginRequest(hSession, userId, pw, OnLoginResponseCallback); 
       bool rc = ATSendRequest(hSession, lastRequest, 3000, OnRequestTimeoutCallback); 
    } 
static void OnLoginResponseCallback(ulong hSession, ulong hRequest, ATLOGINRESPONSE response) 
     { 
      Console.WriteLine("!THIS SHOULD FIRE, but fire only timeout callback"); 
     } 

[StructLayout(LayoutKind.Sequential)] 
public struct ATLOGINRESPONSE 
    { 
     public byte loginResponse; 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] 
     public byte[] permissions; 
     public ATTIME serverTime; 
    } 

[StructLayout(LayoutKind.Sequential)] 
public struct ATTIME 
{ 
    public ushort year; 
    public ushort month; 
    public ushort dayOfWeek; 
    public ushort day; 
    public ushort hour; 
    public ushort minute; 
    public ushort second; 
    public ushort milliseconds; 
} 

в C++ это работа (получение ответа входа в систему). Вот описание функции от API Docs:

ACTIVETICKSERVERAPI_API uint64_t ATCreateLoginRequest ( uint64_t session, 
    const wchar16_t * userid, 
    const wchar16_t * password, 
    ATLoginResponseCallback  pCallback 
) 

работает структура из C++

typedef struct _ATLOGIN_RESPONSE 
{ 
    ATLoginResponseType loginResponse; 
    uint8_t permissions[255]; 
    ATTIME serverTime; 
} ATLOGIN_RESPONSE, *LPATLOGIN_RESPONSE; 

    typedef struct _ATTIME 
{ 
    uint16_t year; 
    uint16_t month; 
    uint16_t dayOfWeek; 
    uint16_t day; 
    uint16_t hour; 
    uint16_t minute; 
    uint16_t second; 
    uint16_t milliseconds; 
} ATTIME, *LPATTIME; 
+0

Возможно, http://stackoverflow.com/a/6089416/696034 даст вам ответ? –

+0

не мое дело, ваша ссылка о параметре out. – Dork

+2

Это может быть опечатка в сообщении, но нулевой терминатор равен \ 0, not/0 – pikzen

ответ

1

Я скачал тестовый проект и сразу же установил заявления делегатов, потому что они были неправы:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    public delegate void ATSessionStatusChangeCallback(ulong hSession, byte statusTyp); 

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    public delegate void ATLoginResponseCallback(ulong hSession, ulong hRequest, ref ATLOGINRESPONSE response); 

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    public delegate void ATRequestTimeoutCallback(ulong origRequest); 

Первый раз, когда я запустил программу, я получил:

SessionStatus - Connected 
request timeout 
!THIS SHOULD FIRE 
SessionStatus - Connected 
!THIS SHOULD FIRE 

Второй и последующие разы я получил:

SessionStatus - Connected 
!THIS SHOULD FIRE 

Время перерыва. Почему он вел себя так, как это делал в первый раз, очень трудно догадаться. Вероятно, вам нужно подождать с помощью ATSendRequest(), пока не получите подтверждение правильного входа в систему, что-то вроде этого.

Но ясно, что делегаты позаботились о проблеме, теперь вы получаете правильное значение hSession в обратном вызове. Это еще не идеально, вам нужно убедиться, что они не могут быть собраны в мусор. Храните их в статической переменной:

static ATSessionStatusChangeCallback statusCallback; 
.... 
    if (res == true) { 
     statusCallback = new ATSessionStatusChangeCallback(OnSessionStatusChangeCallback); 
     res = ATInitSession(sessionNumber, "activetick1.activetick.com", 
       "activetick2.activetick.com", 443, statusCallback, true); 
    } 

Сделайте то же самое на двух других.

+0

Спасибо, Ханс, ты замечательный! Я смогу вознаградить награду за 5 часов. Хотя это становилось все сложнее, чем я думал. Получили некоторые сумасшедшие значения во временной структуре этого обратного вызова и loginDenied ответ типа означает, что пользователь ATGUID ошибочен, хотя я просто скопировал требуемые значения. Это должно быть что-то с переводом структур взад и вперед .. Я должен вникать в эту тему. – Dork

1

Декларация ATLOGINRESPONSE почти наверняка неправильно.

public class ATLOGINRESPONSE 
{ 
    public byte loginResponse; 
    public byte[] permissions; 
    public ATTIME serverTime; 
} 

Объявление, объявленное как класс, гарантирует, что оно будет обработано по ссылке. Но вы исключаете его, когда-либо ориентируясь на ценность. Что может быть хорошо. В любом случае, я бы предпочел объявить как структуру.

Кроме того, первые два параметра выглядят неправильно. Первый, вероятно, перечислит и должен быть объявлен как таковой. Второй - массив байтов, но вам нужно указать, как его маршалировать. Как это:

[StructLayout(LayoutKind.Sequential)] 
public struct ATLOGINRESPONSE 
{ 
    public ATLoginResponseType loginResponse; // you need to define the ATLoginResponseType enum 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] 
    public byte[] permissions; 
    public ATTIME serverTime; 
} 

Мы не знаем, является ли или не ATTIME объявляются правильно.

Я декодированный ваше имя функции здесь: https://demangler.com/

Функция C++ имеет эту подпись

unsigned __int64 __cdecl ATCreateLoginRequest(
    unsigned __int64, 
    wchar_t const *, 
    wchar_t const *, 
    void (__cdecl*)(unsigned __int64,unsigned __int64,struct _ATLOGIN_RESPONSE *) 
) 

Делегат поэтому объявленную с неправильным вызовах. Оно должно быть:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public delegate void ATLoginResponseCallback(
    ulong hSession, 
    ulong hRequest, 
    ref ATLOGINRESPONSE response 
); 

Обратите внимание, что поменяв ATLOGINRESPONSE быть-структуру, мы должны сделать параметр параметр ref.

Функция ATLoginResponseCallback правильно объявлена ​​в коде C#.

Ваши другие делегаты также должны быть объявлены [UnmanagedFunctionPointer(CallingConvention.Cdecl)].

Если callback вызван только от ATCreateLoginRequest, то передаваемый делегат подвергается сбору мусора. Поэтому, если ATCreateLoginRequest берет копию этого делегата и звонит позже, вам нужно будет сохранить делегат в живых. Назначьте его переменной типа ATLoginResponseCallback, чья жизнь выходит за рамки окончательного вызова обратного вызова.

Совершенно правдоподобно, что проблемы лежат в другом месте.

+0

Дэвид благодарит за эту информацию, я буду копаться в вашем ответе. Разрешения - это перечисление, поэтому я сделал его байт. Что касается класса/структуры - в той же программе у меня уже возникла проблема, когда она меняла структуру на класс, что делало работу. Вот почему я изначально загрузил свои проекты. Это простые 1-страничные программы, где C++-код работает правильно, но C# - нет. Pro dev может выявить проблему через 5 минут. – Dork

+0

Если это перечисление, объявите его как перечисление. Массив байтов, конечно, ошибочен. –

+0

извините. из документов. ЬурейиЙ недействительная (* ATLoginResponseCallback) (uint64_t сессия, uint64_t запрос, LPATLOGIN_RESPONSE pResponse) где ЬурейаЯ структура _ATLOGIN_RESPONSE * LPATLOGIN_RESPONSE где ATLoginResponseType \t loginResponse uint8_t \t разрешение [255] ATTIME \t ServerTime – Dork