Ensuring a single instance of an application using WINAPI? - c#

Got curious when someone down-voted this code as a solution to running only a single instance of an application without stating why they did so.
int hWnd = FindWindow(null, "My Application Title");
if (hWnd > 0) //If found
{
Process.GetCurrentProcess().WaitForExit(600);
try
{
SetForegroundWindow(hWnd); //Activate it
ShowWindow(hWnd, 9);
Process.GetCurrentProcess().Kill();
}
catch (Exception ex)
{
//write to log
}
}
//Import the FindWindow API to find our window
[DllImport("User32.dll")]
public static extern int FindWindow(String ClassName, String WindowName);
//Import the SetForeground API to activate it
[DllImport("User32.dll")]
public static extern IntPtr SetForegroundWindow(int hWnd);
//Import the ShowWindow API to show it
[DllImport("User32.dll")]
public static extern bool ShowWindow(int hWnd, int nCmdShow);
Could someone be kind as to explain the drawbacks of this method to me? Thanks.

Because if the application is starting twice (accidental clicking), there is a small window of time where the test will fail. Both instances could be starting but neither has created a window yet.

The drawbacks as far as I can see are that it is overly complicated for what should be a simple solution. You do not need to crack into the windows api to force a single instance of an api. I would guess that is why you got downvoted.
If you follow the links in the Uwe's answer, you will see that you can remain in managed code, which should be your default unless there is some reason you MUST dig a little deeper.

Related

How to destroy a Third Party WPF Child Window using c#?

I am trying to destroy a child window created by a third party application. I tried to use WinApi to make this happen but it seems that DestroyWindow(hwnd) doesn't do the trick. I indeed get a handle to work with but nothing happens, the child window remains. I tried to use CloseWindow(hwnd) just to verify the child window can be modified and yes, CloseWindow(hwnd) minimizes the Child Window but I want to close it.
This is my Code:
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DestroyWindow(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseWindow(IntPtr hWnd);
private static void CloseChildWindow();
{
IntPtr _hwnd = IntPtr.Zero;
if (WindowController.FindWindow(null, "System Alert") != IntPtr.Zero)
{
_hwnd = WindowController.FindWindow(null, "System Alert");
DestroyWindow(_hwnd); //Returns error code 5: Access Denied
CloseWindow(_hwnd); //Minimizes the window therefore _hwnd is correct
}
}
Any ideas on what is happening or a workaround would be appreciated, thanks in advance.
EDIT1: Destroywindow returns code error 5: Access Denied. Any ideas on a workaround?
The documentation for the win32 API DestroyWindow is pretty clear:
A thread cannot use DestroyWindow to destroy a window created by a
different thread.
You may be able to inject a DLL in the third party application, hooking the correct thread and then issuing the DestroyWindow call from here, but I will not recommand that, as it may have weird side effects (read: crash). Some applications don't react well when their windows are destroyed under their feet.
The proper way is probably to fake an user interaction, using regular Windows message (as WM_CLOSE), or SendInput, or UI Automation.
If the user has no way to close that window, you are out of luck.

Showing Outlook instead of displaying a message in C#.Net [duplicate]

We only want one instance of our app running at any one time. So on start up it looks to see if the app is running and if it is, it calls SetForegroundWindow on the Main Window.
This is all good and well ... for the most part..
When our app starts up it will show a Splash screen and a Logon form. Both of these forms have ShowInTaskBar = false.
Because of this, if you try to start up another copy of the app when the Logon form is showing, that Logon form is not brought to the front!
Especially as the user cant see anything in the taskbar as well, all they figure is that the app is duff and cannot start. There is no indication that there is another instance running.
Is there any way around this problem?
Well, code is here. Even if the ShowInTaskBar is false, you should be able to bring it to the front.
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public static void bringToFront(string title) {
// Get a handle to the Calculator application.
IntPtr handle = FindWindow(null, title);
// Verify that Calculator is a running process.
if (handle == IntPtr.Zero) {
return;
}
// Make Calculator the foreground application
SetForegroundWindow(handle);
}
Note: you should FindWindow using the form's class and not by name as the splash screen forms sometimes do not have titles or even the controlbox. Use Spy++ to dig deeper.
Use FindWindow on splash. I think this is what you want to do - bring the splash screen in front while loading of the main form.
I think this is the better solution because its restores from minimized state:
public static class WindowHelper
{
public static void BringProcessToFront(Process process)
{
IntPtr handle = process.MainWindowHandle;
if (IsIconic(handle))
{
ShowWindow(handle, SW_RESTORE);
}
SetForegroundWindow(handle);
}
const int SW_RESTORE = 9;
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr handle);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool ShowWindow(IntPtr handle, int nCmdShow);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool IsIconic(IntPtr handle);
}
Simple call:
WindowHelper.BringProcessToFront(process);
FindWindow(null, title);
Will find the first window that matches the query. This may lead to unexpected behavior if another window uses the same title.
Although the chances of this to happen may seem rare or impossible (single instance application) this can easily occur. The windows explorer for instance uses the name of the selected directory as window title (although invisible). Now if the window title is a common term or matches the name of the application directory this can be an issue.

