Finding previously focused application - WinAPI - c#

I 'answered' this in a related question - but it is more of an additional question that I having trouble with and I need more recent answers...
Basically I have an application that stays open on the screen and the user can press a button on my app once they have made an entry into one of three 3rd party applications.
When they click the button on my app, I need to determine which of the three applications they last used in order to know which database to talk to. I have gone down the route of looking at GetForeGroundWindow and GetWindow however the Window handle I get from GetWindow always refers to a window with title M. I have used the Winternal Explorer tool from the Managed Windows API tools and I can locate the M handle being returned and it is a 'child' of the process that I am after - but from this handle I cant get the process name.
I have done up a small example app using simple windows forms - and I lauch it and then make Notepad the focus and then click on my button and I get the handle - but when looking at the MainWindowHandle of all the processes, it is not listed, but using Winternal Explorer I can see that is a sub process of the notepad process.
Any suggestions on why I am only getting this subprocess handle returned instead of the actual process handle??
Sample code is below - being run on a Windows XP sp 3 machine
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TestWindowsAPI
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
IntPtr thisWindow = GetForegroundWindow();
IntPtr lastWindow = GetWindow(thisWindow, 2);
tbThisWindow.Text = thisWindow.ToString();
tbLastWindow.Text = lastWindow.ToString();
}
}
}

You can use GetWindowThreadProcessId to get the process id from the (sub)window handle:
uint lastProcess;
GetWindowThreadProcessId(lastWindow, out lastProcess);

Pent Ploompuu - that was spot on - excellent work! Cheers
For anyone else - this is what my test function ended up looking like:
private void button1_Click(object sender, EventArgs e)
{
IntPtr thisWindow = GetForegroundWindow();
IntPtr lastWindow = GetWindow(thisWindow, 2);
uint processID = 0;
var parentWindow = GetWindowThreadProcessId(lastWindow, out processID);
tbThisWindow.Text = thisWindow.ToString();
tbLastWindow.Text = lastWindow.ToString();
tbParentWindow.Text = parentWindow.ToString();
tbLastProcess.Text = processID.ToString();
var processName = from cp in Process.GetProcesses() where cp.Id == processID select cp.ProcessName;
tbParentName.Text = processName.FirstOrDefault();
}

Try overriding the WndProc (or adding an IMessageFilter) for each of the programs and returning an "app ID" when a particular message is sent. Then just use SendMessage on the window handle to get the app ID.

Related

Listen for a save file dialog window in an application, then automate saving it?

Sorry if this is a vague question, it's a very specific case and difficult to explain. Here's what I'm trying to do (this is for a 64 bit Windows application by the way)
Look to see if a particular save file dialog window is open inside an application (image of it below, it's a dialog box that pops up inside of the application when exporting something)
Once I have a pointer to that window, somehow access and use its elements in such a way that I'm able to name the file I'm saving, navigate to a desired file path, then save it, all through code
Here's a photo of the window that I'm trying to control through code (for reference)
Image
So far I've been able to find code that gives me all the active windows, including the one I'm targeting. Here is that code:
using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using HWND = System.IntPtr;
using System.Text;
/// <summary>Contains functionality to get all the open windows.</summary>
public static class OpenWindowGetter
{
/// <summary>Returns a dictionary that contains the handle and title of all the open windows.</summary>
/// <returns>A dictionary that contains the handle and title of all the open windows.</returns>
public static IDictionary<HWND, string> GetOpenWindows()
{
HWND shellWindow = GetShellWindow();
Dictionary<HWND, string> windows = new Dictionary<HWND, string>();
EnumWindows(delegate (HWND hWnd, int lParam)
{
if (hWnd == shellWindow) return true;
if (!IsWindowVisible(hWnd)) return true;
int length = GetWindowTextLength(hWnd);
if (length == 0) return true;
StringBuilder builder = new StringBuilder(length);
GetWindowText(hWnd, builder, length + 1);
windows[hWnd] = builder.ToString();
return true;
}, 0);
return windows;
}
private delegate bool EnumWindowsProc(HWND hWnd, int lParam);
[DllImport("USER32.DLL")]
private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
[DllImport("USER32.DLL")]
private static extern int GetWindowText(HWND hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("USER32.DLL")]
private static extern int GetWindowTextLength(HWND hWnd);
[DllImport("USER32.DLL")]
private static extern bool IsWindowVisible(HWND hWnd);
[DllImport("USER32.DLL")]
private static extern IntPtr GetShellWindow();
}
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
foreach (KeyValuePair<IntPtr, string> window in OpenWindowGetter.GetOpenWindows())
{
IntPtr handle = window.Key;
string title = window.Value;
Console.WriteLine("{0}: {1}", handle, title);
}
}
}
}
I'm not sure what to do from here. What I need now help with now is checking for the particular window I'm targeting (named Export Selection as seen in the photo).. then somehow get references to its components and control them.
I've looked into using Spy++ to get info about the components, and then using FindWindowEx and SendMessage to control them. This is not something I completely understand, as I'm limited in my C# knowledge. Is this the correct approach, and if so how would I go about doing it?
Yea, using Spy++ is a good approach to understand that application and its controls. As you seem already to be able to detect the correct window, the next step is now to use the SendMessage function to send data to the individual controls of the window. Look up in the Win32 API documentation to find the correct messages to send. I.e. you would use the WM_SETTEXT message to put text into the "File name" input box. See here: https://learn.microsoft.com/en-US/windows/win32/winmsg/wm-settext.
Or you use the SetWindowText function, which basically does the same, but is easier to use. You already have the prototype for the GetWindowText method declared in your code. The C# declaration for SetWindowText should be
[DllImport("USER32.DLL")]
private static extern int SetWindowText(HWND hWnd, String lpString);

