c# can't focus when SetParent user32.dll - c#

I'm trying to do a HUD for poker,
So the idea is to set Form over the poker application ( which is not my application ) and keep the form always over the table even if i move the poker table
So I get the IntPtr from the table using
[DllImport("user32.dll")]
public static extern int EnumWindows(EnumWindowsCallback lpEnumFunc, int lParam);
I need a Form for each player at the table for display the informations about a player
So I create my Form which I attach to the poker table using
[DllImport("user32.dll")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
But then when I show the Form I lost the focus ! I just can't click on the poker table or on my main frame. A solution ?
private void displayHudForEachPlayer(IntPtr pokerTable) {
StringBuilder windowTitle = new StringBuilder(256);
GetWindowText(pokerTable, windowTitle, windowTitle.Capacity);
if (windowTitle.ToString().Contains("Real money"))
{
HudPlayerView hudPlayer1 = new HudPlayerView();
//hudPlayer1.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
Console.WriteLine(windowTitle.ToString());
IntPtr childHandle = hudPlayer1.Handle;
ParentWndWrapper oParentWnd = new ParentWndWrapper(pokerTable);
SetParent(childHandle, pokerTable);
hudPlayer1.SetBounds(0, 0, 100, 50);
hudPlayer1.ShowDialog();
}
}
I tried also with
IWin32Window w = Control.FromHandle(pokerTable);
hudPlayer1.ShowDialog(w);
instead of SetParent but same result.

You are calling ShowDialog() which shows a modal window that will stop you from being able to interact with anything else until the modal dialog is closed.
Try using Form.Show() and set TopMost = true on each of the forms so they don't get covered up when you click on the poker app.

Related

Is it possible to set the parent form of an external application before starting the process?

I have a winforms application (.net 5.0) that comprises two forms - one for the operator (to setup different options/enter data, admin duties) and another for the user to interact with (play games, follow instructions etc). Each form is displayed on separate monitors, with both visible/available when the application is run.
One requirement of the application is to run external applications (games) in the user form. The user form contains a panel (as a header) and several custom user controls. One of the user controls becomes the parent of the external application.
Using the code below I am able to run external applications inside the user form. However, the applications all start outside of the form (as indicated by the appearance of a 'splash' screen) before being moved inside the user form using SetParent(...).
What I want to achieve is for the 'splash' screen not to appear before moving the external application to the user control. I understand that causes/solutions may range depending on the application in question, so guidance is welcomed in lieu of a solution.
Much of the code below has been sourced from SO and Google more widely, however I have been unable to find references to 'splash' screen issues.
public static int GWL_STYLE = -16;
public static int WS_BORDER = 0x00800000; //window with border
public static int WS_DLGFRAME = 0x00400000; //window with double border but no title
public static int WS_CAPTION = WS_BORDER | WS_DLGFRAME; //window with a title bar
public const uint WS_SIZEBOX = 0x00040000;
...
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern bool MoveWindow(IntPtr Handle, int x, int y, int w, int h, bool repaint);
[DllImport("user32.dll")]
static extern bool RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);
public static void HideWindowBorders(IntPtr hWnd)
{
var style = GetWindowLong(hWnd, GWL_STYLE); //gets current style
SetWindowLong(hWnd, GWL_STYLE, (uint)(style & ~(WS_CAPTION | WS_SIZEBOX))); //removes caption and the sizebox from current style
}
...
// Button click in the operator form starts the external application
private void playSuperTuxBtn_Click(object sender, EventArgs e)
{
Process superTux = new Process();
superTux.StartInfo.FileName = #"C:\Program Files\SuperTux\bin\supertux2.exe"; // 0.6.3
superTux.StartInfo.UseShellExecute = false;
superTux.StartInfo.CreateNoWindow = false;
superTux.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
superTux.Start();
superTux.WaitForInputIdle();
while (superTux.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(100);
superTux.Refresh();
}
RemoveMenuBar(superTux.MainWindowHandle);
HideWindowBorders(superTux.MainWindowHandle);
SetParent(superTux.MainWindowHandle, RebotControlForm.uiForm.conUIGamePlay.Handle);
MoveWindow(superTux.MainWindowHandle, 0, 0, RebotControlForm.uiForm.conUIGamePlay.Width, RebotControlForm.uiForm.conUIGamePlay.Height, true);
}
MainWindowHandle is not an actual thing in Windows, it is faked by .NET.
To catch all windows you would have to use a hook of some kind, SetWinEventHook or a CBT hook.

