Introduction
This article shows how to display a modal dialog/form against a parent processes window.
The download file contains a demo solution that shows how to use all of the code shown here.
The important code is wrapped in a class that makes things easier to maintain!
Background
This does not happen very often, but I recently needed to create a Windows service that needed to display a dialog window to collect some user information during the installation. During the development of the installation project, I started thinking how to make sure that the data collection form is not hidden and that the installation does not continue without the user completing the necessary details.
I remember doing something like that in the past using MFC and WIN32, but I needed this for a .NET based solution. I decided to do some research.
In CodeProject resources, I found a solution that shows how to get the parent process using WIN32, and I also found a way of wrapping the IntPtr
handle into an IWin32Window
!
Here is the function that will retrieve the parent process:
Process GetParentProcess()
{
int iParentPid = 0;
int iCurrentPid = Process.GetCurrentProcess().Id;
IntPtr oHnd = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(oHnd == IntPtr.Zero)
return null;
PROCESSENTRY32 oProcInfo = new PROCESSENTRY32();
oProcInfo.dwSize =
(uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(PROCESSENTRY32));
if(Process32First(oHnd, ref oProcInfo) == false)
return null;
do
{
if(iCurrentPid == oProcInfo.th32ProcessID)
iParentPid = (int)oProcInfo.th32ParentProcessID;
}
while(iParentPid == 0 && Process32Next(oHnd, ref oProcInfo));
if(iParentPid > 0)
return Process.GetProcessById(iParentPid);
else
return null;
}
Here are the PInvoke definitions:
#region WIN32 Definitions
static uint TH32CS_SNAPPROCESS = 2;
[StructLayout(LayoutKind.Sequential)]
public struct PROCESSENTRY32
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szExeFile;
};
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID);
[DllImport("kernel32.dll")]
static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);
[DllImport("kernel32.dll")]
static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);
#endregion
What is also important is the implementation of IWin32Window
:
class WinWrapper : System.Windows.Forms.IWin32Window
{
public WinWrapper(IntPtr oHandle)
{
_oHwnd = oHandle;
}
public IntPtr Handle
{
get { return _oHwnd; }
}
private IntPtr _oHwnd;
}
Using It All
Assume that the parent process is form based and the child process is a console application. The child process needs to display a dialog box and it can do so like this:
Process oParent = GetParentProcess();
WinWrapper oParentHandle = new WinWrapper(oParent.MainWindowHandle);
MessageBox.Show(oParentHandle, "Hello Child Parent proc world!");
Points of Interest
It is interesting to see that the window IntPtr
handle has to be wrapped in IWin32Window
which just returns the same. I have tried the NativeWindow
class and it did not help!
Good luck, hope that someone finds this code useful, and thanks to all the people who have put their code on The Code Project!
History
- 15th March, 2008: Initial post