How can I send WM_DROPFILES from C#?

I want to use C# code to simulate a user dragging and dropping a file onto a control in a separate process. As a stepping stone to this goal, I am trying to send a WM_DROPFILES message to my own TextBox and verifying that the DragDrop event is triggered.
With the code below in a Form containing a single TextBox and two Buttons, clicking on button1 successfully sets the text of textBox1 to "Hello world". So, it seems that I'm using SendMessage correctly and am able to supply arguments via pointers. Dragging and dropping a file from Windows Explorer onto textBox1 displays the MessageBox, so textBox1 is set up to receive drag-dropped files correctly. However, when I click button2, nothing happens. Why don't I see a MessageBox when I click button2?
using System;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace StackOverflow
{
public partial class BadDragDrop : Form
{
#region WINAPI
[Serializable]
[StructLayout(LayoutKind.Sequential)]
struct POINT
{
public Int32 X;
public Int32 Y;
}
[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
class DROPFILES
{
public Int32 size;
public POINT pt;
public Int32 fND;
public Int32 WIDE;
}
const uint WM_DROPFILES = 0x0233;
const uint WM_SETTEXT = 0x000C;
[DllImport("Kernel32.dll", SetLastError = true)]
static extern int GlobalLock(IntPtr Handle);
[DllImport("Kernel32.dll", SetLastError = true)]
static extern int GlobalUnlock(IntPtr Handle);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
#endregion
public BadDragDrop()
{
InitializeComponent();
textBox1.AllowDrop = true;
}
private void button1_Click(object sender, EventArgs e)
{
string textToSet = "Hello world\0";
IntPtr p = Marshal.AllocHGlobal(textToSet.Length);
Marshal.Copy(textToSet.Select(c => (byte)c).ToArray(), 0, p, textToSet.Length);
int success = GlobalUnlock(p);
SendMessage(textBox1.Handle, WM_SETTEXT, IntPtr.Zero, p);
Marshal.FreeHGlobal(p);
}
private void button2_Click(object sender, EventArgs e)
{
string filePath = #"C:\Windows\win.ini" + "\0\0";
DROPFILES s = new DROPFILES()
{
size = Marshal.SizeOf<DROPFILES>(),
pt = new POINT() { X = 10, Y = 10 },
fND = 0,
WIDE = 0,
};
int wparamLen = s.size + filePath.Length;
IntPtr p = Marshal.AllocHGlobal(wparamLen);
int iSuccess = GlobalLock(p);
Marshal.StructureToPtr(s, p, false);
Marshal.Copy(filePath.Select(c => (byte)c).ToArray(), 0, p + s.size, filePath.Length);
iSuccess = GlobalUnlock(p);
var verify = new byte[wparamLen];
Marshal.Copy(p, verify, 0, wparamLen);
var ipSuccess = SendMessage(textBox1.Handle, WM_DROPFILES, p, IntPtr.Zero);
Marshal.FreeHGlobal(p);
}
private void textBox1_DragDrop(object sender, DragEventArgs e)
{
MessageBox.Show(this, "Drag drop!");
}
private void textBox1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
}
}
The reason you don't see your MessageBox appear is likely because the TextBox doesn't handle WM_DROPFILES messages to begin with. It implements its drop support using OLE Drag&Drop instead by implementing the IDropTarget interface (see Drag and Drop Overview in the WPF documentation).
WM_DROPFILES has been deprecated ever since DoDragDrop() was introduced way back in Windows 95. OLE Drag&Drop has been the preferred way to implement drag&drop on Windows for a very long time. WM_DROPFILES is still supported by Windows (but not .NET) but only for backwards compatibility with legacy apps.
Dragging items from Windows Explorer to other apps uses OLE Drag&Drop under the hood, even if the receiver doesn't implement OLE Drag&Drop.
If you drag&drop an IDataObject onto a window that has had RegisterDragDrop() called on it (like your TextBox has), the IDataObject will be passed to the window's IDropTarget interface for handling.
If you drag&drop an IDataObject onto a window that doesn't implement IDropTarget, but has had DragAcceptFiles() called on it, or at least has the WS_EX_ACCEPTFILES window style, Windows will generate a WM_DROPFILES message if the IDataObject contains CF_HDROP data in it.
So, to do what you are attempting with your TextBox, you need to implement the IDropSource and IDataObject interfaces, and then call DoDragDrop() with them. Let Windows handle the actual dragging and dropping for you. See Shell Clipboard Formats and Handling Shell Data Transfer Scenarios.
Now, with that said, if you are still determined to send a WM_DROPFILES message to another process (which your question says is your ultimate goal), you can do that, and Windows will marshal your DROPFILES struct across process boundaries for you, but you need to use PostMessage() instead of SendMessage() (not sure why, only that it is required), and be sure to free the DROPFILES only if PostMessage() fails. Windows will free the DROPFILES for you if PostMessage() is successful.
But even then, there is no guarantee that the receiving process actually handles WM_DROPFILES messages (and even if it does, your manual WM_DROPFILES message might get blocked by UIPI, unless the receiver calls ChangeWindowMessageFilter/Ex() on itself to allow WM_DROPFILES and WM_COPYGLOBALDATA messages). The receiver might be expecting IDataObject instead. If the receiver even supports drag&drop at all.

run another application within a C# program with his childs

I use a sample code to hunt process to my windows form application from specific List
void processStartEvent_EventArrived(object sender, EventArrivedEventArgs e)
{
string processName = e.NewEvent.Properties["ProcessName"].Value.ToString();
int processID = Convert.ToInt32(e.NewEvent.Properties["ProcessID"].Value);
if (_processNames.Contains(processName))
{
Process proc = Process.GetProcessById(processID);
if (GlobalVar.SourceWinForm.InvokeRequired)
{
GlobalVar.SourceWinForm.Invoke(new MethodInvoker(delegate { ProcessHandler.SetParent(proc.MainWindowHandle, GlobalVar.SourceWinForm.Handle); }));
}
else
{
ProcessHandler.SetParent(proc.MainWindowHandle, GlobalVar.SourceWinForm.Handle);
}
}
}
as you can see i use the function :
[DllImport("user32.dll")]
public static extern IntPtr SetParent(IntPtr hwc, IntPtr hwp);
everything work good except one thing.
for example i hunted notepad application into my app.
so it really give me the notepad into my app window but the problem start when
i press for example in the notepad "Format -> Font" it open a new sub window of notepad , this sub window , my apllication is not father of this sub window.
how i can hunt the full process ? include his child (subs) windows ?
you can use:
[DllImport("user32.dll")]
private static extern
bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern
bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern
bool IsIconic(IntPtr hWnd);
and then
// bring it to the foreground
if (IsIconic(proc.MainWindowHandle))
ShowWindowAsync(proc.MainWindowHandle, SW_RESTORE);
SetForegroundWindow(proc.MainWindowHandle);
Solution:
first of all i want to say the comments are correct and can help to someone else so please read the comments to my main question first.
but if you have specific scenario and you don't have any other option and you must use SetParent or other function like that. ( make sure you read first about SetParent and you understand what it dose )
the solution is to take the entire forms to foreground:
bool SetForegroundWindow(IntPtr hWnd);
this make all the other forms that inherit from the main form you loaded to see it on your main application ( this in case you application block and streach entire screen )

Restore a minimized window of another application

I'm adding some code to an app that will launch another app if it isn't already running, or if it is, bring it to the front. This requires a small amount of interop/WinAPI code, which I've gotten examples for from other sites but can't seem to get to work in Win7.
If the window is in some visible state, then the API's SetForegroundWindow method works like a treat (and this would be the main case, as per company policy if the external app is running it should not be minimized). However, if it is minimized (exceptional but important as my app will appear to do nothing in this case), neither this method nor ShowWindow/ShowWindowAsync will actually bring the window back up from the taskbar; all of the methods simply highlight the taskbar button.
Here's the code; most of it works just fine, but the call to ShowWindow() (I've also tried ShowWindowAsync) just never does what I want it to no matter what the command I send is:
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hWnd);
private const int SW_SHOWNORMAL = 1;
private const int SW_SHOWMAXIMIZED = 3;
private const int SW_RESTORE = 9;
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
...
//The app is named uniquely enough that it can't be anything else,
//and is not normally launched except by this one.
//so this should normally return zero or one instance
var processes = Process.GetProcessesByName("ExternalApp.exe");
if (processes.Any()) //a copy is already running
{
//I can't currently tell the window's state,
//so I both restore and activate it
var handle = processes.First().MainWindowHandle;
ShowWindow(handle, SW_RESTORE); //GRR!!!
SetForegroundWindow(handle);
return true;
}
try
{
//If a copy is not running, start one.
Process.Start(#"C:\Program Files (x86)\ExternalApp\ExternalApp.exe");
return true;
}
catch (Exception)
{
//fallback for 32-bit OSes
Process.Start(#"C:\Program Files\ExternalApp\ExternalApp.exe");
return true;
}
I've tried SHOWNORMAL (1), SHOWMAXIMIZED (3), RESTORE (9), and a couple other sizing commands, but nothing seems to do the trick. Thoughts?
EDIT: I found an issue with some of the other code I had thought was working. The call to GetProcessesByName() was not finding the process because I was looking for the executable name, which was not the process name. That caused the code I thought was running and failing to actually not execute at all. I thought it was working because the external app will apparently also detect that a copy is already running and try to activate that current instance. I dropped the ".exe" from the process name I search for and now the code executes; however that seems to be a step backwards, as now the taskbar button isn't even highlighted when I call ShowWindow[Async]. So, I now know that neither my app, nor the external app I'm invoking, can change the window state of a different instance programmatically in Win7. What's goin' on here?
Working code using FindWindow method:
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string className, string windowTitle);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowEnum flags);
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hwnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowPlacement(IntPtr hWnd, ref Windowplacement lpwndpl);
private enum ShowWindowEnum
{
Hide = 0,
ShowNormal = 1, ShowMinimized = 2, ShowMaximized = 3,
Maximize = 3, ShowNormalNoActivate = 4, Show = 5,
Minimize = 6, ShowMinNoActivate = 7, ShowNoActivate = 8,
Restore = 9, ShowDefault = 10, ForceMinimized = 11
};
private struct Windowplacement
{
public int length;
public int flags;
public int showCmd;
public System.Drawing.Point ptMinPosition;
public System.Drawing.Point ptMaxPosition;
public System.Drawing.Rectangle rcNormalPosition;
}
private void BringWindowToFront()
{
IntPtr wdwIntPtr = FindWindow(null, "Put_your_window_title_here");
//get the hWnd of the process
Windowplacement placement = new Windowplacement();
GetWindowPlacement(wdwIntPtr, ref placement);
// Check if window is minimized
if (placement.showCmd == 2)
{
//the window is hidden so we restore it
ShowWindow(wdwIntPtr, ShowWindowEnum.Restore);
}
//set user's focus to the window
SetForegroundWindow(wdwIntPtr);
}
You can use it by calling BringWindowToFront().
I always have one instance of the application running so if you can have several open instances simultaneously you might want to slightly change the logic.
... Apparently you cannot trust the information a Process gives you.
Process.MainWindowHandle returns the window handle of the first window created by the application, which is USUALLY that app's main top-level window. However, in my case, a call to FindWindow() shows that the handle of the actual window I want to restore is not what MainWindowHandle is pointing to. It appears that the window handle from the Process, in this case, is that of the splash screen shown as the program loads the main form.
If I call ShowWindow on the handle that FindWindow returned, it works perfectly.
What's even more unusual is that when the window's open, the call to SetForegroundWindow(), when given the process's MainWindowHandle (which should be invalid as that window has closed), works fine. So obviously that handle has SOME validity, just not when the window's minimized.
In summary, if you find yourself in my predicament, call FindWindow, passing it the known name of your external app's main window, to get the handle you need.
I had the same problem. The best solution I have found is to call ShowWindow with the flag SW_MINIMIZE, and then with SW_RESTORE. :D
Another possible solution:
// Code to display a window regardless of its current state
ShowWindow(hWnd, SW_SHOW); // Make the window visible if it was hidden
ShowWindow(hWnd, SW_RESTORE); // Next, restore it if it was minimized
SetForegroundWindow(hWnd); // Finally, activate the window
from comments at: http://msdn.microsoft.com/en-us/library/ms633548%28VS.85%29.aspx
Tray calling ShowWindow(handle, SW_RESTORE); after SetForegroundWindow(handle);
This might solve your problem.
It sounds like you're trying to perform an action that has the same result as alt-tabbing, which brings the window back if it was minimized while "remembering" if it was maximized.
NativeMethods.cs:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
// Specify your namespace here
namespace <your.namespace>
{
static class NativeMethods
{
// This is the Interop/WinAPI that will be used
[DllImport("user32.dll")]
static extern void SwitchToThisWindow(IntPtr hWnd, bool fUnknown);
}
}
Main code:
// Under normal circumstances, only one process with one window exists
Process[] processes = Process.GetProcessesByName("ExternalApp.exe");
if (processes.Length > 0 && processes[0].MainWindowHandle != IntPtr.Zero)
{
// Since this simulates alt-tab, it restores minimized windows to their previous state
SwitchToThisWindow(process.MainWindowHandle, true);
return true;
}
// Multiple things are happening here
// First, the ProgramFilesX86 variable automatically accounts for 32-bit or 64-bit systems and returns the correct folder
// Secondly, $-strings are the C# shortcut for string.format() (It automatically calls .ToString() on each variable contained in { })
// Thirdly, if the process was able to start, the return value is not null
try { if (Process.Start($"{System.Environment.SpecialFolder.ProgramFilesX86}\\ExternalApp\\ExternalApp.exe") != null) return true; }
catch
{
// Code for handling an exception (probably FileNotFoundException)
// ...
return false;
}
// Code for when the external app was unable to start without producing an exception
// ...
return false;
I hope this provides a much simpler solution.
(General Rule: If a string value is ordinal, i.e. it belongs to something and isn't just a value, then it is better to get it programmatically. You'll save yourself a lot of trouble when changing things. In this case, I'm assuming that the install location can be converted to a global constant, and the .exe name can be found programmatically.)
I know its too late, still my working code is as follows so that someone later can get quick help :)
using System.Runtime.InteropServices;
using System.Diagnostics;
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
private static void ActivateApp(string processName)
{
Process[] p = Process.GetProcessesByName(processName);
if (p.Length > 0)
{
IntPtr handle = FindWindowByCaption(IntPtr.Zero, p[0].ProcessName);
ShowWindow(handle, 9); // SW_RESTORE = 9,
SetForegroundWindow(handle);
}
}
ActivateApp(YOUR_APP_NAME);
Actually, FindWindowByCaption is the key here, this method collects the window handle correctly when app is running silently in the system tray and also when app is minimized.

