【问题标题】:convert C++ code to C#: SendMessageTimeout()将 C++ 代码转换为 C#:SendMessageTimeout()
【发布时间】:2013-11-01 23:14:34
【问题描述】:

首先是 SendMessageTimeout 的文档:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644952%28v=vs.85%29.aspx

我有这个 C++ 代码,我想把它转换成 C#:

LRESULT success = SendMessageTimeout(
    HWND_BROADCAST,
    WM_SETTINGCHANGE,
    0,
    (LPARAM) "Environment",
    SMTO_ABORTIFHUNG,
    5000,
    NULL
);

我在 C# 中做了什么:

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessageTimeout(
        IntPtr hWnd,
        uint Msg,
        UIntPtr wParam,
        IntPtr lParam,
        uint fuFlags,
        uint uTimeout,
        out UIntPtr lpdwResult
    );

    SendMessageTimeout(
        (IntPtr)0xFFFFFFFF,    //HWND_BROADCAST
        0x001A,                //WM_SETTINGCHANGE
        (UIntPtr)0,
        (IntPtr)"Environment", // ERROR_1: can't convert string to IntPtr
        0x0002,                // SMTO_ABORTIFHUNG
        5000,
        out UIntPtr.Zero       // ERROR_2: a static readonly field can not be passed ref or out
    );

【问题讨论】:

  • 查看编组 c-string vs Marshal.StringToHGlobalUni + FreeHGlobal。对于最后一部分,只需定义一个本地并将其传入。

标签: c# c++ sendmessage


【解决方案1】:

为您的问题。

  1. HWND_BROADCAST 是 0xFFFF 不是 0xFFFFFFFF
  2. 您必须使用Marshal.StringToHGlobalUni 手动为LPARAM 值分配内存,然后在调用后使用Marshal.FreeHGlobal 释放它。您必须释放此内存,否则它将泄漏。 Marshal 的内存不会被垃圾回收。
  3. 对于lpdwResult,只需创建一个IntPtr 变量并将其传入即可。您可以忽略它的值。

代码应该是这样的:

IntPtr result = IntPtr.Zero;
IntPtr setting = Marshal.StringToHGlobalUni("Environment");

SendMessageTimeout(
    (IntPtr)0xFFFF,        //HWND_BROADCAST
    0x001A,                //WM_SETTINGCHANGE
    (UIntPtr)0,
    (IntPtr)setting,
    0x0002,                // SMTO_ABORTIFHUNG
    5000,
    out result
);

Marshal.FreeHGlobal(setting);

通常,在释放传递给SendMessage 调用的内存时需要小心,因为您不知道接收窗口将如何处理传递给它的指针。但是,由于WM_SETTINGCHANGE 是 Windows 内置消息,Windows 会为您处理此指针。

【讨论】:

  • 嗨,这使我不必重新启动系统以使更改生效。但我仍然需要重新启动 CMD/Powershell。你也碰巧知道防止这种情况的方法吗?
【解决方案2】:

SendMessage 由于它使用的非描述参数类型而有点痛苦。必要,因为它需要做很多工作。在 C 语言中是必需的,但在 C# 中不是。您在这里要做的是利用支持重载的 C# 语言。 IntPtr 参数可以只是引用类型引用,pinvoke marshaller 会正确地将它们转换为指针并处理内存管理的麻烦。因此,只需制作另一个与您想要使用它的方式兼容的产品:

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
    IntPtr hWnd,
    int Msg,
    IntPtr wParam,
    string lParam,
    int fuFlags,
    int uTimeout,
    IntPtr lpdwResult
);

现在你可以使用了:

 SendMessageTimeout((IntPtr)0xffff, 0x001A, IntPtr.Zero, "Environment", 
                    2, 5000, IntPtr.Zero);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-17
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多