Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Question
Friday, June 16, 2006 10:08 PM
Hi,
I'm trying to retrieve a process from the System Tray programmatically.
My program starts the process and then saves the Process object. Later, the program tries to locate the process and bring it to the front (in focus). For all processes that do not reside in the system tray, I can do this with no problems. When I call AppActivate on a process that is in the SystemTray, the ArgumentException is raised.
Does anyone know how to retrieve a process from the system tray?
Here is the code that brings the process in focus. The Process.Id is non-zero, but the Process.MainWindowTitle and Process.MainWindowHandle are undefined.
if ((null != sender) && (sender is Control) && (null != ((Control)sender).Tag) && (((Control)sender).Tag is int))
{
// Gets the index of this process
int app_index = (int)((Control)sender).Tag;
try
{
Interaction.AppActivate(m_processes_list[app_index].Id);
}
catch (ArgumentException aex)
{
MessageBox.Show("Program may be in the system Tray.", "Unable To Locate Program");
}
}
All replies (10)
Monday, June 19, 2006 11:27 AM
Dear Customer,
Can you elaborate the system tray application that you are trying to activate and failed?
If you make a simple Winform application with NotifyIcon enabled, and try to activate the application did that failed?
Here is a link about how to make a Winform Application with NotifyIcon(System Tray application)
NotifyIcon Class
http://msdn2.microsoft.com/en-us/library/system.windows.forms.notifyicon.aspx
or
An Alternative Implementation of the NotifyIcon Class
http://www.codeproject.com/cs/miscctrl/notifyiconex.asp
Also here is a reference from MSDN about the method.
<remark>
For more detailed information, see the Visual Basic topic AppActivate Function.
You use AppActivate to bring an application's active window into focus. You might not have a handle or a reference to the active window, or even know which window is active at a given moment. In such a case, you cannot use the Focus method.
The AppActivate function changes the focus to the named application or window but does not affect whether it is maximized or minimized. Focus moves away from the activated application window when the user takes some action to change the focus or close the window. You can use the Shell function to start an application and set the window style.
If you use the Title parameter, AppActivate uses a case-insensitive comparison but otherwise requires an exact match with the contents of the title bar. It first looks through the top-level windows and then through the child windows. If it cannot find a match, it throws an ArgumentException.
You can use AppActivate only with processes that own windows. Most console applications do not own windows, which means that they do not appear in the list of processes that AppActivate searches. When running from a console application, the system creates a separate process to run the application and returns the output to the console process. Consequently, when you request the current process ID, you get the process ID of this separate process, rather than the console application's process ID.
At run time, the AppActivate function activates any running application with a title that matches Title or with a process ID that matches ProcessId. If there is no exact match, it activates any application whose title string ends with Title. If there is more than one application named Title, the AppActivate function arbitrarily chooses one to activate.
Note
The AppActivate function requires UIPermission at the SafeTopLevelWindows level, which may affect its execution in partial-trust situations. For more information, see Requesting Permissions and UIPermission Class.
</remark>
If you still have any concern, please feel free to post here.
Best regards,
Peter Huang
Monday, June 19, 2006 4:50 PM
Thanks Peter.
The application I created can launch any other application, so I wouldn't know if they have enabled NotifyIcon or not. The application that I am having the problem locating is the program Prime95.exe. My application starts Prime95 using Process.start, and it caches the Process information on the method return. I then use the Process.Id from the cached information in the call to AppActivate. I can't use the MainWindowTitle because it does not exist.
I think you explained my problem when you said:
"You can use AppActivate only with processes that own windows. Most console applications do not own windows, which means that they do not appear in the list of processes that AppActivate searches. When running from a console application, the system creates a separate process to run the application and returns the output to the console process. Consequently, when you request the current process ID, you get the process ID of this separate process, rather than the console application's process ID."
I don't believe Prime95 has a Window when it is minimized to the System Tray. So, when I call AppActivate on it, the process Id I have is probably not associated with a window, right?
Would you have any other suggestions for me to retrieve and bring the application (Prime95 in this case) from the System Tray into focus?
Thanks,
Derrick Price
Tuesday, June 20, 2006 12:45 AM
If you can get the Handle...this seems to work:
[DllImport("user32.dll", SetLastError = true)]
static extern int FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern bool ShowWindow(int hWnd, int nCmdShow);
int trayApp = FindWindow(null, trayAppCaption);
ShowWindow(trayApp, 1);
Tuesday, June 20, 2006 6:54 PM
Thanks Cablehead, but the problem is I don't know the proper title to pass to FindWindow because I tried this before using 'Prime95.exe' or 'Prime95' and it did not work. If I call FindWindow with the hard coded value, 'Prime95 - xxxxxxxx' (as shown when I hover my mouse over the icon) it works, but that information is not readily available on the resources I have for the process.
Thanks,
DP
Tuesday, June 20, 2006 7:23 PM
How about:
Process[] ProcessArray = Process.GetProcesses();
try
{
for (int i = 0; i < ProcessArray.Length; i++)
{
if(ProcessArray.ProcessName == "Prime95orWhatever")
Console.WriteLine(ProcessArray.MainWindowHandle.ToString());
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Or:
Console.WriteLine(ProcessArray.MainWindowTitle);
Tuesday, June 20, 2006 8:00 PM
Tried this with trayed PRIME95...works fine.
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private IntPtr appWin;
private void Form1_Load(object sender, EventArgs e)
{
Process p = null;
try
{
// Start the process
p = System.Diagnostics.Process.Start("C:\test\PRIME95.exe");
// Wait for process to be created and enter idle condition
p.WaitForInputIdle();
// Get the main handle
appWin = p.MainWindowHandle;
Console.WriteLine(appWin);
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "Error");
}
}
private void Form1_Click(object sender, EventArgs e)
{
Console.WriteLine(appWin);
ShowWindow(appWin, 1);
}
Wednesday, June 21, 2006 4:23 PM
Well Cablehead I appreciate the help but something is not right for me. I tried exactly what you have hear and the MainWindowHandle is always zero. My coworker tried this and it worked for him (sometimes).
We then used code that after a button is hit the Process is started, then when another button is hit, the FindWindow code is called. For me the MainWindowHandle is always zero whereas my coworker get a non-zero on the first call, but not on subsequent calls.
Here is my code:
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private IntPtr appWin;
private void locate_button_click(object sender, EventArgs e)
{
Console.WriteLine("The window handle {0}", appWin.ToString());
textBox1.AppendText("The window handle - " + appWin.ToString() + "\r\n");
ShowWindow(appWin, 1);
}
private void Form1_Load(object sender, EventArgs e)
{
textBox2.Text = "D:\Prime95\PRIME95.exe";
}
private void start_button_click(object sender, EventArgs e)
{
Process p = null;
try
{
if ("" != textBox2.Text && textBox2.Text.Length > 0)
{
// Start the process
p = System.Diagnostics.Process.Start(textBox2.Text);
// Wait for process to be created and enter idle condition
p.WaitForInputIdle();
// Get the main handle
appWin = p.MainWindowHandle;
Console.WriteLine("The window handle {0}", appWin.ToString());
textBox1.AppendText("The window handle - " + appWin.ToString() + "\r\n");
textBox1.AppendText("The main window handle - " + p.MainWindowHandle.ToString() + "\r\n");
textBox1.AppendText("The window title - [" + p.MainWindowTitle + "]\r\n");
}
}
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "Error");
}
}
Thank you.
Wednesday, June 21, 2006 6:23 PM
Weird...works every time here (XP Home)
Are you sure your button event handlers are wired correctly?
I noticed "start_button_click", that click is lowercase in your code....usually ends up Click if done by the IDE.
Saturday, July 15, 2006 11:39 AM
There is a bug in the process class, some things such as MainWindowHandle, MainWindowTitle. Are cached when you read them, so they never change after reading them
Versions of Dot Net affected = ALL
workaround for MainWindowHandle
make a loop
a var in the loop ups 1 per loop
start the program
sleep for loop var * 1000
if MainWindowHandle not equal to 0 then exit the loop
end the program
goto start of loop
end of loop
Monday, July 31, 2006 10:55 AM
Thx a lot for your post. I was going crazy, trying to figure out why sometimes MainWindowHandle was != 0 and sometimes it alway starved on 0.
I start a process that i'm sure has a main window, but it takes a lot to start and the firts time i checked the MainWindowHandle it was always 0. I couldn't use WaitForInputIdle() because it caused starvation on my main application.
So, lokking in methods of Process class, i found this simple solution:
Proces myProcess = Process.Start(myStartInfo);
while(myProcess.MainWindowHandle == 0)
{
myProcess.Refresh();
}
The Refresh() method refreshes () all cached information about a process.