PInvoke PostMessage无法跨用户帐户工作

本文关键字:工作 用户 PostMessage PInvoke | 更新日期: 2023-09-27 17:59:31

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

[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,因为如果另一个用户正在运行应用程序,而当前用户没有足够的权限,我会遇到问题。

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

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

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错误。我似乎找不到任何关于跨用户帐户在进程之间发布消息的文档。这是一件实际上无法做到的事情吗?

PInvoke PostMessage无法跨用户帐户工作

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

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

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

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

如果互斥对象还不存在,那么您就是第一个实例,所以创建一个线程来等待信号量。如果信号量发出信号,则退出进程。确保在其他进程安全运行之前,不要释放互斥对象;如果有疑问,不要发布。一旦进程退出,Windows将发出放弃的信号。

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

如果您在进程之间需要一个更复杂的通信通道,例如,如果您需要移交通信通道的当前状态,那么您的最佳选择可能是命名管道。