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

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.

Related

c# How to Intercept "Save Print Output As" dialog in Revit

I am trying to intercept the "Save Print Output As" dialog in Revit so I can programmatically insert a path and file name. This dialog pops up every time for every view or sheet that is printed when printed separately or only once if a combined document is printed once.
So far, I have assembled the viewset however when it comes time to print, I do not have access to the window because it opens and closes too soon. I am hoping there is a more "hacky" way to intercept it.
In Revit, the event handler:
a.ControlledApplication.DocumentPrinting += new EventHandler<Autodesk.Revit.DB.Events.DocumentPrintingEventArgs>(AppDocumentPrinting);
only activates after the name/destination has been selected but before the document has printed so it doesnt quite help.
I have found this post which explains how to cycle through the windows which I am able to do but I don't know how to listen for the window to be opened and I can't find anything on it. The SO post also mentions something about p/invoke but I haven't found much documentation on it.
I have looked at subscribing to events but I haven't found anything on being able to subscribe to listen to a window opening.
My printer setup is currently Microsoft print to pdf which doesn't seem to allow the PrintToFile option. Regardless though, I would still like to be able to handle the dialog if it pops up if it is possible.
Any and all help/direction is appreciated.
My code:
EnumWindows(new EnumWindowsProc(EnumTheWindows), IntPtr.Zero); // cant find window because it doesnt exist
printManager.SubmitPrint(); // Window opens for user input here and then closes
// doc.Print(pdfviewSet); // option B: Window opens for user input here and then closes
EnumWindows(new EnumWindowsProc(EnumTheWindows), IntPtr.Zero); // cant find window because it doesnt exist
code adapted from other SO post
// P/Invoke declarations <--- suspect the answer might lie here?
protected delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
protected static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
[DllImport("user32.dll")]
protected static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
// Callback for examining the window
protected static bool EnumTheWindows(IntPtr hWnd, IntPtr lParam)
{
int size = GetWindowTextLength(hWnd);
if (size++ > 0 && IsWindowVisible(hWnd))
{
StringBuilder sb = new StringBuilder(size);
GetWindowText(hWnd, sb, size);
if (sb.ToString().Equals("Save Print Output As", StringComparison.Ordinal))
{
uint procId = 0;
GetWindowThreadProcessId(hWnd, out procId);
Debug.WriteLine($"Found it! ProcID: {procId}");
FlaUI.Core.Application application = FlaUI.Core.Application.Attach(Process.GetCurrentProcess());
string appName = application.Name;
Window mainWindow = application.GetMainWindow(new UIA3Automation());
ConditionFactory cf = new ConditionFactory(new UIA3PropertyLibrary());
mainWindow.FindFirstDescendant(cf.ByProcessId(int.Parse(procId.ToString()))).AsTextBox().Enter("test"); // try to enter info here but returns null because window has closed.
}
}
return true;
}
Dialog I am trying to intercept:
Other info I have looked at:
p/invoke - not too sure how to implement this.
tried this but printed a blank page
How to subscribe to events
Tim Corry's Youtube video on subscribing to events

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.

Send key "MediaPlayPause" to an application without setting focus to it