run another application within a C# program with his childs

I use a sample code to hunt process to my windows form application from specific List
void processStartEvent_EventArrived(object sender, EventArrivedEventArgs e)
{
string processName = e.NewEvent.Properties["ProcessName"].Value.ToString();
int processID = Convert.ToInt32(e.NewEvent.Properties["ProcessID"].Value);
if (_processNames.Contains(processName))
{
Process proc = Process.GetProcessById(processID);
if (GlobalVar.SourceWinForm.InvokeRequired)
{
GlobalVar.SourceWinForm.Invoke(new MethodInvoker(delegate { ProcessHandler.SetParent(proc.MainWindowHandle, GlobalVar.SourceWinForm.Handle); }));
}
else
{
ProcessHandler.SetParent(proc.MainWindowHandle, GlobalVar.SourceWinForm.Handle);
}
}
}
as you can see i use the function :
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hwc, IntPtr hwp);
everything work good except one thing.
for example i hunted notepad application into my app.
so it really give me the notepad into my app window but the problem start when
i press for example in the notepad "Format -> Font" it open a new sub window of notepad , this sub window , my apllication is not father of this sub window.
how i can hunt the full process ? include his child (subs) windows ?
you can use:
[DllImport("user32.dll")]
private static extern
bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern
bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern
bool IsIconic(IntPtr hWnd);
and then
// bring it to the foreground
if (IsIconic(proc.MainWindowHandle))
ShowWindowAsync(proc.MainWindowHandle, SW_RESTORE);
SetForegroundWindow(proc.MainWindowHandle);
Solution:
first of all i want to say the comments are correct and can help to someone else so please read the comments to my main question first.
but if you have specific scenario and you don't have any other option and you must use SetParent or other function like that. ( make sure you read first about SetParent and you understand what it dose )
the solution is to take the entire forms to foreground:
bool SetForegroundWindow(IntPtr hWnd);
this make all the other forms that inherit from the main form you loaded to see it on your main application ( this in case you application block and streach entire screen )

StartInfo.WindowStyle = ProcessWindowStyle.Hidden is still not working

