I'm starting internet explorer process. the problem is that it always return zero in p.MainWindowHandle.
my objective is to get mainwindowHandler and minimize that particular window which is just started. the same code is working with chrome browser. but in internet explorer it's not working.
my code is below.
Process p = Process.Start("IEXPLORE.EXE", "www.google.com");
ShowWindow(p.MainWindowHandle, 2);
ShowWindow is a method resize window.
Among the many other reasons (see comments to the question), you have to wait for process to create the main window:
// Process is IDisposable, wrap it into using
using (Process p = Process.Start("IEXPLORE.EXE", "www.google.com")) {
...
// wait till the process creates the main window
p.WaitForInputIdle();
// Main window (if any) has been created, so we can ask for its handle
ShowWindow(p.MainWindowHandle, 2);
}
Related
I'm building a Windows Form application and I want to open "Microsoft Edge" through my app with a specific URL and wait until the user closes the Edge Window.
I tried it with this code:
using (Process p = Process.Start("microsoft-edge:www.mysite.com"))
{
p.WaitForExit();
}
When I execute this code, Edge is launching with the correct URL ... but got a null object reference. The "p" object that I'm getting from Process.Start is null.
I think it's related to the reuse of Windows application.
Does anyone have a workaround/have an idea how I can wait for the user to close Edge?
Finally I did managed to do so:
When you launch Edge (at least) two process get created:
MicrosoftEdge and MicrosoftEdgeCP.
MicrosoftEdgeCP - foreach tab. So we can "wait" on this new tab process that was just created.
//Edge process is "recycled", therefore no new process is returned.
Process.Start("microsoft-edge:www.mysite.com");
//We need to find the most recent MicrosoftEdgeCP process that is active
Process[] edgeProcessList = Process.GetProcessesByName("MicrosoftEdgeCP");
Process newestEdgeProcess = null;
foreach (Process theprocess in edgeProcessList)
{
if (newestEdgeProcess == null || theprocess.StartTime > newestEdgeProcess.StartTime)
{
newestEdgeProcess = theprocess;
}
}
newestEdgeProcess.WaitForExit();
From an older solution - use a WebBrowser control to load the HTML. Once you get the data back, use an ObjectForScripting to call a c# method to notify when done.
See http://www.codeproject.com/Tips/130267/Call-a-C-Method-From-JavaScript-Hosted-in-a-WebBrowser
I'm trying to pop notepad window in my c# Application using this code:
Process[] Processes = Process.GetProcessesByName("notepad");
IntPtr hWnd = IntPtr.Zero;
Debug.WriteLine("Processes: " + Processes.Length);
// do something
foreach(Process p in Processes)
{
Console.WriteLine(p.ProcessName);
SetForegroundWindow(p.Handle);
ShowWindow(p.Handle, ShowWindowEnum.Show);
//SetActiveWindow(p.Handle);
//p.Kill();
}
The console logs "notepad" just fine. I can even kill notepad process. However, for some reason, showWindow works randomly. Most of the time it spawns something like GDI+server titled empty windows and etc and rarely pops the notepad.
What am I doing wrong?
ShowWindow expects a window handle, not a process handle.
Try passing the MainWindowHandle instead.
SetForegroundWindow(p.MainWindowHandle);
ShowWindow(p.MainWindowHandle, ShowWindowEnum.Show);
This should be ok for Notepad.exe, but won't be generally reliable for applications that have multiple top level windows.
My application is System Tray Application, using C#, .NET 4.0.
I'm trying to display many PDF files at a time and each PDF should split screen with other window, that I determine by ProcessName, that's all.
The difficulty is that I need to wait until user closes that window (.WaitForExit() method), because later I'm deleting PDF file. And here problem comes.
In first thread everything goes fine but the problem is when i try to show second PDF file window processList[0] THOUGH MoveWindow function returns true, and handle to that window is also correct only pdf window is resizing, the other window fails.
In main method the only thread that I'm creating (I call this piece of code couple of times, everytime user want to see pdf file):
Thread pdfThread = new Thread(() => ShowPdfFile(fullPath));
pdfThread.Start();
Then (simplified code)
public static void ShowPdfFile(string fileName)
{
try
{
Process pdfProcess = Process.Start(fileName);
Thread.Sleep(500);
string windowTitle = GetActiveWindowTitle();
IntPtr pdfHandle = (IntPtr)FindWindow(null, windowTitle);
MoveWindow(pdfHandle, 0, 0, 0, 0, true);
Process[] processList = Process.GetProcessesByName("someProcess");
MoveWindow(processList[0].MainWindowHandle, 0, 0, 0, 0, true);
pdfProcess.WaitForExit();
MoveWindow(processList[0].MainWindowHandle, 0, 0, max, max, true);
}
catch (Exception ex)
{
LogToFile(ex);
}
finally
{
try
{
File.Delete(fileName);
}
catch
{
LogToFile("Cannot delete file");
}
}
UPDATE: Well, I was debugging it whole day but just now i note that on second thread it doesn't wait on line pdfProcess.WaitForExit();
What should I change to force thread to wait for that exact pdfProcess exit?
There is a basic problem to your usage:
You start the external PDF application for each file. However this doesn't ensure that you have more than one process.
For example Acrobat reader only starts a single process. Additional files will just be "added" as new windows to the first process (You can check this by trying to manually open Acrobat reader twice --> won't work).
So in short: If you cannot control which PDF reader is used (and can ensure that you have a single process for EACH file) your approach will not work.
Note: Theoretically it would be possible to wait until the user closes the "reader window" that contains the specific file. However I strongly advise against this:
Looking up a window in a different process is very error prone (unless the process is explicitly designed in such a way...)
The solution would again depend on the reader application (you cannot be sure that Acrobat and Nitro use similar architecture to just name two readers)
Principally, if this feature is very important, you should consider to buy a PDF viewer component that allows to show the PDF's as windows of your own process.
Edit
The reason that the second thread isn't waiting is that the functionality to "add" a file to the first process uses a temporary process:
Temporary process is started for new file
Temporary process checks if the application is already running
Temporary process notifies the first process to open the specific file
Temporary process shuts down.
So waiting for that process will return almost immediately since the process in question already has stopped (or will stop in just a couple of milliseconds).
Daniel is right, however I found a solution! Here, maybe someone will use it.
Small comment: We must use winapi solutions instead of .WaitForExit().
The most important part is while loop, that waits for close pdf window.
Remember that FindWindow() and IsWindow() methods are from user32.dll (winapi).
This code run process based on it's paths, then gets it's handle and wait for it's closure.
Process pdfProcess = new Process();
pdfProcess.StartInfo.FileName = filePath;
if (pdfProcess.Start())
{
Thread.Sleep(500);
Process[] processlist = Process.GetProcesses();
string windowTitle = string.Empty;
foreach (Process process in processlist)
{
if (!String.IsNullOrEmpty(process.MainWindowTitle) && process.MainWindowTitle.Contains(fileName))
{
windowTitle = process.MainWindowTitle;
}
}
IntPtr pdfHandle = FindWindow(null, windowTitle);
while (IsWindow(pdfHandle) && userExitedApp == false)
Thread.Sleep(100);
}
Multiple threads could utilize one method one after the other. Perhaps this video will help
https://www.youtube.com/watch?v=XhGXh9Z5GTw&feature=em-upload_owner
I have a situation where I'm starting a process in my code in order to set up an IPC channel. The process I'm starting is an MFC application with no CLR support. The application from which I am starting this process is a C# module in a WPF application (thought I don't think that that is consequential to my problem). This works with a version of the application that does support CLR, and it works on every computer except the deployment target, a touch screen computer with Windows 7. But for some reason, when I try it with this exact scenario, the Process object never resolves a main window handle (Process.MainWindowHandle). Is there another (perhaps even pinvoke) method of doing this? Is this a security thing? I'm the one staring the process. The process's main window handle does exist. I don't see what could be wrong.
If it helps, here is my code.
_applicationProcess = new Process();
_applicationProcess.StartInfo.FileName = _strProcessPath;
_applicationProcess.StartInfo.Arguments = _strProcessArguments;
_applicationProcess.Start();
long nTicks = Environment.TickCount;
if (_applicationProcess.WaitForInputIdle(1 /*minute(s)*/ * 60000))
{
try
{
do
{
// Don't let total processing take more than 1 minute(s).
if (Environment.TickCount > nTicks + 1 /*minute(s)*/ * 60000)
throw new ApplicationException("MFCApplication.Startup failed! The main window handle is zero!");
_applicationProcess.Refresh();
}
while (_applicationProcess.MainWindowHandle.ToInt32() == 0);
_applicationHandle = new IntPtr(_applicationProcess.MainWindowHandle.ToInt32());
}
catch (Exception ex)
{
//Do some stuff...
throw;
}
}
else
{
// Do exception handling.
}
The ApplicationException is hit after a minute of trying to get a main window handle other than zero.
The value you get out of Process.MainWindowHandle is unfortunately a guess. There is no API function available to a program that lets it tell Windows "this is my main window". The rule it uses is documented, it is the first window that's created by a process when it gets started. That causes trouble if that first window is, say, a login window or a splash screen.
Not much you can do about this, you have to know more about how the program behaves to find that real main window. Enumerating windows with EnumThreadWindows() could help you find it, as long as the first window was created on the same thread as the main window. A more elaborate EnumWindows() will be necessary if that is not the case.
My habit is to call EnumWindows in a loop combined with GetWindowThreadProcessId to find the window handle.
C Code, adapt to your language
DWORD TargetHWND;
//...
while (EnumWindows(EnumWndProc, (LPARAM)(DWORD)pid)) {
Sleep(100);
}
//...
BOOL EnumWndProc(HWND hWnd, LPARAM lParam) {
DWORD pid = (DWORD)-1;
GetWindowThreadProcessId(hWnd, &pid);
if (pid == (DWORD)lParam) {
TargetHWND = hWnd;
return FALSE;
}
return TRUE;
}
In order to get MainWindowHandle by means of your process, please make sure your WPF application is shown on the taskbar i.e ShowInTaskbar="True" and set Application.Current.MainWindow property to the window that you'd like to set as your main window.
If I execute code below in my WPF main window without setting ShowInTaskbar="True" I always got 0 as the MainWindowHandle because my WPF window was full screen and not displayed on the taskbar.
Application.Current.MainWindow = this;
var Query = System.Diagnostics.Process.GetProcessesByName("ProcessName");
if (Query.Any())
{
Query.FirstOrDefault().Refresh();
MessageBox.Show(Query.FirstOrDefault().MainWindowHandle.ToInt32().ToString());
}
I don't know why it could be different, but after you create the process, try doing:
Process[] allProcesses = Process.GetProcessesByName("YourWindowTitle");
and see if any of the processes returned have a MainWindowHandle.
I have a C# winform application that during its work opens another Winform process. The other process has UI of its own.
When I close the parent application, I want the other application to be closed automatically.
How can I achieve that?
Thanks
If you are using Process.Process there is the CloseMainWindow method. If you keep a reference to the object you can use it later.
Here's the relevant page in the MSDN
and the relevant code:
// Close process by sending a close message to its main window.
myProcess.CloseMainWindow();
// Free resources associated with process.
myProcess.Close();
There are several different options. I would suggest that you have your application keep track of the processes that it starts:
private Stack<Process> _startedProcesses = new Stack<Process>();
private void StartChildProcess(string fileName)
{
Process newProcess = new Process();
newProcess.StartInfo = new ProcessStartInfo(fileName); ;
newProcess.Start();
_startedProcesses.Push(newProcess);
}
When the application closes, you can call a method that will close all started child processes that are still running. You can use this either with the Kill method or by calling the CloseMainWindow and Close methods. CloseMainWindow/Close will perform a more graceful close (if you start Notepad and there are unsaved changes, Kill will lose them, CloseMainWindow/Close will make notepad ask if you want to save):
private void CloseStartedProcesses()
{
while (_startedProcesses.Count > 0)
{
Process process = _startedProcesses.Pop();
if (process != null && !process.HasExited)
{
process.CloseMainWindow();
process.Close();
}
}
}
The most graceful way to do this is probably to send a window message to the main from of the other process. You can get the handle of this main form simply using the Process.MainWindow.Handle property (I assume you are using the Process class, and then just use the PostMessage Win API call to send a message with a custom ID to the main window of this "child" process. Then, the message loop of the other process can easily detect this message (by overriding the WndProc method) and perform a proper shutdown accordingly. An alternative would be to send the standard WM_CLOSE method, which would mean you would just have to unload the application from the handler of the Form.Closed event, but may perhaps allow you less control (over whether to cancel the shutdown in certain situations).