Finding the Child Window inside a Microsoft RemoteApp Programmatically - c#

Background
I'm using SendKeys() to send keyboard commands to the active window, but I'm not having any luck finding the child window when the application is running through RemoteApp. It all works as expected when I run the application locally.
Microsoft RemoteApp allows users to connect to applications through the RDP protocol, but instead of showing the entire remote Virtual machine, it just shows the application window. To the end user, there is no difference between an application running under RemoteApp and it running on their desktop.
I've been using ManagedSpy to determine the class name of the .NET application window so that I can use the Win32 API function FindWindowEx to make one of the child windows active, and it works well. However, I'm having a problem when the application is running over RemoteApp.
I can still use the .NET Process.GetProcessesByName() to find the application, I just have to have it invoke mstsc.exe:
IntPtr hwndChild = IntPtr.Zero;
Process[] processess = Process.GetProcessesByName("mstsc");
IntPtr appHandle = IntPtr.Zero;
foreach (Process p in processess)
{
if ((p.MainWindowHandle != IntPtr.Zero))
{
appHandle = p.MainWindowHandle;
}
}
if (appHandle == IntPtr.Zero)
{
MessageBox.Show("Application is not Running.");
return;
}
However, I can't use FindWindowEx in the same way. This question revolves around that.
For the unmanaged code to tell me what windows mstsc.exe has active, I used Spy++, but for mstsc.exe it comes back with a different class name, called RAIL_WINDOW:
Here is the code I'm using to find the Child Window:
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
hwndChild = FindWindowEx(appHandle, IntPtr.Zero, "RAIL_WINDOW", "MyApplication (Remote)");
SetForegroundWindow(hwndChild);
Questions
I can use Spy++ to highlight the active child window in the RemoteApp version of the application, and I get RAIL_WINDOW, but I cannot seem to access this window programmatically. Given the code above, what am I missing to be able to do so?
Are there other ways of sending keyboard strokes to an application running over Remote App?

Knowing how Microsoft does things, I'll bet the "rail window" is nothing more than a dumb, local proxy that doesn't bother responding to what SendKeys is sending. I haven't looked, but I'll bet that ends up sending WM_CHAR messages, to which a dumb proxy probably wouldn't bother responding. Instead, try sending it WM_KEYUP and WM_KEYDOWN messages manually and see if that works, given that I expect it would transmit those and mouse clicks (and what not) rather than the translated versions.

