如何编写以引用字符串作为参数的托管 C# dll 的 C++ 包装器
How to write a C++ wrapper of managed C# dll having ref string as its parameter
我正在使用 C++/CLR 编写包装器。托管 C# class 具有如下函数签名
//C#
int WriteToInstrument(string command, ref string response, int stage);
我必须为这个函数编写一个 C++ 包装器,类似于下面的签名
//C++
int WriteToInstrumentWrap(const char * command, char * response, int stage);
我的问题是:如何处理从 C# 中的 "ref string" 到 C++ 中的 char* 的转换?或者我如何处理需要从 C# 中获取可在 C/C++ 中使用的引用字符串的情况?提前谢谢了。
在高级溢出中,您需要 C++/CLI(使用托管 .NET 代码的 C++)
好的,您获取类型 System::String (.NET) 的句柄并获得其长度 属性。使用该值分配大小为 2 个字符的新缓冲区,使用 malloc 和 memset 将其清零。锁定字符串,复制其内容并再次解锁。
有一个转换运算符可以从 System::String ^ 到 MFC 的 CString,如果有帮助的话。它将使代码成为单行代码
是的。但是,
CString unmanaged = CString(System::String ^) 为您完成这一切。
我将添加一些我今天早上编写的代码示例。一般来说,当谈到 returning 对象时(在广义上,即使 char*
字符串也是一个对象),C/C++ 中的大问题是:
- 谁分配内存
- 需要多少元素
- 内存是如何分配的(使用哪个分配器)
- 作为推论,必须如何释放内存
最后一个可选问题是内存是否必须真正释放:一个方法可以 return 一个指向内部对象的指针,该对象的生命周期等于程序的生命周期,并且不应该被释放。例如:
const char* Message()
{
return "OK";
}
您不能释放由 Message()
编辑的 return 内存!
当您编写将被其他程序使用的库(dll)时,这个问题会变得更加复杂:dll 中使用的 malloc
和 new
可以是different/distinct 来自主程序(或另一个 dll)使用的 malloc
和 new
,所以你不应该 free
和 你的(主程序)释放由 dll malloc
(编辑) 的内存。
这个特定问题有三种可能的解决方案:
- 使用共享分配器,例如 OS 给出的分配器。 Windows 给出
LocalAlloc
和 CoTaskMemAlloc
。它们甚至可以从 .NET 访问(Marshal.AllocHGlobal
和 Marshal.AllocCoTaskMem
)。通过这种方式,主应用程序可以释放 dll 分配的内存
- 你的dll的API有一个
Free()
方法必须用来释放dll分配的内存
- 你的 dll 的 API 有一些方法,比如
SetAllocator(void *(*allocator)(size_t))
和 SetFree(void (*free)(void*))
,所以存储函数指针的方法,主应用程序可以使用它来设置分配器和释放由 dll 使用,以便它们在主应用程序和 dll 之间共享。 dll 将使用这些分配器。请注意 SetAllocator(malloc); SetFree(free)
如果由主应用程序完成是完全合法的:现在 dll 将使用主应用程序的 malloc
,而不是 dll 的 malloc
!
- 我将给出一些示例中使用的快捷方式:该方法将分配器(函数指针)作为参数,然后将使用该分配器
作为一个重要的旁注:我们现在是 2018 年。你应该至少在 15 年之前忘记了 char*
用于 C 中的字符串 Windows。使用 wchar_t
。总是。
最后是一些代码:-)
现在...给定(C# 代码):
int WriteToInstrument(string command, ref string response, int stage)
{
response = "The quick brown fox jumps over the lazy dog";
return 0;
}
调用 WriteToInstrument
然后将 response
结果复制到 ansi 字符串 (char*
) 的简单方法。缓冲区由调用者分配,大小为 length
。执行该方法后,length
包含使用的字符数(包括终止 [=54=]
)。 response
总是 [=54=]
终止。这里的问题是 response
可能会被截断 and/or 调用者可能会分配一个太大的缓冲区(如果不幸的话,这并不能真正保护它免受截断问题的影响:-))。我在这里重复一遍:在 2018 年对字符串使用 char*
是古老的技术。
// Utility method to copy a unicode string to a fixed size buffer
size_t Utf16ToAnsi(const wchar_t *wstr, char *str, size_t length)
{
if (length == 0)
{
return 0;
}
// This whole piece of code can be moved to a method
size_t length2 = WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, (int)length, nullptr, nullptr);
// WideCharToMultiByte will try to write up to *length characters, but
// if the buffer is too much small, it will return 0,
// **and the tring won't be 0-terminated**
if (length2 != 0)
{
return length2;
}
// Buffer too much small
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
// We add a terminating 0
str[length - 1] = 0;
return length;
}
// Big bad error, shouldn't happen. Return 0 but terminate the string
str[0] = 0;
return 0;
}
使用示例:
char response[16];
size_t length = sizeof(response) / sizeof(char); // useless / sizeof(char) == / 1 by definition
WriteToInstrumentWrap1("cmd1", response, &length, 1);
std::cout << "fixed buffer char[]: " << response << ", used length: " << length << std::endl;
或(使用std::vector<>
/std::array<>
)
//Alternative: std::array<char, 16> response;
std::vector<char> response(16);
size_t length = response.size();
WriteToInstrumentWrap1("cmd1", response.data(), &length, 1);
std::cout << "fixed buffer vector<char>: " << response.data() << ", used length: " << length << std::endl;
调用 WriteToInstrument
然后将 response
结果复制到 unicode 字符串 (wchar_t*
) 的简单方法。缓冲区由调用者分配,大小为 length
。执行该方法后,length
包含使用的字符数(包括终止 [=54=]
)。 response
总是 [=54=]
终止。
// in input length is the size of response, in output the number of characters (not bytes!) written to response
// (INCLUDING THE [=15=]!). The string is always correctly terminated.
int WriteToInstrumentWrap2(const wchar_t *command, wchar_t *response, size_t *length, int stage)
{
auto str1 = gcnew String(command);
String ^str2 = nullptr;
int res = WriteToInstrument(str1, str2, 5);
pin_ptr<const Char> ppchar = PtrToStringChars(str2);
const wchar_t *pch = const_cast<wchar_t*>(ppchar);
*length = (size_t)str2->Length < *length ? str2->Length : *length - 1;
memcpy(response, pch, *length * sizeof(wchar_t));
response[*length] = '[=15=]';
*length++;
return res;
}
使用示例:
wchar_t response[16];
size_t length = sizeof(response) / sizeof(wchar_t);
WriteToInstrumentWrap2(L"cmd1", response, &length, 1);
std::wcout << L"fixed buffer wchar_t[]: " << response << L", used length: " << length << std::endl;
或(使用std::vector<>
/std::array<char, 16>
)
//Alternative: std::array<wchar_t, 16> response;
std::vector<wchar_t> response(16);
size_t length = response.size();
WriteToInstrumentWrap2(L"cmd1", response.data(), &length, 1);
std::wcout << L"fixed buffer vector<wchar_t>: " << response.data() << ", used length: " << length << std::endl;
接下来的所有示例都将使用 char
而不是 wchar_t
。转换它们很容易。我会在这里重复一遍:在 2018 年对字符串使用 char*
是古老的技术。这就像使用 ArrayList
而不是 List<>
调用 WriteToInstrument
的简单方法,使用 CoTaskMemAlloc
分配 response
缓冲区并将结果复制到 ansi 字符串 (char*
)。调用者必须 CoTaskMemFree
分配的内存。 response
总是 [=54=]
终止。
// Memory allocated with CoTaskMemAlloc. Remember to CoTaskMemFree!
int WriteToInstrumentWrap3(const char *command, char **response, int stage)
{
auto str1 = gcnew String(command);
String ^str2 = nullptr;
int res = WriteToInstrument(str1, str2, 5);
pin_ptr<const Char> ppchar = PtrToStringChars(str2);
const wchar_t *pch = const_cast<wchar_t*>(ppchar);
// length includes the terminating [=18=]
size_t length = WideCharToMultiByte(CP_ACP, 0, pch, -1, nullptr, 0, nullptr, nullptr);
*response = (char*)CoTaskMemAlloc(length * sizeof(char));
WideCharToMultiByte(CP_ACP, 0, pch, -1, *response, length, nullptr, nullptr);
return res;
}
使用示例:
char *response;
WriteToInstrumentWrap3("cmd1", &response, 1);
std::cout << "CoTaskMemFree char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Must free with CoTaskMemFree!
CoTaskMemFree(response);
调用 WriteToInstrument
的简单方法,使用 "private" "library" 分配器分配 response
缓冲区并将结果复制到 ansi 字符串(char*
).调用者必须使用库释放器 MyLibraryFree
来释放分配的内存。 response
总是 [=54=]
终止。
// Free method used by users of the library
void MyLibraryFree(void *p)
{
free(p);
}
// The memory is allocated through a proprietary allocator of the library. Use MyLibraryFree() to free it.
int WriteToInstrumentWrap4(const char *command, char **response, int stage)
{
auto str1 = gcnew String(command);
String ^str2 = nullptr;
int res = WriteToInstrument(str1, str2, 5);
pin_ptr<const Char> ppchar = PtrToStringChars(str2);
const wchar_t *pch = const_cast<wchar_t*>(ppchar);
// length includes the terminating [=20=]
size_t length = WideCharToMultiByte(CP_ACP, 0, pch, -1, nullptr, 0, nullptr, nullptr);
*response = (char*)malloc(length);
WideCharToMultiByte(CP_ACP, 0, pch, -1, *response, length, nullptr, nullptr);
return res;
}
使用示例:
char *response;
WriteToInstrumentWrap4("cmd1", &response, 1);
std::cout << "Simple MyLibraryFree char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Must free with the MyLibraryFree() method
MyLibraryFree(response);
调用 WriteToInstrument
的简单方法,使用可设置的(通过 SetLibraryAllocator
/SetLibraryFree
方法)分配器分配 response
缓冲区(有一个默认值如果没有选择特殊分配器则使用)并将结果复制到 ansi 字符串(char*
)。调用者必须使用库解除分配器 LibraryFree
(使用由 SetLibraryFree
选择的分配器)来释放分配的内存,或者如果它设置了不同的分配器,则可以直接使用该解除分配器。 response
总是 [=54=]
终止。
void *(*libraryAllocator)(size_t length) = malloc;
void (*libraryFree)(void *p) = free;
// Free method used by library
void SetLibraryAllocator(void *(*allocator)(size_t length))
{
libraryAllocator = allocator;
}
// Free method used by library
void SetLibraryFree(void (*free)(void *p))
{
libraryFree = free;
}
// Free method used by library
void LibraryFree(void *p)
{
libraryFree(p);
}
// The memory is allocated through the allocator specified by SetLibraryAllocator (default the malloc of the dll)
// You can use LibraryFree to free it, or change the SetLibraryAllocator and the SetLibraryFree with an allocator
// of your choosing and then use your free.
int WriteToInstrumentWrap5(const char *command, char **response, int stage)
{
auto str1 = gcnew String(command);
String ^str2 = nullptr;
int res = WriteToInstrument(str1, str2, 5);
pin_ptr<const Char> ppchar = PtrToStringChars(str2);
const wchar_t *pch = const_cast<wchar_t*>(ppchar);
// length includes the terminating [=22=]
size_t length = WideCharToMultiByte(CP_ACP, 0, pch, -1, nullptr, 0, nullptr, nullptr);
*response = (char*)libraryAllocator(length);
WideCharToMultiByte(CP_ACP, 0, pch, -1, *response, length, nullptr, nullptr);
return res;
}
使用示例:
void* MyLocalAlloc(size_t size)
{
return LocalAlloc(0, size);
}
void MyLocalFree(void *p)
{
LocalFree(p);
}
然后:
// Using the main program malloc/free
SetLibraryAllocator(malloc);
SetLibraryFree(free);
char *response;
WriteToInstrumentWrap5("cmd1", &response, 1);
std::cout << "SetLibraryAllocator(malloc) char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Here I'm using the main program free, because the allocator has been set to malloc
free(response);
或
// Using the Windows LocalAlloc/LocalFree. Note that we need to use an intermediate method to call them because
// they have a different signature (stdcall instead of cdecl and an additional parameter for LocalAlloc)
SetLibraryAllocator(MyLocalAlloc);
SetLibraryFree(MyLocalFree);
char *response;
WriteToInstrumentWrap5("cmd1", &response, 1);
std::cout << "SetLibraryAllocator(LocalAlloc) char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Here I'm using diretly the Windows API LocalFree
LocalFree(response);
更复杂的方法调用 WriteToInstrument
但具有作为参数的 allocator
将用于分配 response
缓冲区。有一个附加参数 par
将传递给 allocator
。然后该方法会将结果复制为 ansi 字符串 (char*
)。调用者必须根据使用的 allocator
使用特定的释放器释放内存。 response
总是 [=54=]
终止。
// allocator is a function that will be used for allocating the memory. par will be passed as a parameter to allocator(length, par)
// the length of allocator is in number of elements, *not in bytes!*
int WriteToInstrumentWrap6(const char *command, char **response, char *(*allocator)(size_t length, void *par), void *par, int stage)
{
auto str1 = gcnew String(command);
String ^str2 = nullptr;
int res = WriteToInstrument(str1, str2, 5);
pin_ptr<const Char> ppchar = PtrToStringChars(str2);
const wchar_t *pch = const_cast<wchar_t*>(ppchar);
// length includes the terminating [=26=]
size_t length = WideCharToMultiByte(CP_ACP, 0, pch, -1, nullptr, 0, nullptr, nullptr);
*response = allocator(length, par);
WideCharToMultiByte(CP_ACP, 0, pch, -1, *response, length, nullptr, nullptr);
return res;
}
使用示例(显示多个分配器:vector<>
、malloc
、new[]
、unique_ptr<>
):
注意 par
参数的使用。
template<typename T>
T* vector_allocator(size_t length, void *par)
{
std::vector<T> *pvector = static_cast<std::vector<T>*>(par);
pvector->resize(length);
return pvector->data();
}
template<typename T>
T* malloc_allocator(size_t length, void *par)
{
return (T*)malloc(length * sizeof(T));
}
template<typename T>
T* new_allocator(size_t length, void *par)
{
return new T[length];
}
template<typename T>
T* uniqueptr_allocator(size_t length, void *par)
{
std::unique_ptr<T[]> *pp = static_cast<std::unique_ptr<T[]>*>(par);
pp->reset(new T[length]);
return pp->get();
}
然后(注意有时传递给 WriteToInstrumentWrap6
的参数之一是 useless
因为我们已经有一个指向缓冲区的指针):
{
std::vector<char> response;
char *useless;
WriteToInstrumentWrap6("cmd1", &useless, vector_allocator<char>, &response, 1);
std::cout << "vector char: " << response.data() << ", used length: " << response.size() << std::endl;
// The memory is automatically freed by std::vector<>
}
{
char *response;
WriteToInstrumentWrap6("cmd1", &response, malloc_allocator<char>, nullptr, 1);
std::cout << "malloc char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Must free with free
free(response);
}
{
char *response;
WriteToInstrumentWrap6("cmd1", &response, new_allocator<char>, nullptr, 1);
std::cout << "new[] char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Must free with delete[]
delete[] response;
}
{
std::unique_ptr<char[]> response;
char *useless;
WriteToInstrumentWrap6("cmd1", &useless, uniqueptr_allocator<char>, &response, 1);
std::cout << "unique_ptr<> char: " << response.get() << ", used length: " << strlen(response.get()) + 1 << std::endl;
// The memory is automatically freed by std::unique_ptr<>
}
我正在使用 C++/CLR 编写包装器。托管 C# class 具有如下函数签名
//C#
int WriteToInstrument(string command, ref string response, int stage);
我必须为这个函数编写一个 C++ 包装器,类似于下面的签名
//C++
int WriteToInstrumentWrap(const char * command, char * response, int stage);
我的问题是:如何处理从 C# 中的 "ref string" 到 C++ 中的 char* 的转换?或者我如何处理需要从 C# 中获取可在 C/C++ 中使用的引用字符串的情况?提前谢谢了。
在高级溢出中,您需要 C++/CLI(使用托管 .NET 代码的 C++)
好的,您获取类型 System::String (.NET) 的句柄并获得其长度 属性。使用该值分配大小为 2 个字符的新缓冲区,使用 malloc 和 memset 将其清零。锁定字符串,复制其内容并再次解锁。
有一个转换运算符可以从 System::String ^ 到 MFC 的 CString,如果有帮助的话。它将使代码成为单行代码
是的。但是,
CString unmanaged = CString(System::String ^) 为您完成这一切。
我将添加一些我今天早上编写的代码示例。一般来说,当谈到 returning 对象时(在广义上,即使 char*
字符串也是一个对象),C/C++ 中的大问题是:
- 谁分配内存
- 需要多少元素
- 内存是如何分配的(使用哪个分配器)
- 作为推论,必须如何释放内存
最后一个可选问题是内存是否必须真正释放:一个方法可以 return 一个指向内部对象的指针,该对象的生命周期等于程序的生命周期,并且不应该被释放。例如:
const char* Message() { return "OK"; }
您不能释放由
Message()
编辑的 return 内存!
当您编写将被其他程序使用的库(dll)时,这个问题会变得更加复杂:dll 中使用的 malloc
和 new
可以是different/distinct 来自主程序(或另一个 dll)使用的 malloc
和 new
,所以你不应该 free
和 你的(主程序)释放由 dll malloc
(编辑) 的内存。
这个特定问题有三种可能的解决方案:
- 使用共享分配器,例如 OS 给出的分配器。 Windows 给出
LocalAlloc
和CoTaskMemAlloc
。它们甚至可以从 .NET 访问(Marshal.AllocHGlobal
和Marshal.AllocCoTaskMem
)。通过这种方式,主应用程序可以释放 dll 分配的内存
- 你的dll的API有一个
Free()
方法必须用来释放dll分配的内存 - 你的 dll 的 API 有一些方法,比如
SetAllocator(void *(*allocator)(size_t))
和SetFree(void (*free)(void*))
,所以存储函数指针的方法,主应用程序可以使用它来设置分配器和释放由 dll 使用,以便它们在主应用程序和 dll 之间共享。 dll 将使用这些分配器。请注意SetAllocator(malloc); SetFree(free)
如果由主应用程序完成是完全合法的:现在 dll 将使用主应用程序的malloc
,而不是 dll 的malloc
! - 我将给出一些示例中使用的快捷方式:该方法将分配器(函数指针)作为参数,然后将使用该分配器
作为一个重要的旁注:我们现在是 2018 年。你应该至少在 15 年之前忘记了 char*
用于 C 中的字符串 Windows。使用 wchar_t
。总是。
最后是一些代码:-)
现在...给定(C# 代码):
int WriteToInstrument(string command, ref string response, int stage)
{
response = "The quick brown fox jumps over the lazy dog";
return 0;
}
调用 WriteToInstrument
然后将 response
结果复制到 ansi 字符串 (char*
) 的简单方法。缓冲区由调用者分配,大小为 length
。执行该方法后,length
包含使用的字符数(包括终止 [=54=]
)。 response
总是 [=54=]
终止。这里的问题是 response
可能会被截断 and/or 调用者可能会分配一个太大的缓冲区(如果不幸的话,这并不能真正保护它免受截断问题的影响:-))。我在这里重复一遍:在 2018 年对字符串使用 char*
是古老的技术。
// Utility method to copy a unicode string to a fixed size buffer
size_t Utf16ToAnsi(const wchar_t *wstr, char *str, size_t length)
{
if (length == 0)
{
return 0;
}
// This whole piece of code can be moved to a method
size_t length2 = WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, (int)length, nullptr, nullptr);
// WideCharToMultiByte will try to write up to *length characters, but
// if the buffer is too much small, it will return 0,
// **and the tring won't be 0-terminated**
if (length2 != 0)
{
return length2;
}
// Buffer too much small
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
// We add a terminating 0
str[length - 1] = 0;
return length;
}
// Big bad error, shouldn't happen. Return 0 but terminate the string
str[0] = 0;
return 0;
}
使用示例:
char response[16];
size_t length = sizeof(response) / sizeof(char); // useless / sizeof(char) == / 1 by definition
WriteToInstrumentWrap1("cmd1", response, &length, 1);
std::cout << "fixed buffer char[]: " << response << ", used length: " << length << std::endl;
或(使用std::vector<>
/std::array<>
)
//Alternative: std::array<char, 16> response;
std::vector<char> response(16);
size_t length = response.size();
WriteToInstrumentWrap1("cmd1", response.data(), &length, 1);
std::cout << "fixed buffer vector<char>: " << response.data() << ", used length: " << length << std::endl;
调用 WriteToInstrument
然后将 response
结果复制到 unicode 字符串 (wchar_t*
) 的简单方法。缓冲区由调用者分配,大小为 length
。执行该方法后,length
包含使用的字符数(包括终止 [=54=]
)。 response
总是 [=54=]
终止。
// in input length is the size of response, in output the number of characters (not bytes!) written to response
// (INCLUDING THE [=15=]!). The string is always correctly terminated.
int WriteToInstrumentWrap2(const wchar_t *command, wchar_t *response, size_t *length, int stage)
{
auto str1 = gcnew String(command);
String ^str2 = nullptr;
int res = WriteToInstrument(str1, str2, 5);
pin_ptr<const Char> ppchar = PtrToStringChars(str2);
const wchar_t *pch = const_cast<wchar_t*>(ppchar);
*length = (size_t)str2->Length < *length ? str2->Length : *length - 1;
memcpy(response, pch, *length * sizeof(wchar_t));
response[*length] = '[=15=]';
*length++;
return res;
}
使用示例:
wchar_t response[16];
size_t length = sizeof(response) / sizeof(wchar_t);
WriteToInstrumentWrap2(L"cmd1", response, &length, 1);
std::wcout << L"fixed buffer wchar_t[]: " << response << L", used length: " << length << std::endl;
或(使用std::vector<>
/std::array<char, 16>
)
//Alternative: std::array<wchar_t, 16> response;
std::vector<wchar_t> response(16);
size_t length = response.size();
WriteToInstrumentWrap2(L"cmd1", response.data(), &length, 1);
std::wcout << L"fixed buffer vector<wchar_t>: " << response.data() << ", used length: " << length << std::endl;
接下来的所有示例都将使用 char
而不是 wchar_t
。转换它们很容易。我会在这里重复一遍:在 2018 年对字符串使用 char*
是古老的技术。这就像使用 ArrayList
而不是 List<>
调用 WriteToInstrument
的简单方法,使用 CoTaskMemAlloc
分配 response
缓冲区并将结果复制到 ansi 字符串 (char*
)。调用者必须 CoTaskMemFree
分配的内存。 response
总是 [=54=]
终止。
// Memory allocated with CoTaskMemAlloc. Remember to CoTaskMemFree!
int WriteToInstrumentWrap3(const char *command, char **response, int stage)
{
auto str1 = gcnew String(command);
String ^str2 = nullptr;
int res = WriteToInstrument(str1, str2, 5);
pin_ptr<const Char> ppchar = PtrToStringChars(str2);
const wchar_t *pch = const_cast<wchar_t*>(ppchar);
// length includes the terminating [=18=]
size_t length = WideCharToMultiByte(CP_ACP, 0, pch, -1, nullptr, 0, nullptr, nullptr);
*response = (char*)CoTaskMemAlloc(length * sizeof(char));
WideCharToMultiByte(CP_ACP, 0, pch, -1, *response, length, nullptr, nullptr);
return res;
}
使用示例:
char *response;
WriteToInstrumentWrap3("cmd1", &response, 1);
std::cout << "CoTaskMemFree char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Must free with CoTaskMemFree!
CoTaskMemFree(response);
调用 WriteToInstrument
的简单方法,使用 "private" "library" 分配器分配 response
缓冲区并将结果复制到 ansi 字符串(char*
).调用者必须使用库释放器 MyLibraryFree
来释放分配的内存。 response
总是 [=54=]
终止。
// Free method used by users of the library
void MyLibraryFree(void *p)
{
free(p);
}
// The memory is allocated through a proprietary allocator of the library. Use MyLibraryFree() to free it.
int WriteToInstrumentWrap4(const char *command, char **response, int stage)
{
auto str1 = gcnew String(command);
String ^str2 = nullptr;
int res = WriteToInstrument(str1, str2, 5);
pin_ptr<const Char> ppchar = PtrToStringChars(str2);
const wchar_t *pch = const_cast<wchar_t*>(ppchar);
// length includes the terminating [=20=]
size_t length = WideCharToMultiByte(CP_ACP, 0, pch, -1, nullptr, 0, nullptr, nullptr);
*response = (char*)malloc(length);
WideCharToMultiByte(CP_ACP, 0, pch, -1, *response, length, nullptr, nullptr);
return res;
}
使用示例:
char *response;
WriteToInstrumentWrap4("cmd1", &response, 1);
std::cout << "Simple MyLibraryFree char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Must free with the MyLibraryFree() method
MyLibraryFree(response);
调用 WriteToInstrument
的简单方法,使用可设置的(通过 SetLibraryAllocator
/SetLibraryFree
方法)分配器分配 response
缓冲区(有一个默认值如果没有选择特殊分配器则使用)并将结果复制到 ansi 字符串(char*
)。调用者必须使用库解除分配器 LibraryFree
(使用由 SetLibraryFree
选择的分配器)来释放分配的内存,或者如果它设置了不同的分配器,则可以直接使用该解除分配器。 response
总是 [=54=]
终止。
void *(*libraryAllocator)(size_t length) = malloc;
void (*libraryFree)(void *p) = free;
// Free method used by library
void SetLibraryAllocator(void *(*allocator)(size_t length))
{
libraryAllocator = allocator;
}
// Free method used by library
void SetLibraryFree(void (*free)(void *p))
{
libraryFree = free;
}
// Free method used by library
void LibraryFree(void *p)
{
libraryFree(p);
}
// The memory is allocated through the allocator specified by SetLibraryAllocator (default the malloc of the dll)
// You can use LibraryFree to free it, or change the SetLibraryAllocator and the SetLibraryFree with an allocator
// of your choosing and then use your free.
int WriteToInstrumentWrap5(const char *command, char **response, int stage)
{
auto str1 = gcnew String(command);
String ^str2 = nullptr;
int res = WriteToInstrument(str1, str2, 5);
pin_ptr<const Char> ppchar = PtrToStringChars(str2);
const wchar_t *pch = const_cast<wchar_t*>(ppchar);
// length includes the terminating [=22=]
size_t length = WideCharToMultiByte(CP_ACP, 0, pch, -1, nullptr, 0, nullptr, nullptr);
*response = (char*)libraryAllocator(length);
WideCharToMultiByte(CP_ACP, 0, pch, -1, *response, length, nullptr, nullptr);
return res;
}
使用示例:
void* MyLocalAlloc(size_t size)
{
return LocalAlloc(0, size);
}
void MyLocalFree(void *p)
{
LocalFree(p);
}
然后:
// Using the main program malloc/free
SetLibraryAllocator(malloc);
SetLibraryFree(free);
char *response;
WriteToInstrumentWrap5("cmd1", &response, 1);
std::cout << "SetLibraryAllocator(malloc) char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Here I'm using the main program free, because the allocator has been set to malloc
free(response);
或
// Using the Windows LocalAlloc/LocalFree. Note that we need to use an intermediate method to call them because
// they have a different signature (stdcall instead of cdecl and an additional parameter for LocalAlloc)
SetLibraryAllocator(MyLocalAlloc);
SetLibraryFree(MyLocalFree);
char *response;
WriteToInstrumentWrap5("cmd1", &response, 1);
std::cout << "SetLibraryAllocator(LocalAlloc) char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Here I'm using diretly the Windows API LocalFree
LocalFree(response);
更复杂的方法调用 WriteToInstrument
但具有作为参数的 allocator
将用于分配 response
缓冲区。有一个附加参数 par
将传递给 allocator
。然后该方法会将结果复制为 ansi 字符串 (char*
)。调用者必须根据使用的 allocator
使用特定的释放器释放内存。 response
总是 [=54=]
终止。
// allocator is a function that will be used for allocating the memory. par will be passed as a parameter to allocator(length, par)
// the length of allocator is in number of elements, *not in bytes!*
int WriteToInstrumentWrap6(const char *command, char **response, char *(*allocator)(size_t length, void *par), void *par, int stage)
{
auto str1 = gcnew String(command);
String ^str2 = nullptr;
int res = WriteToInstrument(str1, str2, 5);
pin_ptr<const Char> ppchar = PtrToStringChars(str2);
const wchar_t *pch = const_cast<wchar_t*>(ppchar);
// length includes the terminating [=26=]
size_t length = WideCharToMultiByte(CP_ACP, 0, pch, -1, nullptr, 0, nullptr, nullptr);
*response = allocator(length, par);
WideCharToMultiByte(CP_ACP, 0, pch, -1, *response, length, nullptr, nullptr);
return res;
}
使用示例(显示多个分配器:vector<>
、malloc
、new[]
、unique_ptr<>
):
注意 par
参数的使用。
template<typename T>
T* vector_allocator(size_t length, void *par)
{
std::vector<T> *pvector = static_cast<std::vector<T>*>(par);
pvector->resize(length);
return pvector->data();
}
template<typename T>
T* malloc_allocator(size_t length, void *par)
{
return (T*)malloc(length * sizeof(T));
}
template<typename T>
T* new_allocator(size_t length, void *par)
{
return new T[length];
}
template<typename T>
T* uniqueptr_allocator(size_t length, void *par)
{
std::unique_ptr<T[]> *pp = static_cast<std::unique_ptr<T[]>*>(par);
pp->reset(new T[length]);
return pp->get();
}
然后(注意有时传递给 WriteToInstrumentWrap6
的参数之一是 useless
因为我们已经有一个指向缓冲区的指针):
{
std::vector<char> response;
char *useless;
WriteToInstrumentWrap6("cmd1", &useless, vector_allocator<char>, &response, 1);
std::cout << "vector char: " << response.data() << ", used length: " << response.size() << std::endl;
// The memory is automatically freed by std::vector<>
}
{
char *response;
WriteToInstrumentWrap6("cmd1", &response, malloc_allocator<char>, nullptr, 1);
std::cout << "malloc char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Must free with free
free(response);
}
{
char *response;
WriteToInstrumentWrap6("cmd1", &response, new_allocator<char>, nullptr, 1);
std::cout << "new[] char: " << response << ", used length: " << strlen(response) + 1 << std::endl;
// Must free with delete[]
delete[] response;
}
{
std::unique_ptr<char[]> response;
char *useless;
WriteToInstrumentWrap6("cmd1", &useless, uniqueptr_allocator<char>, &response, 1);
std::cout << "unique_ptr<> char: " << response.get() << ", used length: " << strlen(response.get()) + 1 << std::endl;
// The memory is automatically freed by std::unique_ptr<>
}