DXの流行にのってPowershellでグラフィカルなランチャーを作成してみる

ローコード開発、ノーコード開発、DX化などが最近話題になっていますね。
ローコード開発ツールなどで、今までの業務で使用していたシステムをアプリ化したり、保守のしやすさが各段と上がったりするので、DX化がどんどん進んでいっています。

ただ、ローコード開発ツールなどは、従事している企業によってはセキュリティが厳しくて個人での導入が難しいと思います。(費用面も…。)

なので、今回はPowershellとCSVファイルを使用して、CSVファイルに設定を追記するだけでグラフィカルなランチャーが出来る、
簡単なショートカットランチャーツールをご紹介したいと思います。
はじめに言っておきますが、大層なモノではないので過度な期待は禁物です!

PowerShellでCSVファイルを読み込む方法

2023/4/7    ,

PowerShellを使用してCSVファイルを読み込む方法を記載します。 Import-Csvというコマンドレットを使用することでCSVファイルの読み込みが実施できます。 #読み込み対象のCSVファイ ...

実際に動かしてみた

実際に動かした際のフォームは左図のようになります。

CSVファイルに設定されたデータ数分繰り返し処理を実施し、

フォームサイズに合わせて折り返ししながらボタンを配置していきます。

もちろんボタンを押下すると指定のプログラムが実行されます。

Powershellで作成できるGUIランチャーのコード

早速コードを以下に記載します。

# アセンブリの読み込み
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

#Win32Apiを使用してSHGetFileInfoを使う
Add-Type @"
    using System;
    using System.Runtime.InteropServices;
    using System.Text;

    public static class Win32Api {
        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, out SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct SHFILEINFO {
        public IntPtr hIcon;
        public int iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    }
    public class FileIconInfo {
        public IntPtr IconHandle { get; set; }
        public string DisplayName { get; set; }
        public string TypeName { get; set; }
    }
"@

#ファイルのアイコン情報を取得する
function Get-FileIconInfo {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$Path
    )
    $SHGFI_ICON = 0x000000100 
    $SHGFI_DISPLAYNAME = 0x000000200  
    $SHGFI_TYPENAME = 0x000000400  
    $FileInfo = New-Object SHFILEINFO
    $FileIcon = New-Object FileIconInfo
    $result = [Win32Api]::SHGetFileInfo($Path, 0, [ref]$FileInfo, [int]([System.Runtime.InteropServices.Marshal]::SizeOf($FileInfo)), $SHGFI_ICON -bor $SHGFI_DISPLAYNAME -bor $SHGFI_TYPENAME)
    if ($result -ne 0) {
        $FileIcon.IconHandle = $FileInfo.hIcon
        $FileIcon.DisplayName = $FileInfo.szDisplayName
        $FileIcon.TypeName = $FileInfo.szTypeName
    }
    return $FileIcon
}
#アイコンハンドルをイメージ化する
function Get-IconFromHandle($iconHandle) {
    $iconImage = [System.Drawing.Icon]::FromHandle($iconHandle)
    return $iconImage
}

# フォームの作成
$form = New-Object System.Windows.Forms.Form
$form.Text = "TEST"
$form.Size = New-Object System.Drawing.Size(250,250)
$btnXDef = 5
$btnYDef = 20
$btnX = $btnXDef
$btnY = $btnYDef

#エンコードをSJISに設定する
$PSDefaultParameterValues['*:Encoding'] ='default'
#読み込み対象のCSVファイルを設定
$targetCsv ="C:\tmp\powershell\test.csv"
#読み込み対象のファイルを設定する
$getCsvData = Import-Csv  -Path $targetCsv
$arrButton = New-Object System.Collections.Generic.List[System.Windows.Forms.Button]
for ($currentIndex = 0; $currentIndex -lt $getCsvData.Count; $currentIndex++){
    $appPath = $getCsvData.item($currentIndex) | select "アプリケーションファイル格納先"
    $appPath = $appPath.アプリケーションファイル格納先
    $IconButtonIndex = New-Object System.Windows.Forms.Button
    #現在参照中のアプリケーションのアイコンを取得する
    $icon = Get-FileIconInfo -Path $appPath
    #アイコンが取得できない場合
    if ($icon.DisplayName -eq $null){
        $h = 47
        $w = 47
        $IconButtonIndex.Image = $null
    }else{
        #アイコンハンドルからイメージ化してボタンアイコンとして使用できるようにする
        $iconImage = Get-IconFromHandle $icon.IconHandle
        $h = $iconImage.Size.Height + 15
        $w = $iconImage.Size.Width + 15
        $IconButtonIndex.Image = $iconImage
    }

    $IconButtonIndex.Height = $h
    $IconButtonIndex.Width = $w

    #動的に生成しているボタンの配置箇所をフォームサイズに合わせて折り返しする
    $widthJudge = $form.Size.Width-$btnX-50
    if (0 -gt $widthJudge){
        $btnX = $btnXDef
        $btnY = $btnY + 75
    }
    $IconButtonIndex.Location = New-Object Drawing.Point($btnX, $btnY)
    $btnX = $btnX + 60
    #動的に生成しているボタンのインデックスをタグとして設定する
    $IconButtonIndex.Tag = $appPath;
    $IconButtonIndex_Click={
        ($sender, $e) = $this, $_
        Start-Process -FilePath $sender.tag
    }
    # ボタンのクリックイベントを格納
    $IconButtonIndex.Add_Click($IconButtonIndex_Click)
    $arrButton.Add($IconButtonIndex)
    $form.Controls.Add($IconButtonIndex)
    $iconImage.Dispose()
    $icon= $null
}

