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 :)
Related
I am trying to intercept the "Save Print Output As" dialog in Revit so I can programmatically insert a path and file name. This dialog pops up every time for every view or sheet that is printed when printed separately or only once if a combined document is printed once.
So far, I have assembled the viewset however when it comes time to print, I do not have access to the window because it opens and closes too soon. I am hoping there is a more "hacky" way to intercept it.
In Revit, the event handler:
a.ControlledApplication.DocumentPrinting += new EventHandler<Autodesk.Revit.DB.Events.DocumentPrintingEventArgs>(AppDocumentPrinting);
only activates after the name/destination has been selected but before the document has printed so it doesnt quite help.
I have found this post which explains how to cycle through the windows which I am able to do but I don't know how to listen for the window to be opened and I can't find anything on it. The SO post also mentions something about p/invoke but I haven't found much documentation on it.
I have looked at subscribing to events but I haven't found anything on being able to subscribe to listen to a window opening.
My printer setup is currently Microsoft print to pdf which doesn't seem to allow the PrintToFile option. Regardless though, I would still like to be able to handle the dialog if it pops up if it is possible.
Any and all help/direction is appreciated.
My code:
EnumWindows(new EnumWindowsProc(EnumTheWindows), IntPtr.Zero); // cant find window because it doesnt exist
printManager.SubmitPrint(); // Window opens for user input here and then closes
// doc.Print(pdfviewSet); // option B: Window opens for user input here and then closes
EnumWindows(new EnumWindowsProc(EnumTheWindows), IntPtr.Zero); // cant find window because it doesnt exist
code adapted from other SO post
// P/Invoke declarations <--- suspect the answer might lie here?
protected delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
protected static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
[DllImport("user32.dll")]
protected static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
// Callback for examining the window
protected static bool EnumTheWindows(IntPtr hWnd, IntPtr lParam)
{
int size = GetWindowTextLength(hWnd);
if (size++ > 0 && IsWindowVisible(hWnd))
{
StringBuilder sb = new StringBuilder(size);
GetWindowText(hWnd, sb, size);
if (sb.ToString().Equals("Save Print Output As", StringComparison.Ordinal))
{
uint procId = 0;
GetWindowThreadProcessId(hWnd, out procId);
Debug.WriteLine($"Found it! ProcID: {procId}");
FlaUI.Core.Application application = FlaUI.Core.Application.Attach(Process.GetCurrentProcess());
string appName = application.Name;
Window mainWindow = application.GetMainWindow(new UIA3Automation());
ConditionFactory cf = new ConditionFactory(new UIA3PropertyLibrary());
mainWindow.FindFirstDescendant(cf.ByProcessId(int.Parse(procId.ToString()))).AsTextBox().Enter("test"); // try to enter info here but returns null because window has closed.
}
}
return true;
}
Dialog I am trying to intercept:
Other info I have looked at:
p/invoke - not too sure how to implement this.
tried this but printed a blank page
How to subscribe to events
Tim Corry's Youtube video on subscribing to events
I am trying to get the HWND of the On-Screen/Touch keyboard - the one that appears on the bottom of the screen. For that I first look for the Process ID according to the name, and then do EnumWindows to look through all windows and find one whose Process ID corresponds to the one I know:
doTheThing(int pid)
{
EnumWindows(new EnumWindowsProc(Report), (IntPtr)pid);
}
protected delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
protected static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
protected static bool Report(IntPtr hwnd, IntPtr lParam)
{
IntPtr lpdwProcessId;
GetWindowThreadProcessId(hwnd, out lpdwProcessId);
if (lpdwProcessId == lParam)
{
MessageBox.Show("True: " + hwnd.ToString());
return false;
}
return true;
}
Now Report is called several times, but the thing never actually matches. If I take another process, it works, but it seems like the OnScreenkeyboard "System.Diagnostics.Process (WindowsInternal.ComposableShell.Experiences.TextInput.InputApp)" does not show up with EnumWindows.
Using FindWindow does not work as the window does not have a Title and its class is the generic ApplicationFrameHost.
In AHK I was able to get the HWND by hovering over the keyboard with MouseGetPos,,, WinUMID, so a HWND definitely exists.
Is there a possibility that some windows are ignored by the EnumWindows? If so, how can I prevent that? What other solutions are there?
As a sidenote, it also does not show up in the UI Automation verify tool.
Note: Windows now has two keyboards apparently. I mean the one you can open via the taskbar.
To clarify why I need this:
I have the following AHK-Script that can make the window I point my mouse over window semi-transparent:
MouseGetPos,,, WinUMID
WinSet, Transparent, 100, ahk_id %WinUMID%
I noticed that when I point it over the Touch-Keyboard, it will make it transparent and that effect stays even if the keyboard disappears, until I restart my system. If I save the WinUMID variable I can even change the transparency while the keyboard is not used.
Now I want to make the Keyboard semi-transparent in C#, and that works as long as I provide the HWND. But I can't figure out a way to get the HWND inside of C#, without the help of AHK.
process.MainWindowHandle returns 0.
I have also tried
ProcessThreadCollection threads = process.Threads;
foreach (ProcessThread thread in threads)
{
EnumThreadWindows((uint)thread.Id, new EnumThreadDelegate(EnumThreadCallback), IntPtr.Zero);
}
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(uint dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
protected delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam);
protected static bool EnumThreadCallback(IntPtr hwnd, IntPtr lParam)
{
MessageBox.Show("Try");
IntPtr lpdwProcessId;
GetWindowThreadProcessId(hwnd, out lpdwProcessId);
if (lpdwProcessId == lParam)
{
MessageBox.Show("True: " + hwnd.ToString());
makeTransparent(lParam);
return false;
} else
MessageBox.Show("False: " + hwnd.ToString());
return true;
}
EnumThreadCallback was never called though, even though it works for any other process.
Edit:
I found out that the Keyboard process was not actually creating the Keyboard. Instead, it seems like the keyboard-UI is created by explorer.
The following code does work:
Process[] processes = Process.GetProcesses();
IntPtr hWnd = IntPtr.Zero;
while ((hWnd = FindWindowEx(IntPtr.Zero, hWnd, "ApplicationFrameWindow", null)) != IntPtr.Zero)
{
IntPtr lpdwProcessId;
GetWindowThreadProcessId(hWnd, out lpdwProcessId);
foreach (Process process in processes)
{
if (process.ToString() == "System.Diagnostics.Process (explorer)")
{
if (process.Id == (int)lpdwProcessId)
{
doThing(hWnd);
}
}
}
}
However, multiple hWnds are used, and only one of them belongs to the keyboard. Now I need to find out how to filter out this specific one. There do not seem to be downsides to this method right now, but I don't feel comfortable releasing this without knowing if it can have any adverse effects because of this. Victim 1: The new Paste window.
Try this alternate solution:
var process = Process.GetProcessById(pid);
IntPtr hwnd = process.MainWindowHandle;
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.
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?
I am developing an automation tool which is reading the file path from an Excel workbook and after launching the application I am firing print job using SendKeys.SendWait() for Ctrl+P and Enter key. Now the Problem is, I am facing synchronization issue for launching the application and handling the print procedure keys. Sometimes Applications are launching little late(like Excel and MsWord files), so at that time I am not able to find till how long I have to wait for a successful launch of the Application. Anybody have any Idea how to check this waiting time till how long I should wait to fire CTRL+P and then after getting PrintDialog ENTER button ?
Any help will be appreciate. Thanks in advance.
I initially read the question as only printing MS type files. If you want to print all kinds of files then I would first leverage Windows 'PrintTo' function.
You can call the commands directly by searching the Registry for PrintTo and you should see commands for PrintTo and also Print. Hit the web for specifics for each application.
The other option that is probably the simplest is to use the PrintTo verb with ShellExecute and let Windows handle the behind the scenes.
System.Diagnostics.Process print = new System.Diagnostics.Process();
print.StartInfo.FileName = #"c:\test\test.pdf";
print.StartInfo.Verb = "PrintTo";
print.StartInfo.CreateNoWindow = True;
print.StartInfo.Arguments = printerName;
print.StartInfo.UseShellExecute = True;
print.Start();
print.WaitForExit();
PrintTo should allow you to specify the printer while the verb "Print" should just send to the default device.
Keep in mind that not all filetypes support these verbs.
In order to determine whether or not the application to automate is ready to accept user input (key strokes) you have to search for the window of the application processing the key strokes you will send. There is quite a bit interop necessary to accomplish the task. Below you will find a small example automating the task of printing an excel document (all error handling details omitted).
I've copied the interop signatures from pinvoke.net.
First, let me describe the necessary steps:
Search for the excel main window using the class name of the excel main window. Use a tool like spy++ to determine the class name.
Bring the excel main window to the foreground.
Send CTRL+C to the main window to open the print dialog.
Wait for the print dialog to appear.
Send ENTER to the print dialog.
Second, let me show you a small code example:
private enum WindowShowStyle : uint
{
Hide = 0,
ShowNormal = 1,
ShowMinimized = 2,
ShowMaximized = 3,
Maximize = 3,
ShowNormalNoActivate = 4,
Show = 5,
Minimize = 6,
ShowMinNoActivate = 7,
ShowNoActivate = 8,
Restore = 9,
ShowDefault = 10,
ForceMinimized = 11
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);
[DllImport("user32.dll")]
private static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("kernel32.dll")]
private static extern uint GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
private static void BringWindowToForeground(IntPtr hWnd)
{
uint foregroundThread, currentThread;
uint pid;
foregroundThread = GetWindowThreadProcessId(GetForegroundWindow(), out pid);
currentThread = GetCurrentThreadId();
if (foregroundThread != currentThread)
{
AttachThreadInput(foregroundThread, currentThread, true);
BringWindowToTop(hWnd);
ShowWindow(hWnd, WindowShowStyle.ShowMaximized);
AttachThreadInput(foregroundThread, currentThread, false);
}
else
{
BringWindowToTop(hWnd);
ShowWindow(hWnd, WindowShowStyle.ShowMaximized);
}
}
private void button1_Click(object sender, EventArgs e)
{
// Find excel window.
IntPtr hWnd;
while (true)
{
hWnd = FindWindow("XLMAIN", null); // XLMAIN is the class name
// of the main excel window.
if (hWnd != IntPtr.Zero)
break;
}
BringWindowToForeground(hWnd);
SendKeys.SendWait("^p"); // Send CTRL+P to main excel window
// Find print dialog.
while (true)
{
hWnd = FindWindow("bosa_sdm_XL9", null); // bosa_sdm_XL9 is the class name
// of the print dialog.
if (hWnd != IntPtr.Zero)
break;
}
BringWindowToForeground(hWnd);
SendKeys.SendWait("~"); // Send ENTER to print dialog.
}
The button_click methods includes the steps to wait for the Excel windows to appear. If the specified window is found the keys are sent.
Hope, this helps.