定位Windows系统日期时间时钟窗口

3
下面的事件方法会弹出Windows系统日期时间时钟窗口。我的标签位于表单的右下角,而系统日期时间时钟窗口出现在表单的左上角。当单击此事件处理程序时,有没有一种方法可以将此日期时间时钟窗口定位到表单的右下角?
    private void LabelDateTime_Click(object sender, System.EventArgs e)
    {
        // bring up the date & time dialog
        System.Diagnostics.Process.Start("timedate.cpl");
    }

查找 SetWindowPos - GuidoG
@namg_engr 以这种方式获取窗口会很困难。你应该通过Rundll32.exe运行CP小程序,否则你的进程将立即关闭。 - Jimi
@DangerZone 这有点不同。启动Win32可执行文件与启动控制面板小程序不同,后者是由另一个进程执行并在执行后关闭的。 - Jimi
1个回答

0
使用System.Diagnostics.Process.Start()启动进程的方式并不有效,因为生成的进程会在创建窗口后立即退出。.cpl小程序不是标准可执行文件,需要操作系统 shell 和启动器才能启动。
然而,可以使用Rundll32.exe创建一个稳定的进程,它将生成一些线程来托管小程序控件和 GDI+ 支持。
尽管如此,要想访问小程序窗口仍然需要进行一些 P/Invoke 操作,因为rundll没有窗口,也没有引用它帮助创建的窗口,所以Process.MainWindowHandle = 0

文档参考 MSDN EnumThreadWndProc() 回调函数, 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 声明:

// 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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接