【问题标题】:Memory corruption calling C++ dll from VB.NET [duplicate]从 VB.NET 调用 C++ dll 的内存损坏 [重复]
【发布时间】:2017-03-08 20:41:42
【问题描述】:

我正在尝试从 vb.net 调用 C++ dll 中的函数,但遇到以下错误:

托管调试助手“PInvokeStackImbalance”在“D:...\calling_project_in_VB.vshost.exe”中检测到问题。

附加信息:调用 PInvoke 函数 'calling_project_in_VB! call_project_in_VB.Module1::add' 使堆栈不平衡。这可能是因为托管 PInvoke 签名与非托管目标签名不匹配。检查 PInvoke 签名的调用约定和参数是否与目标非托管签名匹配。

我希望有人可能对这些东西足够精通以发现我哪里出错了?我将不胜感激任何建议,到目前为止,谷歌在帮助我确定问题所在方面一直没有成效。这是我正在尝试运行的可重现的小示例:

C++ 是这样设置的:

调用_c.h:

extern "C"  __declspec( dllexport ) 
                   int add(int* a, int* b); 

调用_c.cpp:

#include "stdafx.h"
#include "called_c.h"
#include <string>

using namespace std;
// using namespace System;

int add(int* a, int* b)
{
    int Aa = *a;
    int Bb = *b;

    return Aa + Bb;
}

这是尝试调用 C 的 VB 函数(其中“...”是我机器上的路径):

Imports System.Runtime.InteropServices
Imports System

Module Module1

    'Public Class called_c
      <DllImport("D:\...\called_c.dll", EntryPoint:="add", ExactSpelling:=False)>
    Public Function add(ByRef a As Int32, ByRef b As Int32) As Int32
    End Function

    'End Class

    Sub Main()

        Dim val1 As Int32
        Dim val2 As Int32
        Dim answer As Int32

        val1 = 3
        val2 = 4

        answer = add(val1, val2)

        MsgBox(answer)

    End Sub

End Module

【问题讨论】:

  • 你到底为什么要传递 int 指针来添加它们(你不需要无论如何都需要这些引用)。
  • @crashmstr 这只是一个可重复的小例子,我希望能作为一个更复杂问题的前兆,其中 0 填充的向量将被传递到 c 代码中,填充,然后返回到VB。如果你有一个小的工作示例,如果你能分享它,我将非常感激。
  • 我不知道这是否会有所帮助,但您可以尝试将 &lt;[In]()&gt; attribute 添加到 ByRef 参数中,以防止 Marshaler 将值复制回来。
  • @TnTinMn CallingConvention:=CallingConvention.Cdecl 工作!在应用于“现实世界”问题之前,继续对我的小示例进行下一次修改。
  • 你应该将他的答案标记为接受。 :)

标签: c++ vb.net


【解决方案1】:

DllImportAttribte 的默认调用约定如下:

CallingConvention 字段的默认值为 Winapi,而后者又默认为 StdCall 约定。

但是 C++ 程序的默认值是__cdecl

您的 C++ 代码似乎使用此默认值,但未指定替代值,因此 VB 签名应为:

<DllImport("D:\...\called_c.dll", EntryPoint:="add", ExactSpelling:=False, CallingConvention:=CallingConvention.Cdecl)>
Public Function add(ByRef a As Int32, ByRef b As Int32) As Int32
End Function

或者,您可能希望将InAttribute 添加到参数中,以防止互操作封送拆收器复制回任何值。

<DllImport("D:\...\called_c.dll", EntryPoint:="add", ExactSpelling:=False, CallingConvention:=CallingConvention.Cdecl)>
Public Function add(<[In]()> ByRef a As Int32, <[In]()> ByRef b As Int32) As Int32
End Function

【讨论】:

  • 我只是建议他使用 StdCall,尽管两者都可以。 :)
  • @VisualVincent,我很好奇你在编辑中做了什么改变?我的眼睛很累,但我看到的只是代码块被删除然后又放回了。
  • 这是一个不可见的代码标签。我在您的第一个代码块上方添加了&lt;!-- language-all: vb.net --&gt;。它发生了变化,因此您的所有代码块都使用 VB.NET 的语法高亮显示。
  • @VisualVincent,感谢您提供信息。似乎我不是唯一一个无法检测到这些变化的人。这个话题刚刚出现在Hot Meta Links sidebar
  • 嘿。它以side-by-side markdown 模式显示。 :)
【解决方案2】:

C++ 中的几乎所有指针都等于 .NET 世界中的 IntPtr,因此您的参数应该属于该类型。但是,正如 crashmstr 所说,如果您只是将两个数字相加,为什么还要首先将它们作为指针?

<DllImport("D:\...\called_c.dll", EntryPoint:="add", ExactSpelling:=False)>
Public Function add(ByVal a As IntPtr, ByVal b As IntPtr) As Integer
End Function

另请注意,参数应作为ByVal 传递,因为C++ 函数当前不包含预期通过引用传递的参数。

用法示例:

Dim Result As Integer = add(New IntPtr(3), New IntPtr(6))
MessageBox.Show(Result) 'Should display "9".

【讨论】:

  • 采纳您的建议更改了错误消息,因此这似乎是一个进步。而不是得到我原来的问题中引用的 PInvoke 错误,我现在得到的是,“calling_project_in_VB.exe 中发生了“System.AccessViolationException”类型的未处理异常附加信息:试图读取或写入受保护的内存。这通常是一个迹象其他内存已损坏。”
  • @user2256085 :当 C++ 代码出现问题时,通常会出现这种情况。参数的类型应该是IntPtr,所以我宁愿认为你在 C++ 部分中遗漏了一些东西。尝试在__declspec( dllexport ) 之前/之后添加__stdcall 调用约定。
猜你喜欢
  • 2021-12-03
  • 1970-01-01
  • 1970-01-01
  • 2016-01-25
  • 2012-06-13
  • 1970-01-01
  • 1970-01-01
  • 2016-05-16
  • 2010-09-06
相关资源
最近更新 更多