Windows App With No Visible Window

G

Guest

I want to create two executable applications, AppA and AppB. AppA uses an
instance of the Process class to launch AppB, via the Process.Start() method.
AppB has a WndProc but does not display a window.

Later, AppA sends a message to AppB telling it to shut down. AppB includes
an event handler that handles this and closes down gracefully.

I have no trouble doing this if AppB can show a visible window since I can
use Process. CloseMainWindow() in AppA to send the message, and the
Form.Closing event in AppB to receive the message. But has soon as I hide the
window, this doesn’t work.

In Win32 this would be easy, but how do you do it in Dot Net?
 
J

Jeffrey Tan[MSFT]

Hi howard39,

Thanks for your post.

Normally, what you want to do is inter-process communication. However, in
.Net, there is few support for inter-process communication. We have to
resort to Win32 ways to get this done.

For example, we may show a hidden window in AppB, then register a certain
message for inter-process communication with p/invoke RegisterWindowMessage
API. And handling this message in hidden window's WndProc. Then in AppA, we
can invoke RegisterWindowMessage with the same string, and use SendMessage
to send this application wide message to AppB, and AppB will get
notification, then do anything it can.

If you do not want to show a hidden window in AppB, and there is no message
loops in AppB, we have to use some other inter-process communication
technology to do this, such as sockets, named pipe, memory mapped file,
etc.., please refer to the article below:
"Inter-Process Communication in .NET Using Named Pipes, Part 1"
http://www.codeproject.com/csharp/DotNetNamedPipesPart1.asp
"DevGlobalCache ¨C A way to Cache and Share data between processes"
http://www.codeproject.com/dotnet/globalcache.asp

Also, you may use .Net remoting to do cross appdomain communication.

Hope this helps

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
G

Guest

Jerffry, I am having a problem getting the method that you suggested, using a
hidden window, to work. I can send the message (using PostMessage with
HWND_BROADCAST), and I can receive it in my Form.WndProc override in AppB
provided the form's window is not hidden. But when I hide the window, I don't
receive any messages in WndProc.

Did you mean that I have to actually *show* the window in AppB (which I
cannot do in the production case)? Or is there a way to create a message loop
and a WndProc without displaying a Window?

According to the dot net documentation, if I were to call Application.Run()
without any argument, it would begin "running a standard application message
loop on the current thread, without a form." This seems to imply that I can
create a WndProc without showing a window. But then how do I get access to
the WndProc?

--Howard
 
J

Jeffrey Tan[MSFT]

Hi Howard,

Thanks for your feedback.

I am not sure how you show a hidden window in AppB. I have created 2 sample
applications. In AppB, I do this:

private string msgstr="interprocess communication";
private uint msg;
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);

private void Form1_Load(object sender, System.EventArgs e)
{
msg=RegisterWindowMessage(msgstr);
if(msg==0)
{
MessageBox.Show(Marshal.GetLastWin32Error().ToString());
}
}

//hide the window in button click
private void button1_Click(object sender, System.EventArgs e)
{
this.Visible=false;
}

protected override void WndProc(ref Message m)
{
if(m.Msg==msg)
{
MessageBox.Show(msgstr+" from wndproc");
}
base.WndProc (ref m);
}

In AppA, I do this:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern bool PostMessage(int hhwnd, uint msg, IntPtr wparam,
IntPtr lparam);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);

private string msgstr="interprocess communication";
private uint msg;
private const int HWND_BROADCAST=0xffff;
private void button1_Click(object sender, System.EventArgs e)
{
msg=RegisterWindowMessage(msgstr);
if(msg==0)
{
MessageBox.Show(Marshal.GetLastWin32Error().ToString());
}
PostMessage(HWND_BROADCAST, msg, IntPtr.Zero, IntPtr.Zero);
}

In my test, AppB will intercept this posted message in WndProc without any
problem. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
G

Guest

Hi Jeffrey,

My code was almost exactly the same as yours, and when I tried it again
today it worked fine. I must have made a debugging mistake. All is well.

Thanks for the excellent help!
 
G

Guest

Unfortunately I still haven't worked this out.

In the test application that I used earlier, I launched AppB in a visible
state and then hid it by clicking on a button. Then I was able to receive the
shutdown message as desired.

