マイナビニュースマイナビ

PowerShell 7 - ウインドウを移動させるスクリプト window_move.ps1 - マイナス値の指定

【連載】

PowerShell Core入門 - 基本コマンドの使い方

【第165回】PowerShell 7 - ウインドウを移動させるスクリプト window_move.ps1 - マイナス値の指定

[2021/09/10 08:00]後藤大地 ブックマーク ブックマーク

前回は、Win32 APIのMoveWindow()関数を利用してウインドウを移動するPowerShellスクリプトを作成した。基本的にはMoveWindow()関数の使い方を変えただけであり、さらにその前に作ったウインドウサイズを変更するPowerShellスクリプトを多少書き換えただけのものだ。

今回はこのスクリプトwindow_move.ps1をもう少し扱いやすくなるように改良する。前回のスクリプトは、移動するウインドウの座標を左上からの距離で指定する必要があった。しかし、この方法ではスクリーンサイズがわかっている場合でないと使いにくい。

例えば、時計アプリケーションや何かのモニタリングアプリケーションを常に右下に配置したいとする。前回のシェルスクリプトだと、ディスプレイサイズを把握した上で左上からの距離を指定する必要がある。その場合、ディスプレイサイズが変わるとうまく使えないのだ。外部ディスプレイを接続したり、画面の表示解像度を変更したりした場合にいちいち指定する距離を変えるのは面倒この上ない。

そことで、今回は左上からの距離ではなく、右下からの距離でも指定できるようにする。こうすることでかなり汎用的に扱えるようになる。指定方法は「マイナス値」を使う。例えば、X座標が「-X -500」のように指定された場合、右端から左方向へ500の距離、という指定だと解釈する。こうすることで前後左右どちらかも距離を指定できるようになり、かなり指定の自由度が増すのだ。

window_move.ps1 ver.2

先に答えから書いておく。次のように書き換えることで目的の処理を実現できる(window_move.ps1)。

#!/usr/bin/env pwsh

#====================================================================
# 引数を処理
#====================================================================
Param(
    [String]$ProcessName="*",   # プロセス名
    [String]$WindowTitle=".*",  # ウインドウタイトル(正規表現)
    [Int32]$X="0",              # ウインドウ幅
    [Int32]$Y="0",              # ウインドウ高さ
    [Switch]$WindowProcessList  # ウインドウプロセス一覧を表示
)

#====================================================================
# ウインドウプロセスを一覧表示
#====================================================================
if ($WindowProcessList) {
    Get-Process                         | 
    ? {$_.MainWindowHandle -ne 0 }              | 
    Format-Table -Property Id,ProcessName,MainWindowTitle
    exit
}

#====================================================================
# Win32 APIのインポート
#====================================================================
Add-Type @"
using System;
using System.Runtime.InteropServices;

public struct RECT
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

public class WinAPI
{
    // ウインドウの現在の座標データを取得する関数
    [DllImport("user32.dll")]
    public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

    // ウインドウの座標を変更する関数
    [DllImport("user32.dll")]
    public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); 

    // スクリーンサイズを取得する関数
    [DllImport("user32.dll")]
    public static extern int GetSystemMetrics(int nIndex); 
}
"@

#====================================================================
# マイナス指定のXおよびYを正規の値へ変換
#====================================================================
# スクリーン幅
$screenWidth = [WinAPI]::GetSystemMetrics(0);
# スクリーン高さ
$screenHeight = [WinAPI]::GetSystemMetrics(1);

if ($X -lt 0) {
    $X = $screenWidth + $X
}
if ($Y -lt 0) {
    $Y = $screenHeight + $Y
}

#====================================================================
# ウインドウを移動する関数 Move-Window
#====================================================================
function Move-Window {
    param (
        $wh  # ウインドウハンドラ
    )

    # ウインドウ座標データ構造体
    $rc = New-Object RECT

    # ウインドウの現在の座標データを取得
    [WinAPI]::GetWindowRect($wh, [ref]$rc) > $null

    # 取得した座標データからウインドウの幅と高さを計算
    $width = $rc.Right - $rc.Left;
    $height = $rc.Bottom - $rc.Top;

    # ウインドウのサイズはそのままに、左上の場所を変更
    [WinAPI]::MoveWindow($wh, $X, $Y, $width, $height, $true) > $null
}

#====================================================================
# 対象となるウインドウを選択し、サイズを変更
#====================================================================
Get-Process -Name $processName |
    ? { $_.MainWindowHandle -ne 0 } |
    ? { $_.MainWindowTitle -match "$windowTitle" } |
    % {
        # ウインドウを移動
        Move-Window($_.MainWindowHandle);
}

前回作成したスクリプトからの変更内容は次の通りだ。

--- window_move.ps1-old 2021-09-01 17:21:01.122830000 +0900
+++ window_move.ps1 2021-09-01 17:21:20.892355000 +0900
@@ -22,7 +22,7 @@
 }

 #====================================================================
-# ウインドウを移動する関数 Move-Window
+# Win32 APIのインポート
 #====================================================================
 Add-Type @"
 using System;
@@ -45,9 +45,31 @@
    // ウインドウの座標を変更する関数
    [DllImport("user32.dll")]
    public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); 
+
+   // スクリーンサイズを取得する関数
+   [DllImport("user32.dll")]
+   public static extern int GetSystemMetrics(int nIndex); 
 }
 "@

