我正在使用C ++ / CLR编写包装器。托管的C#类具有如下函数签名
//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 ++中使用的ref字符串的情况?提前谢谢了。
答案 0 :(得分:1)
在高级溢出中,您需要C ++ / CLI(使用托管.NET代码的C ++)
好的,你可以使用System :: String(.NET)类型的句柄并获取它的长度属性。使用该值分配一个大小为+ 2个字符的新缓冲区,使用malloc和memset将其清零。锁定字符串,复制其内容并再次解锁。
如果有帮助,有一个转换运算符可以从System :: String ^转到MFC的CString。它将使代码成为单个衬垫
答案 1 :(得分:1)
我将添加一些我今天早上写的代码示例。一般来说,当谈到返回对象时(即使char*
字符串是一个对象的广义),C / C ++中的大问题是:
最后一个可选问题是,是否必须真正释放内存:一个方法可以返回一个指向内部对象的指针,该内部对象的生命周期等于程序的生命周期并且不能被释放。例如:
const char* Message()
{
return "OK";
}
你不能释放Message()
返回的内存!
当您编写将由其他程序使用的库(dll)时,此问题会变得更加复杂:dll中使用的malloc
和new
可能不同/与主程序(或其他dll)使用的malloc
和new
不同,因此您不应该使用 来free
(主程序)用dll释放malloc
(ed)的内存。
这个特殊问题有三种可能的解决方案:
LocalAlloc
和CoTaskMemAlloc
。甚至可以从.NET(Marshal.AllocHGlobal
和Marshal.AllocCoTaskMem
)访问它们。通过这种方式,主应用程序可以释放由dll Free()
方法,必须用来释放dll分配的内存SetAllocator(void *(*allocator)(size_t))
和SetFree(void (*free)(void*))
,所以存储函数指针的方法,主应用程序可以使用它来设置分配器并由dll自由使用,以便它们在主应用程序和DLL之间共享。该DLL将使用这些分配器。请注意,SetAllocator(malloc); SetFree(free)
如果由主应用程序完成,则完全合法:现在dll将使用主应用程序malloc
,而不是dll' malloc
!< / LI>
作为一个重要的旁注:我们是在2018年。至少15年你应该忘记{C} for Windows中的char*
字符串。使用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
包含使用的字符数(包括终止\0
)。 response
始终\0
已终止。这里的问题是response
可能会被截断和/或调用者可能会分配一个太大的缓冲区(如果不幸的话,它不会真正保护它免受截断问题:-))。我在这里重复一遍: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
包含使用的字符数(包括终止\0
)。 response
始终\0
已终止。
// in input length is the size of response, in output the number of characters (not bytes!) written to response
// (INCLUDING THE \0!). 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] = '\0';
*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
的简单方法,使用response
分配CoTaskMemAlloc
缓冲区并将结果复制到ansi字符串(char*
)。调用者必须CoTaskMemFree
分配的内存。 response
始终\0
已终止。
// 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 \0
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
的简单方法,使用&#34; private&#34;分配response
缓冲区。 &#34;文库&#34; allocator并将结果复制到ansi字符串(char*
)。调用者必须使用库解除分配器MyLibraryFree
来释放分配的内存。 response
始终\0
已终止。
// 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 \0
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
的简单方法,使用settable(通过response
/ SetLibraryAllocator
方法)分配器分配SetLibraryFree
缓冲区(如果使用了默认值,则使用没有选择特殊的分配器)并将结果复制到ansi字符串(char*
)。调用者必须使用库解除分配器LibraryFree
(使用SetLibraryFree
选择的分配器)来释放分配的内存,或者如果它已设置了不同的分配器,它可以直接使用该解除分配器。 response
始终\0
已终止。
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 \0
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
始终\0
已终止。
// 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 \0
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<>
}
答案 2 :(得分:0)
是。但是,再次,
CString unmanaged = CString(System :: String ^)为你完成所有这些。