トップ回答者
ウィンドウを最大化したい。

質問
-
こんにちは。
WindowsXP SP3
Visual C# 2010についてお伺いします。C#でボタンを押したときに、特定のウィンドウ("Public")のウィンドウサイズを変更したいです。
private void button1_Click(object sender, EventArgs e){
EnumWindows(new EnumerateWindowsCallback(EnumerateWindows), 0);
}EnumWindows(new EnumWindowsDelegate(delegate(IntPtr hWnd, int lParam) {
string title = "";
Process p = null;
int pid;
StringBuilder stringBuilder = new StringBuilder(0x1024);
if (IsWindowVisible(hWnd) != 0 && GetWindowText(hWnd, stringBuilder, stringBuilder.Capacity) != 0) {
title = stringBuilder.ToString();
GetWindowThreadProcessId(hWnd, out pid);
p = Process.GetProcessById(pid);
}
return 1;
}), 0);
if (title.Contains("Public"))
maxwindow();
}private delegate int EnumWindowsDelegate(IntPtr hWnd, int lParam);
// Win32API の extern 宣言クラス
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();[DllImport("user32.dll")]
private static extern int EnumWindows(EnumWindowsDelegate lpEnumFunc, int lParam);
[DllImport("user32.dll")]
private static extern int IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);private void maxwindow(){
//ここを知りたい。}
おなじことを、VisualBasicの場合、
Imports System
Imports System.Text
Imports System.Diagnostics
Imports System.Threading
Imports System.Runtime.InteropServices
Imports Microsoft.VisualBasic
'vbc Example.vb /r:System.dll
Namespace Example
Class ExampleClass
'Win32API
<DllImport("USER32.DLL")> _
Public Shared Function ShowWindow( _
ByVal handle As IntPtr, _
ByVal style As Integer ) As Integer
End FunctionShared Sub Main()
const SW_MAXMIZE as Integer = 3
Dim instance As Process
instance=Process.Start("notepad.exe") 'アプリケーションを起動する
Dim hWnd As IntPtr
hWnd = instance.MainWindowHandle 'ウィンドウハンドルを得る
Thread.Sleep(5000) '起動の完了を5秒待つ
ShowWindow(hWnd, SW_MAXMIZE) '最大化する
End Sub
End Class
End Namespaceと書くようです。
とすると、メソッドの引数をhWndとして、maxwindow(hWnd);
private void maxwindow(IntPtr hWnd){
ShowWindow(hWnd, SW_MAXMIZE);
}
のようにすればよいでしょうか? と考えました。private void button1_Click(object sender, EventArgs e){
EnumWindows(new EnumerateWindowsCallback(EnumerateWindows), 0);
}EnumWindows(new EnumWindowsDelegate(delegate(IntPtr hWnd, int lParam) {
string title = "";
Process p = null;
int pid;
StringBuilder stringBuilder = new StringBuilder(0x1024);
if (IsWindowVisible(hWnd) != 0 && GetWindowText(hWnd, stringBuilder, stringBuilder.Capacity) != 0) {
title = stringBuilder.ToString();
GetWindowThreadProcessId(hWnd, out pid);
p = Process.GetProcessById(pid);
}
return 1;
}), 0);
if (title.Contains("Public"))
maxwindow(hWnd);
}private void maxwindow(IntPtr hWnd){
ShowWindow(hWnd, SW_MAXMIZE);
}
でこうすると、当然、
ByVal handle As IntPtr, _
ByVal style As Integer ) As Integer
というあたりをC#で書いてやる必要があるのですが、どのように書けばよいでしょうか?
回答
-
個人的には P/Invoke の書き方を学んでほしいとは思います。
ただ、"P/Invoke" というキーワードと対象の API の名前を打ち込むと答えが得られる便利な世の中ではありますね。質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
-
この状態で、showWindowCommandsに値を入れればよさそうです。
で、3とかMaximizeとかを入れてみても値を「ShowWindowCommandsに変換できない」とエラーです。
Enum.Parse(3)とかしてみてもだめです。
もうすこしアドバイスを。引数に列挙体のメンバ与えるには
ShowWindow(IntPtr.Zero, ShowWindowCommands.Maximize);
とかすればいいです。でも列挙体の使い方はVBでも変わらないんですがね・・・ひらぽん http://d.hatena.ne.jp/hilapon/
- 回答としてマーク ルアド 2012年5月21日 3:14
すべての返信
-
個人的には P/Invoke の書き方を学んでほしいとは思います。
ただ、"P/Invoke" というキーワードと対象の API の名前を打ち込むと答えが得られる便利な世の中ではありますね。質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
-
参考にいただいたサイトを参考に、
Public Shared Function ShowWindow( _
ByVal handle As IntPtr, _
ByVal style As Integer ) As Integer
End FunctionをC#に翻訳することを考えました。
End Functionは、関数の終了を意味するから不要。
Public Shared Function ShowWindow( _
ByVal handle As IntPtr, _
ByVal style As Integer ) As Integerこのうち、
As Integer
は、intに相当するから、public int ShowWindow(
);
あとは、
ByVal handle As IntPtr, _
ByVal style As Integer
の部分。IntPtr handle,
int styleとすると、
public int ShowWindow(
IntPtr handle,
int style
);???
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);を使い、
const SW_MAXMIZE as Integer = 3
はつまり、C#だと、
int SW_MAXMIZE = 3;
なので、private void maxwindow(IntPtr hWnd){
int SW_MAXMIZE = 3;
ShowWindow(hWnd, SW_MAXMIZE);
}としてみました。
public void maximizewindow() {
EnumWindows(new EnumWindowsDelegate(delegate(IntPtr hWnd, int lParam) {
string title = "";
Process p = null;
int pid;
StringBuilder stringBuilder = new StringBuilder(0x1024);
if (IsWindowVisible(hWnd) != 0 && GetWindowText(hWnd, stringBuilder, stringBuilder.Capacity) != 0) {
title = stringBuilder.ToString();
GetWindowThreadProcessId(hWnd, out pid);
p = Process.GetProcessById(pid);
if (title.Contains("Public"))
ShowWindow(hWnd, 【ここの値】);
}
return 1;
}), 0);
}private delegate int EnumWindowsDelegate(IntPtr hWnd, int lParam);
[DllImport("user32.dll")]
private static extern int EnumWindows(EnumWindowsDelegate lpEnumFunc, int lParam);
enum ShowWindowCommands : int{
Hide = 0,
Normal = 1,
ShowMinimized = 2,
Maximize = 3, // is this the right value?
ShowMaximized = 3,
ShowNoActivate = 4,
Show = 5,
Minimize = 6,
ShowMinNoActive = 7,
ShowNA = 8,
Restore = 9,
ShowDefault = 10,
ForceMinimize = 11
}[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);としています。
この状態で、showWindowCommandsに値を入れればよさそうです。
で、3とかMaximizeとかを入れてみても値を「ShowWindowCommandsに変換できない」とエラーです。
Enum.Parse(3)とかしてみてもだめです。
もうすこしアドバイスを。 -
この状態で、showWindowCommandsに値を入れればよさそうです。
で、3とかMaximizeとかを入れてみても値を「ShowWindowCommandsに変換できない」とエラーです。
Enum.Parse(3)とかしてみてもだめです。
もうすこしアドバイスを。引数に列挙体のメンバ与えるには
ShowWindow(IntPtr.Zero, ShowWindowCommands.Maximize);
とかすればいいです。でも列挙体の使い方はVBでも変わらないんですがね・・・ひらぽん http://d.hatena.ne.jp/hilapon/
- 回答としてマーク ルアド 2012年5月21日 3:14
-
いつもお世話になります。
先日の質問の続きです。
private void button1_Click(object sender, EventArgs e){
ShowWindow(hWnd, ShowWindowCommands.Maximize);
}(DllImport("user32.dll"))
(return: MarshalAs(UnmanagedType.Bool))
static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
で、ウィンドウの最大化はできるようになりました。ところが、最大化と全画面とは違うことがわかりました。
enumの列挙対には、Maximize(ShowMaximized)はあるのですが、全画面がないです。全画面とは、ウィンドウ枠のない全画面表示をいい、最大化とは画面いっぱいにウィンドウを拡大し、ウィンドウ枠がある状態をいっています。
ターゲットのウィンドウを全画面にするにはどうしたらよいでしょう?
-
> 全画面とは、ウィンドウ枠のない全画面表示をいい、最大化とは画面いっぱいにウィンドウを拡大し、ウィンドウ枠がある状態をいっています。ターゲットのウィンドウを全画面にするにはどうしたらよいでしょう?
正確には 「全画面表示」 ですよね。 その場合、ShowWindow ではなく SetWindowLong を使ってください。
使い方については、以下二つの記事が参考になるのではないでしょうか。
http://support.microsoft.com/kb/111011/ja
http://soudan1.biglobe.ne.jp/qa404193.html
ひらぽん http://d.hatena.ne.jp/hilapon/
-
ひらぽんさん、さっそくありがとうございます。
http://techracho.bpsinc.jp/baba/2009_12_15/768
を参考に、次のようにして、removeFrameを呼び出してみました。
エラーが出て困っています。(DllImport("user32.dll"))
static extern long GetWindowLong(IntPtr hWnd, int nIndex);
(DllImport("user32.dll"))
static extern long SetWindowLong(IntPtr hWnd, int nIndex, long dwNewLong);
(DllImport("user32.dll"))
static extern UInt32 SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);private void removeFrame(IntPtr hWnd){
long WS_THICKFRAME = 0x00040000L;
int GWL_STYLE = -16;
int SWP_FRAMECHANGED = 0x0020;
int SWP_NOSIZE = 0x1;
int SWP_NOMOVE = 0x2;
int SWP_NOZORDER = 0x4;
//GetWindowLongでWindowStyleを取得
long windowstyle = GetWindowLong(hWnd, GWL_STYLE);
//SetWindowLongで、今までのスタイルからWS_THICKFRAME(ウィンドウ枠)を除く
SetWindowLong(hWnd, GWL_STYLE, windowstyle ^ WS_THICKFRAME);
//SetWindowPosで表示を更新
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
}これで、
SetWindowLong(hWnd, GWL_STYLE, windowstyle ^ WS_THICKFRAME);
の行で、
PInvoke 関数 'form1!form1.form1::SetWindowLong' がスタックを不安定にしています。PInvoke シグネチャがアンマネージ ターゲット シグネチャに一致していないことが原因として考えられます。呼び出し規約、および PInvoke シグネチャのパラメーターがターゲットのアンマネージ シグネチャに一致していることを確認してください。
とエラーになります。long windowstyle = GetWindowLong(hWnd, GWL_STYLE);
で取得した値は、
7796540200218198016
なのですが、これはどう読めばよいのでしょう? -
Azuleanさん、ありがとうございます。
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern long SetWindowLong(IntPtr hWnd, int nIndex, long dwNewLong);
[DllImport("user32.dll")]
static extern UInt32 SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);private void removeFrame(IntPtr hWnd){
int WS_THICKFRAME = 0x00040000;
int GWL_STYLE = -16;
int SWP_FRAMECHANGED = 0x0020;
int SWP_NOSIZE = 0x1;
int SWP_NOMOVE = 0x2;
int SWP_NOZORDER = 0x4;
//GetWindowLongでWindowStyleを取得
int windowstyle = GetWindowLong(hWnd, GWL_STYLE);
//SetWindowLongで、今までのスタイルからWS_THICKFRAME(ウィンドウ枠)を除く
SetWindowLong(hWnd, GWL_STYLE, windowstyle ^ WS_THICKFRAME);
//SetWindowPosで表示を更新
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
}としましたが、同様でした。
P/Invokeの参考資料などありましたらご呈示ください。 -
VC++ではデフォルトの呼び出し規約を変更できるんですが
C#でもできましたっけ・・・?
intとlongについて直したとして、まだ出るとなるとメッセージを読んだ感じでは
そうであってもなくても、呼び出し規約が食い違ってる可能性があるのでは?
[DllImport("user32.dll")] ↓ [DllImport("user32.dll", CallingConvention = CallingConvention.Winapi)]
Note:CallingConvention 列挙体 (System.Runtime.InteropServices)
WindowsではWindows APIはまずstdcallです(C/C++サイドではWINAPIあるいはCALLBACKといったマクロを書いたりしますね。)のでそいつが指定されるようにしてやってください。
追記:
と思いきや
DllImportAttribute.CallingConvention フィールドには規定値はWinapiって書かれてますね。
それじゃそういうことじゃないか
・・・と思ったら、まだintとlongが直りきってないってことじゃないですかね?
[DllImport("user32.dll")] static extern long SetWindowLong(IntPtr hWnd, int nIndex, long dwNewLong); ↓ [DllImport("user32.dll")] static extern int SetWindowLong( IntPtr hWnd, int nIndex, int dwNewLong );
理由はAzuleanさんの仰る通りです。
※ただ、私はメインでC++を使っているのでその観点からですが、技量や環境が許すのであれば、ネイティブ関数を大量に使う必要がある部分に関しては、そこだけは別途C++(C++/CLI)で書いてDLL化してしまった方が、こういう妙な集中力が要らなくて楽そうな印象を受けます。
int WS_THICKFRAME = 0x00040000; int GWL_STYLE = -16; int SWP_FRAMECHANGED = 0x0020; int SWP_NOSIZE = 0x1; int SWP_NOMOVE = 0x2; int SWP_NOZORDER = 0x4;
のような指定も自分でしなくていいので、ブラックボックスをブラックボックスのまま扱うのも簡単ですし
これは、「書いたコード」の環境への依存度が、より低いということにもつながります。
SetWindowLongとかも、ネイティブサイドで実はSetWindowLongPtrというものがあり
前者はLONGですが後者ではLONG_PTRという型を用い、これがうまい事使う事で、64bit用と32bit用に自動対応できるようになっています。(うまい事やんないと多少厄介な問題があるようですが)
なので、どっちかっていうと.NET Frameworkでラップされてないものをガシガシ使いたい場合は
それについてはネイティブサイドでコーディングするのが本筋かも・・・ていうことは感じますね。
- 編集済み mr.setup 2012年5月27日 10:35
-
質問者の方に気にしていただきたいこととして、その場しのぎで適当にやる、一カ所だけなおすといったことではこの先が思いやられます。指摘を受けた、助言を受けた際にはほかに同じようなものがないか、見直しをしてください。
// 個人的に、今の C++/CLI は提案に値しないかなと思っています。
// 独自の言語仕様の上、2010 では IntelliSense が使えないのでハードルが高いため。
質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
- 編集済み AzuleanMVP, Moderator 2012年5月27日 11:11
-
mr.setupさん、
[DllImport("user32.dll", CallingConvention = CallingConvention.Winapi)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", CallingConvention = CallingConvention.Winapi)]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", CallingConvention = CallingConvention.Winapi)]
static extern UInt32 SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);private void removeFrame(IntPtr hWnd){
int WS_THICKFRAME = 0x00040000;
int GWL_STYLE = -16;
int SWP_FRAMECHANGED = 0x0020;
int SWP_NOSIZE = 0x1;
int SWP_NOMOVE = 0x2;
int SWP_NOZORDER = 0x4;
//GetWindowLongでWindowStyleを取得
int windowstyle = GetWindowLong(hWnd, GWL_STYLE);//値は374013952
int newwindowstyle = windowstyle ^ WS_THICKFRAME;//値は、360819648
//SetWindowLongで、今までのスタイルからWS_THICKFRAME(ウィンドウ枠)を除く
SetWindowLong(hWnd, GWL_STYLE, newwindowstyle);
//SetWindowPosで表示を更新
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
}として、とりあえずエラーはなくなりました。
long-intは単純に書き換えればよかったのですね。
Azuleanさん、重ねてありがとうございます。しかしながら、まだ全画面表示に至りません。
先にいただいたページを見ると、
WS_OVERLAPPED = 0x00000000,
WS_POPUP = 0x80000000,
WS_CHILD = 0x40000000,
WS_MINIMIZE = 0x20000000, //最小化してウィンドウを表示
WS_VISIBLE = 0x10000000,
WS_DISABLED = 0x08000000, //無効なウィンドウを作成
WS_CLIPSIBLINGS = 0x04000000, //クリップの子ウィンドウ
WS_CLIPCHILDREN = 0x02000000,//ウィンドウを描画するときに子ウィンドウが占有していた領域を除外
WS_MAXIMIZE = 0x01000000, //最大サイズでウィンドウを表示
WS_BORDER = 0x00800000, //ウィンドウに輪郭を表示
WS_DLGFRAME = 0x00400000, //ウィンドウにタイトルを表示しない
WS_VSCROLL = 0x00200000, //ウィンドウに垂直スクロール バーを表示
WS_HSCROLL = 0x00100000, //ウィンドウに水平スクロール バーを表示
WS_SYSMENU = 0x00080000, //ウィンドウにシステム メニュー ボックス、タイトル バーを表示
WS_THICKFRAME = 0x00040000, //太いフレームでウィンドウを表示
WS_GROUP = 0x00020000,
WS_TABSTOP = 0x00010000,
WS_MINIMIZEBOX = 0x00020000,//ウィンドウに最大化ボタンを表示
WS_MAXIMIZEBOX = 0x00010000,//ウィンドウに最小化ボタンを表示WS_CAPTION = WS_BORDER | WS_DLGFRAME,
WS_TILED = WS_OVERLAPPED,
WS_ICONIC = WS_MINIMIZE,
WS_SIZEBOX = WS_THICKFRAME,
WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW,
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU,
WS_CHILDWINDOW = WS_CHILD,という一覧があったのですが、これを解読できずにいます。
全画面表示(タイトルバーなし)にするには、値をどう設定すればよいのでしょう?
ぜんぶなしにして、
int WS_THICKFRAME = 0x00000000;
int WS_THICKFRAME = 0x00400000;
int WS_THICKFRAME = 0x00040000;
などを試してみたのですが、どれを入れてもターゲットのウィンドウを全画面表示できません。値の読み方をご教示いただけないでしょうか。
p.s. P/InvokeやC/C++にも興味があります。 -
初歩的なところからの確認になって申し訳ないのですが、以下確認事項です。
- 2 進数とか、論理和とか、そういった基礎知識を身につけておられますか?
- 対象のウィンドウスタイルの値がどうなっていて、どのスタイルを組み合わせているか調べましたか?
- Window styles を元にどのフラグを落とさないといけないか確認しましたか?
どれか一つでも確認していない、わからないのであればコードを完成させようとする前に学ぶ、調べるべきです。
あるサンプルコードは特定の場合にしか使えないことがあります。あなたが対象にしているウィンドウが今のフラグの操作でうまくいくかはわかりません。このため、あなたにしかわからない、対象のウィンドウの性質をきちんと調査し、対応してください。
あと、WS_THICKFRAME という名前で値を変えることは正直わからなくなるだけなのでやめるべきです。他人にコードを見せたら混乱しますから…。(質問にあたって他人にコードを見せているので、一般的に知られている定数の名前で別の値を割り当てないでください)ちなみに、P/Invoke は C# などの .NET Framework アプリケーションで Win32API を呼ぶことといえるのですでに試していることです。また、C++/CLI と、C/C++ は異なるものです。
いろいろと興味を持つことはよいことですが、目の前のことをうまく解決できないと「二兎を追う者は一兎をも得ず」という言葉の通りになってしまいますよー。質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
-
// 個人的に、今の C++/CLI は提案に値しないかなと思っています。
// 独自の言語仕様の上、2010 では IntelliSense が使えないのでハードルが高いため。
確かに「今のC++/CLI」を2010のそれとして考えるなら、インテリセンスの問題は熟練者であっても、誰でもそう思いそうな事ですねw
どちらかと言うと、この場合ではC#との橋渡しを簡単にする部分に絞って・・・という気分で書きました(括弧でくくったのはそのため)
ただ、それだけの部分ですらも、全くインテリセンスが使えないとなると、十分に把握している人でも少々骨かもしれません。
したがって、仮に現状でC++/CLIを使うなら、VC++2010ではなくVC++2008を使う方が良いと思います。(Visual Studio 11 Betaは、やはりBeta版なので現状は提案いたしませんw)
ただ、独自仕様による学習コストの増加を考えれば、確かにC++/CLIは全く使わず
C++だけ、すなわちネイティブonlyに限定する、という方針で行く方が良いかもしれません。
それならば、VC++2010でインテリセンスは効きますから(それから2010では最新のC++の規格の一部分は使える)
この場合はそれが一番自然で、かつ、学習コストも比較的少なくすむ形かもしれません。
もちろん、ごくわずかで済むのであれば、C#だけでやってしまうのも手かと思います。
などを試してみたのですが、どれを入れてもターゲットのウィンドウを全画面表示できません。
そのままの意味合いのWS_THICKFRAMEをそのまま同じように使いたいの でしたら 0x00040000 にしないといけません。
これはMicrosoftちゃまの決定です。(別の環境でいっつも絶対そうかどうかは私は知りませんが。)
このビットフラグの立場からしてみると
Change me if you can
というよりは
You must follow me
ってとこですかね。
※ちなみに、こういうことについてはCやC++のコードで書くなら
#include <windows.h>
の一行(またはwinuser.h)で解決できますね。
WS_THICKFRAMEはサイズ変更可能の周囲の枠みたいなやつで
WS_CAPTION(0x00C00000)
がタイトルバー
です。
ただ、既に作られているウインドウにSetWindowLong(やSetWindowLongPtr)でスタイル変更して
いっつもいっつも絶対うまくいくかどうかというと、undocumentedの可能性が高いです。
ウインドウスタイルをいじくって色々試したという事では、実際私も、複雑な事をやろうとしたときに明記されてない部分に突っ込んで裏仕様が次々に発覚し、長時間格闘しなければならなかった苦い経験が過去にありますw
なので、とりあえずは目的のウインドウのWS_CAPTIONをいじって支障なく出来るかたしかめてください。
- 編集済み mr.setup 2012年5月27日 14:11
-
Asleanさん、
論理和については理解していると思います。
対象のウィンドウスタイルの値の調査方法はわからないので、値を入れて試しています。
調査方法をご示唆いただけないでしょうか。Window Stylesを参考に、値をいろいろ変更して試しています。
これは調査で確認だと思っています。
コードを完成させる途中で、値を変更しているのですが、ほとんど変化がないので、疑心暗鬼になって質問しました。WS_THICKFRAME
の値を変更したことについてですが、値をいろいろ入れるのには、値を変更するのが簡単だと考えました。
WS_THICKFRAME
はローカル変数なので、変数名を含めて、自由に変更できると考えたためです。
それともこれはローカル変数ではないのでしょうか? -
ルアドさんの
int windowstyle = GetWindowLong(hWnd, GWL_STYLE);//値は374013952
からビットフラグの状態を16進やら2進やらで単純計算してみて、ですが
374013952
ならば
16進で
0x164B0000
2進で
00010110010010110000000000000000
ですか
つまり
WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN
|WS_DLGFRAME|WS_SYSMENU|WS_GROUP|WS_TABSTOPとなっています。
このフラグの範囲内の場合はWS_SYSMENUがタイトルバー上のボタンなどなのとWS_DLGFRAMEあたりが原因なはず。
(とはいえ「目的のウインドウが作成時にどの状態で作られたのか」まではこの情報だけでは分からず
それによって挙動が変わる可能性も確か0ではなかったはずなので、確実な事は言えませんが)
ネイティブC++を使い
WindowsAPI サイドでCreateWindowExと言う関数(厳密にはCreateWindowExW)を使って
WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN
|WS_DLGFRAME|WS_SYSMENU|WS_GROUP|WS_TABSTOPと、Get時の結果とまったく同じフラグで生成してみました。
すると、ウインドウ生成後
GetWindowLong( hw, GWL_STYLE );
をやってみると
10110010010110000000000000000
ではなく
10110110010110000000000000000
になっていました。
つまりWS_BORDERが加わっています。つまり
WS_DLGFRAME→WS_CAPTIONと同じ状態になっていました。
その状態から
SetWindowLong( hw, GWL_STYLE, WS_VISIBLE );
と,WS_VISIBLEたったひとつを指定して呼び出したのち、SWP_FRAMECHANGEDを含むSetWindowPosを呼び出す
という手でも、(SYSMENU自体はなくなりましたが)タイトルバーが存在すると言う意味においては見た目に変化がありませんでした。
ただし、SetWindowLongのGWL_STYLEでスタイルを変化させられるのは間違いない事です。
逆に、ためしにWS_VISIBLEを消してみてください。それはさすがに消える(見えなくなるし移動などもできなくなる)はずです。
※一応断って置きますが、フラグをあらかじめ知る事が出来るならともかく、先に知らない状況や無駄なチェックをせずに「確実に外す」と言う事でしたら^ではなく&~を使うべきではないでしょうか?
SetWindowLong( hWnd, GWL_STYLE, GetWindowLong( hw, GWL_STYLE ) & ~WS_VISIBLE ); SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
おそらくこれは
現在目的としている対象のウインドウのスタイルの場合
そしてそこからタイトルバーを消す(ように見せたい)場合
SetWindowLongでそれ自体を変化させるのではなく
ボタンクリック時に別途自分でウインドウを作るなりなんなりしないといけない、というパターンである可能性が高いです。(別の要件でですが、私も以前に同じことを経験しました。)
多少面倒にはなるかと思いますが、そうじゃないと茨の道かもしれません。
逆に、新たにウインドウを作ってしまえばだいぶ自由がききます。
※全画面表示というのはタイトルバーなしで全画面埋まってればそれでOKですか?それともDirectXのフルスクリーン表示みたいに「なんか切り替わるような感じ」が欲しいっていうようなイメージでしょうか?
(込み入った内容になってくると検証に時間がかかる可能性があり、さらにちょいとこの後また忙しくなるかもしれないので、答えられるという保証はありませんが、その辺の情報もあると、他にご存知の方がいらっしゃった場合答えやすいかもしれません。)
- 編集済み mr.setup 2012年5月28日 2:58
-
mr.setupさんありがとうございます。
とても勉強になりました。対象のウィンドウを制御できるかどうかはわからない、ということですね。
そこで、自前でウィンドウをつくってテストするのが先決。
その前に、値をひとつずつ入れてみるのがよい。
と理解しました。
そこで、private void showWindowStyle(IntPtr hWnd){
int GWL_STYLE = -16;
int SWP_FRAMECHANGED = 0x0020;
int SWP_NOSIZE = 0x1;
int SWP_NOMOVE = 0x2;
int SWP_NOZORDER = 0x4;
int windowstyle = GetWindowLong(hWnd, GWL_STYLE);
string sixteen = windowstyle.ToString("X");//10進数を16進数に変換して確認用に表示。
Array array = Enum.GetValues(typeof(WindowStyles));
string windowstylelist = "";
foreach (WindowStyles ws in array) {
int now = (int)ws;
string nowsixteen = now.ToString("X");//確認用に16進数にする。
if (0 < windowstyle - now && !windowstylelist.Contains(ws.ToString())) {
windowstylelist += ws.ToString() + " | ";
windowstyle -= now;
}
}
}enum WindowStyles : uint {
WS_OVERLAPPED = 0x00000000,
WS_POPUP = 0x80000000,
WS_CHILD = 0x40000000,
WS_MINIMIZE = 0x20000000,
WS_VISIBLE = 0x10000000,
WS_DISABLED = 0x08000000,
WS_CLIPSIBLINGS = 0x04000000,
WS_CLIPCHILDREN = 0x02000000,
WS_MAXIMIZE = 0x01000000,
WS_BORDER = 0x00800000,
WS_DLGFRAME = 0x00400000,
WS_VSCROLL = 0x00200000,
WS_HSCROLL = 0x00100000,
WS_SYSMENU = 0x00080000,
WS_THICKFRAME = 0x00040000,
WS_GROUP = 0x00020000,
WS_TABSTOP = 0x00010000,
WS_MINIMIZEBOX = 0x00020000,
WS_MAXIMIZEBOX = 0x00010000,WS_CAPTION = WS_BORDER | WS_DLGFRAME,
WS_TILED = WS_OVERLAPPED,
WS_ICONIC = WS_MINIMIZE,
WS_SIZEBOX = WS_THICKFRAME,
WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW,WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU,
WS_CHILDWINDOW = WS_CHILD
}
のようにして、GetWindowLongした値になにが含まれているかを表示しようとしています。
ところが、この
Array array = Enum.GetValues(typeof(WindowStyles));
のところで、arrayを作ったときに、期待したのは、enumに列挙した値の大きい順に入れることですが、そうではない順番に入ってしまいます。
ここを値の順番にソートする方法をご教示いただけるとうれしいです。ちなみに、制御したいと思っている対象のウィンドウは、VMwareのウィンドウです。タイトルバーなしで、全画面が埋まっていればオーケーです。VMwareは仕様で、Windowsがメッセージを表示すると、全画面で実行していてもウィンドウモードに戻ってしまうのです。
そこで、ウィンドウモードに戻ったときに(というのを検出するのをどうするかはまた別途ですが)、自動的に全画面に戻したい、と考えています。
じぶんのウィンドウではないので、うまく制御できるかどうかはなんともいえないのですが…。 -
それなりに時間を掛けて調べたこともあるので、出来れば参考になった場合は
せめて参考になった投稿として投票していただきたいなぁ、という気持ちはありますがw
(もちろん私以外の方の投稿でも)
ただ、この調子でやってくと結構時間がかかるかもという印象はあるものの
とりあえず、それらについては答えられそうなので
・C#で、enumに対するforeachはenumに書いた順に処理されます。したがってそういうことなら大きい順に並べればいいです。
追記:↑大ウソです。詳しくは下のレスにて
・WS_OVERLAPPED = 0x00000000 とか WS_CAPTION = WS_BORDER | WS_DLGFRAME とかを含んでいると、なんどもいろんなものが表示される可能性がありますが、それでいいのでしょうか?
・普通に自分で作る場合は、ウインドウスタイルは普通、現在の状態を全部把握しなくてもいいものです。
・普通に自分で作る場合は、むしろ、それほどウインドウスタイルをしっかり意識したい場合では、やはりウインドウ生成の段階からWindows APIサイド(CreateWindowなど)でガリガリ書いて作る方が理にかなっています。
>じぶんのウィンドウではないので、うまく制御できるかどうかはなんともいえないのですが…。
これが問題ですが、共同開発でしょうか?それとも全く別の人が作ったアプリケーションをハックしたいということでしょうか?
後者では、もともとなかなか難しい内容かもしれません。
ChangeDisplaySettingsという選択肢もあるようですが
これをやっといた状況でアプリが不正終了したら・・・とかの時にどうなるか、とかまでは分かりません。
http://q.hatena.ne.jp/1060914329
- 編集済み mr.setup 2012年5月28日 13:33
-
すみません
C#で、enumに対するforeachはenumに書いた順に処理されます。したがってそういうことなら大きい順に並べればいいです。
の部分に関しては、テキストを読んだだけでちゃんと試していなかったのと、その上で完全に読みちがえてたのと、enumは今まで基本的に昇順にばかり書いていたので、勘違いしていました。
大変失礼しました。
実際にはEnum.GetValuesとforeachで、実際の値での昇順に処理されました。
ただし、同じものが含まれるとうまくいかなかったりしました。
単純に、1つのビットに対して1つのフラグだけがまんべんなく存在するようにコードを書いて、順番に関しては気にしない、か、どうしても気になるとかもっと自由な順で表示したいのであれば、foreachでなく自分で順番を制御できるコードにするのが良いと思います。
(それか、何かやれば降順に切り替える事が出来るのかな?
この辺は詳しくないので知ってる方がいたらむしろ教えて欲しいかもですね。)
落ち度が全くないかどうかは分かりませんが
例えばアイデアとしてはこんなの感じで『昇順の逆順』→『つまり降順』てのもありかもしれません。
enum ID_A : int { A0 = 0, A2 = 2, A4 = 4, A3 = 3, A1 = 1 } private void funcA() { var array = Enum.GetValues(typeof(ID_A)); string s = ""; int i = array.GetLength(0); for (; --i >= 0; ) s += array.GetValue(i).ToString() + " "; MessageBox.Show( s ); //A4 A3 A2 A1 A0 と表示 }
失礼しました。初めてなので使い方がよくわかっていませんでした。
なんと、そういうことでしたか。しかし、詳しく知ろうとする姿勢もそうですが、ルアドさんもなかなか誠実な方ですね。
順番にやっていけば必ず優れたプログラマーになれるはずですし、今回の問題に対する結論めいたものも見えてくるはずです。
対象のウィンドウは、まったく別のひとの作ったものです。
上に書いたとおり、VMwareです。なるほど、そういうことであれば、とりあえず私の知識内で現状判断出来ることは上記の範囲内ぐらいです。別のウインドウを作るというのは、流石にそっちにメッセージを飛ばすようにしないといけなくなる可能性がありきわめて難しい可能性が高いので、ChangeDisplaySettingsを使って出来るとしたら、その方が本筋かもしれません。
(それでも、メッセージの捕捉を的確にしないといけない部分があるかどうかなども含め、まだ結構難しいかもしれません)
頑張ってください。応援してます。
※ネイティブ絡みのやり方だったら結構私は強いと思うので、ある程度一般的な、そっちサイドの質問だったらあまり時間を掛けずに、精度の高い回答が出来るかもしれません。
- 編集済み mr.setup 2012年5月28日 14:14
-
mr.setupさんありがとうございます。
別に落ち度とか思っていないです。
あれ、順番が違ってるな。ソートしなきゃダメだな、となにか違っているのだろうか、あるいはソートの方法があるのかなと確認したんです。array.Sortとかありそうな気がしたのですが、ないみたいなので…。
さらに、WS_POPUP = 0x80000000は、intの扱える範囲を超えてしまうみたいなので、次のようにして、GetWindowLongした値を、列挙体に分けるところまでなんとか到達しました。
uint windowstyle = GetWindowLong(hWnd, GWL_STYLE);
Array array = Enum.GetValues(typeof(WindowStyles));
string workinglist = "";
string allwindowstyle = "";
foreach (WindowStyles ws in array) {
uint now = (uint)ws;
string nowsixteen = now.ToString("X");//確認用に16進数にする。
workinglist = now + "\t" + nowsixteen + "\t" + ws.ToString() + "\r\n" + workinglist;
}
string[] lines = workinglist.Split(new string[] { "\r\n" }, StringSplitOptions.None);
foreach(string line in lines){
string[] cell = line.Split(new string[] { "\t" }, StringSplitOptions.None);
if (1 < cell.Length){
uint now = uint.Parse(cell[1], System.Globalization.NumberStyles.HexNumber);
if (0 < windowstyle - now && !allwindowstyle.Contains(cell[2])){
allwindowstyle += cell[2] + " | ";
windowstyle -= now;
}
}
}
MessageBox.Show(allwindowstyle);WS_POPUP | WS_CHILD | WS_MINIMIZE | WS_VISIBLE | WS_DISABLED | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZE | WS_BORDER | WS_DLGFRAME | WS_VSCROLL | WS_HSCROLL | WS_SYSMENU | WS_THICKFRAME | WS_GROUP | WS_TABSTOP | WS_OVERLAPPED |
これが最大化した状態のVMwareの状態です。
そのあと、VMwareをウィンドウ表示にして取得しましたが、WS_POPUP | WS_CHILD | WS_MINIMIZE | WS_VISIBLE | WS_DISABLED | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_MAXIMIZE | WS_BORDER | WS_DLGFRAME | WS_VSCROLL | WS_HSCROLL | WS_SYSMENU | WS_THICKFRAME | WS_GROUP | WS_TABSTOP | WS_OVERLAPPED |
と、ほぼおなじことに…。
ここまで試していたら、Azuleanさんからのコメント…。
- 編集済み ルアド 2012年5月29日 6:52
-
>VMwareと単独で表記される場合会社名を指さず、VMware社の製品全般、あるいはもっとも広く利用されているPC向けソフトウェア群のことを指すことが多い。
と書いてあったので、VMware製品以外の可能性もあるのかーと思ってそれ以上突っ込めませんでしたが
実際には、仮にVMware Playerに限定して話を進める事が出来る
ということであれば、ということで
Azuleanさんの画像から、「内側が別ウインドウである可能性があるのでは?」と思ったので
せっかくなので先ほどインストールしていじってみました。
Spy++とかWinspectorとか使って目的のウインドウを調べ
EnumWindowsで、ウインドウ名"VMware Player"のウインドウハンドルを得て
そのハンドルに対してさらにEnumChildWindowsから
GetWindowTextとGetClassNameを用いて
ウインドウ名 "" , ウインドウクラス名 "#32770"
のウインドウに対し
SetParent関数で第2引数にHWND_DESKTOPを指定して
みました。
結果
右下のが元々おさまってた外側のウインドウで、内側の奴だけ外に持ってくる事は出来ました。
"" , "#32770"
に該当するウインドウが2つあったので、それを区別できる方法がいるのかもしれません。
ただしVMware Playerについては
先ほどインストールしたばかりで使ってないですし、こういうことをして正しい動作をする保証はないと思いますし
当然意図しない使い方(あぶないハックの類)
であるとおもうので、色々な意味で、これ以上はここでは追及しません。
バージョンの違いで同じように出来ない可能性もあるかもしれませんし、これで目的が達せられるのかどうかも不明です。
ただし、ここまで出来れば
あとは最前面にするとかしたり、ChangeDisplaySettings等の併用で、ルアドさんがやりたい事が出来る「可能性は」十分あるのかもしれません。
あくまで、色んな意味であぶないハックの類だと思うので、自由に使える自作アプリ以外の相手に対して、これ以上は私はここでは追及できない可能性が高い、と宣言しておきます。上記のやり方はあくまで「相手がどんなアプリケーションでもある程度の確率で通用する方法」にすぎないですし。
- 編集済み mr.setup 2012年5月29日 10:06
-
左上のアイコンから出るメニューの操作として、WM_SYSCOMMAND があります。
SC_MAXIMIZE を付与して送れば、それでできたりしますか?質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
-
Windowsキー+↑
っていうのが、初期状態で素でやってみて何も反応せず、また左上のアイコンから出るメニューの操作として全画面表示は見当たらず、また
最大化はグレー状態になっています。(これらは何も手を加えない状態なので正常動作の範囲です)
そのためそれらの事やCOMMANDのシミュレートなどについてはノーコメでしたが、何かやるとこの状況は変わるということなのでしょうか?(それともVMware Playerのバージョンとかの違い・・・?)
また、本当はほかアプリ宛てではSendMessageやらPostMessageやらでやりとりしたいところなのはやまやまなのでしょうが
WM_SYSCOMMAND の SC_MAXIMIZEって受け身なほうのやつなんでしょうか?それとも他にフラグがいるのかな?
ともかく、変わらなかったので
単純にShowWindowでSW_MAXIMIZEをやったら
外す前のメインウインドウに試したら内側のウインドウのサイズが置いてかれました。
(グレー状態だったので正常動作の範囲では、この時のサイズの追随は対応してないんでしょう)
上の『あぶない外し方』ののちに、内側にあったウインドウに対して
ShowWindowでSW_MAXIMIZEをやったら
確かに『全画面表示』っぽくなりました。
(最大化がしたいわけではない、のでしょうが、タイトルバーなしのウインドウに対して最大化なので、結果的にそんな感じ、という事です。)
- 編集済み mr.setup 2012年5月29日 14:46
-
みなさまありがとうございます。
理解が追いつかなくなってきたので、すこし整理させてください。
【目的】
VMware Playerのウィンドウを最大化(全画面)にしたい
自作アプリケーションで、ユーザーは自分だけ。【履歴】
(1)ShowWindow(hWnd, ShowWindowCommands.Maximize);で、最大化はできた。しかしウィンドウの枠を外せない。
(2)GetWindowLong(hWnd, GWL_STYLE);
SetWindowLong(hWnd, GWL_STYLE, newwindowstyle);
で、全画面化しようとした。まず、GetWindowLongで、現在のウィンドウの状況を取得した。しかし、取得した状態(枠あり)と、見た目の状態(枠なし)が一致しない。VMwarePlayerは独自でウィンドウを描いている。そこでウィンドウに対して操作して全画面表示することはできないか?
【新展開?】
(3-1)目的のウィンドウは別ウィンドウ? or 子ウィンドウ?
Spy++、Winspectorを使うと調査できるらしい。
(3-1-1)EnumWindowsでVMware Playerのハンドルを取得
(3-1-2)そのハンドルに対してEnumChildWindowsを取得。
(3-1-3)取得したウィンドウに対して、GetWindowTextとGetClassNameでSetParent関数で、第2引数にHWND_DESKTOPを指定。(デスクトップを親ウィンドウにする?)
(3-1-4?)最前面にする、ChangeDisplaySettingなどを併用?????
(3-1-4-2)ShowWindowでSW_MAXIMIZEで全画面になる。(3-2)
適切なウィンドウに対して、
PostMessage()
WM_COMMAND+コマンドID
すればよい。(3-3)
WM_SYSCOMMAND
SC_MAXIMIZE(0xF030)を付与して送る。【現在】
(4-1)PostMessage()
WM_COMMAND+コマンドID
について調査中。[DllImport("user32.dll",CharSet=CharSet.Unicode)]
public static extern IntPtr FindWindow(String sClassName, String sWindowText);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindowEx(IntPtr hWnd, IntPtr hwndChildAfter, String lpszClass, String lpszWindow);
[DllImport("user32.dll",CharSet=CharSet.Unicode)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", CharSet=CharSet.Unicode)]
public static extern bool PostMessage(IntPtr hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam);private void button_Click(object sender, EventArgs e){
IntPtr hWnd;
string windowName = "VMware";
string windowTitle = "WindowsXP";
IntPtr wParam, lParam;
// VMwareのWindowハンドル取得
hWnd = FindWindowEx(hWnd, IntPtr.Zero, windowName, windowTitle);
wParam = new IntPtr(0x41);//これでよいのか?
lParam = IntPtr.Zero;//これでよいのか?
// メッセージ送信。WM_COMMAND+commandIDはなにか?
PostMessage(hWnd, WM_COMMAND+commandID, wParam, lParam);
}
http://www.asp-edita.jp/doda/one/doda5621_40.html(4-2)WM_SYSCOMMANDに関する調査。
C#での列挙体は、次のとおり。
public enum SystemCommands{
SC_SIZE = 0xF000,
SC_MOVE = 0xF010,
SC_MINIMIZE = 0xF020,
// Sent when form maximizes
SC_MAXIMIZE = 0xF030, //今回使うとしたらこれ。
// Sent when form maximizes because of doubcle click on caption
// JTE: Don't use this constant. As per the documentation, you must mask off the last 4 bits of wParam by AND'ing it with 0xFFF0. You can't assume the last 4 bits.
SC_MAXIMIZE2 = 0xF032,
SC_NEXTWINDOW = 0xF040,
SC_PREVWINDOW = 0xF050,
// Closes the form。メッセージでウィンドウを閉じれるんだ。
SC_CLOSE = 0xF060,
SC_VSCROLL = 0xF070,
SC_HSCROLL = 0xF080,
SC_MOUSEMENU = 0xF090,
SC_KEYMENU = 0xF100,
SC_ARRANGE = 0xF110,
// Sent when form is maximized from the taskbar
SC_RESTORE = 0xF120,
// Sent when form maximizes because of doubcle click on caption
SC_RESTORE2 = 0xF122,
SC_TASKLIST = 0xF130,
SC_SCREENSAVE = 0xF140,
SC_HOTKEY = 0xF150,
SC_DEFAULT = 0xF160,
SC_MONITORPOWER = 0xF170,
SC_CONTEXTHELP = 0xF180,
SC_SEPARATOR = 0xF00F
}WM_SYSCOMMANDをC#から使う方法は別途。
方針とかで大きく間違えていないかどうか、コメントいただければ幸いです。