【问题标题】:C# Marshalling (C# call C++ DLL)C#编组(C#调用C++ DLL)
【发布时间】:2017-08-16 02:43:51
【问题描述】:

你们能帮我解决以下问题吗?我有一个 C++ 函数 dll,它将被另一个 C# 应用程序调用。我需要的功能之一如下:

unsigned long makeArray(unsigned char* sendArr, unsigned long sendArrLen, unsigned char *recvArr, unsigned long *recvArrLen);

我用 C# 编写了以下代码:

[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ulong makeArray(byte[] sendArr, ulong sendArrLen, byte[] recvArr, ulong recvArrLen);

    private byte[] MakeArray()
    {
        byte[] arrSend = new byte[] { 0x00, 0x12, 0x34 };

        ulong nRecvArrLen = 0;
        byte[] arrRecv = null; // assign in c++ dll function (variable size)

        if(makeArray(arrSend, (ulong)arrSend.Length, arrRecv, nRecvArrLen) == 1)
        {
            return arrRecv;
        }

        return null;
    }

很遗憾,上面的代码不起作用... 我可以知道如何将指向指针的指针传递给 C++ 函数吗?如果不可能,是否有任何解决方法?

谢谢。

【问题讨论】:

    标签: c# c++ dll marshalling


    【解决方案1】:

    MSVC中的unsigned long是一个32位无符号整数,所以你应该把它映射到System.UInt32.NET类型,对应C#中的uint关键字。

    C#ulong是一个无符号的64位整数,对应MSVC的unsigned __int64unsigned long long

    unsigned long *recvArrLen 参数应该在 C# PInvoke 声明中使用 ref 进行映射,因为您可以通过指针进行间接级别。

    似乎arrRecv 数组参数应该由调用者(在你的情况下为 C#)分配,并由 DLL 函数填充。

    如果 DLL 函数分配了缓冲区,您应该添加另一个间接级别 (unsigned char **recvArr),并且您应该提供一种方法来释放分配的内存(例如 DLL 也应该导出一个释放函数)。

    我会为 PInvoke 尝试类似的方法:

    [DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern uint makeArray(
        byte[] sendArr, 
        uint sendArrLen, 
        [Out] byte[] recvArr, 
        ref uint recvArrLen
    );
    

    【讨论】:

      【解决方案2】:

      您的“test.dll”在哪里?我认为这是一个路径问题...

      该文件必须位于以下目录之一..

      [%SystemRoot%] (Windows directory)
      [%SystemRoot%]\system32\(32 bit)   or 
      [%SystemRoot%]\sysWOW64\(64 bit)
      The same location with your executable file
      PATH variable
      

      也可能是类型不匹配...参考 [site]

      我将 csharp 的 ulong 类型与 windows 上 c/c++ 中的 unsigned __int64 匹配。

      C# 代码的声明略有改动。

      [DllImport(@"testdll.dll", CallingConvention = CallingConvention.Cdecl)]
          static extern ulong makeArray
          (
           byte[] sendArr, 
           ulong sendArrLen, 
           [Out] byte[] recvArr, 
           ref ulong recvArrLen
           );
      

      这是我测试的 testdll.cpp abd testdll.h

      #include "testdll.h"
      
      unsigned __int64 makeArray(
          unsigned char* sendArr, 
          unsigned __int64 sendArrLen, 
          unsigned char *recvArr, 
          unsigned __int64 *recvArrLen
      )
      {
          int i;
      
          for(i=0; i < sendArrLen; i++)
          {
              recvArr[i] = sendArr[i];
          }
      
          memcpy(recvArrLen, &sendArrLen, sizeof(unsigned __int64));
      
          return i;
      }
      

      testdll.h 代码。

      #pragma once
      
      #ifdef EXPORT_TESTDLL
      #define TESTDLL_API __declspec(dllexport) 
      #else
      #define TESTDLL_API __declspec(dllimport) 
      #endif
      
      extern "C" TESTDLL_API unsigned __int64 makeArray(
          unsigned char* sendArr, 
          unsigned __int64 sendArrLen, 
          unsigned char *recvArr, 
          unsigned __int64 *recvArrLen
      );
      

      最后,控制台应用的C#代码如下,调用c++中的原生dll函数——testdll.dll 在控制台上打印项目。

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Runtime.InteropServices;
      using System.Text;
      using System.Threading.Tasks;
      
      namespace ConsoleApplication2
      {
          class Program
          {
      
              [DllImport(@"testdll.dll", CallingConvention = CallingConvention.Cdecl)]
              static extern ulong makeArray(byte[] sendArr, ulong sendArrLen, [Out] byte[] recvArr, ref ulong recvArrLen);
      
              static byte[] MakeArray()
              {
                  byte[] arrSend = new byte[] { 0x00, 0x12, 0x34 };
                  ulong nRecvArrLen = 0;
                  ulong ret = 0;
                  byte[] arrRecv = new byte[3]; // assign in c++ dll function (variable size)
      
                  try
                  {
                      if ((ret = makeArray(arrSend, (ulong)arrSend.Length, arrRecv, ref nRecvArrLen)) > 0)
                      {
                          if(arrRecv != null)
                              Console.WriteLine("nRecvArrLen2============>" + arrRecv.Length);
      
      
                          return arrRecv;
                      }
      
                  }
                  catch (DllNotFoundException dne)
                  {
                      Console.WriteLine("============> dll not found....");
                  }
      
      
                  return null;
              }
      
      
              static void Main(string[] args)
              {
                  byte[] retbytes = MakeArray();
      
                  if (retbytes != null)
                  {
                      Console.WriteLine("=====LEN=======>" + retbytes.Length);
      
                      for (int i = 0; i < retbytes.Length; i++)
                          Console.WriteLine("====ITEM========>" + retbytes[i]);
                  }
                  else
                      Console.WriteLine("=====NULL=======>");
      
      
              }
          }
      }
      

      【讨论】:

      • 错误是“System.AccessViolationException”。不是“EntryPointNotFoundException”。
      • 你的标题中是否有 extern 关键字?像 extern "C" __declspec(dllexport)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-22
      • 1970-01-01
      • 2021-07-11
      相关资源
      最近更新 更多