tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトルメモ帳のカーソル位置
記事No15514
投稿日: 2012/07/04(Wed) 13:57
投稿者Boze
環境:VB6.08 WindowsXP SP3

VBでメモ帳を開くときに記入者と時刻を記入します。
そのファイルは何度も呼び出され、そのたびに記入者と時刻が追記されていきます。

ここで問題なのですが、ファイルを開くときにカーソル位置を一番最後に移動するには
どうしたらよいでしょうか?

宜しくお願いします

[ツリー表示へ]
タイトルRe: メモ帳のカーソル位置
記事No15515
投稿日: 2012/07/04(Wed) 15:46
投稿者魔界の仮面弁士
> 環境:VB6.08
Office VBA をお使いですか?

> ここで問題なのですが、ファイルを開くときにカーソル位置を一番最後に移動するには
> どうしたらよいでしょうか?
カーソル位置を指定して開けるソフト(秀丸エディタなど)もありますが、
少なくともメモ帳はそうではありません。そのため、起動後に API で
『EM_SETSEL メッセージ』を送るなどして対応する必要があると思います。

[ツリー表示へ]
タイトルRe^2: メモ帳のカーソル位置
記事No15516
投稿日: 2012/07/05(Thu) 10:37
投稿者Boze
魔界の仮面弁士さん、いつもお世話になっております。
今回も宜しくお願いします。

> Office VBA をお使いですか?
僕のバージョンの書き方がおかしかったでしょうか?
VB6.0 EnterpriseEditionを使用しています。

> 起動後に API で『EM_SETSEL メッセージ』を送るなどして対応する必要があると思います。
調べてみます。
わからなければまた質問させてください。

[ツリー表示へ]
タイトルRe^2: メモ帳のカーソル位置
記事No15517
投稿日: 2012/07/05(Thu) 11:00
投稿者Boze
検索したところ、魔界の仮面弁士さんのSendMessageの書き込みを発見しました。
拝見したところ、メモ帳の先頭行に挿入ということで

'メモ帳の先頭行に挿入
        SendMessage(hwndTextBox, EM_SETSEL, IntPtr.Zero, Nothing)
        SendMessage(hwndTextBox, EM_REPLACESEL, IntPtr.Zero, selectedText & vbCrLf)

という内容でした。

移動だけだと EM_SETSEL を使用すると理解しています。
では末尾となると
    SendMessage(hwndTextBox, EM_SETSEL, 末尾, Nothing)
でよろしいでしょうか?
ただ、末尾を指定するパラメータがわからないので教えていただけるとありがたいです。

[ツリー表示へ]
タイトルRe^3: メモ帳のカーソル位置
記事No15518
投稿日: 2012/07/05(Thu) 15:26
投稿者魔界の仮面弁士
> > > 環境:VB6.08
> > Office VBA をお使いですか?
> 僕のバージョンの書き方がおかしかったでしょうか?
「VB6」や「VB6.0」という表現ならばよく目にしますが、
VB6.08 というバージョンは聞いたことが無かったので、
VBA 系かと勘繰ってしまいました。

# Major=6、Minor=0 系統(6.0.9782 や 6.0.98.15 など)なら、
# たまに見かけるのですけれどね。