But in production, I want to launch AppB without ever displaying anything.
In order to do this I have to use either Application.Run() or
Application.Run(context), since Application.Run(form) launches with the form
visible.

In Win32, I believe one is able to start an application with a WndProc for
an invisible window. With managed code, tried the following:

static void Main()
{
Application.Run(new TestAppForm.TestApplicationContext());
}

public class TestApplicationContext : ApplicationContext
{
private TestAppForm form1;
public TestApplicationContext()
{
form1 = new TestAppForm();
form1.Closed += new EventHandler (OnFormClosed);
form1.Visible = false;
}

private void OnFormClosed(object sender, EventArgs e)
{
ExitThread();
}
}

However, the WndProc overload for form1 doesn’t receive any messages. If I
change "form1.Visible = false" to "form1.Show()," then AppB launches with a
visible window, I can hide it by pressing the Hide button, the WndProc
overload for form1 now does receive messages, and I can shutdown AppB
successfully. So the question is, using WinForms, can I launch an application
that can receive windows messages?

"Jeffrey Tan[MSFT]" said:
Hi Howard,

Thanks for your feedback.

I am not sure how you show a hidden window in AppB. I have created 2 sample
applications. In AppB, I do this:

private string msgstr="interprocess communication";
private uint msg;
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);

private void Form1_Load(object sender, System.EventArgs e)
{
msg=RegisterWindowMessage(msgstr);
if(msg==0)
{
MessageBox.Show(Marshal.GetLastWin32Error().ToString());
}
}

//hide the window in button click
private void button1_Click(object sender, System.EventArgs e)
{
this.Visible=false;
}

protected override void WndProc(ref Message m)
{
if(m.Msg==msg)
{
MessageBox.Show(msgstr+" from wndproc");
}
base.WndProc (ref m);
}

In AppA, I do this:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern bool PostMessage(int hhwnd, uint msg, IntPtr wparam,
IntPtr lparam);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);

private string msgstr="interprocess communication";
private uint msg;
private const int HWND_BROADCAST=0xffff;
private void button1_Click(object sender, System.EventArgs e)
{
msg=RegisterWindowMessage(msgstr);
if(msg==0)
{
MessageBox.Show(Marshal.GetLastWin32Error().ToString());
}
PostMessage(HWND_BROADCAST, msg, IntPtr.Zero, IntPtr.Zero);
}

In my test, AppB will intercept this posted message in WndProc without any
problem. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
G

Guest

What I meant was, "So the question is, using the Dot Net framework, can I
launch an application that never displays a window but can receive windows
messages?"

Unfortunately I still haven't worked this out.

In the test application that I used earlier, I launched AppB in a visible
state and then hid it by clicking on a button. Then I was able to receive the
shutdown message as desired.

But in production, I want to launch AppB without ever displaying anything.
In order to do this I have to use either Application.Run() or
Application.Run(context), since Application.Run(form) launches with the form
visible.

In Win32, I believe one is able to start an application with a WndProc for
an invisible window. With managed code, tried the following:

static void Main()
{
Application.Run(new TestAppForm.TestApplicationContext());
}

public class TestApplicationContext : ApplicationContext
{
private TestAppForm form1;
public TestApplicationContext()
{
form1 = new TestAppForm();
form1.Closed += new EventHandler (OnFormClosed);
form1.Visible = false;
}

private void OnFormClosed(object sender, EventArgs e)
{
ExitThread();
}
}

However, the WndProc overload for form1 doesn’t receive any messages. If I
change "form1.Visible = false" to "form1.Show()," then AppB launches with a
visible window, I can hide it by pressing the Hide button, the WndProc
overload for form1 now does receive messages, and I can shutdown AppB
successfully. So the question is, using WinForms, can I launch an application
that can receive windows messages?

"Jeffrey Tan[MSFT]" said:
Hi Howard,

Thanks for your feedback.

I am not sure how you show a hidden window in AppB. I have created 2 sample
applications. In AppB, I do this:

private string msgstr="interprocess communication";
private uint msg;
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);

private void Form1_Load(object sender, System.EventArgs e)
{
msg=RegisterWindowMessage(msgstr);
if(msg==0)
{
MessageBox.Show(Marshal.GetLastWin32Error().ToString());
}
}

//hide the window in button click
private void button1_Click(object sender, System.EventArgs e)
{
this.Visible=false;
}

