PostMessage from windows service - c#

My issue is PostMessage windows API is not working properly as it works when running from console application.
Working code:
I have 2 application [1] is console application [2] Windows Forms application.
Requirement is I want to send message to all the running instances of application.
console application code:
class Program
{
#region Dll Imports
public const int HWND_BROADCAST = 0xFFFF;
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
#endregion Dll Imports
public static readonly int WM_ACTIVATEAPP = RegisterWindowMessage("CLOSE");
static void Main(string[] args)
{
//we tried to create a mutex, but there's already one (createdNew = false - another app created it before)
//so there's another instance of this application running
Process currentProcess = Process.GetCurrentProcess();
//get the process that has the same name as the current one but a different ID
foreach (Process process in Process.GetProcessesByName("ClientApp1"))
{
if (process.Id != currentProcess.Id)
{
IntPtr handle = process.MainWindowHandle;
//if the handle is non-zero then the main window is visible (but maybe somewhere in the background, that's the reason the user started a new instance)
//so just bring the window to front
//if (handle != IntPtr.Zero)
//SetForegroundWindow(handle);
//else
//tough luck, can't activate the window, it's not visible and we can't get its handle
//so instead notify the process that it has to show it's window
PostMessage((IntPtr)HWND_BROADCAST, WM_ACTIVATEAPP, IntPtr.Zero, IntPtr.Zero);//this message will be sent to MainForm
break;
}
}
}
}
Windows Forms application code:
public partial class Form1 : Form
{
#region Dll Imports
public const int HWND_BROADCAST = 0xFFFF;
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
#endregion Dll Imports
public static readonly int WM_ACTIVATEAPP = RegisterWindowMessage("CLOSE");
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
//someone (another process) said that we should show the window (WM_ACTIVATEAPP)
if (m.Msg == WM_ACTIVATEAPP)
this.Close();
}
}
Above code is working as expected.
My issues start from here. I want to run the same code from windows service instead of console application. Need immediate guidance.
It seems when I run this code from windows service its not getting hold of the process or service runs in different account so message is not getting delivered.

Most probably you run your service as Local System account in session 0 and it is rather isolated for good reasons. For example you don't have access to other desktops/sessions.
You have to implement a different IPC method, e.g. pipes or memory mapped files.

Related

PostMessage not received when application is hidden from taskbar

