P / Invoke с [Out] StringBuilder / LPTSTR и многобайтовыми символами: искаженный текст? - PullRequest
4 голосов
/ 28 февраля 2009

Я пытаюсь использовать 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 ]

3 голосов
/ 15 мая 2009

Не используйте paramaeter, поскольку вы не выделяете в функции c

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

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

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

3 голосов
/ 28 февраля 2009

Одной из проблем является использование 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() для собственного экспорта.

0 голосов
/ 28 февраля 2009

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

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

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

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

...