> 魔界の仮面弁士さんのSendMessageの書き込みを発見しました。
何度か書いているかも知れませんが、本人も覚えていません。
どれのことだろう…。(^^;

> メモ帳の先頭行に挿入ということで
手抜き実装としては、AppActivate ステートメントでメモ帳をアクティブにした上で、
先頭なら Ctrl + Home、末尾なら Ctrl + End を送るという方法があります。

SendKeys "^{END}", True

これならば API 無しで実現できます。確実性はありませんが。


> 移動だけだと EM_SETSEL を使用すると理解しています。
EM_SETSEL メッセージの場合、wParam が開始位置、lParam が選択文字列の長さです。
VB6 の TextBox で言えば、wParam が SelStart プロパティ、
lParam が SelLength プロパティに相当すると考えれば良いかと。


今回は lParam は 0 固定で構いません。
wParam は、SendMessageA ではバイト数、SendMessageW では文字数を
渡すことになりますが、末尾にしたい場合には手を抜いて、固定で
Long 型の 最大値(&H7FFFFFFF)を指定するという手法も使われます。


> SendMessage(hwndTextBox, EM_SETSEL, 末尾, Nothing)
参考にしたものは、VB6 のものではなく、VB.NET 向けのコードのようですね。

[ツリー表示へ]
タイトルRe^4: メモ帳のカーソル位置
記事No15519
投稿日: 2012/07/06(Fri) 15:37
投稿者Boze
ありがとうございます。

教えていただいた方法で操作は出来たのですが、今度は違う問題にぶつかりカーソルが移動できなくなりました。
 それはマルチモニターです。
 
VBでメモ帳を起動するとサブ画面に開くのですが、その場合SendMessageが使えません。
マルチモニタでの開発環境がないのでハンドルが拾えているのかどうかも確認できない状態です。
メモ帳をメインモニタに起動できれば解決できると思うのですが・・・
ちなみに、左モニタが19インチ縦置き、右モニタが17インチ横置きです。
右モニタの左上隅の座標を拾ってそこに指定して開くことはかのうでしょうか?
 現在ネットで検索中ですがもしご存知であれば教えていただけるとありがたいです。

それともう一つなのですが、APIで使用する値、たとえばSM_XXXなどですが、
これを使用する場合あらかじめ
Private Const SM_XXXX = 0000
などと値を設定しなければいけないわけですが、
この値はどこを調べればいいのでしょうか?
どこをみても定数に入れるべき値が載っていなくて調べるのに困っています。

宜しくお願いします

[ツリー表示へ]
タイトルRe^5: メモ帳のカーソル位置
記事No15520
投稿日: 2012/07/06(Fri) 17:29
投稿者魔界の仮面弁士
> それはマルチモニターです。
当方もマルチモニター環境です(Win7 x64)。
検証してみたいので、現象を再現可能な最低限の実験コードを提示できますか?


> サブ画面に開くのですが
一方がメイン画面、他方がサブ画面という構成なのですね。

# すべてのモニタに同じ画面を表示する設定(ミラー)とか、
# 2 画面合わせて 1 つの広いデスクトップとするモードが
# 使われているケースもありますので、念のため確認…。


> その場合SendMessageが使えません。
SendMessage の呼出し後、Err.LastDLLError は何を返してきますか?



> マルチモニタでの開発環境がないのでハンドルが拾えているのかどうかも確認できない状態です。
この場合のハンドルとは、Window Handle (HWND) のことかと思いますが、
操作対象としているメモ帳の HWND は、どのようにして取得してますか?
また、取得した HWND の値がゼロになってはいませんか?


> ちなみに、左モニタが19インチ縦置き、右モニタが17インチ横置きです。
モニタの物理サイズではなく、論理解像度はどのようになっていますか?
(左が長辺1920x短辺1080、右が長辺1280x短辺1024 など)

また、左右どちらをメインモニタにしているのでしょうか。
(右がメインモニタなら、左側はマイナス座標系ということになります)

また、それぞれの画面の(論理上の)配置はどうなっているのでしょうか。

┌───┐       ┌───┬───┐ 
│   │下辺合わせ  │   │   │ 
│   ├───┐   │   │   │ 
│   │   │   │   ├───┘ 
│   │   │   │   │上辺合わせ
└───┴───┘   └───┘     
                      
            ┌───┐     
┌───┐       │   │     
│   ├───┐   │   │     
│   │   │   │   ├───┐ 
│   │   │   │   │   │ 
│   ├───┘   └───┤   │ 
└───┘上下端が一致していない└───┘ 



> 右モニタの左上隅の座標を拾ってそこに指定して開くことはかのうでしょうか?
CreateProces API あるいは Win32_Process.Create メソッドを使えば
「起動時」に座標指定ができますが…メモ帳はこの設定を無視します。

strCommand = "notepad.exe"
strCommand = "cmd.exe"
currentDirectory = Environ("TEMP")

Set w = GetObject("winmgmts:")
Set ps = w.Get("Win32_ProcessStartup").SpawnInstance_
ps.ShowWindow = VBAppWinStyle.vbNormalFocus
ps.X = 50
ps.Y = 50
ps.XSize = 300
ps.YSize = 300
Set proc = w.Get("Win32_Process")

outProcId = 0
ret = proc.Create(strCommand, currentDirectory, ps, outProcId)

Debug.Print ret, outProcId


そのため、起動後に MoveWindow API で移動することになりそうです。


指定する座標については、デスクトップ全体のサイズは GetSystemMetrics API
http://www.gizcollabo.jp/vbtomo/log/archive/vbqanda_6678_0.html
各モニタの情報については、EnumDisplayMonitors API でどうぞ。
http://support.microsoft.com/kb/194578/

> たとえばSM_XXXなどですが、
Const SM_XVIRTUALSCREEN    As Long = 76
Const SM_YVIRTUALSCREEN    As Long = 77
Const SM_CXVIRTUALSCREEN   As Long = 78
Const SM_CYVIRTUALSCREEN   As Long = 79
Const SM_CMONITORS         As Long = 80
Const SM_SAMEDISPLAYFORMAT As Long = 81
などについては、上記VB6サンプルに記載されていますね。

> この値はどこを調べればいいのでしょうか?
Platform SDK 付属の「C 言語用のヘッダーファイル」です。

たとえば上記の定数であれば、"MultiMon.h" というファイルにて

#define SM_XVIRTUALSCREEN       76
#define SM_YVIRTUALSCREEN       77
#define SM_CXVIRTUALSCREEN      78
#define SM_CYVIRTUALSCREEN      79
#define SM_CMONITORS            80
#define SM_SAMEDISPLAYFORMAT    81

などと記述されていますし、前回の回答にあった「EM_SETSEL」なら "WinUser.h" ファイルです。

#define EM_GETSEL               0x00B0
#define EM_SETSEL               0x00B1
#define EM_GETRECT              0x00B2
#define EM_SETRECT              0x00B3
#define EM_SETRECTNP            0x00B4
#define EM_SCROLL               0x00B5
#define EM_LINESCROLL           0x00B6
#define EM_SCROLLCARET          0x00B7
#define EM_GETMODIFY            0x00B8
#define EM_SETMODIFY            0x00B9
#define EM_GETLINECOUNT         0x00BA
#define EM_LINEINDEX            0x00BB
#define EM_SETHANDLE            0x00BC
#define EM_GETHANDLE            0x00BD
#define EM_GETTHUMB             0x00BE
#define EM_LINELENGTH           0x00C1
#define EM_REPLACESEL           0x00C2
#define EM_GETLINE              0x00C4
#define EM_LIMITTEXT            0x00C5
#define EM_CANUNDO              0x00C6
#define EM_UNDO                 0x00C7
#define EM_FMTLINES             0x00C8
#define EM_LINEFROMCHAR         0x00C9
#define EM_SETTABSTOPS          0x00CB
#define EM_SETPASSWORDCHAR      0x00CC
#define EM_EMPTYUNDOBUFFER      0x00CD
#define EM_GETFIRSTVISIBLELINE  0x00CE
#define EM_SETREADONLY          0x00CF
#define EM_SETWORDBREAKPROC     0x00D0
#define EM_GETWORDBREAKPROC     0x00D1
#define EM_GETPASSWORDCHAR      0x00D2
#if(WINVER >= 0x0400)
#define EM_SETMARGINS           0x00D3
#define EM_GETMARGINS           0x00D4
#define EM_SETLIMITTEXT         EM_LIMITTEXT   /* ;win40 Name change */
#define EM_GETLIMITTEXT         0x00D5
#define EM_POSFROMCHAR          0x00D6
#define EM_CHARFROMPOS          0x00D7
#endif /* WINVER >= 0x0400 */

#if(WINVER >= 0x0500)
#define EM_SETIMESTATUS         0x00D8
#define EM_GETIMESTATUS         0x00D9
#endif /* WINVER >= 0x0500 */

[ツリー表示へ]
タイトルRe^6: メモ帳のカーソル位置
記事No15521
投稿日: 2012/07/09(Mon) 09:03
投稿者Boze
> 一方がメイン画面、他方がサブ画面という構成なのですね。
すみません、# 2 画面合わせて 1 つの広いデスクトップとするモードです

> SendMessage の呼出し後、Err.LastDLLError は何を返してきますか?
〜> 操作対象としているメモ帳の HWND は、どのようにして取得してますか?

#1 Windowタイトルからメモ帳のウィンドウハンドルを拾いそこからChildWindowのWindowHandleを  拾っています。
#2 SendMessageで末尾までカーソルを移動しています。

1画面のときは期待どおりの動きをし、2画面のときは左画面の左上に起動しカーソルはトップにあるままで末尾には移動していません。

> > ちなみに、左モニタが19インチ縦置き、右モニタが17インチ横置きです。
> モニタの物理サイズではなく、論理解像度はどのようになっていますか?
> (左が長辺1920x短辺1080、右が長辺1280x短辺1024 など)
>
> また、左右どちらをメインモニタにしているのでしょうか。
> (右がメインモニタなら、左側はマイナス座標系ということになります)
>
> また、それぞれの画面の(論理上の)配置はどうなっているのでしょうか。
下辺そろえです

>そのため、起動後に MoveWindow API で移動することになりそうです。
GetSystemMetrics APIの定数値を調べる段階でとまっていたので教えていただいた値を使用してうまくできそうです。

作業結果はまた報告させて頂きます。
有難うございました。

[ツリー表示へ]
タイトルRe^7: メモ帳のカーソル位置
記事No15522
投稿日: 2012/07/09(Mon) 12:09
投稿者魔界の仮面弁士
>>>> EM_SETSEL メッセージの場合、wParam が開始位置、lParam が選択文字列の長さです。
ごめんなさい! 間違えてました。lParam は終了位置です。

たとえば 5文字目から3文字選択して8文字目までであれば、
wParam = 5、lParam = 3 ではなく、wParam = 5、lParam = 8 とします。

なので通常は wParam≦lParam となるはずです。
(OS によっては、sParam>lParam だと「後ろから前への選択」になることもあるようですが)


> すみません、# 2 画面合わせて 1 つの広いデスクトップとするモードです
ウィンドウを最大化すると、片方の画面内だけで最大化されるモード
(タスクバーはメイン画面のみに表示されている状態)ではなく、
ウィンドウを最大化すると、両画面にまたがって最大化されるモード
(タスクバーは2つの画面にまたがって表示されている状態)ということですね。

ミラーモードや、広いデスクトップとするモードの場合、アプリケーション側にとっては
マルチモニタとしては認識されていません。ひとつのデスクトップとして扱われます。

アプリケーション側でマルチモニタとして処理されるのは、上記でいう所の
「ウィンドウを最大化すると、片方の画面内だけで最大化されるモード」の時だけです。


>> 操作対象としているメモ帳の HWND は、どのようにして取得してますか?
> #1 Windowタイトルからメモ帳のウィンドウハンドルを拾いそこからChildWindowのWindowHandleを  拾っています。

FindWindowEx APIのことでしょうか。当方でテストしてみたところ、
メモ帳の位置によって動作が変わってしまう事はありませんでした。

Option Explicit

Private Declare Function GetDesktopWindow Lib "user32" () As OLE_HANDLE

Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExW" _
( _
    ByVal hwndParent As OLE_HANDLE, _
    ByVal hwndChildAfter As OLE_HANDLE, _
    ByRef lpszClass As Any, _
    ByRef lpszWindow As Any _
) As OLE_HANDLE

Private Const EM_SETSEL As Long = &HB1&

Private Declare Function SendMessage Lib "user32" Alias "SendMessageW" _
( _
    ByVal hWnd As OLE_HANDLE, _
    ByVal msg As Long, _
    ByVal wParam As Long, _
    ByVal lParam As Long _
) As Long


Private Const WM_VSCROLL As Long = &H115&
Private Const SB_BOTTOM As Long = &H7&

Private Sub Form_Load()
    Text1.Text = "無題 - メモ帳"
    Label1.Caption = "0"
    Label2.Caption = "0"
    Command1.Caption = "テスト"
    Dim dbl As Double
    dbl = Shell("notepad", vbNormalNoFocus)
    Debug.Print dbl
End Sub

Private Sub Command1_Click()
    Dim hwndDesktop As OLE_HANDLE
'    hwndDesktop = GetDesktopWindow()

    Dim className() As Byte
    className = "Notepad" & vbNullChar

    Dim windowTitle() As Byte
    windowTitle = Text1.Text & vbNullChar

    Dim hWndNotepad As OLE_HANDLE
    'hWndNotepad = FindWindowEx(hwndDesktop, 0&, className(0), ByVal 0&)
    hWndNotepad = FindWindowEx(hwndDesktop, 0&, ByVal 0&, windowTitle(0))
    Label1.Caption = Hex(hWndNotepad)
    Label2.Caption = "0"
    If hWndNotepad = 0& Then
        MsgBox "該当する親ウィンドウが見つかりませんでした。" & vbCrLf _
            & "LastDllError=" & CStr(Err.LastDllError), vbInformation
        Exit Sub
    End If
    Dim hWndEditBox As OLE_HANDLE
    className = "Edit" & vbNullChar
    hWndEditBox = FindWindowEx(hWndNotepad, 0&, className(0), ByVal 0&)
    Label2.Caption = Hex(hWndEditBox)
    If hWndEditBox = 0& Then
        MsgBox "子ウィンドウが見つかりませんでした。" & vbCrLf _
            & "LastDllError=" & CStr(Err.LastDllError), vbInformation
        Exit Sub
    End If

    Dim result As Long
    Dim nStart As Long, nEnd As Long
    nStart = &H7FFFFFFF
    nEnd = &H7FFFFFFF
    result = SendMessage(hWndEditBox, EM_SETSEL, nStart, nEnd)

    'おまけ:スクロールバー制御
    result = SendMessage(hWndEditBox, WM_VSCROLL, SB_BOTTOM, ByVal CLng(0))
End Sub

[ツリー表示へ]
タイトルRe^8: メモ帳のカーソル位置
記事No15523
投稿日: 2012/07/09(Mon) 13:13
投稿者Boze
> ごめんなさい! 間違えてました。lParam は終了位置です。

気付きませんでした。でも正しい記述をしていたので大丈夫でしたよ。

> > すみません、# 2 画面合わせて 1 つの広いデスクトップとするモードです
> ウィンドウを最大化すると、片方の画面内だけで最大化されるモード
> (タスクバーはメイン画面のみに表示されている状態)ではなく、
> ウィンドウを最大化すると、両画面にまたがって最大化されるモード
> (タスクバーは2つの画面にまたがって表示されている状態)ということですね。

画面に対する知識がまるでないため#2と返答していましたがどうも違うのでしょうか?
2画面合わせて1つの広いモニタ(windowを行き来できる、またがることができる)と思っていましたが、最大化すると片方だけですしタスクバーは右モニタだけです。
ということは初めの通り右がメインで左がサブでいいのでしょうか?

> FindWindowEx APIのことでしょうか。

そのとおりです。
やはりカーソルは動きません。

ところで、右モニタの左上の座標って具体的にはどうすればいいのでしょうか?
SM_CXVIRTUALSCREENの説明書きは左隅の座標ということでこれは0ではないのでしょうか?
SM_CYVIRTUALSCREENはモニタが下辺ぞろえのため0ではない。つまり右モニタの上辺の座標を何ら方法で調べなければならない。
 当方の会社のモニタ構成で、右画面が100%17インチか?というと実ははっきりしません。
なのでまず右モニタのサイズを取得しないといけないということになります。
その後SM_XVIRTYAUSCREENでサイズを取得し計算するという方法になるのかと思いますが間違っていますでしょうか?

[ツリー表示へ]
タイトルRe^9: メモ帳のカーソル位置
記事No15524
投稿日: 2012/07/09(Mon) 14:59
投稿者Boze
前の投稿が修正できないのでこっちに書きます。

やはりWindowHandleが取得できていません。
そのためMoveWindowも当然ながら反応しません。
MoveWindowで(0,0)を指定してみましたがNotePad起動後なにも反応しません。

そしてさらに問題です。
実はメインが右の構成と左の構成が存在するようです。
つまりはメイン画面が
左のときはMoveWindowで(0,0)を指定
右のときはメイン画面の左上座標を取得
としなければいけないようです。
メイン画面が左か右かを取得することがまず必要になりました。

ちなみに、左がメイン画面のときNotepadはモニタのつなぎ目の中途半端な位置に開きます。

右がメイン画面のときの操作の手順としては
SM_CXSCREENでプライマリモニタのサイズを取得しSM_CXVIRTUALSCREENでフルサイズを取得する。
その差分が左からの位置に相当すると考えたのですがどうやら違うようです。
画面から消えて行きました。

いったい何から手をつけたらいいのでしょうか・・・
何かいい案がありましたら教えてください

[ツリー表示へ]
タイトルRe^10: メモ帳のカーソル位置
記事No15526
投稿日: 2012/07/09(Mon) 17:31
投稿者魔界の仮面弁士
> メイン画面が左か右かを取得することがまず必要になりました。

EnumDisplayMonitors API なり GetMonitorInformation API なりで
各画面の MONITORINFO 構造体を取得してみてください。
そうすれば、その dwFlags フィールドで判定できます。このフィールドに
MONITORINFOF_PRIMARY が入っていれば、それがメイン画面です。

[ツリー表示へ]
タイトルRe^11: メモ帳のカーソル位置
記事No15528
投稿日: 2012/07/09(Mon) 17:40
投稿者Boze
プライマリモニタはどの位置にあっても左上の座標は0,0になると書いてありましたので
恐らくWindowハンドルが取得できていない、もしくはRemoveWindowがきいていない
ということが推測されます。
そこを少し掘り下げてみようかと思います。

[ツリー表示へ]
タイトルRe^12: メモ帳のカーソル位置
記事No15529
投稿日: 2012/07/09(Mon) 18:51
投稿者魔界の仮面弁士
> プライマリモニタはどの位置にあっても左上の座標は0,0になると書いてありましたので
ですね。


> 恐らくWindowハンドルが取得できていない、
HWND = 0 ならば、ハンドルが取得できていないということになりますね。


> もしくはRemoveWindowがきいていないということが推測されます。
RemoveWindow → MoveWindow のことかと思いますが、この場合、
戻り値が 0 なら成功、0 以外なら移動失敗です。0 以外が返された場合は、
VB6 の Err.LastDllError プロパティから、詳細なエラーコードを得られます。

[ツリー表示へ]
タイトルRe^9: メモ帳のカーソル位置
記事No15525
投稿日: 2012/07/09(Mon) 17:04
投稿者魔界の仮面弁士
>> FindWindowEx APIのことでしょうか。
> そのとおりです。
> やはりカーソルは動きません。
No15521 のサンプルコードにおいて、メモ帳をドラッグ移動して、
右画面に置いてからボタンを押したときと、
左画面に置いてからボタンを押したときとで
結果が異なるかどうかを確認しておいてください。

画面位置によって結果が異なるのであれば、マルチモニタ対応も意味がありますが、
画面位置と無関係なのであれば、モニタ解像度を得ても改善できないように思います。



> ところで、右モニタの左上の座標って
それぞれのモニタを列挙して画面領域を調べてみてください。たとえばトリプルモニタ構成で、
 ┏━━━┯━━━┯━━━┓-880
 ┃***│   │***┃    A: 800x 600 [横置き]: (0, 0)-(800, 600)
 ┃***│   │***┃    B:1680x1050 [横置き]: (800, 400)-(2480, 1450)
 ┃***│   │***┃    C: 800x1280 [縦置き]: (800, -880)-(1600, 400)
 ┠───┤ C │***┃0
 ┃   │   │***┃   ※Aをプライマリモニタとする
 ┃ A ├───┴───┨400
 ┠───┤       ┃600
 ┃***│  B    ┃
 ┃***│       ┃
 ┗━━━┷━━━━━━━┛1450
 0      800     1600    2480
のような座標配置になっていた場合、「右モニタ」としたいのは B と C のどちらでしょうか。

B の左上座標を得た場合、その座標は (800, 400) となりますし、
C の左上座標という事であれば、(800, -880) ということになりますね。


> 具体的にはどうすればいいのでしょうか?
VB6 の場合は、GetMonitorInfo API を使います。
先に紹介した MultiMon サンプル(monitors.vbp) でいえば、
リストボックス(frmSample.lstMonitors)のアイテムを
ダブルクリックしたときに表示される矩形情報が該当します。


これが VB.NET であれば、
 For Each scr As Screen In Screen.AllScreens
  Debug.Print(scr.DeviceName)
  If scr Is Screen.PrimaryScreen Then Debug.WriteLine("プライマリスクリーン")
  Debug.Print( "Left  = " & CStr(scr.Bounds.Left  ) )
  Debug.Print( "Top   = " & CStr(scr.Bounds.Top   ) )
  Debug.Print( "Right = " & CStr(scr.Bounds.Right ) )
  Debug.Print( "Bottom= " & CStr(scr.Bounds.Bottom) )
  Debug.Print( "Width = " & CStr(scr.Bounds.Width ) )
  Debug.Print( "Height= " & CStr(scr.Bounds.Height) )
 Next
のようにして簡単に列挙できるのですけれどね。


> SM_CXVIRTUALSCREENの説明書きは左隅の座標ということでこれは0ではないのでしょうか?
Virtual-Desktop-Screen とは、各モニタの最も外側の辺に重なる領域の事です。

これは上記テキスト図でいうところの太線部の領域であり、先ほどの例では、
(0, -880)-(2480, 1450) という 2480x2330 の領域を意味します。つまり、
 GetSystemMetrics(SM_XVIRTUALSCREEN ) = 0
 GetSystemMetrics(SM_YVIRTUALSCREEN ) = -880
 GetSystemMetrics(SM_CXVIRTUALSCREEN) = 2480
 GetSystemMetrics(SM_CYVIRTUALSCREEN) = 2330
という値が返されるという事です。



> 画面に対する知識がまるでないため#2と返答していましたがどうも違うのでしょうか?
下記の「マルチデスクトップ」「ビッグデスクトップ」「クローンデスクトップ」の違いです。
(どのモードが使えるのかは、お使いの製品によって異なります)
http://shop.tsukumo.co.jp/special/050418c/


たとえば、1280x1024 を表示可能なモニタが17インチモニタが2台あるとします。
(このサイズを表示可能なら、実際には物理サイズは違っていても構いません)

このモニタを 2 台とも横置き配置で使う場合、マルチモニタ環境としては
 (A) 1280x1024 … ミラー(クローン)
 (B) 2560x1024 … マルチモニタ、またはビッグデスクトップ
 (C) 1280x2048 … マルチモニタ
 (D) 上記以外  … マルチモニタ
の 4 パターンが考えられます。


(A) は、アプリケーションからは 1280x1024 が 1 つだけあるように見えます。
   デスクトップサイズ … 1280x1024
   モニタ数 = 1
     1: 1280x1024 : (0, 0)-(1280, 1024)
一方のモニタがオフラインの場合も、この状態となりますね。


(B)/(C)は、縦または横に並べた場合のパターンです。
たとえば (B) の場合、メイン画面が左側にある場合は、
   デスクトップサイズ … 2560x1024
   モニタ数 = 2
     1: 1280x1024 : (0, 0)-(1280, 1024)
     2: 1280x1024 : (1280, 0)-(2560, 1024)
となり、また、メイン画面が右側にある場合は、
   デスクトップサイズ … 2560x1024
   モニタ数 = 2
     1: 1280x1024 : (0, 0)-(1280, 1024)
     2: 1280x1024 : (-1280, 0)-(0, 1024)
という座標系になります。

そして B の『ビッグデスクトップ』モードの場合、アプリケーションからは
   デスクトップサイズ … 2560x1024
   モニタ数 = 1
     1: 1280x1024 : (0, 0)-(2560, 1024)
という構成に見えます。今回の例では 1280x1024 の横置き 2 枚の構成ですが、
縦置きの左右構成なら、2480x1280 構成のデスクトップになります。

ビッグデスクトップでは、それぞれの画面の論理解像度と色数が
完全に一致している必要があります。


なお (D) は、非矩形のレイアウトであり、それぞれの画面サイズが
異なっているため、ビッグデスクトップになれません。これは
 ┏━━━━━┯━━━━━━━┓
 ┃*****│       ┃デスクトップサイズ … 2480x1050
 ┠─────┤       ┃モニタ数 = 2
 ┃     │       ┃  1: 1680x1050 : [横置き]: (0, 0)-(1680, 1050)
 ┃ 画面2 │  画面1  ┃  2:  800x 600 : [横置き]: (-800, 450)-(0, 1050)
 ┃     │       ┃
 ┗━━━━━┷━━━━━━━┛
のように左右で違う解像度のパターンの他、接する辺の長さが一緒であっても、
 ┏━━━━━┯━━━━━━━┓
 ┃     │       ┃
 ┃  1  │       ┃デスクトップサイズ … 2480x1024
 ┃  縦  │   2   ┃モニタ数 = 2
 ┃  置  │  横置き  ┃  1:  768x1024 : [縦置き]: (0, 0)-(768, 1024)
 ┃  き  │       ┃  2: 1280x1024 : [横置き]: (768, 0)-(2048, 1024)
 ┃     │       ┃
 ┗━━━━━┷━━━━━━━┛
のような構成の場合も、パターン D に該当します。

[ツリー表示へ]
タイトルRe^10: メモ帳のカーソル位置
記事No15527
投稿日: 2012/07/09(Mon) 17:38
投稿者Boze
あまり時間が取れないのですぐには返信できませんが今週中にはテストして結果を
報告させて頂きます。

EnumWindowでハンドルを取得しそれをSetWindowPosで動かしたら出来そうなサイトが
見つかったのでそちらも試してみます。
http://monta.moe.in/wp/2010/03-21/13-20_721

FindWindowでハンドルを取得できずEnumWindowで取得出来ることなんてあるのでしょうか?
FindWindowの後に子windowのハンドルも取得しているのでもしかしたらFindWindowEx、RemoveWindowの組み合わせで作業していたのかもしれません。
ここをFindWindowに変えればうま行きそうな気もします。
もしくはRemoveWindowでは無理でSetWindowPosなら移動可能とか?

[ツリー表示へ]
タイトルRe^11: メモ帳のカーソル位置
記事No15530
投稿日: 2012/07/09(Mon) 19:20
投稿者魔界の仮面弁士
> FindWindowでハンドルを取得できずEnumWindowで取得出来ることなんてあるのでしょうか?
EnumWindows は、トップレベルウィンドウの全列挙ですが、
FindWindow は、クラス名/ウィンドウ名による検索です。

通常はどちらでも取得できるはずですが、たとえば、Unicodeテキストなウィンドウ名に対して
うっかり ANSI バージョンである FindWindow(Ex)A を呼び出していたり、あるいは、
半角/全角、マイナスとハイフン、空白文字の有無などの指定ミスがあったとすれば、
FindWindow(Ex) での取りこぼしはありえるかと思います。


> EnumWindowでハンドルを取得しそれをSetWindowPosで動かしたら出来そうなサイトが
> 見つかったのでそちらも試してみます。
> http://monta.moe.in/wp/2010/03-21/13-20_721
移動自体は、MoveWindow でも SetWindow でも問題ないはずです。

Private Declare Function MoveWindow Lib "user32" _
( _
    ByVal hWnd As OLE_HANDLE, _
    ByVal X As Long, _
    ByVal Y As Long, _
    ByVal nWidth As Long, _
    ByVal nHeight As Long, _
    ByVal bRepaint As Long _
) As Long


Private Enum SWPIA
    HWND_NOTOPMOST = -2 '自身が最前面なら、どの最前面ウィンドウよりも後ろに移動する
    HWND_TOPMOST   = -1 '最前面にないどのウィンドウよりも手前に移動する
    HWND_TOP       = 0  '先頭に移動する
    HWND_BOTTOM    = 1  '一番後ろに移動する
End Enum
Private Enum SWP
    SWP_NOSIZE         = &H1    'ウィンドウのサイズを変更しない(cx と cy を無視する)
    SWP_NOMOVE         = &H2    'ウィンドウの位置を変更しない(X と Y を無視する)
    SWP_NOZORDER       = &H4    'Zオーダーを変更しない(hWndInsertAfter を無視する)
    SWP_NOREDRAW       = &H8    '再描画しない
    SWP_NOACTIVATE     = &H10   'ウィンドウをアクティブ化しない
    SWP_FRAMECHANGED   = &H20   '新しい枠線スタイルの設定を適用する
    SWP_DRAWFRAME      = SWP_FRAMECHANGED   '枠線を描画する
    SWP_SHOWWINDOW     = &H40   'ウィンドウを表示する
    SWP_HIDEWINDOW     = &H80   'ウィンドウを非表示にする
    SWP_NOCOPYBITS     = &H100  'クライアント領域のすべての内容を破棄する
    SWP_NOOWNERZORDER  = &H200  'オーナーウィンドウのZオーダーを変更しない
    SWP_NOOWNERZORDER  = SWP_NOREPOSITION
    SWP_NOSENDCHANGING = &H400  'WM_WINDOWPOSCHANGING メッセージを抑制する
    SWP_DEFERERASE     = &H2000 'WM_SYNCPAINT メッセージを抑制する
    SWP_ASYNCWINDOWPOS = &H4000 'UIスレッドへ要求する
End Enum
Private Declare Function SetWindowPos Lib "user32" _
( _
    ByVal hWnd As OLE_HANDLE, _
    ByVal hWndInsertAfter As OLE_HANDLE, _
    ByVal X As Long, _
    ByVal Y As Long, _
    ByVal cx As Long, _
    ByVal cy As Long, _
    ByVal wFlags As SWP _
) As Long

[ツリー表示へ]
タイトルRe^12: メモ帳のカーソル位置
記事No15531
投稿日: 2012/07/10(Tue) 09:35
投稿者Boze
> FindWindow(Ex) での取りこぼしはありえるかと思います。
なるほど、ではEnumWindowを使用します。

> 移動自体は、MoveWindow でも SetWindow でも問題ないはずです。
マルチモニタ環境において、サブ画面にメモ帳があるときのハンドル取得ができているかの検証ができませんので確証はないですが、おそらくFindWindowでハンドルが拾えないためにMoveWindowが効いていなかったと考えるのが普通でしょうか。
Setwindowでは位置を指定したいだけなのに最後のパラメータを何にしたらいいのかよくわからないので(いくつかのパラメータの意味がよくわからないため)MoveWindowを使用してみます。

有難うございました。

[ツリー表示へ]