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...
Related
I want to get all text of a window.
I prepared below code.But i can only get window captions/titles.
How can i get all text written inside a window ?
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
Process[] processlist = Process.GetProcesses();
foreach (Process process in processlist)
{
if (!String.IsNullOrEmpty(process.MainWindowTitle))
{
IntPtr xero = new IntPtr(0);
var x = FindWindowByCaption(xero, process.MainWindowTitle);
int length = GetWindowTextLength(x);
StringBuilder sb = new StringBuilder(length + 1);
GetWindowText(x, sb, sb.Capacity);
}
}
You need to enumerate all child windows of the top level windows. You can use EnumChildWindows API in order to accomplish that.
Here is the sample code i have written in C# for you
internal class Program
{
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, StringBuilder lParam);
const uint WM_GETTEXT = 0x000D;
static bool EnumAllChilds(IntPtr hWnd, IntPtr lParam)
{
StringBuilder sb = new StringBuilder(2048);
SendMessage(hWnd, WM_GETTEXT, new IntPtr(sb.Capacity), sb);
if (!string.IsNullOrEmpty($"{sb}"))
{
Console.WriteLine($"\t{hWnd:X}\t{sb}");
}
EnumChildWindows(hWnd, EnumAllChilds, lParam);
return true;
}
static bool EnumTopLevel(IntPtr hWnd, IntPtr lParam)
{
StringBuilder sb = new StringBuilder(2048);
SendMessage(hWnd, WM_GETTEXT, new IntPtr(sb.Capacity), sb);
Console.WriteLine($"TopLevel: hWnd: {hWnd:X}\t{(string.IsNullOrEmpty($"{sb}") ? "No Caption" : $"{sb}")}");
// Call for child windows
EnumChildWindows(hWnd, EnumAllChilds, lParam);
return true;
}
static void Main(string[] args)
{
// Call for TopLevel windows
EnumWindows(EnumTopLevel, IntPtr.Zero);
Console.ReadLine();
}
}
For some time, I have created a console application (cmd & powershell) by integrating the multi tab. I'm using the setparent function of user32. Everything works except when I move the parent window. It is impossible to access the child window. It is visible but impossible to click on it. To remedy this, the parent window must be replaced where it was. I noticed that this "bug" only appears with the new Windows 10 console.
I do not know how to do.
(Sorry for my bad English I'm French)
I will try to explain better. I start a child window at a position, if I move the parent window, the child window will be deactivated. Like this :
Start set parent to child window...
Move parent window..
And the problem appears..
Here is the code:
[DllImport("user32.dll")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[... Code ...]
Process process = new Process();
process.StartInfo.FileName = pProcess;
process.StartInfo.Arguments = pArgs;
process.EnableRaisingEvents = true;
process.Exited += new EventHandler((s, e) =>
{
FermerOnglet(tabpage);
});
process.Start();
while (process.MainWindowHandle == (IntPtr)0)
{
}
SetParent(process.MainWindowHandle, metroPanel1.Handle);
I finally found the solution, it was not that complicated but I did not understand why it did that.
Solution :
[DllImport("user32.dll")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern long SetWindowLongA(IntPtr hWnd, int nIndex, long dwNewLong);
[DllImport("User32.Dll")]
static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
private static extern void RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, uint flags);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
static extern bool EnableWindow(IntPtr hWnd, bool bEnable);
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
[... Code ...]
Process process = new Process();
process.StartInfo.FileName = pProcess;
process.StartInfo.Arguments = pArgs;
process.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
process.EnableRaisingEvents = true;
process.Exited += new EventHandler((s, e) =>
{
FermerOnglet(tabpage);
});
process.Start();
while (process.MainWindowHandle == (IntPtr)0)
{
}
SetParent(process.MainWindowHandle, metroPanel1.Handle);
SetWindowLongA(process.MainWindowHandle, -16, 0x80000000L);
[... Code ...]
RedrawWindow(CurrentOngletSelect.HandleProcess.MainWindowHandle, (IntPtr)0, (IntPtr)0, 0x0400/*RDW_FRAME*/ | 0x0100/*RDW_UPDATENOW*/ | 0x0001/*RDW_INVALIDATE*/);
MoveWindow(CurrentOngletSelect.HandleProcess.MainWindowHandle, 0, 0, metroPanel1.Width, metroPanel1.Height, true);
SetWindowPos(CurrentOngletSelect.HandleProcess.MainWindowHandle, (IntPtr)(-1), 0, 0, 0, 0, 0);
ShowWindow(CurrentOngletSelect.HandleProcess.MainWindowHandle, 5);
EnableWindow(CurrentOngletSelect.HandleProcess.MainWindowHandle, true);
can you help, how you can change the screen size, which cefsharp transmits ? I go to ipleak.net and saw
Your screen: 1920 x 1080
Available screen: 1920 x 1040
Is there any way to change it ?
I tried that, but it didnt work
DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
int hWnd, // window handle
int hWndInsertAfter, // placement-order handle
int X, // horizontal position
int Y, // vertical position
int cx, // width
int cy, // height
uint uFlags); // window positioning flags
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,
string lpWindowName);
protected delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
protected static extern bool EnumWindows(EnumWindowsProc enumProc, 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 IsWindowVisible(IntPtr hWnd);
public static IntPtr m_hwndForm;
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);
string strName = sb.ToString();
if (strName.Contains("Form111111"))
m_hwndForm = hWnd;
}
return true;
}
...
EnumWindows(new EnumWindowsProc(EnumTheWindows), IntPtr.Zero);
SetWindowPos(m_hwndForm.ToInt32(), 0, 10, 20, 600, 800, 0x0040);
InitializeChromium();
or
settings.CefCommandLineArgs.Add("Screen-width", "800");
settings.CefCommandLineArgs.Add("Screen-height", "600");
I've found, that's possible through chrome DevTools protocol using setDeviceMetrics method. Many thanks to amaitland!
var client = browser.GetDevToolsClient();
await client.Emulation.SetDeviceMetricsOverrideAsync(1000, 800, 1, true);
You also need to fully initialize CefSharp browser before use this method.
I am trying to host a .exe within my .net application (Mainly video viewing software) however certain applications do not allow me to use their menu's or some controls. Has anyone had this problem before or any idea of why it could be happening?
Here is my code to host the application:
#region Methods/Consts for Embedding a Window
[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 long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", EntryPoint = "GetWindowLongA", SetLastError = true)]
private static extern long GetWindowLong(IntPtr hwnd, int nIndex);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long x, long y, long cx, long cy, long wFlags);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)]
private static extern bool PostMessage(IntPtr hwnd, uint Msg, int wParam, int lParam);
private const int SWP_NOOWNERZORDER = 0x200;
private const int SWP_NOREDRAW = 0x8;
private const int SWP_NOZORDER = 0x4;
private const int SWP_SHOWWINDOW = 0x0040;
private const int WS_EX_MDICHILD = 0x40;
private const int SWP_FRAMECHANGED = 0x20;
private const int SWP_NOACTIVATE = 0x10;
private const int SWP_ASYNCWINDOWPOS = 0x4000;
private const int SWP_NOMOVE = 0x2;
private const int SWP_NOSIZE = 0x1;
private const int GWL_STYLE = (-16);
private const int WS_VISIBLE = 0x10000000;
private const int WM_CLOSE = 0x10;
private const int WS_CHILD = 0x40000000;
private const int WS_MAXIMIZE = 0x01000000;
#endregion
#region Variables
private IntPtr hostedProcessHandle;
private Process hostedProcess = null;
private ProcessStartInfo hostedPSI = new ProcessStartInfo();
#endregion
//Helper method to start a process contained within the form
private void HostProcess(string processPath)
{
//Start the process located at processPath
hostedPSI.FileName = processPath;
hostedPSI.Arguments = "";
hostedPSI.WindowStyle = ProcessWindowStyle.Maximized;
hostedProcess = System.Diagnostics.Process.Start(hostedPSI);
//Stop watch is used to calculate time out period.
Stopwatch sw = new Stopwatch();
sw.Start();
//Loop to aquire application handle. Exit loop if the time out period is past.
do
{
hostedProcessHandle = hostedProcess.MainWindowHandle;
if (sw.ElapsedMilliseconds > 10000) throw new TimeoutException();
} while (hostedProcessHandle == new IntPtr(0));
//Host the process in the forms panel.
SetParent(hostedProcessHandle, this.panel1.Handle);
SetWindowLong(hostedProcessHandle, GWL_STYLE, WS_VISIBLE + WS_MAXIMIZE);
MoveWindow(hostedProcessHandle, 10, 10, this.panel1.Width - 20, this.panel1.Height - 20, true);
}
private void CloseHostedProcess()
{
hostedProcess.Kill();
}
Here is a screen shot of my test application hosting VLC, Some of the menu's and buttons as you can see are grayed out and not working:
This is not just a problem with VLC — I see this issue when hosting other applications too.
Just an update. If i right click on VLC -> Play -> Add and play a video back manually the menu bar works again. However the video controls at the bottom are still not working! They change color when rolled over but clicking them still doesn't work!
The reason I was experiencing problems seems to be because TeamViewer was running in the background. I am currently trying to find out why this causes me issues but stopping TeamViewer's process seems to rectify my problem.
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.