PInvoke PostMessage does not work through user accounts

I create a kill switch in my application so that only one instance can run on the same computer at a time. I accomplish this by sending messages between the process of the running application and the process of the new instance of the application:

[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); 

I cannot use Process.Kill because if another user starts the application and the current user does not have sufficient privileges, I get problems.

When starting 2 instances under the same user account, there are no problems. Messages are sent correctly and received correctly. However, when starting one instance from one user account, when switching users and starting the second instance, my first instance does not receive messages.

Here is my logic for subscribing to window messages:

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

And here is my hook handler:

 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; } 

Here's the logic for sending a message:

 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); } 

No Win32 errors are set by the PostMessage function. I cannot find any documentation related to sending messages between processes through user accounts. Is this something that really cannot be done?

+5
source share
1 answer

You cannot send window messages to a process in another session. You must use the kernel based IPC mechanism, not user objects.

In your scenario, one of the named mutexes and one semaphore is probably all you need. Note that names must be prefixed with Global\ to allow multiple sessions to share the same object.

When starting a process, first create or open a semaphore. Set the initial value to zero.

Then create a named mutex with bInitialOwner set to TRUE and check the last error code to see if the mutex already exists.

If the mutex does not exist yet, you are the first instance, so create a thread to wait on the semaphore. If the semaphore is signaled, exit the process. Make sure you don't let go of the mutex until it is safe to start another process; If in doubt, do not release it at all. Windows will display it as abandoned after the process exits.

If the mutex already exists, another instance is executed. Signal semaphore, then wait on the mutex. Once you get ownership of the mutexes, it's safe to continue. Then you need to create a thread to wait on the semaphore if another instance appears and you want to pick you up.

If you need a more complex communication channel between processes, for example, if you need to transfer the current state of the communication channel, then your best choice is probably named pipes .

+1
source

All Articles