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

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

【連載】

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

【第166回】ウインドウを移動させるスクリプト window_move.ps1 - マイナス値の指定(改良)

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

本連載では、ここ数回にわたり、PowerShellスクリプトでウインドウの配置座標を変更する処理を実装してきた。常に左上からの距離で座標を指定するのは面倒なので、前回は右下からも移動先の座標を指定できるように、マイナス値が指定されていた場合には右下からの距離として解釈するように変更した。

例えば、次のように引数を指定してスクリプトを実行したとしよう。

window_move.ps1 -ProcessName msedge -X -1400 -Y -1000

すると、画像のようにウインドウが移動する。

実行前のスクリーン

実行後のスクリーン

「-X -1400 -Y -1000」という座標の指定は、前回の実装では「スクリーン右下からウインドウ左上への距離」ということになっている。つまり、ウインドウの右下をスクリーンの右下から200の距離にしたいのであれば、ウインドウの幅と高さを加味して「-X -1400 -Y -1000」のように指定しなければならないということだ。

ウインドウの基本座標は左上になっていることが多いので、スクリーンの右下からの指定であっても、基準をウインドウの左上とするのはそれほど不自然なことはない。しかし、実際にマイナスの値を指定して右下からの距離を使いたいときというのは、ウインドウの右下が移動の基準になっているほうが考えることが少なくて済む。

例えば、先ほどのコマンドは、ウインドウの幅と高さを加味して座標を指定している。これを、ウインドウの幅も高さも加味せずに、次のように指定できるともっとわかりやすい。

window_move.ps1 -ProcessName msedge -X -200 -Y -200

実行後のスクリーン

ちょっとした違いでしかないのだが、小さな手間が大きなストレスとなって溜まっていくのだ。ということで、処理を変更してみよう。

シェルスクリプトの書き換え

まず、前回作成したスクリプト「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」は、次のようになる。

#!/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); 
}
"@

#====================================================================
# ウインドウを移動する関数 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;

    # マイナス指定のXおよびYを正値へ変換
    if ($X -lt 0) {
        # スクリーン幅を取得
        $screenWidth = [WinAPI]::GetSystemMetrics(0);
        # Xの値を算出
        $X = $screenWidth + $X - $width
    }
    if ($Y -lt 0) {
        # スクリーン高さを取得
        $screenHeight = [WinAPI]::GetSystemMetrics(1);
        # Yの値を算出
        $Y = $screenHeight + $Y - $height
    }

    # ウインドウのサイズはそのままに、左上の場所を変更
    [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.org 2021-09-09 11:25:58.602759000 +0900
+++ window_move.ps1 2021-09-09 11:54:32.370757000 +0900
@@ -53,21 +53,6 @@
 "@

 #====================================================================
-# マイナス指定の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 {
@@ -84,6 +69,20 @@
    # 取得した座標データからウインドウの幅と高さを計算
    $width = $rc.Right - $rc.Left;
    $height = $rc.Bottom - $rc.Top;
+
+   # マイナス指定のXおよびYを正値へ変換
+   if ($X -lt 0) {
+       # スクリーン幅を取得
+       $screenWidth = [WinAPI]::GetSystemMetrics(0);
+       # Xの値を算出
+       $X = $screenWidth + $X - $width
+   }
+   if ($Y -lt 0) {
+       # スクリーン高さを取得
+       $screenHeight = [WinAPI]::GetSystemMetrics(1);
+       # Yの値を算出
+       $Y = $screenHeight + $Y - $height
+   }

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

以降では、変更部分を順に説明していこう。

書き換えの内容

移動座標の対象がウインドウの右上だった場合、その指定座標をマイナス値から正値に変換するのに必要なデータはスクリーンサイズと指定された座標データのみだった。そのため、スクリーンサイズを取得した段階で、次のように値の変換を行っていた。

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

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

今回はこれに加えて、ウインドウの幅と高さも計算に必要になる。そのため、新しいスクリプトでは上記コードは全て削除している。

前回はウインドウを特定した後で次のコードによってウインドウの幅と高さを計算している。

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

ここでウインドウの幅と高さが計算されているので、この後であればマイナス値を今回欲しい正値へ変換することができる。ということで、この処理の後に次の処理を追加して、マイナス値を正値へ変換している。

    # マイナス指定のXおよびYを正値へ変換
    if ($X -lt 0) {
        # スクリーン幅を取得
        $screenWidth = [WinAPI]::GetSystemMetrics(0);
        # Xの値を算出
        $X = $screenWidth + $X - $width
    }
    if ($Y -lt 0) {
        # スクリーン高さを取得
        $screenHeight = [WinAPI]::GetSystemMetrics(1);
        # Yの値を算出
        $Y = $screenHeight + $Y - $height
    }

これで書き換えは完了だ。すでにあるデータのみで書き換えが可能なので、新しくWinAPIを呼び出す処理は追加していない。

扱いやすいように書き換えることが大切

実行結果は最初に示したとおりだ。次のようにウインドウを移動させることができる。

window_move.ps1 -ProcessName msedge -X -200 -Y -200

書き換え後の実行結果

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用語の意味を事典でチェック!

一覧はこちら

会員登録(無料)

ページの先頭に戻る