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: