5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

launcher_diagram (1).png

よく使うフォルダやアプリをカテゴリごとにタブで整理し、ダブルクリックで開けるランチャーを PowerShell で作成します。

「こんなツールがあったら便利だな」と思ったものを、必要最低限の機能に絞って実装しました。

勝手にアプリをインストールしにくい環境を想定し、簡易的なランチャーを PowerShell で自作してみました。似たような要件や要望がある方は、ぜひチェックしてみてください。必要に応じて改造し、活用してもらえればと思います。

前提環境

  • Windows 10 / 11
  • PowerShell 5.1 以上
  • 実行ポリシーでスクリプト実行が許可されていること(Set-ExecutionPolicy RemoteSigned など)

完成イメージ

image.png

  • ダブルクリックで項目を開く
  • 起動対象は設定ファイル(JSON)で定義
  • カテゴリごとにタブでグルーピング可能

設定ファイル(launcher.json)

{
  "settings": {
    "width": 480,
    "height": 360,
    "fontName": "Meiryo UI",
    "fontSize": 11,
    "itemHeight": 36,
    "position": "bottomRight"
  },
  "作業": [
    { "name": "作業フォルダ", "path": "C:\\Work" },
    { "name": "プロジェクト", "path": "C:\\Projects" }
  ],
  "システム": [
    { "name": "Downloads", "path": "%USERPROFILE%\\Downloads" },
    { "name": "ドキュメント", "path": "%USERPROFILE%\\Documents" },
    { "name": "スタートアップ", "path": "shell:startup" }
  ],
  "アプリ": [
    { "name": "メモ帳", "path": "notepad.exe" },
    { "name": "電卓", "path": "calc.exe" },
    { "name": "VS Code", "path": "%LOCALAPPDATA%\\Programs\\Microsoft VS Code\\Code.exe" },
    { "name": "Google検索", "path": "https://www.google.com" },
    { "name": "特定ファイルを開く", "path": "notepad.exe", "args": "C:\\Work\\memo.txt" }
  ]
}
設定項目 説明 デフォルト
width ウィンドウ幅(px) 520
height ウィンドウ高さ(px) 420
fontName フォント名 Yu Gothic UI
fontSize フォントサイズ(pt) 10
itemHeight 項目の高さ(px) 32
position 表示位置 center
x, y 座標(positionが manual の時、両方必須) -

position の指定方法:

表示位置
center 画面中央(デフォルト)
topLeft 左上
topRight 右上
bottomLeft 左下
bottomRight 右下
manual x, y で指定した座標(x, y 未指定時は center)
項目設定 説明 必須
name 表示名
path パス(フォルダ/EXE/URL)
args 引数(任意) -

pathの指定方法:

  • フォルダ: C:\\Work%USERPROFILE%\\Documents(環境変数可)
  • シェル特殊フォルダ: shell:startup など(内部で explorer.exe 経由で開く)
  • アプリ: notepad.exe や絶対パス
  • URL: https://...
  • スペースを含むパスもそのまま指定可能

実装例 (FolderLauncher.ps1)

#requires -version 5.1
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$configPath = Join-Path $scriptDir "launcher.json"

if (-not (Test-Path $configPath)) {
  [System.Windows.Forms.MessageBox]::Show("設定ファイルが見つかりません: $configPath")
  exit 1
}

$config = Get-Content -Raw -Encoding UTF8 $configPath | ConvertFrom-Json

# 設定値の取得(デフォルト値付き)
$settings = $config.settings
$formWidth  = if ($settings.width)      { $settings.width }      else { 520 }
$formHeight = if ($settings.height)     { $settings.height }     else { 420 }
$fontName   = if ($settings.fontName)   { $settings.fontName }   else { "Yu Gothic UI" }
$fontSize   = if ($settings.fontSize)   { $settings.fontSize }   else { 10 }
$itemHeight = if ($settings.itemHeight) { $settings.itemHeight } else { 32 }
$position   = if ($settings.position)   { $settings.position }   else { "center" }
$posX       = $settings.x
$posY       = $settings.y

$font = New-Object System.Drawing.Font($fontName, $fontSize)

# フォーム作成
$form = New-Object System.Windows.Forms.Form
$form.Text = "Folder Launcher"
$form.Size = New-Object System.Drawing.Size($formWidth, $formHeight)
$form.Font = $font

# 表示位置の設定
switch ($position) {
  "manual" {
    if ($null -ne $posX -and $null -ne $posY) {
      $form.StartPosition = "Manual"
      $form.Location = New-Object System.Drawing.Point($posX, $posY)
    } else {
      $form.StartPosition = "CenterScreen"
    }
  }
  "topLeft" {
    $form.StartPosition = "Manual"
    $form.Location = New-Object System.Drawing.Point(0, 0)
  }
  "topRight" {
    $form.StartPosition = "Manual"
    $screen = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea
    $form.Location = New-Object System.Drawing.Point(($screen.Width - $formWidth), 0)
  }
  "bottomLeft" {
    $form.StartPosition = "Manual"
    $screen = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea
    $form.Location = New-Object System.Drawing.Point(0, ($screen.Height - $formHeight))
  }
  "bottomRight" {
    $form.StartPosition = "Manual"
    $screen = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea
    $form.Location = New-Object System.Drawing.Point(($screen.Width - $formWidth), ($screen.Height - $formHeight))
  }
  default {
    $form.StartPosition = "CenterScreen"
  }
}

