将结构从 C# 传递给非托管代码

发布于 2024-12-09 19:48:12 字数 3342 浏览 0 评论 0原文

我已经和这个问题作斗争有一段时间了,希望有人能提供帮助。我正在将 VB6 应用程序转换为 C#。我将要展示的所有内容在 VB6 中都可以完美运行,但在 C# 中却失败了。

我正在对 C dll 进行 API 调用,并传递一个结构。该结构在 C 端修改了值,我应该在返回结构中看到修改后的值。

注意,Sig 是一个 int,Status 是一个 int,CLIENTID 是一个 UINT_PTR。

以下是 C 结构:

typedef struct
{
    short               vers;               
    short               flags;              
    short               cmd;                
    short               objtype;            
    DWORD               id;                 
    DWORD               client;             
    char                buf[MAX_TOOLBUF];   
    DWORD               toolID;             
    NMSG                msg;                
    DWORD               caller;             
    CLIENTID                    clientID;           
    DWORD               ticket; 
    ToolResult PTR                      result;     
        long                spare[4];           
} Request;

typedef struct
{
    DWORD       hwnd;
    DWORD       message;
    DWORD       wParam;
    DWORD       lParam;
} NMSG;

typedef struct
{
Sig          sig;               
short               cnt;                
Status              error;              
DWORD               ticket;             
DWORD               toolID;             
long                spare[4];           
} ToolResult;

在我的 C# 代码中,我定义了以下结构来映射到上面的 C 结构:

[StructLayout(LayoutKind.Sequential)]
public struct NMSG
{
    public int hWnd;
    public int msg;
    public int wParam;
    public int lParam;
}

[StructLayout(LayoutKind.Sequential)]
public struct Request
{
    public Int16 vers;        
    public Int16 flags;        
    public Int16 cmd;         
    public int16 objType;     
    public int id;             
    public int Client;         

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
    public string buf;

    public int toolID;         
    public NMSG msg;           
    public int caller;        
    public IntPtr clientID;       
    public int ticket;         
    public ToolResult result;         

    [MarshalAs(UnmanagedType.ByValArray, SizeConst= 4) ]
    public int64[] spare;       
}

这是 C 代码中的方法修饰:

SendRequest(Request PTR req)
{
    ....
}