What commands are you sending using SendKeys()?
It may be better to look for an alternative solution instead of using `SendKeys()'.

And you can probably take advantage of handling IMsTscAxEvents::OnRemoteWindowDisplayed event that gives you the proper window handle at the right time without calling FindWindowEx, etc.

Related

How to get WH_KEYBOARD_LL to work in a Universal Windows Platform Builds

Background: I have a library which requires keyboard scancodes (Not KeyCodes from the enum) which I am trying to get working with Unity for the HoloLens.
The only way to obtain scancodes from C# I have found so far is to use the Windows Hook system through PInvoke by registering a hook for WH_KEYBOARD_LL through SetWindowsHookEx
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
Documentation https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx
The suggested way is to call it like this
IntPtr hHook;
using (Process process = Process.GetCurrentProcess())
using (ProcessModule module = process.MainModule)
{
IntPtr hModule = GetModuleHandle(module.ModuleName);
hHook = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, hook, hModule, 0);
}
see http://www.pinvoke.net/default.aspx/user32.setwindowshookex
This works well on PC builds, but the Remark for UWP in the documentation for SetWindowsHookEx is confusing me:
Windows Store app development If dwThreadId is zero, then window hook
DLLs are not loaded in-process for the Windows Store app processes and
the Windows Runtime broker process unless they are installed by either
UIAccess processes (accessibility tools). The notification is
delivered on the installer's thread for these hooks:
WH_JOURNALPLAYBACK WH_JOURNALRECORD WH_KEYBOARD WH_KEYBOARD_LL
WH_MOUSE WH_MOUSE_LL This behavior is similar to what happens when
there is an architecture mismatch between the hook DLL and the target
application process, for example, when the hook DLL is 32-bit and the
application process 64-bit.
As I understand I have to specify dwThreadId on UWP, otherwise it will not work. But if I do so using GetCurrentThreadId() (again through pinvoke), I get an error code:
ERROR_GLOBAL_ONLY_HOOK
1429 (0x595)
This hook procedure can only be set globally.
Setting hMod from GetModuleHandle(NULL) and dwThreadId to NULL creates a valid hookID but again no events arrive.
Since then I tried to search for ways to obtain a module handle, but did not find any way that works on UWP:
Process.GetCurrentProcess does not compile, missing
Marshal.GetHINSTANCE missing
GetType().Module.Name missing (for GetModuleHandle)
What else can I try?
The only way to obtain scancodes from C# I have found so far is to use the Windows Hook system through PInvoke by registering a hook for WH_KEYBOARD_LL through SetWindowsHookEx.
Unfortunately, the solution that you have mentioned is not applicable for UWP. For your requirement, you could use the AcceleratorKeyActivatedevent, and the AcceleratorKeyEventArgs provides the ScanCode value directly.
public MainPage()
{
this.InitializeComponent();
Window.Current.Dispatcher.AcceleratorKeyActivated += Dispatcher_AcceleratorKeyActivated;
}
private void Dispatcher_AcceleratorKeyActivated(Windows.UI.Core.CoreDispatcher sender, Windows.UI.Core.AcceleratorKeyEventArgs args)
{
var scanCode = args.KeyStatus.ScanCode;
}

Controlling Message Boxes In Citrix

I have an instance of an application running on a Citrix server. I was recently tasked with writing a plugin for that piece of software, and for reasons to do with how software is deployed where I work, the plugin actually just executes another piece of software sitting on a remote server that actually does the work and waits for that process to end (this is hidden from the user).
Occasionally this external software has to display a message with a message box. In order to make sure that it came up on top of the application, I used the user32 messagebox method with that application's handle.
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern MessageBoxResult MessageBox(IntPtr hWnd, String text, String caption, int options);
public static MessageBoxResult ShowMessageBox(IntPtr windowHandle, string text, string caption, MessageBoxOptions options)
{
return MessageBox(windowHandle, text, caption, (int)options);
}
That works great when the application is being run on a local machine, but when it is being run through Citrix, the message box no longer shows up on top. Does anyone have any ideas how to make the Citrix version behave like the local version?
I worked around the issue by setting the topmost flag on the messagebox. This isn't exactly what I wanted, but it was better than having the messagebox not show up on top initially.

How to close a WPF application running in system tray from a C# console application?

I'm developing an application which has 2 parts:
1) A WPF application which runs from the system tray. It does have a GUI window, which can be brought up by right-clicking the sys-tray icon & choosing an option on the context menu, but it is not needed very frequently & the app will run from the sys-tray most of the time. Moreover, I've changed the Closing() event of the MainWindow, so as to minimize the app, if the user tries to close it.
2) A console application which runs without displaying a console & its purpose is to poll for launch/close of another application. As soon as the other application is launched/closed, the console application launches/closes the WPF application too. (Please don't tell me to do this using a Windows Service. I've already explored the route before. It doesn't work for me due to several reasons, which I won't enlist here).
PROBLEM: I am not able to close the WPF application from the console application. What I'm doing is as follows. First I obtain the process for my WPF application:
Process AppProcess = Process.GetProcessById((int)AppID);
After this I've tried a lot of options as follows:
1) Killing the process: AppProcess.Kill();
This is the only one which worked, but is very unelegant. It also leaves the SysTray icon undisposed, so it is not acceptable.
2) AppProcess.Close();
Doesn't work at all & I don't know why.
3) AppProcess.Dispose();
Doesn't work at all & I don't know why.
4) AppProcess.CloseMainWindow();
This only works if the user has kept the GUI of the WPF app opened, which is very rarely the case as I mentioned before. So, normally this also doesn't work. (You might say that I've hacked the Closing() event to prevent closing the window. Don't worry I've taken appropriate care to handle that. I've provided other measures to close the app. There is a boolean variable which decides whether the Closing action is to be cancelled or not.)
5) Passing custom/standard message to the WPF Application's main window.
This also works only if the main window (GUI) of the WPF app is open, else it doesn't receive the message.
So, all in all, no method is working. I need a reliable method to close the WPF app gracefully from the console app. Please suggest something.
The current way I'm doing it is as follows:
In the Console App:
const uint WM_CUSTOMCLOSE = 0xFE;
IntPtr hWnd = AppProcess.MainWindowHandle;
SendMessage(hWnd, WM_CUSTOMCLOSE, 0, 0);
In the WPF app:
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if(msg == 0xFE)
{
ExitFlag = true; //Exitflag is checked later to decide whether closing is to be cancelled.
this.Close();
}
return IntPtr.Zero;
}
Use IPC as JeffRSon suggested. MSMQ is a very easy and effective way when two processes are on the same machine.
Define a queue of your WPF application and when it starts, no matter showing UI or not, let it listen the queue. Console application can send a message to the queue with closing application message. You have to define the data contract. You can refer to:
http://www.codeproject.com/Articles/3944/Programming-MSMQ-in-NET-Part-1
Ok, I found a way to close the WPF app. The reason it wasn't receiving the message was that for some reason, AppProcess.MainWindowHandle was not giving the handle of the main window of the WPF app, when the WPF app ran from the system tray.
So, instead I'm using the user32.dll:FindWindow() method found in the Win32 APIs to find the main window of the WPF app. After that when I pass my custom message to it, the WPF app shuts down gracefully.

ShowWindow Function Doesn't Work When Target Application Is Run As Administrator

I am writing a program that shows/hides the window of some target application. I was testing it out earlier and noticed something strange. If I run the target application as Administrator (right-click->Properties, "Compatability" tab, "Run this program as administrator") it doesn't work.
To demonstrate I wrote a simple GUI app called "TargetApplication" and then I wrote the following code to test showing/hiding this application:
class Program
{
static void Main(string[] args)
{
IntPtr windowPtr = FindWindow(null, "TargetApplication");
ShowWindow(windowPtr, 0); // 0 = Hide
Console.WriteLine("The window is now hidden. Press Enter to restore");
Console.ReadLine();
ShowWindow(windowPtr, 9); // 9 = Restore
Console.WriteLine("The window is now restored. Press Enter to exit.");
Console.ReadLine();
}
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
}
If I start the windowed application without Administrator rights it doesn't work.
Would anyone mind testing this for me? I have uploaded the .exe's for both applications here:
TestShowWindow Download
All you have to do is download them and run TestApplication.exe then run TestShowWindow.exe. You will find that by changing TestApplication.exe to run as Administrator causes ShowWindow to no longer work.
Of course if you don't trust downloading my stuff you can always compile my code and test it on any target application in Windows that you are able to change compatability mode of.
P.S. I am not sure if it makes a difference but I am running Windows 8 Pro. 64-bit.
This is by design. It is the lesser known twin of UAC, called UIPI or User Interface Privilege Isolation. An un-elevated program cannot commandeer an elevated one. Given the capabilities of UI Automation, this is an obvious counter-measure to stop programs from hijacking the capabilities of an elevated process. A security violation called a shatter attack.
Workarounds are to provide a manifest with uiAccess = true for a program stored in c:\windows or c:\program files and provided with a certificate. And for the target program to call ChangeWindowMessageFilter to allow certain messages to be sent. In your case that ought to be WM_SHOWWINDOW.
If you don't mind the window acting like you minimized it to the taskbar; You can, generally, show and hide windows from elevated processes by posting WM_SYSCOMMAND with a wParam of SC_RESTORE or SC_MINIMIZE.

Send windows message to a Windows Service

Is there any tool to send (mimic) a windows message like 'WM_ENDSESSION' to a windows service?
OR
How can I send a windows message to a process using C#?
(I know only C#)
EDIT: Purpose: Basically I have to debug a windows service for fixing a bug that occurs only on system shut down.
Services should be controlled using ServiceController class
Represents a Windows service and allows you to connect to a running or stopped service, manipulate it, or get information about it.
You can use it to start, stop and communicate with services using this class.
Generally, services don't have windows (let alone message pumps) to receive a windows message.
If the bug really does only happen on shutdown (as opposed to just stopping the service), it may be the case that something depends on a resource that is going away, which is not being handled gracefully (in which case, the bug fix might be to set up the service dependencies correctly). Have you tried using the remote debugging tools to attach to the process prior to shutdown?
It's worth investigating if you can get the problem to occur without shutting down, perhaps when just stopping the service with the Service Control Manager (no need to do this programmatically, as this is a debugging scenario), in which case, you can breakpoint OnStop() in your service (I'm assuming C#) and watch what happens.
Check out the answers to How to simulate windows shutdown while debugging?
Services have an 'event' called OnShutdown they can subscribe to, so it could be the problem is in that code. If the code is .net you could subclass it so you can call the protected OnShutdown method to debug. But the problem could also be as suggested by others that the service is expecting resources to be available which aren't because they've already been closed.
Also, if the service was written in .net 2.0 note that the Stop() command isn't called automatically on a service when the workstation is being shut down! This is very surprising and was fixed in .net 3.5, but if you're using .net 2.0 you need to call Stop() yourself within OnShutdown().
If you have the hwnd of a window you can send it messages. The only limitation is that you can't send messages that contain pointers like setting window text.
Simply call PostMessage() with the value of the hwnd and the message you want to send.
To find the hwnd you can use spy++.
I'm not sure how you connect all this to windows services since windows services don't have windows.
I would recommend Importing and defining the following:
[System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
public static extern bool PostMessage(IntPtr handleWnd, UInt32 Msg, Int32 wParam, Int32 lParam);
const int WM_ENDSESSION = 0x0016,
WM_TRUE = 0x1,
WM_FALSE = 0x0;
Then send through 0x1 or 0x0 representing true or false as the wParam message.
So in your code you will use:
PostMessage(HandleToSendTo, WM_ENDSESSION, WM_TRUE, 0);
Where HandleToSendTo is the Window Handle of the window you would like to send the message to.
Edit
To get the windows handle if you don't know it, I'm assuming that you will know it's title or name. If so you can use this:
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
public static extern int FindWindowEx(int hwndParent, int hwndEnfant, int lpClasse, string lpTitre);
Which more information about can be found in this question.
Or maybe
I don't know if this is a similar handle, I doubt it, but someone could let me know if it is, but you can get a Process handle, which means you could get the process using Process.GetProcessesByName("MyAppName");, althought don't rely on this as I don't think that it will get the handle that you are after. Just a suggestion.
I don't think there is a tool to send arbitrary messages because each message can have arbitrary LPARAM and WPARAM values.
But the most useful tool surrounding windows messages is spy++. Spy++ is included with Visual Studio and it helps you see what messages are sent, window hierarchy, and more.
You can send messages via C# with the SendMessage Win32 API. You can get the window handle it asks for by using a Win32 API like FindWindow or FindWindowEx.
Edit (to match question's edit): Services are stopped by windows automatically on shutdown. So to fix your bug it sounds like you need to modify the code of the service itself to properly shut down.
Edit2: Or if you want to stop the service you should use the Win32 API ControlService passing in SERVICE_CONTROL_STOP 0x00000001.
I don't know if this is a similar handle, I doubt it, but someone could let me know if it is, but you can get a Process handle, which means you could get the process using Process.GetProcessesByName("MyAppName");, althought don't rely on this as I don't think that it will get the handle that you are after. Just a suggestion.
Actually this method will work...you just need to access the 'MainWindowHandle' property of the process object. For Instance...
Process myProcess;
Int handle;
myProcess = Process.GetProcessesByName("MyAppName");
handle = myProcess.MainWindowHandle;

Categories

Resources