C++ 从dll返回多个字符串
我们正在讨论从一个dll函数返回多个字符串的好方法。目前我们有8个字符串,但会有更多。为了简单起见,现在我认为所有的字符串都有相等的长度。C++ 从dll返回多个字符串,c++,string,windows,dll,C++,String,Windows,Dll,我们正在讨论从一个dll函数返回多个字符串的好方法。目前我们有8个字符串,但会有更多。为了简单起见,现在我认为所有的字符串都有相等的长度。 extern "C" int DLLNAME_ _stdcall GetResult(TestResults* testResults); 在哪里 或者第二个选择:在哪里 struct TestResults { int stringLengths; char string1[64]; char string2[64]; c
extern "C" int DLLNAME_ _stdcall GetResult(TestResults* testResults);
在哪里
或者第二个选择:在哪里
struct TestResults
{
int stringLengths;
char string1[64];
char string2[64];
char string3[64];
char string4[64];
...
};
第三种选择:
外部“C”int DLLNAME_ustdcall GetResult(int stringlength、char*string1、char*string2、char*string3等)
dll将通过串行线进行通信,并检索将填充到字符串中的信息。需要分配内存的地方是开放讨论的,可以作为答案的一部分
背景是我们有一个更喜欢第二种方法的VB6应用程序团队和一个更喜欢第一种方法的C++/C团队。最后一种方法看起来适合这两个团队,但对于我来说,有这么多参数看起来有点奇怪
也许有更多的选择。Windows下的常见做法是什么?从Windows API或参数中选择一个或另一个的示例
编辑:字符串的含义与名字、姓氏、电子邮件中的含义相同。我们目前有八个,但在未来,我们可能会添加一对,例如地址。数组不是正确的选择,但从原始上下文中看不清楚。当您将函数声明为
extern“C”
时,我想您不能使用std::vector
作为返回类型
另一种可能性是:
struct String
{
int size; /* size of string */
const char* str; /* actual string */
}
struct TestResults
{
int size; /* number of strings */
String* arr; /* pointer to an array of String */
};
然后和以前一样:
extern "C" int DLLNAME_ _stdcall GetResult(TestResults* testResults);
这样,您就可以灵活地返回任意多的字符串。此外,循环查看测试结果也很容易
编辑#1:如评论中所述:使用。因此,您的结构将如下所示:
struct TestResults
{
int size; /* number of strings */
BSTR* arr; /* pointer to an array of BSTR */
};
一个BSTR
将通过:BSTR MyBstr=SysAllocString(L“我是一个快乐的BSTR”)代码>。此分配还设置包含字符串长度的成员。您必须使用:SysFreeString(MyBstr)释放分配的内存代码>。您还需要分配整个数组BSTR*
最好的方法可能是使用安全数组存储BSTR
字符串
VB和C都非常了解安全数组:在C#中,由BSTR
字符串组成的安全数组会自动转换为string[]
数组
在C++方面,可以使用<代码> ATL::CCOMSAFAREAREX < /COD>助手类,以简化安全数组编程。
您将在中找到有趣的内容(特别是,请查看生成安全字符串数组的段落)
从上述文章中可以看出:在C++端,可以实现C接口DLL,导出这样的函数:
extern "C" HRESULT MyDllGetStrings(/* [out] */ SAFEARRAY** ppsa)
{
try {
// Create a SAFEARRAY containing 'count' BSTR strings
CComSafeArray<BSTR> sa(count);
for (LONG i = 0; i < count; i++) {
// Use ATL::CComBSTR to safely wrap BSTR strings in C++
CComBSTR bstr = /* your string, may build from std::wstring or CString */ ;
// Move the the BSTR string into the safe array
HRESULT hr = sa.SetAt(i, bstr.Detach(), FALSE);
if (FAILED(hr)) {
// Error...
return hr;
}
}
// Return ("move") the safe array to the caller
// as an output parameter (SAFEARRAY **ppsa)
*ppsa = sa.Detach();
} catch (const CAtlException& e) {
// Convert ATL exceptions to HRESULTs
return e;
}
// All right
return S_OK;
}
使用char*
如何管理内存分配/释放?另外,conciderBSTR
作为一种跨语言类型(具有定义的内存管理)。我更新了问题以回答您的评论。我们没有严格的规则来说明分配需要在哪里进行。这可能是我们最终不知道什么是好的/常见的想法的部分原因。如果您与VB 6交互,您绝对应该使用BSTR
。这就是VB6使用的字符串类型。在.NET应用程序中也可以正常工作,因为它是标准的COM类型。或者,您可以以以下形式返回它:一系列以null结尾的字符串,以空字符串(\0)结尾。以下是一个示例:String1\0String2\0String3\0LastString\0\0将您的String
类型与MS Windows类型BSTR
(请注意,BSTR已定义了语言间内存管理)无论如何,您都不应该在DLL中使用STL类型作为返回值。它们不能在不同版本的运行库之间互换使用。我强烈建议提供单独的freeResult函数。这使得用户可以轻松地解除分配,无论需要解除分配多少个不同的阵列。这样,就不再需要BSTR了,我们可以再次回到简单的C字符串。我们甚至可以将结构作为指针返回(错误时为NULL)。。。这两个函数将等价于Sys(Alloc | Free)字符串对。。。。。。而且不必担心字符串是如何分配的,我们甚至可以使用运算符new[]和delete[]。这很好,@Aconcagua,当您想要使用任何需要内存管理的东西时,包括STL类型时,这是推荐的模式。它也是一个真正的“通用”API,因为它适用于所有语言,从汇编语言和C语言到Python和Haskell语言。不幸的是,这给程序员带来了更大的负担,这在通常不需要手动内存管理的语言中是一个特别的缺点,因为程序员没有受到约束。BSTR是一种COM类型,在Windows中无处不在,并且在VB和.NET中自动管理。方便,误差小。
extern "C" HRESULT MyDllGetStrings(/* [out] */ SAFEARRAY** ppsa)
{
try {
// Create a SAFEARRAY containing 'count' BSTR strings
CComSafeArray<BSTR> sa(count);
for (LONG i = 0; i < count; i++) {
// Use ATL::CComBSTR to safely wrap BSTR strings in C++
CComBSTR bstr = /* your string, may build from std::wstring or CString */ ;
// Move the the BSTR string into the safe array
HRESULT hr = sa.SetAt(i, bstr.Detach(), FALSE);
if (FAILED(hr)) {
// Error...
return hr;
}
}
// Return ("move") the safe array to the caller
// as an output parameter (SAFEARRAY **ppsa)
*ppsa = sa.Detach();
} catch (const CAtlException& e) {
// Convert ATL exceptions to HRESULTs
return e;
}
// All right
return S_OK;
}
[DllImport("MyDll.dll", PreserveSig = false)]
public static extern void MyDllGetStrings(
[Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
out string[] result);