Send Keystrokes to a program even if its in background using c#

I wanna send key stroke to a program even if it is running in background. But I can do this only for NOTEPAD like this,
[DllImport("user32.dll")]
protected static extern byte VkKeyScan(char ch);
[DllImport("user32.dll", SetLastError = true)]
protected static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
protected static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
char Key = // key value to send
IntPtr hWnd = FindWindowEx(_handle, IntPtr.Zero, "edit", null); // _handle is the windows handle of the program (here its notepad)
PostMessage(hWnd, WM_KEYDOWN, VkKeyScan(Key), 0);
But for all other applications I can't send keystrokes if its in background. Since I don't know the lpszClass of that program (I think this is the userControl name of the typing area in that program. For NotePad it is "edit". I found this surfing internet).
For all other applications what I'm doing is, get the application to foreground, then send the key, then again get my program foreground. I need my program to be run as foreground always.
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
SetForegroundWindow(_handle); // _handle is the windows handle of the program
System.Threading.Thread.Sleep(50); // Waiting few milliseconds till application coming to foreground.
wsh.SendKeys(Key.ToString(), ref wait); // wsh is WshShellClass wsh= new WshShellClass();
SetForegroundWindow(_mainHandle); // _mainHandle is the windows handle of my application
But this way is not working. some keys getting missed and the program foreground->background->foregound->background...... like its dancing...
How to send keys to other applications if its running in background.
or are there any way/source to find the lpszClass of a program ?
Sorry if I have missed any required information. this is a large application. I have posted only required parts here. If someone needs any additional information, pls ask.
I think you'll need to have the background program install a low-level keyboard hook via the win32 function SetWindowsHookEx().
Here's the MSDN documentation for SetWindowsHookEX()
http://msdn.microsoft.com/en-us/library/ms644990(v=vs.85).aspx
And here's the KB article on how to do it from C#
http://support.microsoft.com/kb/318804
This article goes into some detail, too: http://www.codeguru.com/columns/vb/article.php/c4829
I expect your app will get caught by various spyware/anti-virus software as a keyboard logger, though.
Good luck.
You may be able to figure out the lpszClass of the program using an inspection tool such as WinSpy++. It gives you a crosshair that you can drag and position over the desired control. This was able to easily provide me with the "edit" class name for notepad.
If things aren't working, click the "More>>" button in the lower right of WinSpy++, then click the "Locate" button to view the control hierarchy; you may need to post the WM_KEYDOWN message to one of the parent or child controls instead.

How can I log the time a message box appears from a 3rd party application?

