Options for closing UI app from windows service in C# - c#

I'd like to hear any options for closing Win UI application from windows service.
My service runs under System account. UI application runs for every logged-in user, so there can be many app instances. I need to close them all. I know UI process name and can bind to each process instance and kill it. BUT UI application has tray icon which stays visible (ghost icon, disappears when hovered by mouse) after the process is killed. I'd like to close the UI application correctly, via managed or unmanaged code. Any ideas would be greatly appreciated.
ADDITION 1:
The UI application has no main window, but only tray icon (NotifyIcon component).
ADDITION 2:
I can modify source code of the UI application. But it is written in a way that prevents it from recieving CUSTOM window messages, only standard ones.
ADDITION 3:
The UI application does not show up any Form, it just creates ApplicationContext and executes NotifyIcon within the context.
Program.cs
public static class Program
{
[STAThread]
private static void Main()
{
ApplicationContext context = new TrayApplicationContext();
Application.Run(context);
}
}
TIA
Ivan

Call Process.CloseMainWindow instead of Kill.
If you have access to the source code of the WinUI app then in your main form (the one you start in Application.Run(mainFormGoesHere)) subscribe to the close event and make the notifyIcon.Visible = false; just before you exit. It is a known issue with the NotifyIcon and the system tray.
If it is a 3rd party app, then hope that they too have something implemented like this to properly clean up after being asked to close through CloseMainWindow()
Another approach would be to attempt to refresh the system tray from your service as described here
public const int WM_PAINT = 0xF;
[DllImport("USER32.DLL")]
public static extern int SendMessage(IntPtr hwnd, int msg, int character, IntPtr lpsText);
//Send WM_PAINT Message to paint System Tray which will refresh it.
SendMessage(traynotifywnd,WM_PAINT,0,IntPtr.Zero);

Try sending a WM_CLOSE message to the application:
const uint WM_CLOSE = 0x10;
[DllImport("user32.dll",EntryPoint="SendMessage", SetLastError=true)]
public static extern int SendMessage(IntPtr hWnd, uint uMsg, int wParam, int
lParam);
SendMessage(hWnd, WM_CLOSE, 0, 0);
hWnd is the window handle of the process' main window you are trying to shut down. You possibly also need to send the same message to the window handle of the notify icon
UPDATE
As you can't receive custom messages, you might try this:
enumerate all windows
iterate over each window and retrieve the processID
if the processID matches the one you want to shutdown, send WM_CLOSE to it
retrieving process ID for hWnd:
[DllImport("user32")]
static extern int GetWindowThreadProcessId(IntPtr hWnd, out int processId);

Maybe silly, and I'm not sure this can work within multiple sessions.
Define a custom message and send it to broadcast using PostMessage; within your app capture incoming messages and if you receive your custom one, close gracefully.
In service:
public const int HWND_BROADCAST = 0xffff;
[DllImport("user32")]
public static extern bool PostMessage(int hwnd, int msg, int wparam, int lparam);
int WM_MYMSG = WM_USER + 1;
When you need to send message:
PostMessage(HWND_BROADCAST,WM_MYMSG,0,0);
In your app:
int WM_MYMSG = WM_USER + 1;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYMSG) Close();
base.WndProc(ref m);
}
EDITED:
If you want to override WndProc you need to have a form, but that doesn't mean you have to show a form: in your app create a form and run it, while in form code you write:
private void Form1_Shown(object sender, EventArgs e)
{
// Show here tray icon
....
....
// Hide form
this.Hide();
}
int WM_MYMSG = WM_USER + 1;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYMSG) Close();
base.WndProc(ref m);
}

Related

C# FormWindowState.Normal not to steal focus

I have a Windows Form Application that will go to system tray when it is minimized. When I received a message to pop-up my application it will call ShowWindowFromTray() function. I do not want to steal focus on the application that has the focus because it might interrupts on what the user is doing.
private void ShowWindowFromTray()
{
this.Show();
this.WindowState = FormWindowState.Normal;
}
BTW this application has option that the users can check if the application will always on top or TopMost on all other windows.
Instead of Show(), use the ShowWindow() API with SW_SHOWNA:
private const int SW_SHOWNA = 4;
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
private void ShowWindowFromTray()
{
ShowWindow(this.Handle, SW_SHOWNA);
}

