【问题标题】:Passing structure to unmanaged code from C#从 C# 将结构传递给非托管代码
【发布时间】:2011-10-13 16:11:03
【问题描述】:

我一直在与这个问题作斗争一段时间,希望有人能提供帮助。我正在将 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 int vers”更改为“public Int16 vers”时,就会发生这种情况。想法?

【问题讨论】:

  • 'vers' 似乎是 C# 结构中的错误大小类型。
  • 'objtype' 的大小也有误。请改用short。
  • 未管理的SendRequest方法的返回值是多少?你能给我们完整的定义吗?

标签: c# dll pinvoke marshalling


【解决方案1】:

UnmanagedType.Struct 实际上不适用于子结构;它使 Marshaler 将相关对象作为 VARIANT 传递。这不是你想要的。尝试删除除字符串(长度)、数组(也用于长度)和枚举类型(我在这里没有看到,但在我的 DLL 中它们是 UnmanagedType.I4)之外的所有 MarshalAs 语句,然后查看如果可行。

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

【讨论】:

  • 感谢您的回复!是的,我刚刚注意到在简化代码时引入了几个错误。今晚我将清理我的错误并实施您的建议。我会把结果发回来。
  • 我已经更新了上面的代码来实现这些建议。请查看我的“更新”评论,看看我现在得到了什么。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-27
  • 2013-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-29
  • 1970-01-01
相关资源
最近更新 更多