如何从c++ DLL导出一个全局变量?

时间:2022-09-06 13:51:22

The problem is I'm trying to access a global variable declared in a C++ DLL from a C# program. Even though I set the variable to 15 in a repeatedly called function (Subtract in this case), each time I invoke the getter (Divide in this case), the return value is zero. So here's my code. Header file:

问题是,我正在尝试访问一个由c#程序在c++ DLL中声明的全局变量。即使我将变量设置为15,在一个重复调用的函数中(在这个例子中减去),每次我调用getter(在这个例子中除以),返回值为零。这是我的代码。头文件:

#ifdef MATHFUNCSDLL_EXPORTS
#define MATHFUNCSDLL_API __declspec(dllexport) 
#else
#define MATHFUNCSDLL_API __declspec(dllimport) 
#endif

namespace MathFuncs
{
    class MyMathFuncs
    {
    public: 
        static MATHFUNCSDLL_API double Add(double a, double b); 

        static MATHFUNCSDLL_API double Subtract(double a, double b); 

        static MATHFUNCSDLL_API double Multiply(double a, double b); 

        static MATHFUNCSDLL_API double Divide(double a, double b); 
    };
}

C++ code:

c++代码:

__declspec(dllexport) double signID; //this is my variable

__declspec(dllexport) double __cdecl MyMathFuncs::Subtract(double a, double b){
   //.. some code
   signID = 15; //this function is the setter
}

__declspec(dllexport) double __cdecl MyMathFuncs::Divide(double a, double b)
{
    return signID; //this function is the getter, it return zero when called from C#
}

In my C# code I used the method found here: http://www.quantcode.com/modules/smartfaq/faq.php?faqid=95 I keep getting a zero return value from the getter function, why, and how can I fix this?

在我的c#代码中,我使用了这里找到的方法:http://www.quantcode.com/modules/smartfaq/faq.php?faqid=95,我从getter函数中得到了一个零返回值,为什么,我该如何解决这个问题呢?

Edit: C# code:

编辑:c#代码:

using UnityEngine;
using System.Collections;
using System;
using System.Runtime.InteropServices;


static class NativeMethods
{

    [DllImport("kernel32.dll")]
    public static extern IntPtr LoadLibrary(string dllToLoad);

    [DllImport("kernel32.dll")]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

    [DllImport("kernel32.dll")]
    public static extern bool FreeLibrary(IntPtr hModule);
}

public class SignDetector : MonoBehaviour
{

    //Lets make our calls from the Plugin
    [DllImport("CVDetectorDLL", CallingConvention = CallingConvention.Cdecl, EntryPoint = @"?Add@MyMathFuncs@MathFuncs@@SANNN@Z")]

    private static extern double add(double a, double b);
    [DllImport("CVDetectorDLL", CallingConvention = CallingConvention.Cdecl, EntryPoint = @"?Subtract@MyMathFuncs@MathFuncs@@SANNN@Z")]

    private static extern double subtract(double a, double b);
    [DllImport("CVDetectorDLL", CallingConvention = CallingConvention.Cdecl, EntryPoint = @"?Multiply@MyMathFuncs@MathFuncs@@SANNN@Z")]

    private static extern double multiply(double a, double b);
    [DllImport("CVDetectorDLL", CallingConvention = CallingConvention.Cdecl, EntryPoint = @"?Divide@MyMathFuncs@MathFuncs@@SANNN@Z")]

    private static extern double divide(double a, double b);

    double myvariable;

    void Start(){
        subtract(0, 0); //here we invoke the setter

        IntPtr mydll = NativeMethods.LoadLibrary("CVDetectorDLL.dll");

        //get a pointer to unmanaged heap address
        IntPtr addrUnmanagedHeap = NativeMethods.GetProcAddress(mydll, "signID");

        if (addrUnmanagedHeap != IntPtr.Zero)
        {
            //convert and read memory from unmanaged pointer 
            myvariable = Marshal.ReadInt32(addrUnmanagedHeap);

        }
    }

    void Update(){

        Debug.Log("Found = " + myvariable); //prints zero
        Debug.Log("Found = " + divide(0,0));  //getter
    }
}

2 个解决方案

#1


2  

I'm guessing (without seeing your c# code that the issue is in your calling convention. __cdecl is default for C++ as noted MSDN __cdecl

