I am trying to get the window handler and press the Save button. I found couple of examples on IE8 & 9. But that code doesn't works on IE 11.
const int BM_CLICK = 0x00F5;
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindowEx(IntPtr parent, IntPtr next, string sClassName, IntPtr sWindowTitle);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern uint GetDlgCtrlID(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);
//hDialog - handle of dialog window. idBtn - Id of button
public static bool ClickButtonOnDialog(IntPtr hDialog, UInt32 idBtn)
{
IntPtr res = IntPtr.Zero;
uint id;
IntPtr hOkBtn = IntPtr.Zero;
int attempt = 0;
do
{
Thread.Sleep(300);
//searching for button
hOkBtn = FindWindowEx(hDialog, hOkBtn, "Button", IntPtr.Zero);
id = GetDlgCtrlID(hOkBtn);
attempt++;
} while (id != idBtn && attempt < 20);
if (!hOkBtn.Equals(IntPtr.Zero))
{
//click the button
res = SendMessage(hOkBtn, (int)BM_CLICK, 1, IntPtr.Zero);
}
if (res.ToInt32() == 1)
return true;
return false;
}
public static void FindAndSave()
{
IntPtr hOkBtn = IntPtr.Zero;
uint message = 0xf5;
IntPtr hwnd = FindWindow(null, "Internet Explorer");
hOkBtn = FindWindowEx(hwnd, hOkBtn, "Button", "Cancel");
SendMessage(hOkBtn, (int)message, 1, IntPtr.Zero);
I was able to download and close the file download dialog box using the below code
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
static void DownLoadFile(IE browser)
{
browser.Link(Find.ByText("download")).ClickNoWait();
Thread.Sleep(1000);
AutomationElementCollection dialogElements = AutomationElement.FromHandle(FindWindow(null, "Internet Explorer")).FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement element in dialogElements)
{
if (element.Current.Name.Equals("Save"))
{
var invokePattern = element.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
invokePattern.Invoke();
}
}
}
My approach:
Identify the window by the window title using a windows API call.
[DllImport("user32.dll"]
static extern IntPtr FindWindowByCaption
Loop through the IE window until we find the Element with "Frame Notification Bar or "Notification Bar" as Window Class Name
Find the Button named "Open" or "Save" and perform the click.
public void DownLoadFile(string strWindowTitle)
{
IntPtr TargetHandle = FindWindowByCaption(IntPtr.Zero, strWindowTitle);
AutomationElementCollection ParentElements = AutomationElement.FromHandle(TargetHandle).FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement ParentElement in ParentElements)
{
// Identidfy Download Manager Window in Internet Explorer
if (ParentElement.Current.ClassName == "Frame Notification Bar")
{
AutomationElementCollection ChildElements = ParentElement.FindAll(TreeScope.Children, Condition.TrueCondition);
// Idenfify child window with the name Notification Bar or class name as DirectUIHWND
foreach (AutomationElement ChildElement in ChildElements)
{
if (ChildElement.Current.Name == "Notification bar" || ChildElement.Current.ClassName == "DirectUIHWND")
{
AutomationElementCollection DownloadCtrls = ChildElement.FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement ctrlButton in DownloadCtrls)
{
//Now invoke the button click whichever you wish
if (ctrlButton.Current.Name.ToLower() == "save")
{
var invokePattern = ctrlButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
invokePattern.Invoke();
}
}
}
}
}
}
}
Here is the IE 11 code that works. Its a mix of System.Windows.Automation and Win32 API. Could probably get it to work with Win32 unmanaged API's. I used WinID to get the menu's class name and then iterate through its child elements.
IE 11 has this download frame.
We needed to access the Save As from the Save's down arrow.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string className, string windowTitle);
public static void IeDownLoadSaveAs(string windowTitle = null)
{
if (windowTitle == null)
windowTitle = "https://googledownload.com - Internet Explorer";
//get the message handle
//the last param "Untitled...is the title of the window and it must match
IntPtr parentHandle = WindowHandleInfo.FindWindow("IEFrame", windowTitle);
var parentElements = AutomationElement.FromHandle(parentHandle).FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement parentElement in parentElements)
{
// Identidfy Download Manager Window in Internet Explorer
if (parentElement.Current.ClassName == "Frame Notification Bar")
{
var childElements = parentElement.FindAll(TreeScope.Children, Condition.TrueCondition);
// Idenfify child window with the name Notification Bar or class name as DirectUIHWND
foreach (AutomationElement childElement in childElements)
{
if (childElement.Current.Name == "Notification bar" || childElement.Current.ClassName == "DirectUIHWND")
{
var downloadCtrls = childElement.FindAll(TreeScope.Descendants, Condition.TrueCondition);
foreach (AutomationElement ctrlButton in downloadCtrls)
{
//Now invoke the button click whichever you wish
if (ctrlButton.Current.Name.ToLower() == "")
{
var saveSubMenu = ctrlButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
saveSubMenu.Invoke();
var saveMenuHandle = WindowHandleInfo.FindWindow("#32768", "");
var subMenuItems = AutomationElement.FromHandle(saveMenuHandle).FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement item in subMenuItems)
{
if (item.Current.Name.ToLower() == "save as")
{
var saveAsMenuItem = item.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
saveAsMenuItem.Invoke();
}
}
}
}
}
}
}
}
}
I have achieved in very simple way. Just by using key strokes.
SendKeys.SendWait("%");
SendKeys.SendWait("%{s}");
Hope this will save your lot of time.
I had two IE processes running - the above code was always picking up the wrong 32bit IE process. So, I've merged the answers from multiple StackOverflow questions and wrote the below code to get this done (without the dll imports).
Please note that you need to add a reference to UIAutomationClient in the project - in order to use the AutomationElement method.
public static void IESaveFile(string title)
{
Thread.Sleep(1000);
//Get the Internet Explorer window handle using the window title
var ieWindowHandle = Process.GetProcesses().FirstOrDefault(process => process.MainWindowTitle.Contains(title))?.MainWindowHandle;
var dialogElements = AutomationElement.FromHandle(ieWindowHandle??IntPtr.Zero).FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement element in dialogElements)
{
if (element.Current.ClassName != "Frame Notification Bar") continue;
var ChildElements = element.FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement ChildElement in ChildElements)
{
// Identify child window with the name Notification Bar or class name DirectUIHWND
if (ChildElement.Current.Name != "Notification bar" && ChildElement.Current.ClassName != "DirectUIHWND") continue;
var DownloadCtrls = ChildElement.FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement ctrlButton in DownloadCtrls)
//Now invoke the button click on the 'Save' button
if (ctrlButton.Current.Name.ToLower().Equals("save"))
((InvokePattern) ctrlButton.GetCurrentPattern(InvokePattern.Pattern)).Invoke();
}
}
}
Related
I am runnig an external application from my WPF Project and I am putting external app inside my WPF form with using "user32.dll"
External app has an exit button. I would like to remove or hide that button. Can I do that "using user32.dll" or different approach?
Thank you in advance.
The below code finds the button and hides it. It works gracefully on my system. The code searches for the window title and then find the control. You have to provide the window title and button text. You can update the code as per your need.
Note: Below code will hide all the controls with the matching text specified in the constant TEXT_BUTTON.
const string TEXT_TITLE = "My Specific Window";
const string TEXT_BUTTON = "&HideMeButton";
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);
const int SW_HIDE = 0;
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
[DllImport("user32.dll", EntryPoint = "GetWindowText", CharSet = CharSet.Auto)]
static extern IntPtr GetWindowCaption(IntPtr hwnd, StringBuilder lpString, int maxCount);
public void HideSpecificButton()
{
//Contains the handle, can be zero if title not found
var handleWindow = WinGetHandle(TEXT_TITLE);
if (GetWindowCaption(handleWindow).Trim() != TEXT_TITLE)
MessageBox.Show("Window is hidden or not running.");
else
GetChildWindows(handleWindow);
}
public IntPtr WinGetHandle(string title)
{
IntPtr hWnd = IntPtr.Zero;
foreach (Process pList in Process.GetProcesses())
{
if (pList.MainWindowTitle.Contains(title))
{
hWnd = pList.MainWindowHandle;
}
}
return hWnd;
}
private string GetWindowCaption(IntPtr hwnd)
{
StringBuilder sb = new StringBuilder(256);
GetWindowCaption(hwnd, sb, 256);
return sb.ToString();
}
public void GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumControls);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
}
private bool EnumControls(IntPtr handle, IntPtr pointer)
{
var controlTitle = GetWindowCaption(handle).Trim();
if (string.Equals(controlTitle, TEXT_BUTTON, StringComparison.CurrentCultureIgnoreCase))
{
//hide the control
ShowWindow(handle, SW_HIDE);
}
return true;
}
"using user32.dll"
No you can't use user32.dll for each app is in their own sandbox so to speak and should be impervious to outside unwanted actions.
(Q: Do you have access to build this external app? A: Y ) ...or different approach?
Since you have access to the code of both apps, have them implement an interprocess named pipe. In the receiving app have it monitor the pipe for a message to turn off the button(s) or change its windows frame style.
See
How to: Use Named Pipes for Network Interprocess Communication
I generally do run the method listed below to maximize iconized windows;
however when it comes to outlook, there are times where it will maximize a Mail (message) that i have open instead of the parent application (outlook) ; its just pulling up anything outlook that it finds and i need the parent, how can i achieve this?
I have tried using WINAPI GetAncestor, I have also tried GetParent .
public static bool EventChecking(string progr)
{
int bb = 0;
if (Process.GetProcessesByName(progr).Length > 0)
{
bb++;
}
if (bb == 0)
{
return false;
}
foreach (Process ddcd in Process.GetProcesses())
{
if (ddcd.ProcessName.Contains(progr))
{
if (ddcd.MainWindowHandle != IntPtr.Zero)
{
pointer = ddcd.MainWindowHandle;
if (IsIconic(pointer))
{
SendMessage(pointer, 0x112, 0xF120, 0);
}
SetForegroundWindow(pointer);
}
};
}
return true;
}
EDIT:
I also recently tried:
if (ddcd.MainWindowTitle.EndsWith("- Outlook"))
and it still pulls up the single email
I have also had trouble working with Outlook via C# but I have had some success with Win32 calls. Below is one way to locate the Outlook Main Window by checking the Caption.
You could also try EnumWindows but it takes more effort to implement due to Callbacks.
using System.Text;
using System.Runtime.InteropServices;
public IntPtr GetOutlookHandle()
{
string windowClass = "rctrl_renwnd32";
uint GW_HWNDNEXT = 2;
IntPtr firstHandle = new IntPtr(0);
IntPtr handle = new IntPtr(0);
// Look for a Window with the right Class
firstHandle = FindWindow(windowClass, null);
// Nothing Found
if (firstHandle.Equals(IntPtr.Zero)) return IntPtr.Zero;
// Remember where we started to avoid an infinite loop
handle = firstHandle;
do
{
// Check the Caption to find the Main Window
if (GetWindowCaption(handle).EndsWith(" - Microsoft Outlook"))
{
return handle;
}
// Get the next Window with the same Class
handle = GetWindow(handle, GW_HWNDNEXT);
} while (handle != firstHandle);
// Didn't find any matches
return IntPtr.Zero;
}
private static string GetWindowCaption(IntPtr windowHandle)
{
// Determine Length of Caption
int length = GetWindowTextLength(windowHandle);
StringBuilder sb = new StringBuilder(length + 1);
// Get Window Caption
GetWindowText(windowHandle, sb, sb.Capacity);
return sb.ToString();
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetWindowTextLength(IntPtr hWnd);
As the title suggests, I'm trying to simulate a button-click in a MessageBox programmatically. I earlier tried to close the MessageBox by finding its handle via its caption, and applying WM_CLOSE or SC_CLOSE in SendMessage(). However, due to the presence of Yes/No buttons, that did not work (the X button is grayed out).
Now I'm trying to click the No button as follows -:
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
IntPtr Window_hWnd = CloseMessageBox.FindWindowByCaption("#32770", "LastQuestion"); //Could use null as the first argument too. "#32770" represents classname Dialog.
CloseMessageBox.EnumChildWindows(Window_hWnd, (hWnd, lParam) =>
{
StringBuilder sb = new StringBuilder();
foreach (var control in GCHandle.FromIntPtr(lParam).Target as List<IntPtr>)
{
CloseMessageBox.GetWindowText(control, sb, 250);
if (sb.Equals("&No"))
{
CloseMessageBox.PostMessage(hWnd, CloseMessageBox.MouseDown, 0, 0);
CloseMessageBox.PostMessage(hWnd, CloseMessageBox.MouseUp, 0, 0);
}
}
return false;
}, GCHandle.ToIntPtr(listHandle));
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
Having come this far on the advice of someone from IRC, I find that a few edits earlier, I was getting the button handle (only the "&Yes" button) but not all of them. He then suggested this approach, but the control List is not populated and hence it never goes inside the foreach. What do I do to remedy this?
Here you go.
// A delegate which is used by EnumChildWindows to execute a callback method.
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
// This method accepts a string which represents the title name of the window you're looking for the controls on.
public static void ClickButtonLabeledNo(string windowTitle)
{
try
{
// Find the main window's handle by the title.
var windowHWnd = FindWindowByCaption(IntPtr.Zero, windowTitle);
// Loop though the child windows, and execute the EnumChildWindowsCallback method
EnumChildWindows(windowHWnd, EnumChildWindowsCallback, IntPtr.Zero);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
private static bool EnumChildWindowsCallback(IntPtr handle, IntPtr pointer)
{
const uint WM_LBUTTONDOWN = 0x0201;
const uint WM_LBUTTONUP = 0x0202;
var sb = new StringBuilder(256);
// Get the control's text.
GetWindowCaption(handle, sb, 256);
var text = sb.ToString();
// If the text on the control == &No send a left mouse click to the handle.
if (text == #"&No")
{
PostMessage(handle, WM_LBUTTONDOWN, IntPtr.Zero, IntPtr.Zero);
PostMessage(handle, WM_LBUTTONUP, IntPtr.Zero, IntPtr.Zero);
}
return true;
}
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "GetWindowText", CharSet = CharSet.Auto)]
private static extern IntPtr GetWindowCaption(IntPtr hwnd, StringBuilder lpString, int maxCount);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
I'm using this webbrowswer feature of C#. Trying to log in a website through my application. Everything goes fine, except when a wrong ID or password is entered there's little message box (that is set on the webpage itself) which pops up and blocks everything until "Ok" is clicked.
So the question is: Is there any possible way to manage this little window (like reading the text inside of it)? If it is then great!
But if there's no way to do that then is there anyway to simply make this message box go away programatically?
You can "manage" the message box dialog by importing some window functions from user32.dll and getting the messagebox dialog's handle by it's class name and window name. For example to click its OK button:
public class Foo
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private void ClickOKButton()
{
IntPtr hwnd = FindWindow("#32770", "Message from webpage");
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Button", "OK");
uint message = 0xf5;
SendMessage(hwnd, message, IntPtr.Zero, IntPtr.Zero);
}
}
Some reading material from MSDN.
Copy paste from Sires anwser: https://stackoverflow.com/a/251524/954225
private void InjectAlertBlocker() {
HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = webBrowser1.Document.CreateElement("script");
IHTMLScriptElement element = (IHTMLScriptElement)scriptEl.DomElement;
string alertBlocker = "window.alert = function () { }";
element.text = alertBlocker;
head.AppendChild(scriptEl);
}
here is refined version of Saeb's answer. Saeb's code was not working for me, I added one more step to activate the button and then clicking on it.
using System;
using System.Runtime.InteropServices;
namespace IE_Automation
{
public class IEPoppupWindowClicker
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
private const int BM_CLICK = 0xF5;
private const uint WM_ACTIVATE = 0x6;
private const int WA_ACTIVE = 1;
public void ActivateAndClickOkButton()
{
// find dialog window with titlebar text of "Message from webpage"
var hwnd = FindWindow("#32770", "Message from webpage");
if (hwnd != IntPtr.Zero)
{
// find button on dialog window: classname = "Button", text = "OK"
var btn = FindWindowEx(hwnd, IntPtr.Zero, "Button", "OK");
if (btn != IntPtr.Zero)
{
// activate the button on dialog first or it may not acknowledge a click msg on first try
SendMessage(btn, WM_ACTIVATE, WA_ACTIVE, 0);
// send button a click message
SendMessage(btn, BM_CLICK, 0, 0);
}
else
{
//Interaction.MsgBox("button not found!");
}
}
else
{
//Interaction.MsgBox("window not found!");
}
}
}
}
I am working on a project with some specific requirement: I need to create a program which can monitor and runs exe inside of it using c#. However, an approach of using Console program to actually host another exe seems to be no end. So i used WinForm.
I have been looking and found some quite good solution, which host an UI Application inside the WinForm. But in my case, the exe has no UI, but it is capable of create an UI (OpenGL), so it is not applicable for those solutions. Is there any way to host this kind of exe inside of the WinForm? Which i can run many of it simultaneously?
Thanks
Hosting a process within another one makes no sens. If you want to launch an exe from another one, you can use System.thread.Process and if those process need to interact with each other, well, WCF is made just for that.
Thank you for all your suggestion. However, i did find out another way around to grab the correct Window which created by my console exe, and put it into the correct winform. This is quite a cheat though.
This idea is originally comes from the Window Tabifier from codeproject: http://www.codeproject.com/KB/cs/WindowTabifier.aspx . As my exe does not have the corresponding UI for the process.WaitForInputIdle, i do a cheat on Thread.Sleep(2000) to skip the starting time, and grab the created Window of the console based on the name of it.
Library import:
public delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
[DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true,
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
private static extern long GetWindowThreadProcessId(long hWnd, long lpdwProcessId);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private extern static bool EnumThreadWindows(int threadId, EnumWindowsProc callback, IntPtr lParam);
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)]
private extern static int GetWindowText(IntPtr hWnd, StringBuilder text, int maxCount);
Some methods to find the correct opened window
public IntPtr FindWindowInProcess(Process process, Func<string, bool> compareTitle)
{
IntPtr windowHandle = IntPtr.Zero;
foreach (ProcessThread t in process.Threads)
{
windowHandle = FindWindowInThread(t.Id, compareTitle);
if (windowHandle != IntPtr.Zero)
{
break;
}
}
return windowHandle;
}
private IntPtr FindWindowInThread(int threadId, Func<string, bool> compareTitle)
{
IntPtr windowHandle = IntPtr.Zero;
EnumThreadWindows(threadId, (hWnd, lParam) =>
{
StringBuilder text = new StringBuilder(200);
GetWindowText(hWnd, text, 200);
if (compareTitle(text.ToString()))
{
windowHandle = hWnd;
return false;
}
else
{
windowHandle = FindChildWindow(hWnd, compareTitle);
if (windowHandle != IntPtr.Zero)
{
return false;
}
}
return true;
}, IntPtr.Zero);
return windowHandle;
}
private IntPtr FindChildWindow(IntPtr hWnd, Func<string, bool> compareTitle)
{
IntPtr windowHandle = IntPtr.Zero;
EnumChildWindows(hWnd, (hChildWnd, lParam) =>
{
StringBuilder text = new StringBuilder(200);
GetWindowText(hChildWnd, text, 200);
if (compareTitle(text.ToString()))
{
windowHandle = hWnd;
return false;
}
return true;
}, IntPtr.Zero);
return windowHandle;
}
Last, start the processes:
String fileName = "myexe.exe";
String dir = Path.GetDirectoryName(fileName);
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = fileName;
process.StartInfo.WorkingDirectory = dir;
process.Start();
// Wait for process to be created and enter idle condition
Thread.Sleep(5000);
IntPtr appWin = FindWindowInProcess(process, s => s.StartsWith("Built on"));
// Put it into this form
SetParent(appWin, this.Handle);
// Remove border and whatnot
SetWindowLong(appWin, GWL_STYLE, WS_VISIBLE);
// Move the window to overlay it on this window
MoveWindow(appWin, 0, 0, this.Width, this.Height, true);
See Is it possible to log message to cmd.exe in C#/.Net? for information how to create/attach to a console.
Also, look at e.g. Poderosa for an open source terminal emulator that you might be able to embed.