如何从C#调用一个导出的C++ DLL函数

6
这是我第一次尝试将C#和非托管的C++混合在一起,所以这可能是一个非常简单的问题,但我不理解。
我需要在C#代码中调用一些来自C++ dll的函数。以下是dll项目的代码:
.h文件:
#pragma once 
#include <iostream>
#if defined FIRSTDLL_EXPORTS
    #define DECLDIR __declspec(dllexport)
#else
    #define DECLDIR __declspec(dllimport)
#endif

extern "C"
    {
      DECLDIR int Add( int a, int b );
      DECLDIR void Function( void );
    }

.cpp文件

#include "stdafx.h"
#include "myFct.h"
#include <iostream>

extern "C"
{
      DECLDIR int Add( int a, int b )
      {
          return( a + b );
}

      DECLDIR void Function( void )
      {
          std::cout << "DLL Called!" << std::endl;
      }
}

我为调试和发布编译了这个程序,并将其复制到我的C#项目的debug文件夹中。但两个版本都没有起作用。

以下是C#代码:

[DllImport("firstDLL.Dll")]
public static extern int Add(int a, int b);


var cyu = Add(3, 5);

当我尝试运行时,出现以下错误:

“托管调试助手‘PInvokeStackImbalance’在‘C:\Program Files\Microsoft Office\Office14\WINWORD.EXE’中检测到问题。 附加信息:对PInvoke函数‘MyAddin!MyAddin.ThisAddIn::Add’的调用已使堆栈不平衡。这很可能是由于托管PInvoke签名与非托管目标签名不匹配。请检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配。”

但是,我发现签名是相同的。我漏掉了什么?

谢谢!


1
你可以尝试在C++函数声明中添加__stdcall调用约定。或者,使用C#的callingconvention属性。 - Jimmy
请编辑您的问题并发布确切的错误。 - gideon
1个回答

8

DLLImport的默认调用约定是stdcall,但您的C++代码的默认值是cdecl。当调用约定不匹配时,您看到的错误消息就是所显示的内容。这两种调用约定的参数堆栈清理要求是不同的,P/Invoke marshaller会检测并报告此问题。

解决方法是使您的调用约定匹配。

例如,您可以像以下方式更改P/Invoke:

[DllImport("firstDLL.Dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Add(int a, int b);

另一种选择是修改您的 C++ 代码:
#if defined FIRSTDLL_EXPORTS(returntype)
    #define DECLDIR __declspec(dllexport) returntype __stdcall
#else
    #define DECLDIR __declspec(dllimport) returntype __stdcall
#endif

显然,你只需要选择其中一个操作。如果你同时更改C#和C++,那么你将面临相反的问题!

如果我是你,我会保留C++代码的cdecl格式,并更改C#以匹配它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接