Here’s the situation:
We have a 3rd party application that intermittently displays an error message. Usually the error occurs when no one is in the office, so we don’t know exactly what time the message box is popping up. We are working with our vendor to determine what the issue is, and they want to know exactly when the error is occurring so we can provide them with network conditions etc… at the time of the error. The error message is not logged anywhere by the application, so I have been tasked with somehow logging when it occurs.
I have C#.NET as my tool. So far the closest things I have found to a solution are FindWindow and EnumChildWindows or hooking into Windows messages. I am a recent college grad and just started my job so both of these routes will be fairly complicated for me. Before I invest a lot of time trying to learn what I need to try to make one of those methods work, I wanted to check here and see if there was a simpler solution.
Really all I need is to log when a message box appears and some identifying information about the message box. It isn’t necessary to log only messages from the application in question.
Thank you for your help. Please let me know if I need to clarify anything.
EDIT:
I've tried to code something with Hans' suggestions and references. I relied pretty heavily on his code sample. Right now I have a form that will accept a process name. Clicking a button will create an instance of the following class. I did some testing with Notepad, but it just cycled through the findMessageBox method even when I had a dialog box open. I tried using EnumChildWindows instead of EnumThreadWindows and the same thing happened. I confirmed that the program had the correct PID with Spy++. I'd appreciate any suggestions on what I need to fix.
EDIT:
It's working now. Thank you so much for your help. I was passing the wrong value from GetWindowProcessThreadId to EnumThreadWindows. I still will be working on it some as I can clean it up some and I don't want an open dialog logged continuously, but those are trivial things. I've posted the code I have now with the primary funcionality just in case anyone else has to do a similar thing in the future:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
namespace LogDialog
{
class DialogLogger
{
TextWriter log = new StreamWriter(#"C:\ErrorLog\ErrorLog.txt");
private Timer mTimer;
private uint lpdwProcessId;
private IntPtr mhwnd;
uint threadID;
//*************P/Invoke Declarations*************//
private delegate bool EnumThreadWndProc(IntPtr hwnd, IntPtr lp);
private delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lp);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern bool EnumThreadWindows(int dwThreadId, EnumThreadWndProc callback, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hwnd, StringBuilder buffer, int buflen);
//Retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the process that created the window.
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint lpdwProcessId);
//***********************************************//
//Constructor; initiates timer
public DialogLogger(string processName)
{
setWindowProcessID(processName); //set process ID
mTimer = new Timer(); //create timer for logging
mTimer.Interval = 50; //set interval
mTimer.Enabled = true; //enable
mTimer.Tick += new EventHandler(findMessageBox); //set event handler
}
private void setWindowProcessID(string processName)
{
mhwnd = Process.GetProcessesByName(processName)[0].MainWindowHandle;
threadID = GetWindowThreadProcessId(mhwnd, out lpdwProcessId);
}
//Enumerates windows to find a message box
private void findMessageBox(object sender, EventArgs e)
{
EnumThreadWndProc callback = new EnumThreadWndProc(checkDialogWindow);
EnumThreadWindows((int)threadID, callback, IntPtr.Zero);
GC.KeepAlive(callback);
}
//Checks if hwnd is a dialog
private bool checkDialogWindow(IntPtr hwnd, IntPtr lp)
{
StringBuilder sb = new StringBuilder(260);
GetClassName(hwnd, sb, sb.Capacity);
if (sb.ToString() != "#32770") return true;
log.WriteLine("Error Logged: {0}", DateTime.Now.ToLongTimeString());
return false;
}
}
}
They are jerking you around, knowing full well that this is not that easy to implement. It is utterly trivial for them to add the logging, they should already have done so if their app is that sensitive to network conditions. The most positive view on this request is that they'll hope you'll figure this out by yourself from trying to make this work. Could happen. Talk to your supervisor and point this out.
How about this simple approach: create a screen capture every 2 minutes and just check the image files the next day?
If you can attach a debugger, it should be rather trivial :)

How do I get the active ChildWindow of an application?

I want to send a pressKey event to a certain application which is not the active application IN Windows so I have to use the sendMessage/postMessage api calls.
However, I need to know the exact child window that is active IN the application and send the pressKey message to it...
I was using GetTopWindow and GetWindow(GW_CHILD) api calls to get the top child window of the main window, and do it again with the obtained child window to get the top grandchildWindow, and keep doing it until I found a childwindow with no more childwindows. This works great for some applications but in some cases it doesn't. Sometimes the parent window is the active window, not one of its childwindows, so getting the parent's top child window will not work cause I will be sending a message to the wrong window.
The only way I found of doing this (getting the handler of the actual active window) was using the GuiThreadInfo api call but it only works if the target application is the active one IN Windows. As I mentioned in the beginning, it isn't so the handler comes null.
I can bring the application to the top using setForegroundWindow api call but I DON'T want to do this. I also tried the AttachThreadInput and GetFocus api calls, but again, they only work if the target application is the active application IN windows.
Any ideas? Thanks
I assume from the things that you have tried that you know how to get a handle to your main window, but if you don't just leave a comment and I will post a snippet for that.
I combined a few things that I found on the net to figure this out, but the main one is this one. I don't have a great app to test this with, but it works in a simple case. One exception is that I think if you use tool windows in your application it will not find that as it is coded because I think the GetLastActivePopup method doesn't include them (not sure about that, and didn't test that case).
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
static extern IntPtr GetLastActivePopup(IntPtr hWnd);
[DllImport("user32.dll", ExactSpelling = true)]
static extern IntPtr GetAncestor(IntPtr hwnd, uint gaFlags);
const uint GA_PARENT = 1;
const uint GA_ROOT = 2;
const uint GA_ROOTOWNER = 3;
public static IntPtr GetAppActiveWindow(IntPtr hwnd)
{
IntPtr activeAppWindow = IntPtr.Zero;
if (hwnd != IntPtr.Zero)
{
//Get the root owner window (make sure we are at the app window
//if you already have a handle to the main window shouldn't have
//to do this but I put it in just in case
hwnd = GetAncestor(hwnd, GA_ROOTOWNER);
while ((activeAppWindow =
GetLastActivePopup(hwnd)) != activeAppWindow)
{
if (IsWindowVisible(activeAppWindow))
break;
hwnd = activeAppWindow;
}
}
return activeAppWindow;
}
If you know the Window title and the Window class name, take a look at FindWindow() and FindWindowEx() and see if those meet your needs.
FindWindow(): http://msdn.microsoft.com/en-us/library/ms633499.aspx
FindWindowEx(): http://msdn.microsoft.com/en-us/library/ms633500(VS.85).aspx

Categories

Resources