【问题标题】:Return multiple strings from dll从 dll 返回多个字符串
【发布时间】:2017-06-07 08:35:17
【问题描述】:

我们正在讨论什么是从一个 dll 函数返回多个字符串的好方法。目前我们有 8 个字符串,但还会有更多。为简单起见,我现在认为所有字符串的长度都相同。

extern "C" int DLLNAME_ _stdcall GetResult(TestResults* testResults);

在哪里

struct TestResults
{
    int stringLengths;
    char* string1;
    char* string2;
    char* string3;
    char* string4;
    ...
};

或第二个选项:哪里

struct TestResults
{
    int stringLengths;
    char string1[64];
    char string2[64];
    char string3[64];
    char string4[64];
    ...
};

第三个选项: extern "C" int DLLNAME_ _stdcall GetResult(int stringLengths, char* string1, char* string2, char* string3, ...);

dll 将通过串行线路进行通信并检索将填充到字符串中的信息。需要分配内存的地方有待讨论,可以作为答案的一部分。

背景是我们有一个喜欢第二种方法的 VB6 应用程序团队和一个喜欢第一种方法的 C++/C# 团队。最后一种方法看起来适合两个团队,但对我来说有这么多参数看起来有点奇怪。

也许还有更多选择。 Windows下的常见做法是什么?来自 Windows API 的任何示例或用于选择其中一个的参数?

编辑:字符串的含义与名字、姓氏、电子邮件一样。我们目前有八个,但将来我们可能会添加一对,例如地址。数组不是正确的选择,但从原始上下文中并不清楚。

【问题讨论】:

  • 使用char*,内存分配/释放是如何管理的?还将BSTR 视为一种跨语言类型(具有定义的内存管理)。
  • 我更新了问题以回答您的评论。我们没有严格的规则来说明分配需要在哪里进行。这可能是我们最终不知道什么是好的/常见的想法的部分原因。
  • 如果您正在与 VB 6 进行互操作,您绝对应该使用BSTR。这就是 VB 6 使用的字符串类型。在 .NET 应用程序中也能正常工作,因为它是标准 COM 类型。
  • 或者您可以以REG_MULTI_SZ 的形式返回它 - 以空字符结尾的字符串序列,以空字符串 (\0) 结尾。下面是一个例子:String1\0String2\0String3\0LastString\0\0

标签: c++ string windows dll


【解决方案1】:

最好的方法可能是使用一个安全数组来存储BSTR 字符串。

VB 和 C# 都非常了解安全数组:在 C# 中,BSTR 字符串的安全数组会自动转换为string[] 数组。

在 C++ 方面,您可以使用 ATL::CComSafeArray 帮助类来简化安全数组编程。

您会在this MSDN Magazine article 中找到有趣的材料(特别是,请查看段落生产一个安全的字符串数组)。


从上述文章中:在 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;
}

在 C# 端,您可以使用此 PInvoke 声明:

[DllImport("MyDll.dll", PreserveSig = false)]
public static extern void MyDllGetStrings(
  [Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
  out string[] result);

【讨论】:

    【解决方案2】:

    当您将函数声明为 extern "C" 时,我想您不能使用 std::vector&lt;std::string&gt; 作为返回类型。

    另一种可能是:

    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);
    

    这样,您可以灵活地返回任意数量的字符串。还可以轻松循环您的TestResults

    编辑 #1: 如 cmets 所述:使用 BSTR。所以你的结构看起来像:

    struct TestResults
    {
       int   size; /* number of strings           */
       BSTR* arr;  /* pointer to an array of BSTR */
    };
    

    BSTR 将通过:BSTR MyBstr = SysAllocString(L"I am a happy BSTR"); 变为 allocated。此分配还设置包含字符串长度的成员。您必须使用以下命令释放分配的内存:SysFreeString(MyBstr);。您还需要分配整个数组BSTR*

    【讨论】:

    • 比较你的String类型和MS-Windows类型BSTR(注意BSTR已经定义了跨语言内存管理)
    • 无论如何,您都不应该将 STL 类型用作 DLL 中的返回值。它们不能在不同版本的运行时库中互换使用。
    • 我强烈建议提供一个单独的 freeResult 函数。无论需要释放多少个不同的数组,这都使用户可以轻松地释放。这样,也不再需要 BSTR,我们可以再次回到简单的 C 字符串。我们甚至可以将结构作为指针返回(错误时为 NULL)...这两个函数将等效于 Sys(Alloc|Free)String 对...
    • ...而且不用担心如何字符串是如何分配的,我们甚至可以使用操作符new[]和delete[]。
    • 这很好用,@Aconcagua,当您想使用任何需要内存管理的东西(包括 STL 类型)时,这是推荐的模式。它也是一个真正的“通用”API,因为它适用于所有语言,从汇编和 C 到 Python 和 Haskell。不幸的是,它给程序员带来了更多的负担,这在通常不需要手动内存管理的语言中是一个特别的缺点,程序员没有纪律。 BSTR 的优点是它是一种 COM 类型,在 Windows 中无处不在,并在 VB 和 .NET 中自动管理。方便,错误少。
    猜你喜欢
    • 1970-01-01
    • 2021-12-01
    • 2012-02-26
    • 2019-12-28
    • 2014-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-13
    相关资源
    最近更新 更多