How to use C# to automate bunch of Print Jobs?

I am developing an automation tool which is reading the file path from an Excel workbook and after launching the application I am firing print job using SendKeys.SendWait() for Ctrl+P and Enter key. Now the Problem is, I am facing synchronization issue for launching the application and handling the print procedure keys. Sometimes Applications are launching little late(like Excel and MsWord files), so at that time I am not able to find till how long I have to wait for a successful launch of the Application. Anybody have any Idea how to check this waiting time till how long I should wait to fire CTRL+P and then after getting PrintDialog ENTER button ?
Any help will be appreciate. Thanks in advance.
I initially read the question as only printing MS type files. If you want to print all kinds of files then I would first leverage Windows 'PrintTo' function.
You can call the commands directly by searching the Registry for PrintTo and you should see commands for PrintTo and also Print. Hit the web for specifics for each application.
The other option that is probably the simplest is to use the PrintTo verb with ShellExecute and let Windows handle the behind the scenes.
System.Diagnostics.Process print = new System.Diagnostics.Process();
print.StartInfo.FileName = #"c:\test\test.pdf";
print.StartInfo.Verb = "PrintTo";
print.StartInfo.CreateNoWindow = True;
print.StartInfo.Arguments = printerName;
print.StartInfo.UseShellExecute = True;
print.Start();
print.WaitForExit();
PrintTo should allow you to specify the printer while the verb "Print" should just send to the default device.
Keep in mind that not all filetypes support these verbs.
In order to determine whether or not the application to automate is ready to accept user input (key strokes) you have to search for the window of the application processing the key strokes you will send. There is quite a bit interop necessary to accomplish the task. Below you will find a small example automating the task of printing an excel document (all error handling details omitted).
I've copied the interop signatures from pinvoke.net.
First, let me describe the necessary steps:
Search for the excel main window using the class name of the excel main window. Use a tool like spy++ to determine the class name.
Bring the excel main window to the foreground.
Send CTRL+C to the main window to open the print dialog.
Wait for the print dialog to appear.
Send ENTER to the print dialog.
Second, let me show you a small code example:
private enum WindowShowStyle : uint
{
Hide = 0,
ShowNormal = 1,
ShowMinimized = 2,
ShowMaximized = 3,
Maximize = 3,
ShowNormalNoActivate = 4,
Show = 5,
Minimize = 6,
ShowMinNoActivate = 7,
ShowNoActivate = 8,
Restore = 9,
ShowDefault = 10,
ForceMinimized = 11
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);
[DllImport("user32.dll")]
private static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("kernel32.dll")]
private static extern uint GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
private static void BringWindowToForeground(IntPtr hWnd)
{
uint foregroundThread, currentThread;
uint pid;
foregroundThread = GetWindowThreadProcessId(GetForegroundWindow(), out pid);
currentThread = GetCurrentThreadId();
if (foregroundThread != currentThread)
{
AttachThreadInput(foregroundThread, currentThread, true);
BringWindowToTop(hWnd);
ShowWindow(hWnd, WindowShowStyle.ShowMaximized);
AttachThreadInput(foregroundThread, currentThread, false);
}
else
{
BringWindowToTop(hWnd);
ShowWindow(hWnd, WindowShowStyle.ShowMaximized);
}
}
private void button1_Click(object sender, EventArgs e)
{
// Find excel window.
IntPtr hWnd;
while (true)
{
hWnd = FindWindow("XLMAIN", null); // XLMAIN is the class name
// of the main excel window.
if (hWnd != IntPtr.Zero)
break;
}
BringWindowToForeground(hWnd);
SendKeys.SendWait("^p"); // Send CTRL+P to main excel window
// Find print dialog.
while (true)
{
hWnd = FindWindow("bosa_sdm_XL9", null); // bosa_sdm_XL9 is the class name
// of the print dialog.
if (hWnd != IntPtr.Zero)
break;
}
BringWindowToForeground(hWnd);
SendKeys.SendWait("~"); // Send ENTER to print dialog.
}
The button_click methods includes the steps to wait for the Excel windows to appear. If the specified window is found the keys are sent.
Hope, this helps.

Categories

Resources