Pinvoke与GCC在VB.net 2010中编译的c++dll有关

Pinvoke issues with GCC compiled c++ dll in VB.net 2010

本文关键字:编译 c++dll 有关 2010 net GCC VB Pinvoke      更新时间:2023-10-16

好吧,我试着自己解决这个问题。这是我遇到的问题:

为了使函数在dll中工作,我必须在vb代码中发送一个结构的指针,因为我需要用文件中的数据填充结构,反之亦然。将函数编译为dll,然后尝试在vb中重写本机C函数会更容易,我从未像memmove()那样使用过这些函数。

Dll文件是有效的,因为我还编写了一个c++应用程序,它从Dll调用函数,从指针填充内存中的结构,然后在命令提示符中显示数据。我只需要弄清楚事物的视觉基础方面出了什么问题。

导出函数的c++标头信息

#define MAX_CHEATS 150
struct SCheat
{
uint32_t address;
uint8_t byte1;
uint8_t saved_byte;
bool enabled;
bool saved;
char    name[22];
};
struct SCheatData
{
struct SCheat c[MAX_CHEATS];
uint32_t num_cheats;
};
__declspec(dllexport) int __cdecl S9xLoadCheatFile (const char *, struct SCheatData *Cheat);
__declspec(dllexport) int __cdecl S9xSaveCheatFile (const char *, struct ScheatData *Cheat);

我写了一个c++应用程序来测试dll的功能,它运行得很完美。

我还应该提到在github上找到的c++代码的原始版本。我只需要函数来加载/保存Snes9x的cht文件。如果我能弄清楚这一点,我可以很容易地添加其余的模拟器欺骗文件结构和加载/保存功能,我的应用程序将为任何模拟器生成cht文件,同时将所有欺骗保存在中央数据库中。

现在我们进入vb部分。

以下是我在应用程序中定义的结构和调用。

<StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)> _
Public Structure SCheat
    <MarshalAs(UnmanagedType.U4)> Public address As UInteger
    <MarshalAs(UnmanagedType.U4)> Public byte1 As Byte
    <MarshalAs(UnmanagedType.U4)> Public saved_byte As Byte
    <MarshalAs(UnmanagedType.U4)> Public enabled As Integer
    <MarshalAs(UnmanagedType.U4)> Public saved As Integer
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=22)> Public name() As Char
End Structure
<StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)> _
Public Structure SCheatData
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=150)> Public Shared c() As SCheat = Arrays.InitializeWithDefaultInstances(Of SCheat)(150)
    <MarshalAs(UnmanagedType.U4)> Public num_cheats As UInteger
End Structure

下面是我如何定义函数的。

<DllImport("c:minGWbinCheat_Functions.dll", CallingConvention:=CallingConvention.Cdecl)> _
Public Function _Z16S9xLoadCheatFilePKcP10SCheatData(ByVal filename As String, ByVal cheat As IntPtr) As Integer
End Function
Declare Function S9xLoadCheatFile Lib "c:minGWbinCheat_Functions.dll" Alias "_Z16S9xLoadCheatFilePKcP10SCheatData" (ByVal filename As String, ByRef cheat As IntPtr) As Integer

Declare Function S9xSaveCheatFile Lib "c:minGWbinCheat_Functions.dll" Alias "_Z16S9xSaveCheatFilePKcP10SCheatData" (ByVal filename As String, ByRef cheat As SCheatData) As Integer

我曾尝试通过ref传递该结构,但它崩溃了,出现了一条关于读/写受保护内存的非描述性消息。我尝试封送structuretoptr的一个变量,它走得更远了一点,但会破坏应用程序中的所有内存。我认为这是一个不完全理解pinvoke和编组实际工作方式的问题,或者可能我的结构与dll所寻找的不匹配。

如有任何帮助,我们将不胜感激。

编辑:很抱歉,我忘了你的目标是VB,不过你应该没有太大问题将我的代码翻译成它:D

以下对我有效:

using System;
using System.Runtime.InteropServices;
using System.Windows;
namespace WpfApplication3
{
    /// <summary>
    ///     Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var intPtr = GetData();
            var ptrToStructure = Marshal.PtrToStructure<SCheatData>(intPtr);
        }
        [DllImport("mydll.dll")]
        private static extern IntPtr GetData();
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct SCheat
        {
            public uint address;
            public byte byte1;
            public byte saved_byte;
            [MarshalAs(UnmanagedType.I1)] public bool enabled;
            [MarshalAs(UnmanagedType.I1)] public bool saved;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 22)] public string name;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct SCheatData
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 150, ArraySubType = UnmanagedType.Struct)] public SCheat[]
                c;
            public uint num_cheats;
        }
    }
}

和:

// mydll.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
typedef unsigned char      uint8_t;
typedef unsigned int       uint32_t;
#define MAX_CHEATS 150
struct SCheat
{
    uint32_t address;
    uint8_t byte1;
    uint8_t saved_byte;
    bool enabled;
    bool saved;
    char    name[22];
};
struct SCheatData
{
    struct SCheat c[MAX_CHEATS];
    uint32_t num_cheats;
};
extern "C" __declspec(dllexport) SCheatData* GetData()
{
    SCheatData* data = new SCheatData();
    data->num_cheats = MAX_CHEATS;
    for (size_t i = 0; i < MAX_CHEATS; i++)
    {
        SCheat c;
        c.address = 1234;
        c.byte1 = 0xAB;
        c.saved_byte = 0xCD;
        c.enabled = true;
        c.saved = true;
        strcpy(c.name, "abcdefghijklmnopqrstu");
        data->c[i] = c;
    }
    return data;
}

我使用了PInvoke互操作助手,它对生成签名很有帮助,也要小心Marshal.PtrToStructure的不同重载,因为并非所有风格都适用于任何情况。

好的,谢谢你的帮助,它做得不太好,但方向是我需要的答案。:)

代码修复。

C++标题:

__declspec(dllexport) int __cdecl S9xLoadCheatFile (const char *);
__declspec(dllexport) int __cdecl S9xSaveCheatFile (const char *);
__declspec(dllexport) SCheatData* __cdecl GetData();
struct SCheatData data1;

Dll.cpp:

SCheatData* __cdecl GetData()
{
   return &data1;
}

在保存和加载函数中直接与structdata1交互。

VB代码。

<StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Public Structure SCheat
Public address As UInteger
Public byte1 As Byte
Public saved_byte As Byte
<MarshalAs(UnmanagedType.I1)> Public enabled As Boolean
<MarshalAs(UnmanagedType.I1)> Public saved As Boolean
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=22, ArraySubType:=UnmanagedType.Struct)> Public name() As Char
End Structure
<StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
Public Structure SCheatData
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=150)> Public c() As SCheat
Public num_cheats As UInteger
End Structure
<DllImport("c:minGWbinCheat_Functions.dll", CallingConvention:=CallingConvention.Cdecl)> _
Public Function _Z16S9xLoadCheatFilePKc(ByVal filename As String) As Integer
End Function
<DllImport("c:minGWbinCheat_Functions.dll", CallingConvention:=CallingConvention.Cdecl)> _
Public Function _Z7GetDatav() As IntPtr
End Function
Private sub try()
  Dim tryv As String = "C:\Users\Diane\Desktop\snes9x\Cheats\Super Mario World (U) [!].cht"
  Dim anotherP As New SCheatData
  MsgBox(_Z16S9xLoadCheatFilePKc(tryv))
  anotherP = System.Runtime.InteropServices.Marshal.PtrToStructure(_Z7GetDatav(), GetType(SCheatData))
  MsgBox(anotherP.num_cheats)
end sub

这适用于加载,从而验证结构是否正确。我认为这也适用于保存函数。至少我知道,如果我真的有问题,问题不会出在结构上。:)如果我在使用此保存功能时遇到问题,我会回复。

Edit,刚刚用System.Runtime.InteropServices.Marshal.StructeToPtr(anotherP,_Z7GetDatav,True)测试了反向,它成功了。所以我想我是做了一些正确定义结构的事情,而structuretoptr需要在dll中制作一个指针。要是我3天前知道这些事情就好了。谢谢你的帮助,尽管为了达到我的目的,我仍然需要把它弄得一团糟,但它给了我一个新的方向,这实际上很有帮助。

现在,我只需要为所有其他模拟器做同样的事情。哈哈。:)现在我有点明白了,应该不会花太长时间。