# タブコントロール
$tabControl = New-Object System.Windows.Forms.TabControl
$tabControl.Dock = "Fill"

# 項目を開く処理(フォルダ or プログラム)
$openAction = {
  param($listBox)
  $sel = $listBox.SelectedItem
  if ($null -eq $sel) { return }

  $p = [Environment]::ExpandEnvironmentVariables([string]$sel.path)
  $selArgs = if ($sel.args) { [Environment]::ExpandEnvironmentVariables([string]$sel.args) } else { $null }

  try {
    # フォルダ or シェル特殊フォルダの場合は explorer.exe で開く
    $isFolder = (Test-Path $p -PathType Container) -or ($p -like "shell:*")
    
    if ($isFolder) {
      Start-Process explorer.exe -ArgumentList $p
    } elseif ($selArgs) {
      Start-Process $p -ArgumentList $selArgs
    } else {
      Start-Process $p
    }
  } catch {
    [System.Windows.Forms.MessageBox]::Show("開けませんでした: $p`n$($_.Exception.Message)")
  }
}

# カテゴリごとにタブを作成(settingsキーはスキップ)
foreach ($category in $config.PSObject.Properties) {
  if ($category.Name -eq "settings") { continue }

  $tabPage = New-Object System.Windows.Forms.TabPage
  $tabPage.Text = $category.Name

  $list = New-Object System.Windows.Forms.ListBox
  $list.Dock = "Fill"
  $list.DisplayMember = "name"
  $list.DrawMode = "OwnerDrawFixed"
  $list.ItemHeight = $itemHeight

  # オーナードロー描画(パネル風)
  $list.Add_DrawItem({
    param($sender, $e)
    if ($e.Index -lt 0) { return }

    # 背景描画
    if (($e.State -band [System.Windows.Forms.DrawItemState]::Selected) -ne 0) {
      $e.Graphics.FillRectangle([System.Drawing.SystemBrushes]::Highlight, $e.Bounds)
    } else {
      $e.Graphics.FillRectangle([System.Drawing.SystemBrushes]::Window, $e.Bounds)
    }

    # 下線を描画(境界線)
    $pen = New-Object System.Drawing.Pen([System.Drawing.Color]::LightGray, 1)
    $y = $e.Bounds.Bottom - 1
    $e.Graphics.DrawLine($pen, $e.Bounds.Left, $y, $e.Bounds.Right, $y)
    $pen.Dispose()

    # テキスト描画
    $item = $sender.Items[$e.Index]
    $text = if ($item.name) { $item.name } else { $item.ToString() }
    
    if (($e.State -band [System.Windows.Forms.DrawItemState]::Selected) -ne 0) {
      $brush = [System.Drawing.SystemBrushes]::HighlightText
    } else {
      $brush = [System.Drawing.SystemBrushes]::WindowText
    }
    
    $sf = New-Object System.Drawing.StringFormat
    $sf.LineAlignment = "Center"
    $textRect = [System.Drawing.RectangleF]::new($e.Bounds.X + 8, $e.Bounds.Y, $e.Bounds.Width - 16, $e.Bounds.Height)
    $e.Graphics.DrawString($text, $e.Font, $brush, $textRect, $sf)

    $e.DrawFocusRectangle()
    $sf.Dispose()
  })

  foreach ($it in $category.Value) {
    if ($null -ne $it.name -and $null -ne $it.path) {
      [void]$list.Items.Add($it)
    }
  }

  $list.Add_DoubleClick({ $openAction.Invoke($this) })

  $tabPage.Controls.Add($list)
  $tabControl.TabPages.Add($tabPage)
}

$form.Controls.Add($tabControl)
[void]$form.ShowDialog()

ポイント解説

image.png

フォルダ・プログラム・URL対応: Start-Processはパスの種類を自動判別します。フォルダならエクスプローラー、EXEなら実行、URLならブラウザで開きます。

引数の指定: argsプロパティでプログラムに引数を渡せます。ファイルを特定アプリで開く場合などに便利です。

オーナードロー描画: DrawMode = "OwnerDrawFixed"ItemHeightを設定し、DrawItemイベントで描画を制御することで、行の高さを自由に変更できます。

実行方法

.\FolderLauncher.ps1

まとめ

フォルダ、プログラム、URLをひとつのランチャーで管理できるようになりました。JSONを編集するだけでカテゴリやショートカットを自由に追加できます。

動画解説

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?