Reputation: 127603
I have a small launcher program, it loads a Splash screen on it's own thread and displays it. If a set of conditions are met it needs to launch another application and keep the splash screen visible till the other application says it is ok to close the splash screen.
The Launcher will always have a lifetime that starts before Child App and ends after Child App closes.
Here is some snippets of relevant code
The common DLL:
namespace Example.Common
{
public partial class SplashScreen : Form
{
public SplashScreen()
{
InitializeComponent();
}
static SplashScreen splashScreen = null;
static Thread thread = null;
static public void ShowSplashScreen()
{
// Make sure it is only launched once.
if (splashScreen != null)
return;
thread = new Thread(new ThreadStart(SplashScreen.ShowForm));
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
// A static entry point to launch SplashScreen.
static private void ShowForm()
{
splashScreen = new SplashScreen();
Application.Run(splashScreen);
}
// A static method to close the SplashScreen
static public void CloseForm()
{
splashScreen.Close();
}
}
}
The Inital Launcher:
/// <summary>
/// This application is a small launcher to launch the real graphical launcher. It is small and lightweight and should be rarely be updated.
/// It will call the ProgramLauncher, the program launcher will return in it's status code the PID of the instance it launched or -1
/// if no subsequent program was started.
/// </summary>
[STAThread]
static void Main()
{
//Show the Splash screen;
Example.Common.SplashScreen.ShowSplashScreen();
//(Snip)
if (rights == UserRights.None)
{
SplashScreen.CloseForm();
MessageBox.Show("Your user does not have permission to connect to the server.", "Unable to logon", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
//If the user has full desktop access, give it to them and launch a new instance of the launcher.
else if (rights.HasFlag(UserRights.FullDesktopAccess))
{
Process explorer = new Process();
explorer.StartInfo.FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe");
if (explorer.Start() == false)
{
MessageBox.Show("Explorer failed to start.");
}
else
{
//Close the splash screen.
SplashScreen.CloseForm();
//If the user can shadow start a new instance of the launcher inside explorer.
if (rights.HasFlag(UserRights.ShadowNormalUser) || rights.HasFlag(UserRights.ShadowDemoUser))
{
//Start a new copy of the program so people can use it to shadow easily.
var shadowProc = new Process();
shadowProc.StartInfo.FileName = "ProgramLauncher.exe";
shadowProc.StartInfo.UseShellExecute = false;
shadowProc.Start();
}
explorer.WaitForExit();
}
}
else
{
Process programLauncher = new Process();
programLauncher.StartInfo.FileName = "ProgramLauncher.exe";
programLauncher.StartInfo.UseShellExecute = false;
//Launch the graphical launcher.
programLauncher.Start();
programLauncher.WaitForExit();
//Check to see if the graphical launcher launched some other process.
if (programLauncher.ExitCode >= 0)
{
//If there was a pid, don't close the micro launcher till after it closes.
Process runningProcess = Process.GetProcessById(programLauncher.ExitCode);
runningProcess.WaitForExit();
}
}
}
What is the easiest way to let ProgramLauncher close the SplashScreen instance MicroLauncher created?
Upvotes: 1
Views: 356
Reputation: 13986
You need to have SplashScreen
pass it's window handle (HWND) to ProgramLauncher
. Then, ProgramLauncher
can use the SendMessage
winapi function to send a WM_SYSCOMMAND
message to the target window:
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_CLOSE = 0xF060;
SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
In WinForms, you can get a form's native handle with Handle
.
The platform invoke code for SendMessage
is here.
At least I don't see an easier way now, but I think it's easier than any IPC mechanism out there.
Upvotes: 1
Reputation: 3302
There are lots of ways of doing this, with pros and cons to each. Possibly the easiest way is to redirect standard output from your ProgramLauncher process and wire it up to an event in the MicroLauncher application (see here for an example). From your ProgramLauncher program, you write a certain message to standard output. When that message is received by MicroLauncher, you close the window.
Another option is to pass the HWND of your splash screen to ProgramLauncher as a command-line parameter, then ProgramLauncher can use SendMessage(WM_SYSCOMMAND, SC_CLOSE) to close the window (see here for an example).
You can also look into methods of IPC, sending custom Windows messages, or probably a thousand other possibilities, but those two ideas may get you started.
Upvotes: 1
Reputation: 61512
Easiest way I can think of: have the child app create a named mutex, and have the parent app wait until someone's created it, checking every now and then.
Not very elegant and open to abuse (where another app intentionally creates a mutex with the same name), but in practice, I doubt that'll be a problem.
Upvotes: 0