私は、別のアプリケーションを他のウィンドウの上に表示する方法を見つけることを任されています (Always On Top)。RetrieveProcesses() 関数を使用して、ウィンドウ タイトルを持つプロセスを取得できます。ユーザーが変更するプロセスを選択すると、アプリケーションは MakeProcessOnTop または MakeProcessNormal を呼び出します。どちらの関数も、メイン アプリケーションのウィンドウを変更します。その子の変更を追加する前は、これは正しく機能していました。
その後、これが子ウィンドウ (Outlook の電子メールなど) では機能しないことがわかったので、子ウィンドウを処理する方法を探し始めました。次のコードの書き方では、子ウィンドウが台無しになります。子コントロールではなく、子ウィンドウのハンドル ポインタを取得するにはどうすればよいですか?
public static class ProcessManagement
{
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
static readonly IntPtr HWND_TOP = new IntPtr(0);
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
public static IEnumerable<Process> RetrieveProcesses()
{
List<Process> returnList = new List<Process>();
Process[] processArray = Process.GetProcesses();
foreach (Process p in processArray)
{
if (!String.IsNullOrEmpty(p.MainWindowTitle))
{
returnList.Add(p);
}
}
return returnList;
}
public static IntPtr GetProcessWindowHandle(int processId)
{
Process p = Process.GetProcessById(processId: processId);
return p.MainWindowHandle;
}
public static List<IntPtr> GetProcessChildWindowHandles(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowsProc childProc = new EnumWindowsProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
// You can modify this to check to see if you want to cancel the operation, then return a null here
return true;
}
public static bool MakeProcessOnTop(IntPtr targetWindowHandle, bool targetChildren = true)
{
bool bReturn = true;
if (!ShowWindow(targetWindowHandle, ShowWindowCommands.Minimize))
{
bReturn = false;
}
if (!ShowWindow(targetWindowHandle, ShowWindowCommands.Restore))
{
bReturn = false;
}
if (!ShowWindow(targetWindowHandle, ShowWindowCommands.ShowNoActivate))
{
bReturn = false;
}
if (!SetWindowPos(targetWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE))
{
bReturn = false;
}
if (targetChildren)
{
List<IntPtr> childProcesses = GetProcessChildWindowHandles(targetWindowHandle);
foreach(IntPtr iPtr in childProcesses)
{
MakeProcessOnTop(iPtr, false);
}
}
return bReturn;
}
public static bool MakeProcessNormal(IntPtr targetWindowHandle, bool targetChildren = true)
{
bool bReturn = true;
if (!ShowWindow(targetWindowHandle, ShowWindowCommands.Minimize))
{
bReturn = false;
}
if (!ShowWindow(targetWindowHandle, ShowWindowCommands.Restore))
{
bReturn = false;
}
if (!ShowWindow(targetWindowHandle, ShowWindowCommands.ShowNoActivate))
{
bReturn = false;
}
if (!SetWindowPos(targetWindowHandle, HWND_NOTOPMOST, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE))
{
bReturn = false;
}
if (targetChildren)
{
List<IntPtr> childProcesses = GetProcessChildWindowHandles(targetWindowHandle);
foreach (IntPtr iPtr in childProcesses)
{
MakeProcessNormal(iPtr, false);
}
}
return bReturn;
}
}