Помимо проблемы, связанной с тем, что закрепленные указатели выходят из области видимости перед передачей на someNativeFunction()
, код можно упростить для большей наглядности, особенно если вы используете MSVC2008 или новее.См. на этой странице для получения информации о том, как преобразовать одну строку (расширение до массива должно быть тривиальным).
Отредактировано:
Если вынужны строки ANSI const char*
, тогда создание копии неизбежно, поскольку строки .NET имеют Unicode (UTF-16).На MSVC2008 и новее ваш код может выглядеть следующим образом:
#include <msclr/marshal.h>
using namespace msclr::interop;
marshal_context context;
array<String^>^ tokenArray = gcnew array<String^> {"TokenONE", "TokenTWO"};
char** tokensAsAnsi = new char* [tokenArray->Length];
for(int i = 0; i < tokenArray->Length; i++)
{
tokensAsAnsi[i] = context.marshal_as<const char*>(tokenArray[i]);
}
int myResult = someNativeFunction(ptr, tokensAsAnsi);
// The marshalled results are freed when context goes out of scope
delete[] tokensAsAnsi; // Please note you must use delete[] here!
Это похоже на ваш пример кода, но без необходимости закрепления указателя и reinterpret_cast
-ing.
Есливы готовы работать с широкой строкой const wchar_t*
в someNativeFunction()
, вы можете напрямую использовать (закрепленные) внутренние данные. Однако вам нужно будет убедиться, что указатели остаются закрепленными до тех пор, пока не вернется someNativeFunction()
, что, как указано вкомментарии, могут негативно повлиять на производительность GC.
Если вы собираетесь использовать marshall много строк и производительность крайне важна, вы можете разделить marshalling на несколько потоков, прежде чем передать все в someNativeFunction()
.Прежде чем сделать это, я бы рекомендовал профилировать ваше приложение, чтобы увидеть, является ли преобразование узким местом или лучше сосредоточить усилия в другом месте.
Отредактировано # 2:
Чтобы получить собственную строку в кодировке UTF-8, вы можете сделать с измененной версией вашего кода:
array<String^>^ tokenArray = gcnew array<String^> {"TokenONE", "TokenTWO"};
char** tokensAsUtf8 = new char* [tokenArray->Length];
for(int i = 0; i < tokenArray->Length; i++)
{
array<Byte>^ encodedBytes = Text::Encoding::UTF8->GetBytes(tokenArray[i]);
// Probably just using [0] is fine here
pin_ptr<Byte> pinnedBytes = &encodedBytes[encodedBytes->GetLowerBound(0)];
tokensAsUtf8[i] = new char[encodedBytes->Length + 1];
memcpy(
tokensAsUtf8[i],
reinterpret_cast<char*>(pinnedBytes),
encodedBytes->Length
);
// NULL-terminate the native string
tokensAsUtf8[i][encodedBytes->Length] = '\0';
}
int myResult = someNativeFunction(ptr, tokensAsAnsi);
for(int i = 0; i < tokenArray->Length; i++) delete[] tokensAsUtf8[i];
delete[] tokensAsUtf8;
Если вас беспокоит скорость, вы можете предварительно выделить большой буфер для собственных строк (если вы знаете, что их будет только ограниченное количество) или использовать пул хранилища.
Отредактировано# 3: (О.Г. Чувак) Просто исправлены некоторые мелкие опечатки.