0

我在将 c++ 非托管 dll 导入 C# [winform] 时遇到问题。有人可以帮忙吗?

基本上我只是想在 C++ 中创建一个安全的字符串数组并尝试将它发送到 C#。

这是我的 C++ 代码。

extern "C" __declspec(dllexport) BOOL GetStringArr(SAFEARRAY* arr)
{
SAFEARRAY*    myArray;
  SAFEARRAYBOUND  rgsabound[1];

  rgsabound[0].lLbound = 0;
  rgsabound[0].cElements = 5;

  myArray = SafeArrayCreate(VT_BSTR, 1, rgsabound);
  VARIANT* pvData = (VARIANT*)(myArray->pvData);

  pvData[0].vt = VT_BSTR;
  pvData[0].bstrVal = SysAllocString(L"FirstString");
  pvData[1].vt = VT_BSTR;
  pvData[1].bstrVal = SysAllocString(L"SecondString");
  pvData[2].vt = VT_BSTR;
  pvData[2].bstrVal = SysAllocString(L"ThirdString");
  pvData[3].vt = VT_BSTR;
  pvData[3].bstrVal = SysAllocString(L"FourthString");
  pvData[4].vt = VT_BSTR;
  pvData[4].bstrVal = SysAllocString(L"FifthString");

  arr = myArray;
  return true;
}

这是我的 C# 代码。

[DllImport("MyData.dll", EntryPoint = "GetStringArr")]
public static extern bool GetStringArr([MarshalAs(UnmanagedType.SafeArray)] out Array strServerList); 

当我从 C# 调用 GetStringArr 时出现异常。我确信我正在做一些愚蠢的事情。有人可以帮忙吗?

提前致谢。

4

4 回答 4

1

您的 C++ 代码中有几个问题。您正在返回一个数组,该数组要求参数为 SAFEARRAY**。您还在用错误的数据填充数组,您创建了一个字符串数组,但您正在编写 VARIANT。不确定意图是什么,我将在代码修复中保留变体:

extern "C" __declspec(dllexport) BOOL GetStringArr(SAFEARRAY** arr)
{
  SAFEARRAY*    myArray;
  SAFEARRAYBOUND  rgsabound[1];

  rgsabound[0].lLbound = 0;
  rgsabound[0].cElements = 5;

  myArray = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
  VARIANT* pvData = 0;
  SafeArrayAccessData(myArray, (void**)&pvData);

  pvData[0].vt = VT_BSTR;
  pvData[0].bstrVal = SysAllocString(L"FirstString");
  // etc..
  SafeArrayUnaccessData(myArray);

  *arr = myArray;
  return true;
}

C#代码:

        object[] array;
        bool ok = GetStringArr(out array);

    [DllImport(@"blah.dll", EntryPoint = "GetStringArr")]
    [return: MarshalAs(UnmanagedType.U1)]
    public static extern bool GetStringArr([MarshalAs(UnmanagedType.SafeArray)] out object[] strServerList); 
于 2010-09-07T14:16:23.437 回答
1

C 和 .NET 方面的一些问题

在 C 侧

  1. 不正确的参数间接。由于您在函数中分配 SAFEARRAY 描述符,因此您需要一个 SAFEARRAY**。
  2. SAFEARRAY 未正确填充。您使用 VT_BSTR 的基本类型创建了 SAFEARRAY 描述符,这意味着数据元素应该是 BSTR。

C代码

extern "C" __declspec(dllexport)
BOOL GetStringArr(SAFEARRAY** arr) 
{ 
  SAFEARRAY*    myArray; 
  SAFEARRAYBOUND  rgsabound[1]; 

  rgsabound[0].lLbound = 0; 
  rgsabound[0].cElements = 5; 

  myArray = SafeArrayCreate(VT_BSTR, 1, rgsabound); 
  BSTR* pvData = (BSTR*)(myArray->pvData); 

  pvData[0] = SysAllocString(L"FirstString"); 
  pvData[1] = SysAllocString(L"SecondString"); 
  pvData[2] = SysAllocString(L"ThirdString"); 
  pvData[3] = SysAllocString(L"FourthString"); 
  pvData[4] = SysAllocString(L"FifthString"); 

  *arr = myArray;
  return true; 
}

在 .NET 方面

  1. 需要指定调用约定,否则您将遇到堆栈问题
  2. 您应该设置 SafeArraySubType
  3. 您可以使用out string[]获取指向 SAFEARRAY 的指针

.NET 代码

  class Program
  {
    static void Main(string[] args)
    {
      string[] data;
      bool b = GetStringArr(out data);      
    }

    [DllImport("MyData.dll", 
               CallingConvention = CallingConvention.Cdecl)]
    public static extern bool GetStringArr(
      [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)] 
      out string[] strServerList);    
  }
于 2010-09-07T15:17:55.283 回答
0

您是否有权访问本机 DLL 的源代码?如果是这样,您可以在托管项目选项中启用非托管调试,并逐步执行非托管代码(最好是调试构建)以查看发生了什么。如果没有别的,您可以在调试器选项中启用异常,并调试以查看引发本机异常的位置。

于 2010-09-07T13:53:57.257 回答
0

我建议您将 C++/CLI 项目(程序集)添加到您的 C# 解决方案中。这将使您能够编写同时存在于托管和非托管土地中的代码。这意味着您的 C++/CLI 代码可以List<string^>改为创建 a 并向其添加托管字符串,然后再将其返回给 C#。:-)

于 2010-09-07T14:04:47.013 回答