ProcessStartInfo proc = new ProcessStartInfo();
proc.FileName = #"C:\\Windows\\System32\\RunDll32.exe";
proc.Arguments = "shell32.dll,Control_RunDLL inetcpl.cpl,Internet,4";//open Internet Properties window
proc.WindowStyle = ProcessWindowStyle.Hidden;
proc.UseShellExecute = false;
proc.CreateNoWindow = true;
Process.Start(proc);
SendKeys.Send("{TAB}{TAB}{TAB}{TAB}{ENTER}");// Open Lan Setting window
I've tried many way to hide/ close "Internet Properties window" and Lan Setting Window after it's called, but this code doesn't work.
Help me !
I think you should find another way to play with your proxy problem, but if you still want to use the trick which plays with showing and closing dialogs, I have a solution here which would help (I've tested) and you don't have to use SendKeys which I feel very unstable. Here is my code:
//You have to add these using first:
//using System.Runtime.InteropServices;
//using System.Threading;
//This is used to find a window
[DllImport("user32", CharSet=CharSet.Auto)]
private static extern IntPtr FindWindow(string className, string windowName);
//This is used to find Button in a window
[DllImport("user32")]
private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr childAfter, string className, string windowName);
//This is to help you Click on a Button
[DllImport("user32")]
private static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam);
//---------------------------
//Here are our methods
//---------------------------
//This is used to Get handle of a Button of a Parent window by its Text
private IntPtr GetButton(IntPtr parent, string text)
{
return FindWindowEx(parent, IntPtr.Zero, "Button", text);
}
//This is used to Click on a Window (usually a Button) with its Handle passed in
private void ClickWindow(IntPtr hwnd)
{
SendMessage(hwnd, 0x201, IntPtr.Zero, IntPtr.Zero);
SendMessage(hwnd, 0x202, IntPtr.Zero, IntPtr.Zero);
}
//This is used to find the Local Area Network (LAN) Settings window
//This is called in a separate thread, because somehow the LAN settings window
//showing causes the main Form not-responding (we can't call anything in our main thread).
private void SearchForLanSettingsWindow()
{
int i = 0;
while (true)
{
Thread.Sleep(100);
IntPtr windowHandle = FindWindow(null,"Local Area Network (LAN) Settings");
if (windowHandle != IntPtr.Zero)
{
//Find the button OK, if you like, you can replace it with "Cancel",...
IntPtr button = GetButton(windowHandle, "OK");
//Click on that OK button to Close your Lan settings window
//You may want to research on the DestroyWindow or CloseWindow
//win32 api without having to click on a Button, but I think this should be better. It's up to you.
ClickWindow(button);
break;
}
i++;
if (i > 20)//timeout
{
break;
}
}
}
//And here is your code with my code appended
System.Diagnostics.ProcessStartInfo proc = new System.Diagnostics.ProcessStartInfo();
proc.FileName = #"C:\\Windows\\System32\\RunDll32.exe";
proc.Arguments = "shell32.dll,Control_RunDLL inetcpl.cpl,Internet,4";//open Internet Properties window
proc.UseShellExecute = false;
System.Diagnostics.Process.Start(proc);
Thread.Sleep(100);//Sleep to be sure the Window is really created
//Get the handle to the window "Internet Properties"
IntPtr mainHandle = FindWindow(null, "Internet Properties");
//Find the tab "Connections", this tab has class "#32770" and is a child window of the window "Internet Properties"
IntPtr child = FindWindowEx(mainHandle, IntPtr.Zero, "#32770", "Connections");
//Get the button "LAN settings"
IntPtr button = GetButton(child, "&LAN settings");
//Create new thread and start it to find the Lan settings window
//we have to do this now because for some reason, after the LAN settings window shows
//we can't call any code in our class.
new Thread(SearchForLanSettingsWindow).Start();
//Click on the LAN settings button to Show the LAN settings window
ClickWindow(button);
//Get the button OK on the window "Internet Properties"
button = GetButton(mainHandle, "OK");
//Click on that button to close the window "Internet Properties"
ClickWindow(button);
And that's all.
I've found that if the computer is installed with a non-English language, the Buttons OK, LAN settings may be different. So the better solution is to use GetDlgItem() to get the buttons from their IDs. To do so, you have to import the function GetDlgItem() first:
[DllImport("user32")]
private static extern IntPtr GetDlgItem(IntPtr dlgHandle, int itemID);
I used Spy++ to know what the control ids of OK and LAN settings are. The control ID of OK is 1 and the control ID of LAN settings is 0x62C. So to get the handles of those buttons you can use this code:
IntPtr button = GetDlgItem(parent, 1);//OK button
button = GetDlgItem(parent, 0x62C);//LAN settings, remember that the dialog containing LAN settings button is Connections not the Internet Properties.
Here is another solution using Process.Kill(), I'm not sure if killing the RunDll32.exe will be OK, but if it's OK this will be another solution which is even cleaner:
//You have to add these using first:
//using System.Runtime.InteropServices;
//using System.Diagnostics;
//using System.Threading;
//This is used to find a window
[DllImport("user32", CharSet=CharSet.Auto)]
private static extern IntPtr FindWindow(string className, string windowName);
[DllImport("user32")]
private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr childAfter, string className, string windowName);
//This is to help you Click on a Button
[DllImport("user32")]
private static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam);
//This is used to get a Button (as an item) on a dialog
[DllImport("user32")]
private static extern IntPtr GetDlgItem(IntPtr dlgHandle, int itemID);
//---------------------------
//Here are our methods
//---------------------------
//This is used to Click on a Window (usually a Button) with its Handle passed in
private void ClickWindow(IntPtr hwnd)
{
SendMessage(hwnd, 0x201, IntPtr.Zero, IntPtr.Zero);
SendMessage(hwnd, 0x202, IntPtr.Zero, IntPtr.Zero);
}
//This is used to find the Local Area Network (LAN) Settings window
//This is called in a separate thread, because somehow the LAN settings window
//showing causes the main Form not-responding (we can't call anything in our main thread).
private void SearchForLanSettingsWindow()
{
int i = 0;
while (i < 20)
{
Thread.Sleep(100);
IntPtr windowHandle = FindWindow(null,"Local Area Network (LAN) Settings");
if (windowHandle != IntPtr.Zero)
{
if(runDll32 != null) runDll32.Kill();
break;
}
i++;
}
}
//the Process RunDll32
Process runDll32;
//And here is your code with my code appended
ProcessStartInfo proc = new ProcessStartInfo();
proc.FileName = #"C:\\Windows\\System32\\RunDll32.exe";
proc.Arguments = "shell32.dll,Control_RunDLL inetcpl.cpl,Internet,4";//open Internet Properties window
proc.UseShellExecute = false;
runDll32 = Process.Start(proc);
Thread.Sleep(100);//Sleep to be sure the Window is really created
//Get the handle to the window "Internet Properties"
IntPtr mainHandle = FindWindow(null, "Internet Properties");
//Find the tab "Connections", this tab has class "#32770" and is a child window of the window "Internet Properties"
IntPtr child = FindWindowEx(mainHandle, IntPtr.Zero, "#32770", "Connections");
//Get the button "LAN settings"
IntPtr button = GetDlgItem(child, 0x62C);
//Create new thread and start it to find the Lan settings window
//we have to do this now because for some reason, after the LAN settings window shows
//we can't call any code in our class.
new Thread(SearchForLanSettingsWindow).Start();
//Click on the LAN settings button to Show the LAN settings window
ClickWindow(button);
Again, I think, you should find another solution to do what you want originally. This kind of solution is just a trick. You may want to use MoveWindow win32 function to move all the dialogs out of the screen.
PS: này, em làm (hay vẫn đang học?) trong ngành tin thật à? Ở đâu vậy? How old? nice to meet you on stack over flow :)