I use PostMessage to send message to my application if another instance is trying to open:
(CUSTOMTEXT replaced with my appname)
NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_CUSTOMTEXT_SHOWME, IntPtr.Zero, IntPtr.Zero);
And in WndProc I receive the message:
protected override void WndProc(ref Message m)
{
if (m.Msg == NativeMethods.WM_CUSTOMTEXT_SHOWME)
{
MessageBox.Show("Message received");
}
base.WndProc(ref m);
}
And NativeMethods class:
class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_CUSTOMTEXT_SHOWME = RegisterWindowMessage("WM_CUSTOMTEXT_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
Everything works fine, but when I hide my application from taskbar (this.ShowInTaskbar = false;) my application stops receiving that message.
Why? Is there any workaround for this?
Found alternative solution: I replaced PostMessage (asyncronous) with SendMessage (syncronous). For some reason, SendMessage gets through while PostMessage does not.
In this application it does not matter which one I use, because when the message is being sent, application just exits. If it takes little time to Windows to process this message, no harm done. Point is only that older instance of application receives this message.

Black WPF Window when activating it from another application?

i am developing a wpf app, but i have a problem
when i activate my app from another application it shows totally black(demo on the bottom)
this code of activating my window work great but the window shows black
class WinApi2
{
public const int SW_SHOWNORMAL = 5;
[DllImportAttribute("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImportAttribute("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImportAttribute("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public static void ShowToFront(string windowName)
{
IntPtr firstInstance = FindWindow(null, windowName);
ShowWindow(firstInstance, SW_SHOWNORMAL);
SetForegroundWindow(firstInstance);
}
}
// Activating my window by calling:
ShowToFront(".");
The result :
i found a solution by calling This.Show(); on the Activated Event of my wpf window
public void Window_Activated(object sender, EventArgs e)
{
this.Show();
}
but when doing that the black window still there it shows for a quick time and goes like flash that give a bad locking at the app showing.
i tried the same code to show another app(created with c plus plus) no problem, but the problem is just with wpf apps i think Winforms too ! who can i solver this problem ??

WPF/Console Hybrid application quit

I am trying to create a WPF - hybrid application. This application should have the option to be started from command prompt, where in this case, it would not show any window but will only start some process and then quit.
For example (made up example) in my WPF application I enable users to create body of an email message that will be send to any user (sort of a template).
Afterwards when user wants to send the email, he can do it via cmd like following (meaning GUI will not even start, it will only call email connector and send message to selected recipient and then quit program):
MyProgram.exe -recipient john#doe.com -send=true
My Main() looks like this (created based on the following website:http://www.jankowskimichal.pl/en/2011/12/wpf-hybrid-application-with-parameters/)
public static class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(int dwProcessId);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[STAThreadAttribute]
[System.Diagnostics.DebuggerNonUserCodeAttribute]
public static void Main(string[] args)
{
//App.Main();
if (args.Length == 0)
{
IP_DynamicMailings.App app = new IP_DynamicMailings.App();
app.InitializeComponent();
app.Run();
}
else
{
// Get uppermost window process
IntPtr ptr = GetForegroundWindow();
int u;
GetWindowThreadProcessId(ptr, out u);
Process process = Process.GetProcessById(u);
// Check if it is console?
if (process.ProcessName == "cmd")
{
// Yes – attach to active console
AttachConsole(process.Id);
}
else
{
// No – create new console
AllocConsole();
}
// Program actions ...
foreach (var item in args)
{
Console.WriteLine(item);
}
FreeConsole();
}
}
}
What happens is, that I need to press Enter in order to quit application.
I am also open to rewrite my hybrid logic, shall you have better solution (this was so far the best I could find).

In a WPF app how do you override the Console Close command?

In my WPF application I use the Console Window as a debug/message window - I have options setup to show and hide the window as the user desires and all that works great. The only issue is that when the user closes the Console window it exits the entire program. How can I override this so that when the user click on the X it just hides the Console instead of exiting the application?
this is what I have so far:
const int SW_HIDE = 0;
const int SW_SHOW = 5;
public static bool ConsoleVisible { get; private set; }
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public static void HideConsole()
{
var handle = GetConsoleWindow();
ShowWindow(handle, SW_HIDE);
ConsoleVisible = false;
}
public static void ShowConsole()
{
var handle = GetConsoleWindow();
ShowWindow(handle, SW_SHOW);
ConsoleVisible = true;
}
** For people wanting to utilize this you need: using System.Runtime.InteropServices;
This code was derived from this answer: https://stackoverflow.com/a/3571628/649382
** Edit **
Looking around a bit more it seems like this is not possible. I saw an answer here: https://stackoverflow.com/a/12015131/649382 that talks about removing the exit button which would also be acceptable, except it looks like the code is in C++ and I can't figure out it's C# alternative.
** Edit 2 **
I was able to figure out the conversion to C# and have written it as the answer below.
So as has been discussed there is no way to prevent the Console Window from closing the WPF/Application Window. Prior to Windows Vista there were some workarounds, but since then they have been removed (probably for security reasons). The work around I was able to come up with was to disable the Exit button on the Console Window and place show/hide options into my application. The application start class looks like this:
using System;
using System.Windows;
using System.Runtime.InteropServices;
namespace MyApp
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
ConsoleVisible = true;
DisableConsoleExit();
}
#region Console Window Commands
// Show/Hide
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
const uint SW_HIDE = 0;
const uint SW_SHOWNORMAL = 1;
const uint SW_SHOWNOACTIVATE = 4; // Show without activating
public static bool ConsoleVisible { get; private set; }
public static void HideConsole()
{
IntPtr handle = GetConsoleWindow();
ShowWindow(handle, SW_HIDE);
ConsoleVisible = false;
}
public static void ShowConsole(bool active = true)
{
IntPtr handle = GetConsoleWindow();
if (active) { ShowWindow(handle, SW_SHOWNORMAL); }
else { ShowWindow(handle, SW_SHOWNOACTIVATE); }
ConsoleVisible = true;
}
// Disable Console Exit Button
[DllImport("user32.dll")]
static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
static extern IntPtr DeleteMenu(IntPtr hMenu, uint uPosition, uint uFlags);
const uint SC_CLOSE = 0xF060;
const uint MF_BYCOMMAND = (uint)0x00000000L;
public static void DisableConsoleExit()
{
IntPtr handle = GetConsoleWindow();
IntPtr exitButton = GetSystemMenu(handle, false);
if (exitButton != null) DeleteMenu(exitButton, SC_CLOSE, MF_BYCOMMAND);
}
#endregion
}
}
Hope this helps everyone out who may have a similar issue.
I think you should look into creating the console using AllocConsole and releasing it using FreeConsole. That way you may be able to give the user the ability to close the console window while keeping your WPF application running.

Global hotkey doesn't work even though RegisterHotKey returns true

I need an activeX which handles global hotkey: Shift + B
The code below is supposed to do this when I call this ActiveX from IE.
I see the result of RegisterHotKey is true which means that the hotkey has been registered ok.
But I don't see that any messages come to the ThreadPreprocessMessage method. Why?
namespace Kosmala.Michal.ActiveXTest{
public class ActiveXObject : NativeWindow, IDisposable {
public const int WM_HOTKEY = 0x0312;
private IntPtr pFoundWindow ;
public ActiveXObject(){
System.Windows.MessageBox.Show("constructor<<");
Process[] processes = Process.GetProcessesByName("iexplore");
foreach (Process p in processes){
pFoundWindow = p.MainWindowHandle;
}
System.Windows.MessageBox.Show("pFoundWindow:" + pFoundWindow);
SetupHotKey(pFoundWindow);
ComponentDispatcher.ThreadPreprocessMessage += ComponentDispatcher_ThreadPreprocessMessage;
System.Windows.MessageBox.Show("constructor>>");
}
void ComponentDispatcher_ThreadPreprocessMessage(ref MSG msg, ref bool handled){
System.Windows.MessageBox.Show("inside handler");
if (msg.message == WM_HOTKEY){
System.Windows.MessageBox.Show("inside handler");
}
}
private void SetupHotKey(IntPtr handle){
bool res = RegisterHotKey(handle, GetType().GetHashCode(), 0x0004, 0x42); //Shift + b
System.Windows.MessageBox.Show("SetupHotKey res:"+res);
}
public void Dispose(){
UnregisterHotKey(_host.Handle, GetType().GetHashCode());
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
}
You're trying to install a hotkey in a window that belongs to another process. The documentation for RegisterHotKey() says:
This function cannot associate a hot
key with a window created by another
thread.
It doesn't even work with multiple threads in the same process, so it can't possibly work with threads from other processes.
RegisterHotKey() is only for application hot keys, that is shortcut keys on a specific window. Global hotkeys are registered differently - see Hot Key Controls.
There is also a project on Code Project, System Hot Keys, to do that in .NET.

Categories