我在猜测(如果没有看到您的c#代码,问题就在您的调用约定中)。__cdecl是C++的默认值,如著名的MSDN __cdecl。

and it means that the calling function is responsible for the stack. In this case your .net program. c# defaults to the calling convention of __stdcall. This is very important for methods that take or return parameters as it determines how the stack will behave.

这意味着调用函数负责堆栈。在这种情况下,你的。net程序。c#默认为__stdcall的调用约定。对于获取或返回参数的方法来说,这是非常重要的,因为它决定了堆栈的行为方式。

I have included code which should show you a way to make this work.

我已经包含了代码,它应该告诉您如何进行这项工作。

c++ Code Header

c++代码头

#define DLLEXPORT __declspec(dllexport)
#ifdef __cplusplus
extern "C" { //Used to prevent name mangling on dll export
#endif //__cplusplus

double signID;

DLLEXPORT void __stdcall SetDoubleValue();
DLLEXPORT double __stdcall ReturnDoubleValue(double dummyone, double dummytwo);

#ifdef __cplusplus
}
#endif //__cplusplus

c++

c++

void __stdcall SetDoubleValue()
{
    signID = 15;
}

double __stdcall ReturnDoubleValue(double dummyone, double dummytwo)
{
    return signID;
}

c# import code

c#代码导入

[DllImport("DllExport.dll")]
public static extern void SetDoubleValue();

[DllImport("DllExport.dll")]
public static extern double ReturnDoubleValue(double dummyone, double dummytwo);

Note if you can't or don't want to change from __cdecl you can also change your c# definitions to

注意,如果您不能或不想从__cdecl更改,您也可以更改c#定义。

[DllImport("DllExport.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetDoubleValue();

[DllImport("DllExport.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern double ReturnDoubleValue(double dummyone, double dummytwo);

#2


0  

Looks like both my original code and the posted solution will indeed work - I seem to have misidentified the problem which was actually not in the DLL.

看起来我的原始代码和发布的解决方案都是有效的——我似乎错误地认识到了这个问题,而实际上不是在DLL中。

#1


2  

I'm guessing (without seeing your c# code that the issue is in your calling convention. __cdecl is default for C++ as noted MSDN __cdecl

我在猜测(如果没有看到您的c#代码,问题就在您的调用约定中)。__cdecl是C++的默认值,如著名的MSDN __cdecl。

and it means that the calling function is responsible for the stack. In this case your .net program. c# defaults to the calling convention of __stdcall. This is very important for methods that take or return parameters as it determines how the stack will behave.

这意味着调用函数负责堆栈。在这种情况下,你的。net程序。c#默认为__stdcall的调用约定。对于获取或返回参数的方法来说,这是非常重要的,因为它决定了堆栈的行为方式。

I have included code which should show you a way to make this work.

我已经包含了代码,它应该告诉您如何进行这项工作。

c++ Code Header

c++代码头

#define DLLEXPORT __declspec(dllexport)
#ifdef __cplusplus
extern "C" { //Used to prevent name mangling on dll export
#endif //__cplusplus

double signID;

DLLEXPORT void __stdcall SetDoubleValue();
DLLEXPORT double __stdcall ReturnDoubleValue(double dummyone, double dummytwo);

#ifdef __cplusplus
}
#endif //__cplusplus

c++

c++

void __stdcall SetDoubleValue()
{
    signID = 15;
}

double __stdcall ReturnDoubleValue(double dummyone, double dummytwo)
{
    return signID;
}

c# import code

c#代码导入

[DllImport("DllExport.dll")]
public static extern void SetDoubleValue();

[DllImport("DllExport.dll")]
public static extern double ReturnDoubleValue(double dummyone, double dummytwo);

Note if you can't or don't want to change from __cdecl you can also change your c# definitions to

注意,如果您不能或不想从__cdecl更改,您也可以更改c#定义。

[DllImport("DllExport.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetDoubleValue();

[DllImport("DllExport.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern double ReturnDoubleValue(double dummyone, double dummytwo);

#2


0  

Looks like both my original code and the posted solution will indeed work - I seem to have misidentified the problem which was actually not in the DLL.

看起来我的原始代码和发布的解决方案都是有效的——我似乎错误地认识到了这个问题,而实际上不是在DLL中。