+#====================================================================
+# マイナス指定のXおよびYを正規の値へ変換
+#====================================================================
+# スクリーン幅
+$screenWidth = [WinAPI]::GetSystemMetrics(0);
+# スクリーン高さ
+$screenHeight = [WinAPI]::GetSystemMetrics(1);
+
+if ($X -lt 0) {
+   $X = $screenWidth + $X
+}
+if ($Y -lt 0) {
+   $Y = $screenHeight + $Y
+}
+
+#====================================================================
+# ウインドウを移動する関数 Move-Window
+#====================================================================
 function Move-Window {
    param (
        $wh  # ウインドウハンドラ

以降で書き換えた内容を説明する。

Win32 API - GetSystemMetrics()

まず、今回の機能を実現するにはディスプレイのサイズを知る必要がある。Windowsでディスプレイサイズを取得する方法はいくつかあるが、今回はWin32 APIのGetSystemMetrics()関数を使うことにする。この関数の詳細は次のページにまとまっている。


GetSystemMetrics()を使うことでシステムメトリックスや構成設定を取得することができる。得られる値のなかにディスプレイサイズも含まれるので、これを使うというわけだ。この関数はusers32.dllファイルに含まれており、GetWindowRect()やMoveWindow()と同じ要領で利用できるようになる。そこで、次のコードを追加してスクリーンサイズを取得する関数として利用している。

    // スクリーンサイズを取得する関数
    [DllImport("user32.dll")]
    public static extern int GetSystemMetrics(int nIndex);

マイナス値をプラスの値へ変換

ディスプレイのサイズ(スクリーンのサイズ)が取得できれば、ほぼ完成したようなものだ。マイナスの値が指定された場合に、スクリーンサイズを使って左上からの距離(プラスの値)に変更すればよいのだ。

そこで、マイナス値をプラス値へ変換する次のコードを追加してある。

#====================================================================
# マイナス指定のXおよびYを正規の値へ変換
#====================================================================
# スクリーン幅
$screenWidth = [WinAPI]::GetSystemMetrics(0);
# スクリーン高さ
$screenHeight = [WinAPI]::GetSystemMetrics(1);

if ($X -lt 0) {
    $X = $screenWidth + $X
}
if ($Y -lt 0) {
    $Y = $screenHeight + $Y
}

後の処理は前回と同じで良い。これで新しい機能が追加されたことになる。

実行してみよう

ではさっそく実行してみよう。まず、前回と同じ指定で、プラス値でウインドウの移動を実行した結果が以下だ。

window_move -ProcessName msedge -X 100 -Y 100

スクリプトの実行結果

処理としては前回と同じなので、指定した通りに動作するのは当然と言えば当然だ。

次はマイナス値を指定して実行してみる。

window_move -ProcessName msedge -X -1300 -Y -900

スクリプトの実行結果

マイナス値のほうは、右下からの距離で機能していることがわかる。これで今回の機能追加は完了だ。

好みでカスタマイズもできる

マイナス値を指定した場合も、ウインドウの基準となる位置はウインドウの左上で計算してある。もし、左上ではなく右下を基準にして移動させたいのであれば、必要となるデータはすでにシェルスクリプト内にあるので、座標計算の部分を書き換えれば変更することが可能だ。もしかすると、使い勝手としてはマイナス値の場合は右下を基準にしたほうが使いやすいかもしれない。

このように、Win32 APIを使うとWindowsからの情報をダイレクトに取得できるし、直接Windowsを操作することも可能になる。これまで手作業で操作していた内容をPowerShellで自動化するのもそれほど難しくないことがわかるはずだ。PowerShellスクリプトでシステムの操作を自動化し、仕事を効率化する。PowerShellがもたらしてくれる恩恵は大きいのだ。

※ 本記事は掲載時点の情報であり、最新のものとは異なる場合がございます。予めご了承ください。

一覧はこちら

連載目次

もっと知りたい!こちらもオススメ

【連載】RPA入門 - ツールで学ぶ活用シーン

【連載】RPA入門 - ツールで学ぶ活用シーン

AIには、ルールベース、機械学習、深層学習(ディープラーニング)の3つのレベルがあり、レベルが上がるに連れてより高度な人工知能を実現しますが、AIのスピンオフという位置付けで、Digital Labor(仮想知的労働者)によるホワイトカラー業務の自動化を実現するRPAが注目されています。

関連リンク

この記事に興味を持ったら"いいね!"を Click
Facebook で TECH+ の人気記事をお届けします
注目の特集/連載
[解説動画] Googleアナリティクス分析&活用講座 - Webサイト改善の正しい考え方
Slackで始める新しいオフィス様式
Google Workspaceをビジネスで活用する
ニューノーマル時代のオウンドメディア戦略
ミッションステートメント
次世代YouTubeクリエイターの成長戦略
IoTでできることを見つけるための発想トレーニング
教えてカナコさん! これならわかるAI入門
AWSではじめる機械学習 ~サービスを知り、実装を学ぶ~
Kubernetes入門
SAFeでつくる「DXに強い組織」~企業の課題を解決する13のアプローチ~
マイクロサービス時代に活きるフレームワーク Spring WebFlux入門
AWSで作るマイクロサービス
マイナビニュース スペシャルセミナー 講演レポート/当日講演資料 まとめ
セキュリティアワード特設ページ

一覧はこちら

今注目のIT用語の意味を事典でチェック!

一覧はこちら

会員登録(無料)

ページの先頭に戻る