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.
Related
How can I embed a non-native application (application installed in windows 7) in a windows form using c#.
What I have tried so far:
public partial class Form1 : Form
{
Process process = new Process();
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
public Form1()
{
InitializeComponent();
// panel1.Container.Add(process);
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = false;
string path = Path.Combine(Path.GetFullPath(#"C:\Program Files (x86)\Plan-G v3.2.0"), "Plan-G3.exe");
process = Process.Start(path);
Debug.WriteLine(process.MainWindowHandle);
while (process.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(1000); // Don't hog the CPU
process.Refresh(); // You need this since `MainWindowHandle` is cached
}
Form1.SetForegroundWindow(process.MainWindowHandle);
SetForegroundWindow(process.MainWindowHandle);
SetParent(process.MainWindowHandle, panel1.Handle);
MoveWindow(process.MainWindowHandle, 0, 0, panel1.Width - 90, panel1.Height, true);
}
}
I am able to get this to work sometimes when I start with debugging.
But when i start without debugging, the application always opens outside of the form. I don't understand why this happens.
I have also tried the [ForceForegroundWindow][1]() workaround.
Is what I am trying to accomplish possible?
Based on other forum answers, it seems it might not be...
So I'm stuck with a problem, I'm trying to send keys to a game and I have the game in the foreground with help of SetForegroundWindow and I'm using SendInputs API to send the keys to the game.
If I focus on another application the keys are sent to that application but as soon as I focus on the application I want the keys to be sent to, they don't appear there.
I'm trying to save me some time to recruit guild members for my guild and with that I'm trying to send keys to the game.
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr GetMessageExtraInfo();
[DllImport("user32.dll", SetLastError = true)]
static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
Process[] procs = Process.GetProcessesByName("BlackDesert64");
if (procs.Length > 0)
{
if (procs[0].MainWindowHandle != IntPtr.Zero)
{
SetForegroundWindow(procs[0].MainWindowHandle);
Thread.Sleep(1000);
}
}
INPUT[] inputs = new INPUT[]
{
new INPUT
{
type = INPUT_KEYBOARD,
u = new InputUnion
{
ki = new KEYBDINPUT
{
wVk = 0x49,
wScan = 0049,
dwFlags = KEYEVENTF_UNICODE,
dwExtraInfo = GetMessageExtraInfo(),
}
}
},
new INPUT
{
type = INPUT_KEYBOARD,
u = new InputUnion
{
ki = new KEYBDINPUT
{
wVk = 0x49,
wScan = 0049,
dwFlags = KEYEVENTF_KEYUP,
dwExtraInfo = GetMessageExtraInfo(),
}
}
}
};
SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT)));
Rest of the code:
https://pastebin.com/RUm7A311
UPDATE
So I've found the API Interceptor that allows to send keys to a game that uses DirectX and I've set it up but still no outcome.. anyone who can point me in the right direction?
What does value SendInput return?
If it returns 0, then its an indication that some error has happened. You can try to invoke GetLastError, to see if the input was blocked by the UIPI, alternatively try to run your code with local administrator privileges.
Are you sure that procs[0].MainWindowHandle is the correct window handle?
Lastly try to send the message directly to the handle using SendMessage.
Implementation using SendMessage (no need to focus on the window).
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll")]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindows);
[DllImport("User32.dll")]
private static extern Int32 SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, StringBuilder lParam);
void SendKeys()
{
IntPtr hWnd = FindWindow("Notepad", "Untitled - Notepad");
if (!hWnd.Equals(IntPtr.Zero))
{
IntPtr edithWnd = FindWindowEx(hWnd, IntPtr.Zero, "Edit", null);
if (!edithWnd.Equals(IntPtr.Zero))
{
SendMessage(edithWnd, WM_SETTEXT, IntPtr.Zero, new StringBuilder("Test"));
}
}
}
Reference: how-do-i-input-to-another-application
For your problem here is trick,
Use
String Keys = "Test";
SendKeys.Send(Keys);
this code to send keys to any application.
Just put this code in timer_start()
add some delay before starting of timer and stop timer after execution.
Now run your project which will initiate timer, before timeout open your game and wait for keys to press!!
Check this link which contains all Keys and their code to send
https://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.send(v=vs.110).aspx
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 trying to find if a window with specific has been open by a Process. That process spawns multiple windows, and I need to check them all.
I have no trouble finding the process, with
foreach (Process p in Process.GetProcesses())
{
if (p.MainModule.FileName.ToLower().EndsWith("foo.exe"))
FindChildWindowWithText(p); //do work
the problem is what to do next. I cannot use Process' MainWindowText, because it changes with whichever window is activated.
Then I've tried to use Windows function EnumChildWindows and GetWindowText, but I am not sure if I'm passing a correct handle to EnumChildWindows. The EnumChildWindows works as expected when passed MainWindowHandle, but of course the MainWindowHandle changes with active window. So I passed Process.Handle, but I get different handles and different results when switching the app's windows. (I understand that EnumChildWindows returns handles to not only windows, but controls in .net speak, that's no problem if I could get the caption of the window too)
Maybe I am doing this the wrong way and I need a different approach - again, my problem is as simple as finding a window with text that matches specific regular expression. So I would probably need a function that enumerates all windows, that are visible in the taskbar or so.
Thanks
Once you have the Process, you can enumerate all the Windows in the process and test if any of them match the window you are looking for.
You will need the following P/Invoke declarations
[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);
The followng is an example of a pair of functions that can be used to find the windows in a specific process, I understood from your question that you have the Process, the problem is enumerating the windows.
public static 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 static 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;
}
return true;
}, IntPtr.Zero);
return windowHandle;
}
Then you can call the FindWindowInProcess function to find a window that's title ends with "ABC" as an example.
IntPtr hWnd = FindWindowInProcess(p, s => s.EndsWith("ABC"));
if (hWnd != IntPtr.Zero)
{
// The window was found....
}
Of course you can replace s => s.EndsWith("ABC") with any expression that will satisfy your search criteria for the window, it could be a regex etc.
Here is also a version of FindThreadWindow that will also check the first level of child windows. You could take this further and make it a recursive function if your windows is deeper down in the hierarchy.
private static 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 static 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 = hChildWnd;
return false;
}
return true;
}, IntPtr.Zero);
return windowHandle;
}
Rather than enumerating processes and finding the window, I'd enumerate the windows (using EnumWindows) and find the process (using GetGuiThreadInfo).