読者です 読者をやめる 読者になる 読者になる

C#でフォームを非アクティブで最前面に表示する

f:id:kidd0320:20170316163006j:plain
今までフォームを最前面に表示するときはフォームのTopMostプロパティをtrueにして対応してきたが、全画面表示されるもの(ゲームやメディアプレイヤー)を起動した時に最前面表示ではなくなっていた。

どうにかどんな時も最前面表示できないかと調べてみたら、どうやらWin32APIのSetWindowPosを使えば出来るようだ。

フォームの最前面表示

[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, uint flags);

private const int HWND_TOPMOST = -1;
private enum SWP : int
{
	NOSIZE = 0x0001,
	NOMOVE = 0x0002,
	NOZORDER = 0x0004,
	NOREDRAW = 0x0008,
	NOACTIVATE = 0x0010,
	FRAMECHANGED = 0x0020,
	SHOWWINDOW = 0x0040,
	HIDEWINDOW = 0x0080,
	NOCOPYBITS = 0x0100,
	NOOWNERZORDER = 0x0200,
	NOSENDCHANGING = 0x400
}

private void SetTopMost(IntPtr hWnd)
{
	SetWindowPos(hWnd, HWND_TOPMOST,0, 0, 0, 0,(uint)(SWP.NOMOVE | SWP.NOSIZE |SWP.NOOWNERZORDER | SWP.FRAMECHANGED |SWP.NOSENDCHANGING | SWP.NOACTIVATE |SWP.SHOWWINDOW));
}


これでフォームのLoadイベント時にSetTopMost関数呼べばフォームが最前面に表示される。

ただ、これだけでは非アクティブ状態にならないためWin32APIのSetWindowLongで設定する必要がある。

フォームの非アクティブ化

[DllImport("user32.dll")]
private static extern UInt32 GetWindowLong(IntPtr hWnd,GWL index);
[DllImport("user32.dll")]
private static extern UInt32 SetWindowLong(IntPtr hWnd,GWL index, UInt32 unValue);

const UInt32 WS_EX_NOACTIVATE = 0x8000000;
private enum GWL : int
{
	WINDPROC = -4,
	HINSTANCE = -6,
	HWNDPARENT = -8,
	ID = -12
	STYLE = -16,
	EXSTYLE = -20,
	USERDATA = -21,
}

private void SetNoActive(IntPtr hWnd)
{
	UInt32 unStyle = GetWindowLong(hWnd, GWL.EXSTYLE);
	unStyle = (unStyle | WS_EX_NOACTIVATE);
	SetWindowLong(hWnd, GWL.EXSTYLE, unStyle);
}

このSetNoActiveをさっきと同じくLoadイベントで呼べば非アクティブ表示になる。

ただ、クリックしてもアクティブにならないがタスクバーで選択するとアクティブになってしまう。まぁそこまでしてアクティブにしたいなら仕方ないよねって諦めよう。

Windows10では最前面表示されない

この2つを組み合わせれば平気だと思っていたらなんとWindows10では最前面表示にならない。Windows8.1では問題なく表示される、何が違うんだろ?

仕方ないので自分のウィンドウが最前面に表示されているかポーリングして、最前面じゃない時はSetTopMostを呼ぶようにしている。何かいい方法はないでしょうか?