Send keystroke to application in c# (sendkeys, postmessage, sendmessage all not working)

I am trying to do one of the following
1. open desired program and press a key programmatically
2. find open window of program and press a key programmatically
(either is fine)
I have tried numerous implementations of SendKeys.SendWait(), PostMessage(), and SendMessage() unsuccessfully. Below are my code snippets
//included all these for attempts
[DllImport("User32.dll")]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll")]
static extern int SetForegroundWindow(IntPtr hWnd);
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
[DllImport("user32.dll")]
static extern byte VkKeyScan(char ch);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
Get handle of window, variables used by sendmessage/postmessage/sendkeys
IntPtr ptrOBS = proc.Handle;//this works properly, proc is instantiated properly
//IntPtr ptrOBS = FindWindow(null, "Open Broadcaster Software v0.472b");
SetForegroundWindow(ptrOBS);
const UInt32 WM_CHAR = 0x0102;
const uint WM_KEYDOWN = 0x100;
const int VK_R = 0x52; // taken from http://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx
const int VK_S = 0x53;
SendMessage attempt:
SendMessage(ptrOBS, WM_KEYDOWN, (IntPtr)VK_R, (IntPtr)1);//tried both WM_CHAR and WM_KEYDOWN
PostMessage attempt:
string message = "rs";
bool sent = PostMessage(ptrOBS, WM_KEYDOWN, VkKeyScan(message[0]), 0);
SendKeys attempt:
SendKeys.SendWait("{r}");
Tried SetFocus on the parent window (application) and child window (button triggered by keypress im trying to send):
static void SetFocus(IntPtr hwndTarget, string childClassName)
{
// hwndTarget is the other app's main window
// ...
IntPtr targetThreadID = GetWindowThreadProcessId(hwndTarget, IntPtr.Zero); //target thread id
IntPtr myThreadID = GetCurrentThread(); // calling thread id, our thread id
try
{
bool lRet = AttachThreadInput(myThreadID, targetThreadID, -1); // attach current thread id to target window
// if it's not already in the foreground...
lRet = BringWindowToTop(hwndTarget);
SetForegroundWindow(hwndTarget);
// if you know the child win class name do something like this (enumerate windows using Win API again)...
IntPtr hwndChild = (IntPtr)1183492;//(IntPtr)EnumAllWindows(hwndTarget, childClassName).FirstOrDefault();
if (hwndChild == IntPtr.Zero)
{
// or use keyboard etc. to focus, i.e. send keys/input...
// SendInput (...);
return;
}
// you can use also the edit control's hwnd or some child window (of target) here
SetFocus(hwndChild); // hwndTarget);
SendKeys.SendWait("{r}");
}
finally
{
SendKeys.SendWait("{r}");
bool lRet = AttachThreadInput(myThreadID, targetThreadID, 0); //detach from foreground window
SendKeys.SendWait("{r}");
}
}
For NSGaga:
string windowName = "Open Broadcaster Software v0.472b";
IntPtr outerPtr = FindWindow(null, windowName);
IntPtr ptrOBS = (IntPtr)527814;//button that im trying to trigger keypress on
SetForegroundWindow(outerPtr);
SetForegroundWindow(ptrOBS);
SetFocus(outerPtr, "OBSWindowClass");//SetFocus(ptrOBS, "Button");
const UInt32 WM_CHAR = 0x0102;
const int VK_R = 0x52; // taken from http://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspx
const int VK_S = 0x53;
//SetForegroundWindow(ptrOBS);
System.Threading.Thread.Sleep(3000);
SendKeys.SendWait("{r}");
SendMessage(outerPtr, WM_KEYDOWN, (IntPtr)VK_R, (IntPtr)1);
PostMessage(outerPtr, WM_KEYDOWN, VkKeyScan('r'), 0);
You cannot reliably use SendMessage and PostMessage for synthesizing keyboard input. They are just not designed for this. These messages (WM_CHAR, WM_KEYDOWN, etc.) are notifications raised by lower-level subsystems when keyboard input has been received, processed, and forwarded on to the appropriate recipient. Sending or posting these messages yourself is like prank-calling someone.
SendKeys (like all other input synthesizer methods, including the SendInput function which was explicitly designed for synthesizing keyboard input and in at least some implementation is what SendKeys actually uses under the hood) works only when the window you wish to receive the keyboard input has the focus. In Windows, only focused (active) windows receive input events.
So SendKeys is probably the way to go if you're ever going to get this to work (either that or P/Invoking SendInput and all of its associated structures), but you do need to respect the caveat that the recipient window must have the focus. Otherwise, it's not going to get anything.
It looks like from your sample code that you're trying to use the SetForegroundWindow function to meet this precondition. Unfortunately, you're passing it an invalid value, and not doing any error checking that might alert you to this mistake. Specifically, this code is wrong:
IntPtr ptrOBS = proc.Handle;//this works properly, proc is instantiated properly
SetForegroundWindow(ptrOBS); // WRONG, ptrOBS is not a window handle
Even if I trust you on ptrOBS being initialized correctly, that makes it a valid handle to a process, which is a very different thing than a valid handle to a window. Aside from the obvious nominal differences, processes can have multiple windows and only a single window can have the focus (i.e., be "in the foreground").
You will need to obtain the handle to a particular window before calling SetForegroundWindow, and given that we know a process can have multiple windows, that can be tricky. You need some reliable way of determining which window you want. Lots of people accomplish this by hard-coding the name of the window as a string, which works great until the target app is recompiled and this implementation detail changes. The only bulletproof way that I can think of is to have the user click the target window and your code to retrieve the handle of the window that is currently under the mouse pointer.
And of course all of this assumes that you've observed the restrictions on the use of SetForegroundWindow, enumerated in the "Remarks" section of the linked SDK documentation.
There is lot of trial and error with that, to get it working
Here is a bit of code I posted before, you might wanna give a try (and there is some more info attached)...
Pinvoke SetFocus to a particular control
Try setting focus first (using the mechanism mentioned) - and then using SendKeys or SendInput.
Here is some detailed code for SendInput...
How to send a string to other application including Microsoft Word

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 Message in C#

