简体   繁体   English

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

[英]Passing structure to unmanaged code from C#

I've been fighting this problem for some time now and am hoping someone can help. 我已经为这个问题解决了一段时间,希望有人可以提供帮助。 I'm converting a VB6 application to C#. 我正在将VB6应用程序转换为C#。 Everything I'm about to show works perfect in VB6, but fails in C#. 我要展示的所有内容都可以在VB6中完美运行,但在C#中却无法实现。

I'm making an API call into a C dll, passing a structure. 我正在通过一个结构对C dll进行API调用。 The structure gets values modified in the C side and I should see the modified values in my return structure. 该结构在C端修改了值,我应该在返回结构中看到修改后的值。

Note, Sig is an int, Status is an int, and CLIENTID is a UINT_PTR. 注意,Sig是一个int,Status是一个int,CLIENTID是UINT_PTR。

Here are the C structures: 这是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;

In my C# code, I have the following structs defined to map to the C structs above: 在我的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;       
}

This is the method decoration in the C code: 这是C代码中的方法修饰:

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

Here is my PInvoke declaration in C# for the API in C that uses these structures: 这是我在C#中使用以下结构的C中API的PInvoke声明:

    [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: 这是我的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);

The problem is, in the VB6 code the structure item 'result' gets filled with a value after I make the API call. 问题是,在VB6代码中,我进行API调用后,结构项“结果”被填充了一个值。 In C#, 'result' is always just 0. I'm guessing I must have something wrong with the way I'm marshaling? 在C#中,“结果”始终始终为0。我想我编组的方式一定有问题吗? 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. 我发现这是在我的Request结构中从“ public int vers”更改为“ public int vers”后立即发生的。 Thoughts? 思考?

UnmanagedType.Struct actually isn't for substructures; 实际上,UnmanagedType.Struct不是用于子结构的。 it makes the Marshaler pass the related object as a VARIANT. 它使Marshaler将相关对象作为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. 尝试删除所有的MarshalAs语句,除了字符串(用于长度),数组(也用于长度)和枚举类型(我在这里没有看到,但在我的DLL中它们一直是UnmanagedType.I4),然后查看如果可行。

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. C#结构中还有一些其他的东西不在您的C版本中,两个变量在这两个变量之间不匹配,并且您给定的C函数不返回任何东西,但您的C#extern期望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.) (我想这只是网站的简化,但要牢记这一点。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM