Here's my problem: we have an automated build process for our product. During the compilation of one of the VB6 projects a message box is spit out that requires the user to click 'ok' before it can move on. Being an automated process this is a bad thing because it can end up sitting there for hours not moving until someone clicks ok. We've looked into the VB6 code to try and suppress the message box, but no one can seem to figure out how to right now. So as a temp fix I'm working on a program that will run in the background and when the message box pops up, closes it. So far I'm able to detect when the message pops up but I can't seem to find a function to close it properly. The program is being written in C# and I'm using the FindWindow function in user32.dll to get a pointer to the window. So far I've tried closeWindow, endDialog, and postMessage to try and close it but none of them seem to work. closeWindow just minimizes it, endDialog comes up with a bad memory exception, and postMessage does nothing. Does anyone know of any other functions that will take care of this, or any other way of going about getting rid of this message? Thanks in advance.
here's the code I have currently:
class Program
{
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
static void Main(string[] args)
{
IntPtr window = FindWindow(null, "Location Browser Error");
while(window != IntPtr.Zero)
{
Console.WriteLine("Window found, closing...");
//use some function to close the window
window = IntPtr.Zero;
}
}
}
You have to found the window, that is the first step. After you can send the SC_CLOSE message using SendMessage.
Sample
[DllImport("user32.dll")]
Public static extern int SendMessage(int hWnd,uint Msg,int wParam,int lParam);
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_CLOSE = 0xF060;
IntPtr window = FindWindow(null, "Location Browser Error");
if (window != IntPtr.Zero)
{
Console.WriteLine("Window found, closing...");
SendMessage((int) window, WM_SYSCOMMAND, SC_CLOSE, 0);
}
More Information
Find and Close the window using WIN API
When you find the message box, try sending it WM_NOTIFY with a BN_CLICKED type and the ID of the OK button.
Related
I sent a WM_ACTIVE message using postmessage api to some programs.
When a program is deactivated, sending a message does not actually activate the window, but the program thinks it is active. ( It actually succeeded. )
However, I think it is very inefficient to send postmessage regularly.
If I want to check the WM_ACTIVE value of the program and it is inactivated, I try to send a WM_ACTIVE message again using the POSTMESSAGE API to confuse the program itself with being active, but I can't think of a way. Although there is an idea that hooking would be easy to use, C# did not support other types of global hooking except for the keyboard and mouse.
Can anyone come up with any other ideas? please help me.
To check if a process is focused you should use GetForegroundWindow to get the focused window handle and then use GetWindowThreadProcessId to get the process from that window handle:
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(int hWnd, out int ProcessId);
IntPtr focusedWindow = GetForegroundWindow(); //get the focused window
int focusedProcessID = 0;
GetWindowThreadProcessID(focusedWindow, out focusedProcessID); //get it's process id
Process focusedProcess = Process.GetProcessById(focusedProcessID);//get the focused process
Console.WriteLine("Current Focused Process:" + focusedProcess.ProcessName);
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.
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.
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 :)
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