P/Invoke с [Out] StringBuilder / LPTSTR и многобайтовыми символами: искаженный текст?


Я пытаюсь использовать P/Invoke для извлечения строки (помимо прочего) из неуправляемой DLL, но строка получается искаженной, независимо от того, что я пытаюсь.

Я не родной кодировщик Windows, поэтому я не уверен насчет битов кодировки символов. DLL настроена на использование "Многобайтового набора символов", который я не могу изменить (потому что это сломало бы другие проекты). Я пытаюсь добавить функцию-оболочку для извлечения некоторых данных из некоторых существующих классов. Рассматриваемая строка в настоящее время существует как CString, и я пытаюсь скопировать ее в LPTSTR, надеясь получить ее в управляемый StringBuilder.

Это то, что я сделал, я считаю, что это наиболее близко к правильности (очевидно, я удалил ненужные биты):

// unmanaged function
DLLEXPORT void Test(LPTSTR result)
{
  // eval->result is a CString
  _tcscpy(result, (LPCTSTR)eval->result);
}

// in managed code
[DllImport("Test.dll", CharSet = CharSet.Auto)]
static extern void Test([Out] StringBuilder result);

// using it in managed code
StringBuilder result = new StringBuilder();
Test(result);
// contents in result garbled at this point

// just for comparison, this unmanaged consumer of the same function works
LPTSTR result = new TCHAR[100];
Test(result);

Очень ценю любые советы! Спасибо!!!

3 ответа

Не используйте paramaeter, поскольку вы не размещаете в функции c

[DllImport("Test.dll", CharSet = CharSet.Auto)]
static extern void Test(StringBuilder result);

StringBuilder result = new StringBuilder(100);
Test(result);

Это должно работать для вас

Одна проблема заключается в использовании CharSet.Auto,

В системе на базе NT предполагается, что параметр результата в собственной DLL будет использовать Unicode. Изменить это на CharSet.Ansi и посмотреть, если вы получите лучшие результаты.

Вам также необходимо определить размер буфера передаваемого StringBuilder:

StringBuilder result = new StringBuilder(100); // problem if more than 100 characters are returned

Кроме того - нативный код C использует 'TCHARТипы и макросы - это означает, что он может быть построен для Unicode. Если это может произойти, это усложняет CharSet ситуация в DllImportAtribute несколько - особенно если вы не используете TestA()/TestW() соглашение об именах для собственного экспорта.

Вы не описали, как выглядит ваша искаженная строка. Я подозреваю, что вы смешиваете некоторые строки MBCS и строки UCS-2 (используя 2-байтовый wchar_ts). Если каждый второй байт равен 0, то вы ищете строку UCS-2 (и, возможно, неправильно используете ее как строку MBCS). Если каждый другой байт не равен 0, то вы, вероятно, просматриваете строку MBCS (и, возможно, неправильно используете ее как строку Unicode).

В общем, я бы рекомендовал не использовать TCHARс (или LPTSRс). Они используют макро-магию для переключения между char (1 байт) и wchar_t (2 байта), в зависимости от того _UNICODE является #defineд. Я предпочитаю явное использование chat а также wchar_t чтобы сделать коды намерения очень ясно. Однако вам нужно будет вызывать формы -A или -W любых Win32 API, которые используют TCHAR параметры: например MessageBoxA() или же MessageBoxW() вместо MessageBox() (это макрос, который проверяет, _UNICODE является #defineд.

Тогда вы должны изменить CharSet = CharSet.Auto к чему-то CharSet = CharSet.Ansi (если вызывающий и вызываемый абоненты используют MBCS) или CharSet = CharSet.Unicode (если и абонент, и вызываемый абонент используют UCS-2 Unicode). Но, похоже, ваша DLL использует MBCS, а не Unicode.

http://www.pinvoke.net/ - отличный справочник по вики со множеством примеров сигнатур функций P/Invoke для Win32 API:

Другие вопросы по тегам