【问题标题】:PInvoke PostMessage not working across user accountsPInvoke PostMessage 不能跨用户帐户工作
【发布时间】:2015-09-10 19:11:02
【问题描述】:

我在我的应用程序中构建了一个终止开关,以便一次只能在一台计算机上运行一个实例。我通过在正在运行的应用程序的进程和应用程序的新实例的进程之间发布消息来实现这一点:

[DllImport("user32.dll", EntryPoint = "PostMessageA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern int PostMessage(int hwnd, int wMsg, int wParam, int lParam);

我不能使用 Process.Kill,因为如果另一个用户正在运行应用程序并且当前用户没有足够的权限,我会遇到问题。

在同一用户帐户下运行 2 个实例时,我没有任何问题。消息被正确发送和正确接收。但是,当从一个用户帐户运行一个实例,然后切换用户并运行第二个实例时,我的第一个实例没有收到消息。

这是我订阅窗口消息的逻辑:

var wih = new WindowInteropHelper(this);
var hwndSource = HwndSource.FromHwnd(wih.Handle);
var hwndSourceHook = new HwndSourceHook(HookHandler);

if (hwndSource != null)
    hwndSource.AddHook(hwndSourceHook);

这是我的钩子处理程序:

private IntPtr HookHandler(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        handled = false;

        switch (msg)
        {
            case 0x400: // interprocess message received
                App.InterprocessManager.BuildString(lParam);
                break;
        }
        return IntPtr.Zero;
    }

这是发送消息的逻辑:

private void SendString()
    {
        //create byte array
        byte[] ba = null;
        //encode string to byte array
        if (object.ReferenceEquals(enc, Encoding.UTF8))
        {
            ba = Encoding.UTF8.GetBytes(lParam);
        }
        else if (object.ReferenceEquals(enc, Encoding.Unicode))
        {
            ba = Encoding.Unicode.GetBytes(lParam);
        }
        else if (object.ReferenceEquals(enc, Encoding.ASCII))
        {
            ba = Encoding.ASCII.GetBytes(lParam);
        }
        else
        {
            ba = Encoding.Default.GetBytes(lParam);
        }
        int i = 0;
        for (i = 0; i <= ba.Length - 1; i++)
        {
            //start post message
            PostMessage(hwnd, wMsg, wParam, ba[i]);
        }
        //post a terminator message to destination window
        PostMessage(hwnd, wMsg, wParam, 0);
    }

PostMessage 函数未设置 Win32 错误。我似乎找不到任何有关跨用户帐户在进程之间发布消息的文档。这是不是真的做不到?

【问题讨论】:

  • 另一个用户在他自己的会话中使用自己的桌面堆运行程序。鉴于您无法从会话中找到它,您使用正确窗口句柄的几率相当低。通过使用命名互斥体的常用方法取得成功。在其名称前加上 `Global` 前缀,以便在会话中可见。请记住,用户将完全不知道为什么他无法启动程序并且不知道如何修复它。鉴于这是多么充满敌意,当然最好不要这样做。
  • 我使用 Process.MainWindowHandle 检索句柄,所以我不确定为什么我们不会得到正确的句柄,除非系统此时只是设置了任意句柄。我已经使用互斥锁来确定另一个实例是否正在运行,但是我们正在使用的场景是用户 A 运行程序,用户 A 锁定系统,用户 B 登录,尝试运行程序。在这种情况下,我们希望用户 B 能够运行,因此我们需要终止用户 A 会话中的程序。杀死程序并不理想,但是我们使用的通信协议一次只能使用一个
  • 该句柄在创建它的会话中可用,您的 PostMessage() 消息无处可去。

标签: c# .net wpf user32 inter-process-communicat


【解决方案1】:

您不能将窗口消息发送到不同会话中的进程。您必须使用基于内核而非用户对象的 IPC 机制。

在您的场景中,您可能只需要一个命名互斥体和一个命名信号量。请注意,名称必须具有 Global\ 前缀,以允许多个会话共享一个对象。

在进程启动时,首先创建或打开信号量。将初始计数设置为零。

然后创建命名互斥体,将bInitialOwner 设置为TRUE,并检查最后一个错误代码以查看互斥体是否已存在。

如果互斥锁不存在,你是第一个实例,所以创建一个线程来等待信号量。如果信号量发出信号,则退出进程。确保在其他进程可以安全运行之前不要释放互斥锁;如果有疑问,请不要发布它。一旦进程退出,Windows 会发出已放弃的信号。

如果互斥体已经存在,另一个实例正在运行。发出信号量,然后在互斥体上等待。一旦您获得了互斥锁的所有权,就可以安全地继续。然后,您需要创建一个线程来等待信号量,以防另一个实例出现并想要撞到您。

如果您需要更复杂的进程间通信通道,例如需要交出当前通信通道状态,那么您的最佳选择可能是named pipes

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    相关资源
    最近更新 更多