I'm creating an application that uses a main project that is connected to several different DLLs. From one DLL window I need to be able to open a window in another but the DLL's can't reference each other.
It was suggested to me to use the sendmessage function in the first DLL and have a listener in the main program that directs that message to the appropriate DLL to open it's window.
However I'm not familiar at all with the sendmessage function and am having a lot of diffculty piecing things together from information I'm finding online.
If someone could please show me the correct way (if there is any) to use the sendmessage function and maybe how a listener captures that message that would be amazing. Here is some of the code I've got so far I'm not sure if I'm heading in the right direction.
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName, String lpWindowName);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
public void button1_Click(object sender, EventArgs e)
{
int WindowToFind = FindWindow(null, "Form1");
}
public static extern int FindWindow(string lpClassName, String lpWindowName);
In order to find the window, you need the class name of the window. Here are some examples:
C#:
const string lpClassName = "Winamp v1.x";
IntPtr hwnd = FindWindow(lpClassName, null);
Example from a program that I made, written in VB:
hParent = FindWindow("TfrmMain", vbNullString)
In order to get the class name of a window, you'll need something called Win Spy
Once you have the handle of the window, you can send messages to it using the SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam) function.
hWnd, here, is the result of the FindWindow function. In the above examples, this will be hwnd and hParent. It tells the SendMessage function which window to send the message to.
The second parameter, wMsg, is a constant that signifies the TYPE of message that you are sending. The message might be a keystroke (e.g. send "the enter key" or "the space bar" to a window), but it might also be a command to close the window (WM_CLOSE), a command to alter the window (hide it, show it, minimize it, alter its title, etc.), a request for information within the window (getting the title, getting text within a text box, etc.), and so on. Some common examples include the following:
Public Const WM_CHAR = &H102
Public Const WM_SETTEXT = &HC
Public Const WM_KEYDOWN = &H100
Public Const WM_KEYUP = &H101
Public Const WM_LBUTTONDOWN = &H201
Public Const WM_LBUTTONUP = &H202
Public Const WM_CLOSE = &H10
Public Const WM_COMMAND = &H111
Public Const WM_CLEAR = &H303
Public Const WM_DESTROY = &H2
Public Const WM_GETTEXT = &HD
Public Const WM_GETTEXTLENGTH = &HE
Public Const WM_LBUTTONDBLCLK = &H203
These can be found with an API viewer (or a simple text editor, such as notepad) by opening (Microsoft Visual Studio Directory)/Common/Tools/WINAPI/winapi32.txt.
The next two parameters are certain details, if they are necessary. In terms of pressing certain keys, they will specify exactly which specific key is to be pressed.
C# example, setting the text of windowHandle with WM_SETTEXT:
x = SendMessage(windowHandle, WM_SETTEXT, new IntPtr(0), m_strURL);
More examples from a program that I made, written in VB, setting a program's icon (ICON_BIG is a constant which can be found in winapi32.txt):
Call SendMessage(hParent, WM_SETICON, ICON_BIG, ByVal hIcon)
Another example from VB, pressing the space key (VK_SPACE is a constant which can be found in winapi32.txt):
Call SendMessage(button%, WM_KEYDOWN, VK_SPACE, 0)
Call SendMessage(button%, WM_KEYUP, VK_SPACE, 0)
VB sending a button click (a left button down, and then up):
Call SendMessage(button%, WM_LBUTTONDOWN, 0, 0&)
Call SendMessage(button%, WM_LBUTTONUP, 0, 0&)
No idea how to set up the listener within a .DLL, but these examples should help in understanding how to send the message.
You are almost there. (note change in the return value of FindWindow declaration). I'd recommend using RegisterWindowMessage in this case so you don't have to worry about the ins and outs of WM_USER.
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, String lpWindowName);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
public void button1_Click(object sender, EventArgs e)
{
// this would likely go in a constructor because you only need to call it
// once per process to get the id - multiple calls in the same instance
// of a windows session return the same value for a given string
uint id = RegisterWindowMessage("MyUniqueMessageIdentifier");
IntPtr WindowToFind = FindWindow(null, "Form1");
Debug.Assert(WindowToFind != IntPtr.Zero);
SendMessage(WindowToFind, id, IntPtr.Zero, IntPtr.Zero);
}
And then in your Form1 class:
class Form1 : Form
{
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
private uint _messageId = RegisterWindowMessage("MyUniqueMessageIdentifier");
protected override void WndProc(ref Message m)
{
if (m.Msg == _messageId)
{
// do stuff
}
base.WndProc(ref m);
}
}
Bear in mind I haven't compiled any of the above so some tweaking may be necessary.
Also bear in mind that other answers warning you away from SendMessage are spot on. It's not the preferred way of inter module communication nowadays and genrally speaking overriding the WndProc and using SendMessage/PostMessage implies a good understanding of how the Win32 message infrastructure works.
But if you want/need to go this route I think the above will get you going in the right direction.
You don't need to send messages.
Add an event to the one form and an event handler to the other. Then you can use a third project which references the other two to attach the event handler to the event. The two DLLs don't need to reference each other for this to work.
It doesn't sound like a good idea to use send message. I think you should try to work around the problem that the DLLs can't reference each other...
Some other options:
Common Assembly
Create another assembly that has some common interfaces that can be implemented by the assemblies.
Reflection
This has all sorts of warnings and drawbacks, but you could use reflection to instantiate / communicate with the forms. This is both slow and runtime dynamic (no static checking of this code at compile time).
Building on Mark Byers's answer.
The 3rd project could be a WCF project, hosted as a Windows Service. If all programs listened to that service, one application could call the service. The service passes the message on to all listening clients and they can perform an action if suitable.
Good WCF videos here - http://msdn.microsoft.com/en-us/netframework/dd728059

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