I have the following code which draws a square on the main window of another process:
private void DrawOnWindow(string processname)
{
IntPtr winhandle = GetWindowHandle(processname);
Graphics grph = Graphics.FromHwnd(winhandle);
grph.DrawRectangle(Pens.Red, 10, 10, 100, 100);
}
private IntPtr GetWindowHandle(string windowName)
{
IntPtr windowHandle = IntPtr.Zero;
Process[] processes = Process.GetProcessesByName(windowName);
if (processes.Length > 0)
{
for (int i = 0; i < processes.Length; i++)
{
if (processes[i].MainWindowHandle.ToInt32() > 0)
{
windowHandle = processes[i].MainWindowHandle;
break;
}
}
}
return windowHandle;
}
This works when I pass "firefox" to the DrawOnWindow method, but it does not work when I pass "iexplore" and I don't understand why. What do I need to do to get this to work on internet explorer? (IE8)
With IE what you're drawing on is the parent window and IE8 has the WS_CLIPCHILDREN window style set. What you need to do is to descent the window hierarchy and figure out exactly which child you want to render on and then go to town on that window instead.
IE8 has one top-level process, which contains the main window and draws the frame, address bar, and other controls in it. t also has several child processes, which own one or more tabs and their corresponding windows.
Your code will return the window handle for the top-level window of the last Iexplore process. However, if that process happens to be a child window, there are two problems:
it does not have a top-level window;
nothing guarantees you that the current visible tab belongs to this process.\
You need to modify your code and special-case it for IE to accommodate for the specifics of the IE process/window hierarchy.
Btw, if you decide to draw on Chrome, you will have similar problems, as they also do similar tricks with multiple processes and window hierarchy that spans across these processes.
Related
I've been tasked with creating an application launcher that hosts both Winforms and WPF applications and via a slightly different methodology Web applications. The user is presented with a list of applications they can launch and these are "captured" upon launch and are lodged inside a panel on a form by using SetParent to make the panel the parent of the processes MainWindowHandle. This bit appears to work well and the applications when launched are captured and displayed in the given panel.
What Iʼm having a particular issue with is that not all applications that are captured are happy to initially draw themselves in the panel. It seems to be isolated to those applications that are WPF based, but thatʼs not guaranteed.
Effectively what happens is that if a WPF application is launched, it is captured and moved to the panel, and the panel will remain blank until I click the panel at which point the application will happily repaint itself. From this point forward the application seems to be happy enough repainting itself as required without intervention.
Basically Iʼm now at my wits ends and have tried the following User32 native and .NET methods;
Invalidate (on the form, on the panel, on the tab control that hosts both of these)
Update
Refresh
SendMessage with a plethora of parameters, including attempting the, "you shouldnʼt do this" WM_PAINT.
RedrawWindow with the UpdateNow and Invalidate flags.
None of the above makes any visible difference and itʼs only when I physically click the panel or move the window that the contained application will behave and repaint itself.
Has anyone else produced anything similar and has a solution to the redraw/repaint issue? Iʼve scoured the entire Google/Bing/Duck Duck Go spheres trying to hunt down an answer but to no avail.
Hopefully one of you out there has an answer.
The following code represents the bulk of the feature in that it starts a process and captures the handle for the main window of the process and sets itsʼ parent to a panel control on a bog-standard WinForms window. I should probably point out that the bog-standard WinForms window is itself “hosted” inside an application using the (EasyTabs) Library. Which, I believe, is not causing any problems.
Example:
// Try to acquire a the process.
ProcessStartInfo startInfo = new ProcessStartInfo(path);
try
{
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.WindowStyle = ProcessWindowStyle.Normal;
Process.StartInfo = startInfo;
Process.EnableRaisingEvents = true;
Process.Exited += TabManagerContainerForm_ProcessExited;
Process.Start();
if (Process != null)
{
// Wait until the process has created a main window or exited.
while (Process.MainWindowHandle == IntPtr.Zero && !Process.HasExited)
{
Thread.Sleep(100);
Process.Refresh();
}
if (!Process.HasExited) // We have acquired a MainWindowHandle
{
// Capture the Process's main window and show it inside the applicationPanel panel control.
SetParent(Process.MainWindowHandle, applicationPanel.Handle);
// Change the captured Process's window to one without the standard chrome. Itʼs provided by our tabbed application.
SetWindowLong(Process.MainWindowHandle, (int)WindowLongFlags.GWL_STYLE, (int)WindowStyles.WS_VISIBLE);
}
else // Process has exited.
{
if (Process.MainWindowHandle == IntPtr.Zero) Log.Information("{0} failed to execute.", Process.ProcessName);
throw new FailedProcessException(string.Format("{0} failed to execute.", path));
}
}
else
{
throw new Exception(string.Format("Invalid path: {0}", path));
}
}
catch (Exception ex) when (!(ex is FailedProcessException)) // Catch everything but FailedProcessExceptions. FPEs are simply passed up the chain.
{
Log.Error(ex.Message);
throw;
}
Summary:
The problem seems to happen when attaching an application to a Panel in a tab that is not the currently visible tab.
Solution:
Add WS_CHILD flag to the call to SetWindowLong:
SetWindowLong(Process.MainWindowHandle, (int)WindowLongFlags.GWL_STYLE, (int)(WindowStyles.WS_VISIBLE | WindowStyles.WS_CHILD));
Details:
I tried reproducing the example in the question (without using EasyTabs). I used a simple Form with a single Panel. With a button press I call a simple WPF app and attach it to the Panel. It works OK, it renders immediately. The only problem I found was the position of the WPF window which was random. I fixed it with calling SetWindowPos (using pinvoke) like this:
SetWindowPos(Process.MainWindowHandle, IntPtr.Zero, 0, 0, 0, 0, SetWindowPosFlags.IgnoreResize);
Than, I tried using a TabControl with two tabs, each containing a Panel and two buttons, each one when pressed, launches a different WPF app to one of the Panels. I found that when a tab (TabPage) is not the visible tab, the problem occurs - the launched application is not visible until the Panel is clicked. I solved this problem by adding WS_CHILD flag to the call to SetWindowLong. I'm not sure why but it works...
My code:
// Capture the Process's main window and show it inside the applicationPanel panel control.
SetParent(Process.MainWindowHandle, applicationPanel.Handle);
// Change the captured Process's window to one without the standard chrome. Itʼs provided by our tabbed application.
SetWindowLong(Process.MainWindowHandle, (int)WindowLongFlags.GWL_STYLE, (int)(WindowStyles.WS_VISIBLE | WindowStyles.WS_CHILD));
// Change the Process's window position to the top-left corner of the panel
SetWindowPos(Process.MainWindowHandle, IntPtr.Zero, 0, 0, 0, 0, SetWindowPosFlags.IgnoreResize);
I'm creating a "desktop gadget" of sorts, I've disabled manual minimizing of the window, but now there is another problem: the system can still hide the window if the user presses Windows+D, for example.
When hidden that way, no usual minimize/resize/visibility events are fired.
I want to do something almost like TopMost, but without forcing the window order.
Maybe it's possible to install a global shortcut event using win32 API, and briefly set TopMost to true, but that sounds very hackish.
I found one solution, but it does not seem to work on Windows 10: Keeping window visible through "Show Desktop"/Win+D
The other common option, which would be writing an actual desktop gadget, is not possible on Windows 10, given their deprecation.
Are there any other methods to keep a window visible (but not on top of the screen) at all moments?
This function is working for me:
BOOL FixShowDesktop(HWND hWnd)
{
HWND hWndTmp = FindWindowEx(NULL, NULL, L"Progman", NULL);
if (hWndTmp)
{
hWndTmp = FindWindowEx(hWndTmp, NULL, L"SHELLDLL_DefView", NULL);
if (hWndTmp)
{
SetWindowLongPtr(hWnd, -8, (LONG_PTR)hWndTmp);
return TRUE;
}
}
return FALSE;
}
Note, this code is a bit better then from Keeping window visible through "Show Desktop"/Win+D because the window can be overflowed by other windows (like any other window). Using SetParent places window under all other windows.
I have many popups in a custom GUI application. These popups are window objects, not popup objects. The popups do not show up in a screenshot when using the Print Screen button on the keyboard. Instead, the disabled mainwindow below is all that shows in the screenshot. The popup never flickers or disappears, it just doesn't show in the screenshot.
WindowInstance.IsEnabled = true;
WindowInstance.Refresh();
DisplayPopUpWindow(WindowInstance);
The code in DisplayPopupWindow:
private void DisplayPopUpWindow(Window theWindow)
{
if (theWindow != null)
{
if (theWindow is MessagePopup)
{
// Only show one popup at a time (queue is handled elsewhere)
RemovePopUpsCommand.Execute(true, null);
}
ActiveWindow.IsEnabled = false; // main screen disabled
foreach (KeyValuePair<Window, int> aPopup in popUpWindows)
{
if ((aPopup.Key.IsVisible) && (aPopup.Key.Opacity == 1) && (aPopup.Key != theWindow))
{
// if window is a lower priority then disable it
if (aPopup.Value > displayPriority)
aPopup.Key.IsEnabled = false;
}
}
theWindow.Show();
theWindow.Opacity = 1;
}
}
Is there some property in the XAML that affects whether the window is visible for screenshots? This is a large issue as this also affects some remoting software we use in that popups do not display on the shared screen. Also affects our combobox implementation.
The "popups" are actually their own standalone windows. Some have instances created once during application startup and simply shown/hidden when needed, however, most are created on demand to be displayed. This problem affects both types.
The remoting software used is axeda access remote.
If I remember correctly I had the same problem and I think it was related to setting the popup windows parent to the main window that fixed it, I'd have to look at my code at home to confirm.
So make sure this is correctly set.
EDIT:
Try using this code when you create the Window object:
MainWindowVariable.Owner = Window.GetWindow(this)
You can read more here:
http://msdn.microsoft.com/en-us/library/system.windows.window.owner(v=vs.110).aspx
How to remove an 3rd party application from the Windows taskbar by its handle?
I've found this:
Remove application from taskbar with C# wrapper?
But it doesnt worked for me.
It only sets another style (small x to close, no maximize/minimize button) to the Window i selected (notepad).
Any ideas about this?
EDIT: I dont want to remove MY application from the taskbar, i want to remove an external application by handle
If you have the handle to the window you can call ShowWindow() through the Win32 API. Then you can do:
// Let the window disappear (even from taskbar)
ShowWindow(this.Handle, WindowShowStyle.Hide);
// Revive the window back to the user
ShowWindow(this.Handle, WindowShowStyle.ShowNoActivate);
So from now, all your problem is to get the handle of the window you like to hide:
Process[] procs = Process.GetProcesses();
IntPtr hWnd;
foreach(Process proc in procs)
{
if ((hWnd = proc.MainWindowHandle) != IntPtr.Zero)
{
Console.WriteLine("{0} : {1}", proc.ProcessName, hWnd);
}
}
To hide it from windows task bar you just need to set ShowInTaskbar property to false :
this.ShowInTaskbar = false;
As for moving of windows you can use spy++ to check windows events and identify it.
How to remove an application from the Windows taskbar?
this.ShowInTaskbar = false;
Easy:
this.ShowInTaskbar = false;
As for the Form movement: you can use the Move event under Layout events
Environment
Windows XP SP3 x32
Visual Studio 2005 Standard
Windows Mobile/Pocket PC 2003
.NET Compact Framework 1.0 SP3 and .NET Framework 1.1
Honeywell Dolphin 9500 Handheld Barcode Scanner
Goal
I have a three form application and an external class (Program.cs) that has the application entry point, Main(). The main form loads first and then, from within MainForm_Load(...), instantiates/shows a new form kind of like a splash screen. I want all three forms to be maximized. All three forms have the following properties set:
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
this.ControlBox = false;
Problem
The "splash screen" form shows up full screen without any issue. I then dispose of it and show the main screen (first to load and the param for Application.Run();. From the main screen, once a list box SelectedIndexChanged event is detected, a third form is shown (leaving the main form behind said third form). That third form shows the task bar over the top portion of my form:
Upon closing this form, the main form now has the task bar overlayed as well.
Code
Friend paste links. Let me know if I should just post the code here. WorkOrderView is over a thousand lines so I figured this would be easier.
"Main" Form (WorkOrders.cs)
"Third" Form (WorkOrderView.cs) - Pictured above
Unrelated Suggestions
I am quite a green programmer and I especially lack experience in this environment. So, if you have any suggestions/criticisms on the way I'm doing some things, don't hesitate to hit me with them. Probably best to just comment on the post instead of posting an answer for those types of replies.
Thanks!
First off, I only run Windows Mobile 5 (WM5) these days. (I got to move up from PocketPC 2003 a couple of years ago. LOL)
I've found that defining a Form's Window Size does not work well on mobile devices, and that showing/hiding multiple forms is awkward and never behaves the way you want.
Still, make sure your Main Form has it's WindowState set to Maximized.
I also set ControlBox and MinimizeBox to False, if that helps.
Instead of using multiple forms, it will look much nicer if you simply use Panels as your forms with the Main Form as some kind of MDI container (bad choice of words, but that is what I'm doing).
In my Designer View, each panel is just a small box full of controls.
To work with each panel, select it in the Visual Studio designer, and you will see a position box (arrows up & down and left & right).
Right-Click the position box and click Bring to Front.
Now go over to the panel control's Properties and set Dock to Fill.
While this panel is full screen, add all of your buttons, textboxes, etc. with appropriate names. Names like pnl1_button1 and pnl2_button1 are easier to understand in your code than the VS default button1 and button2.
When you are through with your design view of this panel, go back to the Dock properties and set it back to None.
Go on to the next panel control.
I have found it also helps to maintain a small sketch of your panels with their names and the names of their controls.
In the Load event of your main form, set every Panel's Dock property to DockStyle.Fill. Then, as you want to show one form, simply call Panel1.BringToFront() instead of dialog.Show().
Mobile development isn't hard, but it is different. :)
EDIT:
In the project's Program.cs file, I keep the following static tools that I can use to turn ON and OFF the Start Menu (doesn't work so well in WM5, but I've still got it in my code from my PocketPC version).
I have not had to open this project in a year or so, but it should all be valid. Give them a try. If I've left something out, just let me know.
Once this is pasted into your project's Program.cs file, just call Program.ShowWindowsMenu(false); when your program starts and Program.ShowWindowsMenu(true); when your program exits.
static IntPtr _taskBar;
static IntPtr _sipButton;
public enum Notify_Events {
NOTIFICATION_EVENT_NONE = 0,
NOTIFICATION_EVENT_TIME_CHANGE = 1,
NOTIFICATION_EVENT_SYNC_END = 2,
NOTIFICATION_EVENT_DEVICE_CHANGE = 7,
NOTIFICATION_EVENT_RS232_DETECTED = 9,
NOTIFICATION_EVENT_RESTORE_END = 10,
NOTIFICATION_EVENT_WAKEUP = 11,
NOTIFICATION_EVENT_TZ_CHANGE = 12,
NOTIFICATION_EVENT_OFF_AC_POWER,
NOTIFICATION_EVENT_ON_AC_POWER
}
public enum WindowPosition {
SWP_HIDEWINDOW = 0x0080,
SWP_SHOWWINDOW = 0x0040
}
[DllImport("coredll.dll", EntryPoint = "FindWindowW", SetLastError = true)]
public static extern IntPtr FindWindowCE(string lpClassName, string lpWindowName);
[DllImport("coredll.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
static void ShowWindowsMenu(bool enable) {
try {
if (enable) {
if (_taskBar != IntPtr.Zero) {
SetWindowPos(_taskBar, IntPtr.Zero, 0, 0, 240, 26, (int)WindowPosition.SWP_SHOWWINDOW); // display the start bar
}
} else {
_taskBar = FindWindowCE("HHTaskBar", null); // Find the handle to the Start Bar
if (_taskBar != IntPtr.Zero) { // If the handle is found then hide the start bar
SetWindowPos(_taskBar, IntPtr.Zero, 0, 0, 0, 0, (int)WindowPosition.SWP_HIDEWINDOW); // Hide the start bar
}
}
} catch (Exception err) {
ErrorWrapper(enable ? "Show Start" : "Hide Start", err);
}
try {
if (enable) {
if (_sipButton != IntPtr.Zero) { // If the handle is found then hide the start bar
SetWindowPos(_sipButton, IntPtr.Zero, 0, 0, 240, 26, (int)WindowPosition.SWP_SHOWWINDOW); // display the start bar
}
} else {
_sipButton = FindWindowCE("MS_SIPBUTTON", "MS_SIPBUTTON");
if (_sipButton != IntPtr.Zero) { // If the handle is found then hide the start bar
SetWindowPos(_sipButton, IntPtr.Zero, 0, 0, 0, 0, (int)WindowPosition.SWP_HIDEWINDOW); // Hide the start bar
}
}
} catch (Exception err) {
ErrorWrapper(enable ? "Show SIP" : "Hide SIP", err);
}
}
static void ErrorWrapper(string routine, Exception e) {
if (!String.IsNullOrEmpty(e.Message)) {
MessageBox.Show(e.Message, routine, MessageBoxButtons.OKCancel, MessageBoxIcon.None, 0);
}
}
EDIT2:
Declare a private static instance of your main form, then wrap that in a try....catch routine inside your project's Program.cs file like so:
static Form1 ppcForm = null;
static void Main() {
ShowWindowsMenu(false);
try {
ppcForm = new Form1();
Application.Run(ppcForm );
} catch (Exception err) {
if (!String.IsNullOrEmpty(err.Message)) {
ErrorWrapper("Mobile Form (Program)", err);
}
} finally {
ShowWindowsMenu(true);
}
}
What you're after is usually referred to as "kiosk mode" (which may help your search engine results).
The underlying issue is that the start bar is not part of your app - it's part of the Shell app and you're competing with it for desired behavior. What you want is something the Platform is trying to prevent you from doing, so you have to be prepared to put your developer boot on the platform's neck to get it to behave.
This blog entry is a really good starting point for this issue and will probably give you what you need, but feel free to use a search engine to find more/aternate proposals. There's actually a lot of material on the web for this - more than would belong in this answer.