Starting a process using System.Diagnostics.Process.Start()
in this manner, is not effective, since the generated Process will exit immediately after the window is created. A .cpl applet is not a standard executable and needs then operating system shell and a launcher to start.
However, a stable process can be created using Rundll32.exe
, which will generate some threads to host the applet controls and the GDI+ support.
Reaching the applet Window requires some P/Invoke(ing), though, since rundll
is window-less and it doesn't reference the one it helps create, so the Process.MainWindowHandle = 0
.
Doc Ref. MSDN EnumThreadWndProc() Callback, EnumThreadWindows(), GetWindowRect(), GetWindowText(), SetWindowPos()
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
ProcessStartInfo psInfo = new ProcessStartInfo() {
UseShellExecute = true,
FileName = "rundll32.exe",
Arguments = "shell32.dll, Control_RunDLL timedate.cpl,,0", //<- 0 = First Tab
WindowStyle = ProcessWindowStyle.Normal
};
Process sysClockProcess = new Process() {
SynchronizingObject = this,
EnableRaisingEvents = true,
StartInfo = psInfo
};
sysClockProcess.Start();
sysClockProcess.WaitForInputIdle();
//Insert the Window title. It's case SENSITIVE
//Window Title in HKEY_CURRENT_USER\Software\Classes\Local Settings\MuiCache\[COD]\[LANG]\
string windowTitle = "Date and Time";
int maxLenght = 256;
SetWindowPosFlags flags = SetWindowPosFlags.NoSize |
SetWindowPosFlags.AsyncWindowPos |
SetWindowPosFlags.ShowWindow;
//The first thread is the Main thread. All Dialog windows' handles are attached here.
//The second thread is for GDI+ Hook Window. Ignore it.
EnumThreadWindows((uint)sysClockProcess.Threads[0].Id, (hWnd, lParam) =>
{
StringBuilder lpString = new StringBuilder(maxLenght);
if (GetWindowText(hWnd, lpString, maxLenght) > 0)
if (lpString.ToString() == windowTitle)
{
GetWindowRect(hWnd, out RECT lpRect);
Size size = new Size(lpRect.Right - lpRect.Left, lpRect.Bottom - lpRect.Top);
//Caculate the position of the Clock Windows relative to the ref. Form Size
SetWindowPos(hWnd, (IntPtr)0, ((this.Width + this.Left) - size.Width),
((this.Height + this.Top) - size.Height), 0, 0, flags);
return false;
}
//Window not found: return true to continue the enumeration
return true;
}, ref windowTitle);
sysClockProcess.Exited += (s, ev) => {
Console.WriteLine($"The process has exited. Code: " +
$"{sysClockProcess.ExitCode} Time: {sysClockProcess.ExitTime}");
sysClockProcess.Dispose();
};
Win32 declarations:
// SetWindowPos() flags
[Flags]
public enum SetWindowPosFlags : uint
{
NoSize = 0x0001,
NoActivate = 0x0010,
ShowWindow = 0x0040,
DeferErase = 0x2000,
AsyncWindowPos = 0x4000
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
//Callback for `EnumThreadWindows()`.
public delegate bool EnumThreadWndProc([In] IntPtr hWnd, [In] IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows([In] uint dwThreadId, [In] EnumThreadWndProc lpfn, [In] ref string lParam);
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, [Out] StringBuilder lpString, [In] int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError=true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);