I am creating a program that will send media key inputs (such as MediaPlayPause, MediaNextTrack, etc) to an application that I have the IntPtr of. Sort of like a virtual remote control.
So after researching I found this, which almost tells me exactly how to solve my problem.
However, there are three problems with the approach mentioned in the link.
I cannot set the application as foreground window as I need my application to be focused.
They use the SendKeys function which requires the target window to be focused, goes against problem 1.
From what I know, SendKeys cannot send keyboard buttons such as the keyboard Play/Pause button.
In the end, I am rather confused on what I have to use (SendInput?, SendMessage?).
Any help would be appreciated.
EDIT
Using the answer I received, I hacked together the sample code below.
Theoretically, it is supposed to find notepad and insert the letter "L" into it.
However nothing shows up on notepad, nor does the application crash. Is there an obvious error I am missing?
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(int ZeroOnly, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, IntPtr lParam);
const int WM_KEYDOWN = 0x100;
//const int WM_KEYUP = 0x101;
const int L_KEY = 0x4C;
private void button1_Click(object sender, EventArgs e)
{
IntPtr ip = FindWindowByCaption(0, "Untitled - Notepad");
SendMessage(ip, WM_KEYDOWN, L_KEY, IntPtr.Zero);
//SendMessage(ip, WM_KEYUP, L_KEY, IntPtr.Zero);
}
Most of these keys are translated to WM_APPCOMMAND* messages... so you can try SendMessage, the other option being SendInput (if the application is DirectInput-based)...
Check out the links in Windows API for common media player functions? - perhaps there is some information you can use...
As for the focus problem - there is no 100% reliable solution (see How do I send key strokes to a window without having to activate it using Windows API?)... best you can achieve with 100% reliability is to focus the application, send the keys, refocus your application... except you would write some sort of device driver (kernel mode)...
To send multimedia keys, including Play/Pause, NextTrack, PrevTrack, etc, you can use keybd_event:
public class Program
{
public const int KEYEVENTF_EXTENTEDKEY = 1;
public const int KEYEVENTF_KEYUP = 0;
public const int VK_MEDIA_NEXT_TRACK = 0xB0;
public const int VK_MEDIA_PLAY_PAUSE = 0xB3;
public const int VK_MEDIA_PREV_TRACK = 0xB1;
[DllImport("user32.dll")]
public static extern void keybd_event(byte virtualKey, byte scanCode, uint flags, IntPtr extraInfo);
public static void Main(string[] args)
{
keybd_event(VK_MEDIA_PLAY_PAUSE, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); // Play/Pause
//keybd_event(VK_MEDIA_PREV_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); // PrevTrack
//keybd_event(VK_MEDIA_NEXT_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); // NextTrack
}
Here is a list to the supported key codes that this windows api can handle:
https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
The SendKeys class is very nice, but it's also limited. The approach above sends the key command directly to Windows OS.

.Net Console Application in System tray

Is there a way I can put a console application in the system tray when minimizing ?
Yes, you can do this.
Create a Windows Forms application and add a NotifyIcon component.
Then use the following methods (found on MSDN) to allocate and display a Console
[DllImport("kernel32.dll")]
public static extern Boolean AllocConsole();
[DllImport("kernel32.dll")]
public static extern Boolean FreeConsole();
[DllImport("kernel32.dll")]
public static extern Boolean AttachConsole(Int32 ProcessId);
When your console is onscreen, capture the minimize button click and use it to hide the console window and update the Notify icon. You can find your window using the following methods (found on MSDN):
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
// Also consider whether you're being lazy or not.
[DllImport("user32.dll", EntryPoint="FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
Be sure to call FreeConsole whenever you're ready to close the app.
using System.Windows.Forms;
using System.Drawing;
static NotifyIcon notifyIcon = new NotifyIcon();
static bool Visible = true;
static void Main(string[] args)
{
notifyIcon.DoubleClick += (s, e) =>
{
Visible = !Visible;
SetConsoleWindowVisibility(Visible);
};
notifyIcon.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
notifyIcon.Visible = true;
notifyIcon.Text = Application.ProductName;
var contextMenu = new ContextMenuStrip();
contextMenu.Items.Add("Exit", null, (s, e) => { Application.Exit(); });
notifyIcon.ContextMenuStrip = contextMenu;
Console.WriteLine("Running!");
// Standard message loop to catch click-events on notify icon
// Code after this method will be running only after Application.Exit()
Application.Run();
notifyIcon.Visible = false;
}
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public static void SetConsoleWindowVisibility(bool visible)
{
IntPtr hWnd = FindWindow(null, Console.Title);
if (hWnd != IntPtr.Zero)
{
if (visible) ShowWindow(hWnd, 1); //1 = SW_SHOWNORMAL
else ShowWindow(hWnd, 0); //0 = SW_HIDE
}
}
A console has no window to minimize by itself. It runs in a command prompt window. You might hook the window messages and hide the window on minimize. In your application it's possible to add a tray icon just the same as you would do it in a windows application. Well, somehow this smells...
But: I'm not sure why you want to do this. A console application is by design different to a windows application. Hence, maybe it's an option to change the app to be a windows form application?
[DllImport("user32.dll")]
internal static extern bool SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);
static Int32 WM_SYSCOMMAND = 0x0112;
static Int32 SC_MINIMIZE = 0x0F020;
static void Main(string[] args)
{
SendMessage(Process.GetCurrentProcess().MainWindowHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
}
I use TrayRunner for exactly this purpose. Essentially, it wraps a console application capturing all output. But when minimized, it minimizes to the system tray instead of the task bar. You can even customize what icon to show when minimized. I use it for things like Tomcat or Apache to free up space on my taskbar without running them as Windows Services.
You can't hide a console application because it does not actually have a window to hide, seeing as how it is running in the console (the console itself is just a window of the console, not the app running in it)

Keep window on top and steal focus in WinForms

I realize that this would be COMPLETELY bad practice in normal situations, but this is just for a test app that needs to be taking input from a bar code scanner (emulating a keyboard). The problem is that I need to start up some scripts while scanning, so I need the window to regain focus directly after I click the script to run it. I've tried using Activate(), BringToFront(), Focus() as well as some Win32 calls like SetForegroundWindow(), Setcapture() and SetActiveWindow()... however the best I can get any of them to do is to make the taskbar item start blinking to tell me that it wants to have focus, but something is stopping it. BTW, I'm running this on XP SP2 and using .NET 2.0.
Is this possible?
Edit: To clarify, I am running the scripts by double-clicking on them in explorer. So I need it to steal focus back from explorer and to the test app.
I struggled with a similar problem for quite a while. After much experimentation and guessing, this is how I solved it:
// Get the window to the front.
this.TopMost = true;
this.TopMost = false;
// 'Steal' the focus.
this.Activate();
Visibility
Make the window a "Top-Most" window. This is the way the Task-Manager can remain on top of other windows. This is a property of a Form and you make the form top-most (floating above other windows) by setting the value to true.
You shouldn't need to override any of the "Active window" behaviour with the top-most setting.
Focus
I asked a similar question previously here on StackOverflow and the answer would solve your problem. You can make the application use a low-level input hook and get notification of the key-codes coming from the scanner. This way, your application always gets these keys even though the application does not have focus.
You may need to enhance the solution to squash the key-codes so that they are not transmitted to the "in-focus" application (e.g. notepad).
Since Windows 2000, there is no official mechanism for an application to grab focus without direct intervention of the user. Peeking at the input streams through the RawInputDevices hook is the only sensible way to go.
A number of articles may help (C# implementations)
RawInput article on CodeProject
MSDN documentation of RawInput
I had a similar problem and found the following to do the trick. Adapted to C# from here
// force window to have focus
uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
uint appThread = GetCurrentThreadId();
const uint SW_SHOW = 5;
if (foreThread != appThread)
{
AttachThreadInput(foreThread, appThread, true);
BringWindowToTop(form.Handle);
ShowWindow(form.Handle, SW_SHOW);
AttachThreadInput(foreThread, appThread, false);
}
else
{
BringWindowToTop(form.Handle);
ShowWindow(form.Handle, SW_SHOW);
}
form.Activate();
EDIT: Here are the necessary PInvoke definitions for C#:
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
/// <summary>The GetForegroundWindow function returns a handle to the foreground window.</summary>
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(HandleRef hWnd);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
The way I approached this problem was to spawn another thread whose only purpose was to ensure the Form is TopMost and has focus at all times. This code will make all other applications unusable while it is running, which is what I needed for my specific applications. You can add in a Sleep in keepFocus or have some other event trigger it.
using System.Threading; // be sure to include the System.Threading namespace
//Delegates for safe multi-threading.
delegate void DelegateGetFocus();
private DelegateGetFocus m_getFocus;
//Constructor.
myForm()
{
m_getFocus = new DelegateGetFocus(this.getFocus); // initialise getFocus
InitializeComponent();
spawnThread(keepFocus); // call spawnThread method
}
//Spawns a new Thread.
private void spawnThread(ThreadStart ts)
{
try
{
Thread newThread = new Thread(ts);
newThread.IsBackground = true;
newThread.Start();
}
catch(Exception e)
{
MessageBox.Show(e.Message, "Exception!", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
//Continuously call getFocus.
private void keepFocus()
{
while(true)
{
getFocus();
}
}
//Keeps Form on top and gives focus.
private void getFocus()
{
//If we need to invoke this call from another thread.
if (this.InvokeRequired)
{
try
{
this.Invoke(m_getFocus, new object[] { });
}
catch (System.ObjectDisposedException e)
{
// Window was destroyed. No problem but terminate application.
Application.Exit();
}
}
//Otherwise, we're safe.
else
{
this.TopMost = true;
this.Activate();
}
}
}
You might try focusing on a specific input, or try the setting .TopMost property to true (and then unsetting it again).
But I suspect your problem is that these methods all just place messages in the windows event queue, and your program has to wait for all existing events to finish processing before it will handle that one and focus the app.

Categories

Resources