P/Invoke 改变传递参数的值

Posted

技术标签:

【中文标题】P/Invoke 改变传递参数的值【英文标题】:P/Invoke changes the values of passed arguments 【发布时间】:2016-07-16 15:38:19 【问题描述】:

所以我在 DLL 中有这个 c++ 函数

__declspec(dllexport) MOUSERAWDATA __stdcall GetMouseRawData(LPARAM lParam)

    UINT bufferSize = 0;
    BYTE *buffer = new BYTE[bufferSize];
    GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &bufferSize, sizeof(RAWINPUTHEADER));
    GetRawInputData((HRAWINPUT)lParam, RID_INPUT, (LPVOID)buffer, &bufferSize, sizeof(RAWINPUTHEADER));
    RAWINPUT *raw = (RAWINPUT*)buffer;
    MOUSERAWDATA data;
    if (raw->header.dwType == RIM_TYPEMOUSE)
    
        data.longX = raw->data.mouse.lLastX;
        data.longY = raw->data.mouse.lLastY;
    
    return data;

以下是在我的 c# 项目中:

[DllImport("RawInput.dll", CallingConvention = CallingConvention.StdCall)]
private static extern MouseRawData GetMouseRawData(IntPtr lParam);
.
.
.
protected override void WndProc(ref Message m)

    switch(m.Msg)
    
        case WM_CREATE:
            if (AttachMouseListener(this.Handle))
                Console.WriteLine("It works!");
            break;
        case WM_INPUT:
            MouseRawData data = GetMouseRawData(m.LParam);
            break;
        default:
            base.WndProc(ref m);
            break;
     

当我运行这段代码时,以下值被传递给 GetMouseRawData

但由于某种原因,lParam 在 C++ 端的值总是不同的。

我本例 0x004fe95c = 5237084

有人知道为什么会这样吗?

【问题讨论】:

您的 pinvoke 声明与您的 C 函数不匹配。可能是因为 MouseRawData 声明,我们没有看到它的好处,返回结构的函数总是很棘手。改用 bool GetMouseRawData(LPARAM lParam, MOUSERAWDATA* data)。 【参考方案1】:

您正在分配一个长度为 0 的缓冲区。然后“询问”缓冲区的大小。然后通过告诉他们您的缓冲区大小正确来欺骗 GetRawInputData。将缓冲区分配移动到第一次调用后您已了解所需缓冲区大小的位置。

UINT bufferSize = 0;
BYTE *buffer = new BYTE[bufferSize];
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &bufferSize, sizeof(RAWINPUTHEADER));
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, (LPVOID)buffer, &bufferSize, sizeof(RAWINPUTHEADER));
RAWINPUT *raw = (RAWINPUT*)buffer;

您需要delete[] 您的缓冲区以防止泄漏。 您需要初始化data (MOUSERAWDATA) 以防止它有条件地包含垃圾。调用者如何知道raw->header.dwType == RIM_TYPEMOUSE 是真是假?

您是否考虑过根据Microsoft guidance 直接调用 GetRawInputData():

public static extern int GetRawInputData(IntPtr hRawInput, RawInputCommand uiCommand, out RAWINPUT pData, ref int pcbSize, int cbSizeHeader);

protected override void WndProc(ref Message m)

    if (m.Msg == (int)WindowMessages.RawInput)  // WindowMessages.RawInput = 0x00FF (WM_INPUT)
    
        RAWINPUT input = new RAWINPUT();
        int outSize = 0;
        int size = Marshal.SizeOf(typeof(RAWINPUT));

        outSize = Win32API.GetRawInputData(m.LParam, RawInputCommand.Input, out input, ref size, Marshal.SizeOf(typeof(RAWINPUTHEADER)));
        if (outSize != -1)
        
            if (input.Header.Type == RawInputType.Mouse)
            
                //input.Mouse.LastX;
                //input.Mouse.LastY;
            
        
    
    base.WndProc(ref m);

【讨论】:

感谢您的意见。但是您的回答并没有告诉我为什么传递的值会发生变化。 你能确认你正在构建你的 .net 和你的 c++ 具有相同的位数吗? 64 位或 32 位。 IntPtr 将是不同的大小。

以上是关于P/Invoke 改变传递参数的值的主要内容,如果未能解决你的问题,请参考以下文章

Java 值传递和引用传递

java中的值传递和引用传递用法详解

JAVA中值传递,引用传递

面试题1,值传递和参数传递

当需要一个形式参数直接改变对应实参的值时,该形式参数应说明为啥参数?

在vb中传递参数的方法有几种 带你了解最常见的2种方法