gpt4 book ai didi

c# - Marshal.PtrToStructure 抛出 System.ArgumentException 错误

转载 作者:可可西里 更新时间:2023-11-01 08:52:16 25 4
gpt4 key购买 nike

我正在尝试从键盘 Hook 的 lParam 获取 KBDLLHOOKSTRUCT。

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{

KBDLLHOOKSTRUCT kbd = new KBDLLHOOKSTRUCT();
Marshal.PtrToStructure(lParam, kbd); // Throws System.ArguementException
...

不幸的是 PtrToStructure 抛出两个

A first chance exception of type 'System.ArgumentException' occurred in myprogram.exe

每次按下一个键都会出错。它还会在其轨道上停止该方法。

MSNDA 说: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

ArgumentException when:

The structureType parameter layout is not sequential or explicit.

-or-

The structureType parameter is a generic type.

我该怎么做才能让它发挥作用? lParam 直接来自键盘 Hook ,所以我希望它是正确的。这些错误中的任何一个在这里有意义吗?我该如何解决?

最佳答案

这是一些适合我的代码:

public struct KBDLLHOOKSTRUCT
{
public Int32 vkCode;
public Int32 scanCode;
public Int32 flags;
public Int32 time;
public IntPtr dwExtraInfo;
}

private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
KBDLLHOOKSTRUCT kbd = (KBDLLHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
Debug.WriteLine(kbd.vkCode); // ***** your code here *****
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}

与您的代码的关键区别在于我调用的是 Marshal.PtrToStructure(IntPtr, Type) 重载而不是 (IntPtr, object) 重载。我认为这就是你出了问题的地方。因为如果你用结构调用 (IntPtr, object) 重载,你会得到以下错误:

System.ArgumentException: The structure must not be a value class.

此错误的明显修复是将 KBDLLHOOKSTRUCT 更改为类(引用类型)而不是结构(值类型):

public class KBDLLHOOKSTRUCT    // not necessarily the right solution!

但是,这会导致错误,MSDN 表示“结构类型参数布局不是顺序的或显式的。”:

System.ArgumentException: The specified structure must be blittable or have layout information.

我猜这就是您现在所处的位置,将 KBDLLHOOKSTRUCT 声明为一个类,并收到“无布局信息”错误。有两种方法可以解决这个问题。

首先,根据 Eric Law 的评论,您可以保持 Marshal.PtrToStructure 调用不变,将 KBDLLHOOKSTRUCT 保持为一个类,并将布局信息添加到 KBDLLHOOKSTRUCT:

[StructLayout(LayoutKind.Sequential)]
public class KBDLLHOOKSTRUCT { ... }

其次,根据我上面的示例代码,您可以将 KBDLLHOOKSTRUCT 更改为 struct 而不是 class,并将 Marshal.PtrToStructure 调用更改为 (IntPtr,类型)重载:

public struct KBDLLHOOKSTRUCT { ... }

KBDLLHOOKSTRUCT kbd = (KBDLLHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));

(在这种情况下,如果您愿意,您仍然可以将 [StructLayout(LayoutKind.Sequential)] 属性添加到 KBDLLHOOKSTRUCT 结构中。这在技术上是多余的,但可以帮助代码的读者将 KBDLLHOOKSTRUCT 识别为布局敏感的互操作类型。)

这两种解决方案都适用于(公认的简单)测试。在这两者中,我会推荐第二个,因为 Win32/C 结构在 P/Invoke 场景中通常被声明为 struct —— 如果没有别的,以 STRUCT 结尾的名称可能应该是一个结构而不是比一个类(class)!

最后,让我提一下替代方法。

与其将 LowLevelKeyboardProc 声明为接收 IntPtr 作为其 lParam,还可以将其声明为接收 ref KBDLLHOOKSTRUCT(其中 KBDLLHOOKSTRUCT 是一个 struct,而不是 )。这也需要更改 CallNextHookEx,但最终结果是通过完全避免 Marshal 调用来简化 KBDLLHOOKSTRUCT 信息的使用。使用 ref 参数还意味着您可以写入 结构(我从其他问题中知道这是您的目标)并且不需要在写入后将其编码回来:

private delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT kbd);

private static IntPtr HookCallback(
int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT kbd)
{
Debug.WriteLine(kbd.vkCode); // look! no marshalling!
return CallNextHookEx(_hookID, nCode, wParam, ref kbd);
}

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, ref KBDLLHOOKSTRUCT kbd);

(不过,我可能应该警告你,当我尝试修改 kbd.vkCode 时,它​​实际上并没有影响文本框等中出现的内容。我对低级键盘钩子(Hook)了解不够,不知道为什么不这样做或者我需要做什么才能完成这项工作;抱歉。)

关于c# - Marshal.PtrToStructure 抛出 System.ArgumentException 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2079868/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com