# フォームを表示
$form.ShowDialog()

動的にボタンを配置する

動的にボタンを配置する部分は以下の部分となります。

ボタンオブジェクトをリストで作成し、繰り返し処理を実施しながらクリックイベントを設定していきます。

これによってCSVファイルに対象のバスを設定するだけでボタンが生成されていきます。

$arrButton = New-Object System.Collections.Generic.List[System.Windows.Forms.Button]
for ($currentIndex = 0; $currentIndex -lt $getCsvData.Count; $currentIndex++){
    $appPath = $getCsvData.item($currentIndex) | select "アプリケーションファイル格納先"
    $appPath = $appPath.アプリケーションファイル格納先
    $IconButtonIndex = New-Object System.Windows.Forms.Button
    #現在参照中のアプリケーションのアイコンを取得する
    $icon = Get-FileIconInfo -Path $appPath
    #アイコンが取得できない場合
    if ($icon.DisplayName -eq $null){
        $h = 47
        $w = 47
        $IconButtonIndex.Image = $null
    }else{
        #アイコンハンドルからイメージ化してボタンアイコンとして使用できるようにする
        $iconImage = Get-IconFromHandle $icon.IconHandle
        $h = $iconImage.Size.Height + 15
        $w = $iconImage.Size.Width + 15
        $IconButtonIndex.Image = $iconImage
    }

    $IconButtonIndex.Height = $h
    $IconButtonIndex.Width = $w

    #動的に生成しているボタンの配置箇所をフォームサイズに合わせて折り返しする
    $widthJudge = $form.Size.Width-$btnX-50
    if (0 -gt $widthJudge){
        $btnX = $btnXDef
        $btnY = $btnY + 75
    }
    $IconButtonIndex.Location = New-Object Drawing.Point($btnX, $btnY)
    $btnX = $btnX + 60
    #動的に生成しているボタンのインデックスをタグとして設定する
    $IconButtonIndex.Tag = $appPath;
    $IconButtonIndex_Click={
        ($sender, $e) = $this, $_
        Start-Process -FilePath $sender.tag
    }
    # ボタンのクリックイベントを格納
    $IconButtonIndex.Add_Click($IconButtonIndex_Click)
    $arrButton.Add($IconButtonIndex)
    $form.Controls.Add($IconButtonIndex)
    $iconImage.Dispose()
    $icon= $null
}

Powershellでアイコンを取得する

アイコンを取得する際にWin32ApiをのSHGetFileInfoを使用しています。

#Win32Apiを使用してSHGetFileInfoを使う
Add-Type @"
    using System;
    using System.Runtime.InteropServices;
    using System.Text;

    public static class Win32Api {
        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, out SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct SHFILEINFO {
        public IntPtr hIcon;
        public int iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    }
    public class FileIconInfo {
        public IntPtr IconHandle { get; set; }
        public string DisplayName { get; set; }
        public string TypeName { get; set; }
    }
"@

#ファイルのアイコン情報を取得する
function Get-FileIconInfo {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$Path
    )
    $SHGFI_ICON = 0x000000100 
    $SHGFI_DISPLAYNAME = 0x000000200  
    $SHGFI_TYPENAME = 0x000000400  
    $FileInfo = New-Object SHFILEINFO
    $FileIcon = New-Object FileIconInfo
    $result = [Win32Api]::SHGetFileInfo($Path, 0, [ref]$FileInfo, [int]([System.Runtime.InteropServices.Marshal]::SizeOf($FileInfo)), $SHGFI_ICON -bor $SHGFI_DISPLAYNAME -bor $SHGFI_TYPENAME)
    if ($result -ne 0) {
        $FileIcon.IconHandle = $FileInfo.hIcon
        $FileIcon.DisplayName = $FileInfo.szDisplayName
        $FileIcon.TypeName = $FileInfo.szTypeName
    }
    return $FileIcon
}
#アイコンハンドルをイメージ化する
function Get-IconFromHandle($iconHandle) {
    $iconImage = [System.Drawing.Icon]::FromHandle($iconHandle)
    return $iconImage
}

最後に

アイコンが取得できない場合、今は仕方がなく空イメージのボタンを生成するようにしていますが、

いずれ自動でアイコンを作る仕組みにできたらいいなと思っています。

スポンサーリンク

-IT関連
-