这是我在 C# 中的 PInvoke 声明,用于使用这些 C 中的 API结构:

    [DllImport("TheCDLL.dll", EntryPoint = "_Request@4", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    public static extern void Request(ref Request req);

这是我的 C# 代码,用于填充结构并进行 API 调用:

Request req = new Request();

req.vers = 1;
req.toolID = 4000;
req.Client = 0;
req.cmd = 11;
req.objType = 1;;
req.id = 1;
req.msg.hWnd = Hwnd; // verified this is a valid window handle
req.msg.msg = 1327;
req.msg.wParam = 101;
req.msg.lParam = 0;
req.spare = new int[4];
req.buf = new string(' ', 260);
req.flags = 11;

Request(ref req);

问题是,在 VB6 代码中,在进行 API 调用后,结构项“结果”会填充一个值。在 C# 中,“结果”始终为 0。我猜我的编组方式一定有问题?我读过几十篇文章,并尝试了很多不同的方法来使其发挥作用,但没有成功。任何帮助表示赞赏!

更新:现在我已经根据下面的建议更新了代码(修复类型并清理最初编写的代码),我遇到了一个异常:

System.AccessViolationException: Attempted to read or write protected memory. This is        often an indication that other memory is corrupt.

我发现一旦我更改为“public”,就会发生这种情况我的请求结构中的“public int vers”中的“Int16 vers”。想法?

I've been fighting this problem for some time now and am hoping someone can help. I'm converting a VB6 application to C#. Everything I'm about to show works perfect in VB6, but fails in C#.

I'm making an API call into a C dll, passing a structure. The structure gets values modified in the C side and I should see the modified values in my return structure.

Note, Sig is an int, Status is an int, and CLIENTID is a UINT_PTR.

Here are the C structures:

typedef struct
{
    short               vers;               
    short               flags;              
    short               cmd;                
    short               objtype;            
    DWORD               id;                 
    DWORD               client;             
    char                buf[MAX_TOOLBUF];   
    DWORD               toolID;             
    NMSG                msg;                
    DWORD               caller;             
    CLIENTID                    clientID;           
    DWORD               ticket; 
    ToolResult PTR                      result;     
        long                spare[4];           
} Request;

typedef struct
{
    DWORD       hwnd;
    DWORD       message;
    DWORD       wParam;
    DWORD       lParam;
} NMSG;

typedef struct
{
Sig          sig;               
short               cnt;                
Status              error;              
DWORD               ticket;             
DWORD               toolID;             
long                spare[4];           
} ToolResult;

In my C# code, I have the following structs defined to map to the C structs above:

[StructLayout(LayoutKind.Sequential)]
public struct NMSG
{
    public int hWnd;
    public int msg;
    public int wParam;
    public int lParam;
}

[StructLayout(LayoutKind.Sequential)]
public struct Request
{
    public Int16 vers;        
    public Int16 flags;        
    public Int16 cmd;         
    public int16 objType;     
    public int id;             
    public int Client;         

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
    public string buf;

    public int toolID;         
    public NMSG msg;           
    public int caller;        
    public IntPtr clientID;       
    public int ticket;         
    public ToolResult result;         

    [MarshalAs(UnmanagedType.ByValArray, SizeConst= 4) ]
    public int64[] spare;       
}

This is the method decoration in the C code:

SendRequest(Request PTR req)
{
    ....
}

Here is my PInvoke declaration in C# for the API in C that uses these structures:

    [DllImport("TheCDLL.dll", EntryPoint = "_Request@4", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    public static extern void Request(ref Request req);

And here is my C# code that fills the structure and makes the API call:

Request req = new Request();

req.vers = 1;
req.toolID = 4000;
req.Client = 0;
req.cmd = 11;
req.objType = 1;;
req.id = 1;
req.msg.hWnd = Hwnd; // verified this is a valid window handle
req.msg.msg = 1327;
req.msg.wParam = 101;
req.msg.lParam = 0;
req.spare = new int[4];
req.buf = new string(' ', 260);
req.flags = 11;

Request(ref req);

The problem is, in the VB6 code the structure item 'result' gets filled with a value after I make the API call. In C#, 'result' is always just 0. I'm guessing I must have something wrong with the way I'm marshaling? I've read dozens of articles and have tried lots of different ways to get this working with no success. Any help is appreciated!

UPDATE: Now that I've updated the code based on the suggestions below (fixing types and cleaning up the code as originally written), I'm getting an exception:

System.AccessViolationException: Attempted to read or write protected memory. This is        often an indication that other memory is corrupt.

I've found that this happens as soon as I changed to 'public Int16 vers' from 'public int vers' in my Request structure. Thoughts?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

携君以终年 2024-12-16 19:48:12

UnmanagedType.Struct 实际上不适用于子结构;它适用于子结构。它使封送拆收器将相关对象作为 VARIANT 传递。这不是你想要的。尝试去掉除字符串(长度)、数组(长度)和枚举类型之外的所有 MarshalAs 语句(我在这里没有看到任何内容,但在我的 DLL 中它们是 UnmanagementType.I4),然后查看如果有效的话。

C# 结构中还有一些 C 版本中没有的额外内容,两个变量之间不匹配,并且给定的 C 函数不返回任何内容,但 C# extern 需要 IntPtr。这些确实需要匹配才能使一切顺利进行。 (我猜这只是网站的简化,但需要记住这一点。)

UnmanagedType.Struct actually isn't for substructures; it makes the Marshaler pass the related object as a VARIANT. That isn't what you want here. Try taking off all the MarshalAs statements except for strings (for the length), arrays (also for the length), and enumerated types (I don't see any here, but in my DLLs they've been UnmanagedType.I4) and see if that works.

There's also some extra things in your C# struct that aren't in your C version, a couple variables don't match between the two, and your given C function doesn't return anything but your C# extern is expecting an IntPtr. These do need to match for everything to go smoothly. (I'm guessing it's just simplification for the site, but it is something to keep in mind.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文