protected override void WndProc(ref Message m)
{
if(m.Msg==msg)
{
MessageBox.Show(msgstr+" from wndproc");
}
base.WndProc (ref m);
}

In AppA, I do this:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern bool PostMessage(int hhwnd, uint msg, IntPtr wparam,
IntPtr lparam);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);

private string msgstr="interprocess communication";
private uint msg;
private const int HWND_BROADCAST=0xffff;
private void button1_Click(object sender, System.EventArgs e)
{
msg=RegisterWindowMessage(msgstr);
if(msg==0)
{
MessageBox.Show(Marshal.GetLastWin32Error().ToString());
}
PostMessage(HWND_BROADCAST, msg, IntPtr.Zero, IntPtr.Zero);
}

In my test, AppB will intercept this posted message in WndProc without any
problem. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
G

Guest

OK, now I have a solution, using both ApplicationContext and IMessageFilter.
There were two tricks:

1. You have to put the Application.AddMessageFilter call *before* the
Application.Run call. It seems the MS documentation doesn’t tell us that.

2. The hidden form has to have a button (or some other kind of control,
presumably). If you delete the button, this thing does not work. This seems
kind of strange.

This approach is quirky not very elegant, Create a Windows Application
project, add a button, and replace the main entry point code with the
following:

[STAThread]
static void Main()
{
Application.AddMessageFilter (new AppBMessageFilter ());
Application.Run(new AppBForm.AppBContext());
}

public class AppBMessageFilter : IMessageFilter
{
private uint _closeMessageNumber;

public AppBMessageFilter ()
{
const string UNIQUE_APP_IDENTIFIER = "AppB";
int pid = Process.GetCurrentProcess().Id;
_closeMessageNumber = RegisterWindowMessage
(UNIQUE_APP_IDENTIFIER + "_" + pid.ToString());
if (_closeMessageNumber == 0)
throw new ApplicationException ("RegisterWindow failed. Error Number = " +
Marshal.GetLastWin32Error().ToString());
}
public bool PreFilterMessage (ref Message m)
{
if (m.Msg == _closeMessageNumber)
{
Application.Exit();
}
return false;
}
}

public class AppBContext : ApplicationContext
{
private AppBForm form1;
public AppBContext()
{
form1 = new AppBForm();
}
}

There must be a simpler and better way.

What I meant was, "So the question is, using the Dot Net framework, can I
launch an application that never displays a window but can receive windows
messages?"

Unfortunately I still haven't worked this out.

In the test application that I used earlier, I launched AppB in a visible
state and then hid it by clicking on a button. Then I was able to receive the
shutdown message as desired.

But in production, I want to launch AppB without ever displaying anything.
In order to do this I have to use either Application.Run() or
Application.Run(context), since Application.Run(form) launches with the form
visible.

In Win32, I believe one is able to start an application with a WndProc for
an invisible window. With managed code, tried the following:

static void Main()
{
Application.Run(new TestAppForm.TestApplicationContext());
}

public class TestApplicationContext : ApplicationContext
{
private TestAppForm form1;
public TestApplicationContext()
{
form1 = new TestAppForm();
form1.Closed += new EventHandler (OnFormClosed);
form1.Visible = false;
}

private void OnFormClosed(object sender, EventArgs e)
{
ExitThread();
}
}

However, the WndProc overload for form1 doesn’t receive any messages. If I
change "form1.Visible = false" to "form1.Show()," then AppB launches with a
visible window, I can hide it by pressing the Hide button, the WndProc
overload for form1 now does receive messages, and I can shutdown AppB
successfully. So the question is, using WinForms, can I launch an application
that can receive windows messages?

"Jeffrey Tan[MSFT]" said:
Hi Howard,

Thanks for your feedback.

I am not sure how you show a hidden window in AppB. I have created 2 sample
applications. In AppB, I do this:

private string msgstr="interprocess communication";
private uint msg;
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);

private void Form1_Load(object sender, System.EventArgs e)
{
msg=RegisterWindowMessage(msgstr);
if(msg==0)
{
MessageBox.Show(Marshal.GetLastWin32Error().ToString());
}
}

//hide the window in button click
private void button1_Click(object sender, System.EventArgs e)
{
this.Visible=false;
}