C# Windows Mobile 6.5.3 extend Winform MainMenu

what I want to do is to extend the MainMenu of the Windows Mobile Winforms to have a second menu level. If you tip short on the menu button it will do the event action but if you press it longer a second menu level should pop up. The MainMenu is very deficient in its managed functions so I had to find another way. I archived this by deriving MainMenu and add some SubClassing.
public delegate IntPtr Win32WndProc(IntPtr hWnd, int msg, int wParam, int lParam);
[DllImport("coredll.dll", EntryPoint = "FindWindow", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, IntPtr lpWindowName);
[DllImport("coredll.dll")]
public static extern bool EnableWindow(IntPtr hWnd, bool bEnable);
[DllImport("coredll")]
public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, Win32WndProc newProc);
int GWL_WNDPROC = (-4);
int GW_CHILD = 5;
IntPtr _oldToolbarProc;
IntPtr _oldMenuWorkerProc
void Hookup()
{
//find the window to hook
var hWndHooked = FindWindow("HHTaskbar", IntPtr.Zero);
if (hWndHooked == IntPtr.Zero)
return;
//enable the taskbar, not realy necessary
EnableWindow(hWndHooked, true);
//find the menu_worker window
var menuWorkerWnd = FindWindow("menu_worker", IntPtr.Zero);
if (menuWorkerWnd == IntPtr.Zero)
return;
var toolbarWnd = GetWindow(menuWorkerWnd, GW_CHILD);
if (toolbarWnd == IntPtr.Zero)
return;
Win32WndProc newMenuWorker = MenuWorkerProc;
_oldMenuWorkerProc = SetWindowLong(menuWorkerWnd, GWL_WNDPROC, newMenuWorker);
Win32WndProc newToolbar = ToolbarProc;
_oldToolbarProc = SetWindowLong(newToolbarWnd, GWL_WNDPROC, newToolbar);
}
The toolbar subclassing measures the time between WM_LBUTTONDOWN and WM_LBUTTONUP
and depending on the time leaped between these events a context Menu is invoked.
If the context menu is invoked the menu_worker subclassing must suppress the WM_COMMAND of the pressed button.
This works fine for a single window. But if I use it on a second form they will recongize both the same toolbar and menuworker and application crashes.
What I tried is to hook and unhook in the onFocus /onLostFocus events of the parent form.
But sadly onFocus is called before the right window is visible and it also gets the wrong window handle :(
What I did now (I know a very bad hack) is to start a time in the onFocus event and wait for 1000ms and hook it up then. This results in a 50:50 change to hook the right window..
Isn't there a better solution for subclassing the right window?

.Net Console Application in System tray

Is there a way I can put a console application in the system tray when minimizing ?
Yes, you can do this.
Create a Windows Forms application and add a NotifyIcon component.
Then use the following methods (found on MSDN) to allocate and display a Console
[DllImport("kernel32.dll")]
public static extern Boolean AllocConsole();
[DllImport("kernel32.dll")]
public static extern Boolean FreeConsole();
[DllImport("kernel32.dll")]
public static extern Boolean AttachConsole(Int32 ProcessId);
When your console is onscreen, capture the minimize button click and use it to hide the console window and update the Notify icon. You can find your window using the following methods (found on MSDN):
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
// Also consider whether you're being lazy or not.
[DllImport("user32.dll", EntryPoint="FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
Be sure to call FreeConsole whenever you're ready to close the app.
using System.Windows.Forms;
using System.Drawing;
static NotifyIcon notifyIcon = new NotifyIcon();
static bool Visible = true;
static void Main(string[] args)
{
notifyIcon.DoubleClick += (s, e) =>
{
Visible = !Visible;
SetConsoleWindowVisibility(Visible);
};
notifyIcon.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
notifyIcon.Visible = true;
notifyIcon.Text = Application.ProductName;
var contextMenu = new ContextMenuStrip();
contextMenu.Items.Add("Exit", null, (s, e) => { Application.Exit(); });
notifyIcon.ContextMenuStrip = contextMenu;
Console.WriteLine("Running!");
// Standard message loop to catch click-events on notify icon
// Code after this method will be running only after Application.Exit()
Application.Run();
notifyIcon.Visible = false;
}
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public static void SetConsoleWindowVisibility(bool visible)
{
IntPtr hWnd = FindWindow(null, Console.Title);
if (hWnd != IntPtr.Zero)
{
if (visible) ShowWindow(hWnd, 1); //1 = SW_SHOWNORMAL
else ShowWindow(hWnd, 0); //0 = SW_HIDE
}
}
A console has no window to minimize by itself. It runs in a command prompt window. You might hook the window messages and hide the window on minimize. In your application it's possible to add a tray icon just the same as you would do it in a windows application. Well, somehow this smells...
But: I'm not sure why you want to do this. A console application is by design different to a windows application. Hence, maybe it's an option to change the app to be a windows form application?
[DllImport("user32.dll")]
internal static extern bool SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);
static Int32 WM_SYSCOMMAND = 0x0112;
static Int32 SC_MINIMIZE = 0x0F020;
static void Main(string[] args)
{
SendMessage(Process.GetCurrentProcess().MainWindowHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
}
I use TrayRunner for exactly this purpose. Essentially, it wraps a console application capturing all output. But when minimized, it minimizes to the system tray instead of the task bar. You can even customize what icon to show when minimized. I use it for things like Tomcat or Apache to free up space on my taskbar without running them as Windows Services.
You can't hide a console application because it does not actually have a window to hide, seeing as how it is running in the console (the console itself is just a window of the console, not the app running in it)

Categories

Resources