【问题标题】:When calling c++ function from windows form gets error从 windows 窗体调用 c++ 函数时出错
【发布时间】:2020-12-18 04:50:56
【问题描述】:

我的 c++ 函数如下所示

# define MyFunction _declspec(dllexport)
extern "C" {

MyFunction int SubtractNumbers(int a, int b)
{
    return a - b;
}

MyFunction const char* GetStringKey()
{
    return "My String";
}
}

从windows窗体调用c++函数如下,

    [DllImport(cppFunctionsDll, CallingConvention = CallingConvention.Cdecl)]

    private const string DllFilePath = @"D:\\Projects\\December\\17-12-2020\\project-device- 
    setup\\Debug\\AccurynCPP.dll";

    [DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
    public static extern int SubtractNumbers(int a, int b);

    [DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
    public static extern string GetStringKey();
    
    public void GetSubtract()
    {
        lblSubtract.Text= Convert.ToString(SubtractNumbers(1, 2));
        
    }
    public void GetStringFunction()
    {
        lblstringKey.Text= GetStringKey();
        
    }

从上述函数中,GetSubtract() 完美运行。但是 GetStringKey() 不起作用。当它在调试时达到它的功能时,它会自动取消Visual Studio中的运行模式。我该如何解决这个问题。

【问题讨论】:

标签: c# c++ winforms calling-convention


【解决方案1】:

调用约定向编译器和链接器描述了如何处理参数、堆栈和寄存器。因此,如果这些不匹配,事情将无法正常工作或内存可能会被破坏。例如,如果调用者认为第一个参数应该在寄存器 eax 中传递,但被调用者认为它在堆栈上。事情显然不会奏效。有关调用约定的更多信息,维基百科有一个很好的描述 here。您选择的调用约定不是很重要,除非编译器强制您使用。例如,据我所知,对于 64 位程序,微软只使用一个。所以,我建议在你写的函数前面加上__cdecl。并在DLLImport 行上使用CallingConvention.Cdecl

此外,所有数据类型必须完全匹配。例如:

[DllImport(DllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern string GetStringKey();

这表示它正在返回一个字符串,但是尽管我们使用术语字符串来表示一系列字符,然后是一个空值。在这种情况下,字符串是一种正式类型。他们键入std :: string。这不是 C 代码返回的内容。 C 代码返回一个指向 char 的指针。

通常基于库传递复杂类型有点不确定。那是因为它要求两种语言都使用准确的数据结构来表示类型。因此,当跨越语言边界时,保留您自己的自定义结构和非常原始的类型通常很有用。

所以我建议你的 C 函数改为:

__cdecl bool GetStringKey(char *buffer, int maximumLength)
{
    // Write code here to copy all the characters from your "My String"
    // to the buffer or use a standard library function for copying char * strings (often called C strings) when copying make sure to copy the null character at the end. It's definitely needed.
    return true; // Yes it fit
}

您需要更改您的 DllImport 以匹配。一旦工作正常,您可以添加代码以永远不会超过最大缓冲区长度(以防止内存损坏),并可能将 bool 更改为字符串的长度或 -1 表示失败。

我建议使用这个特殊的函数原型,因为它不会在语言之间传递内存。相反,C 代码从 C++ 代码中填充缓冲区。这是做某事最安全的方法。在不相关的模块之间传递内存(在这种情况下,C 和 C++ 并不总是有效)。尽管我怀疑在这种情况下您的 C 和 C++ 编译器和库已经足够相似了。你可以切换到返回一个 std::string。只要双方完美匹配。

【讨论】:

  • 感谢您的回复。如何在函数中赋予 __cdecl?在哪里申请__cdecl?我的意思是 c++ 函数还是 .Net 函数?你能举一个我的问题的例子吗
  • 我已经在 c++ 代码中尝试了代码 => MyFunction const char* __cdecl GetStringKey(),但仍然遇到同样的错误
  • 我重新阅读了您的代码并看到了另一个问题,并添加到我的答案中。
  • __cdecl bool GetStringKey(char *buffer, int maximumLength) 我已经给出了上述函数并出现错误。你能帮我举个例子吗
猜你喜欢
  • 2011-05-31
  • 1970-01-01
  • 1970-01-01
  • 2020-02-01
  • 1970-01-01
  • 2020-07-27
  • 2019-05-07
  • 2022-01-26
  • 1970-01-01
相关资源
最近更新 更多