protected override void WndProc(ref Message m)
{
if(m.Msg==msg)
{
MessageBox.Show(msgstr+" from wndproc");
}
base.WndProc (ref m);
}

In AppA, I do this:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern bool PostMessage(int hhwnd, uint msg, IntPtr wparam,
IntPtr lparam);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);

private string msgstr="interprocess communication";
private uint msg;
private const int HWND_BROADCAST=0xffff;
private void button1_Click(object sender, System.EventArgs e)
{
msg=RegisterWindowMessage(msgstr);
if(msg==0)
{
MessageBox.Show(Marshal.GetLastWin32Error().ToString());
}
PostMessage(HWND_BROADCAST, msg, IntPtr.Zero, IntPtr.Zero);
}

In my test, AppB will intercept this posted message in WndProc without any
problem. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
J

Jeffrey Tan[MSFT]

Hi howard39,

Thanks for your feedback and followup.

Yes, I can reproduce out this problem, and this is why I say "I am not sure
how you show a hidden window in AppB". In .Net winform, it seems that to
display a hidden window for the application is not easy :).
Application.Run will internally re-show the form, so our Visible=false
calling may take no effect.

Your solution of using IMessageFilter should be a correct way. However
there are several notes to this way:
#1, IMessageFilter can only intercept posted message and can not intercept
messages that is sent(using SendMessage), for more information, please
refer to my another link:
http://groups.google.com/group/microsoft.public.dotnet.framework.windowsform
s/browse_thread/thread/2b14ca6f4a12ff78/bbe44f82b980d8a9?lnk=st&q=IMessageFi
lter+%22Jeffrey+Tan%22&rnum=2&hl=zh-CN#bbe44f82b980d8a9

But we need not worry about this, because we can control that the message
is posted.

#2, Yes, I have the same problem as you. When there are no controls on the
form, the IMessageFilter can not get notification. Currently, I still did
not find the root cause for this problem. I will do some more research into
it.

In win32 world, we can write our own message loop in the main thread, then
use PeekMessage API to get the posted message. But in Winform, the message
loop is encapsulated in Application.Run method, we can not break into it,
so I still have not find a perfect workaround for it. If you have concern
on current solution, I suggest you go for other non-UI inter-process
communication ways I have provided in first reply.

I will reply to you once I have any further progress. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
J

Jeffrey Tan[MSFT]

Hi howard39,

After doing some research, I think we can resort to another way. There is
no need for us to show a hidden window in AppB, we can just customize a
message loop, then get the customized message in this loop. Sample like
this:
//Code for AppB
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
[DllImport("user32.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
public static extern bool GetMessageW( ref MSG msg, IntPtr hWnd, int
uMsgFilterMin, int uMsgFilterMax);

private static uint msg;
private const string msgstr="interprocess communication";

[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
public IntPtr hwnd;
public int message;
public IntPtr wParam;
public IntPtr lParam;
public int time;
public int pt_x;
public int pt_y;
}

[STAThread]
static void Main()
{
msg=RegisterWindowMessage(msgstr);
if(msg==0)
{
MessageBox.Show(Marshal.GetLastWin32Error().ToString());
}

MSG message=new MSG();
while(GetMessageW(ref message,IntPtr.Zero, 0, 0))
{
if(message.message==msg)
{
MessageBox.Show(msgstr+" from wndproc");
}
}
}

In AppA, we should get AppB's main thread id, then use PostThreadMessage to
send the customized message to AppB, like this:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int PostThreadMessage(int id, uint msg, IntPtr wparam,
IntPtr lparam);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);

private string msgstr="interprocess communication";
private uint msg;
private const int HWND_BROADCAST=0xffff;
private void button1_Click(object sender, System.EventArgs e)
{
msg=RegisterWindowMessage(msgstr);
if(msg==0)
{
MessageBox.Show(Marshal.GetLastWin32Error().ToString());
}
Process [] processes=Process.GetProcessesByName("AppB");
PostThreadMessage(processes[0].Threads[0].Id, msg, IntPtr.Zero,
IntPtr.Zero);
}
Note: because you use Process.Start to invoke AppB in AppA, it is much
easier to get the thread id :).

Also, in AppB's main method loop, you may also start a new winform
application with Applicaiton.Run method after getting out of the GetMessage
loop, yes, you can do what you want. :)

This code works well on my side. Hope this helps.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top