Skip to main content

launchan

launchan つくり中

launchan 作成中
ps1

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

function Main {
# $table =
Import-CSVToDataTable

Write-Host $table.Rows.Count
Write-Host $table.GetType().FullName

if ($null -eq $table.DefaultView) {
Write-Host "table.DefaultView is null"
return
}

$size = Get-Size 690 280

# Form
$form = New-Object System.Windows.Forms.Form
$form.Text = "launchan.ps1"
$form.Size = $size.FormSize

# 画面サイズを取得
$screen = [System.Windows.Forms.Screen]::PrimaryScreen
$screenBounds = $screen.WorkingArea # タスクバー除いた領域

# 左下に配置
$form.StartPosition = 'Manual'
$form.Location = New-Object System.Drawing.Point(((0 - 10), ($screenBounds.Height - $form.Height + 10)))

# Search TextBox
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = $size.textBoxLocation
$textBox.Width = $size.TextBoxWidth
$textBox.Anchor = "Top, Left, Right"
$form.Controls.Add($textBox)



<#
$clearButton = New-Object System.Windows.Forms.Button
$clearButton.Text = "×"
$clearButton.Size = New-Object System.Drawing.Size(25, 23)
$clearButton.Location = New-Object System.Drawing.Point(190, 10)
$clearButton.Add_Click({ $textBox.Text = "" })
$form.Controls.Add($clearButton)
# $form.Controls.AddRange(@($textBox, $clearButton))
#>

# Category ComboBox
$comboBox = New-Object System.Windows.Forms.ComboBox
$comboBox.DropDownStyle = 'DropDownList'
$comboBox.Location = $size.ComboBoxLocation
$comboBox.Width = $size.ComboBoxWidth
$comboBox.Anchor = "Top, Right"

# set ComboBox items
Write-Host $table.DefaultView # -> null
$distinctFiles = $table.DefaultView.ToTable($true, "Category") | ForEach-Object { $_.Category }
$comboBox.Items.Add("All") | Out-Null
$distinctFiles | Sort-Object -Unique | ForEach-Object { $comboBox.Items.Add($_) | Out-Null }
$comboBox.SelectedIndex = 0
$form.Controls.Add($comboBox)

# Result Grid
$grid = New-Object System.Windows.Forms.DataGridView
$grid.Location = $size.GridLocation
$grid.Size = $size.GridSize
$grid.Anchor = "Top, Left, Right, Bottom"
$grid.ReadOnly = $true
$grid.SelectionMode = 'FullRowSelect'
# $grid.AutoSizeColumnsMode = 'Fill'
$grid.AutoSizeColumnsMode = 'None'
$grid.DataSource = $table.DefaultView
$grid.RowHeadersVisible = $false
$grid.AllowUserToAddRows = $false
$grid.AllowUserToResizeRows = $false

$form.Controls.Add($grid)

# ▼ 検索フィルタ処理
function ApplyFilter {
$query = $textBox.Text
$category = $comboBox.SelectedItem
$filters = @()

if (-not [string]::IsNullOrWhiteSpace($query)) {
$escapedQuery = $query -replace "'", "''"
$filters += "Title LIKE '%$escapedQuery%'"
}

if ($category -ne "All" -and $category) {
$escapedFile = $category -replace "'", "''"
$filters += "Category = '$escapedFile'"
}

$table.DefaultView.RowFilter = ($filters -join " AND ")
}

$textBox.Add_TextChanged({ ApplyFilter })
$comboBox.Add_SelectedIndexChanged({ ApplyFilter })

$textBox.Add_KeyDown({
if ($_.Control -and $_.KeyCode -eq 'A') {
$textBox.SelectAll()
$_.Handled = $true
}
})

$grid.add_CellClick({
$row = $grid.CurrentRow
if ($row) { ShowRowDetails $row }
})

$grid.add_KeyDown({
param($_sender, $e)
if ($e.KeyCode -eq [System.Windows.Forms.Keys]::Enter) {
$row = $grid.CurrentRow
if ($row) {
ShowRowDetails $row
$e.SuppressKeyPress = $true
}
}
})

$grid.Add_CellMouseEnter({
param($sender, $e)
if ($e.ColumnIndex -ge 0 -and $e.RowIndex -ge 0) {
$colName = $grid.Columns[$e.ColumnIndex].Name
# if ($colName -eq 'Title') {
$grid.Cursor = [System.Windows.Forms.Cursors]::Hand
#}
}
if ($e.RowIndex -ge 0) {
if ($script:lastHoverRowIndex -ge 0 -and $script:lastHoverRowIndex -ne $e.RowIndex) {
$grid.Rows[$script:lastHoverRowIndex].DefaultCellStyle.BackColor = [System.Drawing.Color]::White
}

$grid.Rows[$e.RowIndex].DefaultCellStyle.BackColor = [System.Drawing.Color]::AliceBlue
$script:lastHoverRowIndex = $e.RowIndex
}

})

$grid.Add_CellMouseLeave({
param($sender, $e)
$grid.Cursor = [System.Windows.Forms.Cursors]::Default

if ($script:lastHoverRowIndex -ge 0) {
$grid.Rows[$script:lastHoverRowIndex].DefaultCellStyle.BackColor = [System.Drawing.Color]::White
$script:lastHoverRowIndex = -1
}
})

$grid.Add_CellFormatting({
param($sender, $e)

if ($e.RowIndex -ge 0 -and $e.ColumnIndex -ge 0) {
$cell = $grid.Rows[$e.RowIndex].Cells[$e.ColumnIndex]
$text = $cell.Value
if ($null -ne $text -and $text -is [string]) {
# セルの表示範囲を取得
$cellRectangle = $grid.GetCellDisplayRectangle($e.ColumnIndex, $e.RowIndex, $false)

# フォントとサイズから描画サイズを見積もる
$textSize = [System.Windows.Forms.TextRenderer]::MeasureText($text, $grid.Font)

# 文字がはみ出していたらツールチップを設定
if ($textSize.Width -gt $cellRectangle.Width) {
$cell.ToolTipText = $text
} else {
$cell.ToolTipText = ""
}
}
}
})

$grid.Add_PreviewKeyDown({
param($sender, $e)

if ($e.KeyCode -eq [System.Windows.Forms.Keys]::Tab) {
$form.SelectNextControl($grid, $true, $true, $true, $true)
$e.IsInputKey = $false # ← Tab キーを通常キーとして処理しないよう明示 (タブストップをほかのコントロールに移動するため)
}
})

# コンテキストメニューの生成
$menu = New-Object System.Windows.Forms.ContextMenuStrip

$grid.Add_MouseDown({
param($sender, $e)

if ($e.Button -eq [System.Windows.Forms.MouseButtons]::Right) {
# セルの位置を取得
$hit = $grid.HitTest($e.X, $e.Y)
if ($hit.RowIndex -ge 0) {
$grid.ClearSelection()
$grid.Rows[$hit.RowIndex].Selected = $true

$script:selectedFileName = $grid.Rows[$hit.RowIndex].Cells["Command"].Value

# 拡張子が .xls or .xlsx のときだけメニュー追加
$menu.Items.Clear()
if ($selectedFileName -match '\.xlsx?$') {
$item = New-Object System.Windows.Forms.ToolStripMenuItem
$item.Text = "読み取り専用で開く"


# クリック時の処理
$item.Add_Click({
Write-Host $selectedFileName
Start-Process "excel.exe" "/r `"$selectedFileName`""
})

$menu.Items.Add($item)
}

if ($menu.Items.Count -gt 0) {
$menu.Show($grid, $e.Location)
}
}
}
})

$form.Add_Shown({
# $form.Activate()
try {
$grid.Columns["Title"].DisplayIndex = 0
$grid.Columns["Command"].DisplayIndex = 1
$grid.Columns["Params"].DisplayIndex = 2
$grid.Columns["Tags"].DisplayIndex = 3
$grid.Columns["Category"].DisplayIndex = 4

$grid.AutoSizeColumnsMode = 'None'
$grid.ScrollBars = 'Both'

$grid.Columns["Title"].Width = 200
$grid.Columns["Command"].Width = 150
# $grid.Columns["Params"].AutoSizeMode = 'Fill'
$grid.Columns["Params"].Width = 100
$grid.Columns["Tags"].Width = 100
$grid.Columns["Category"].Width = 80

# $grid.Columns["Title"].Frozen = $true
# $grid.Columns["Command"].Frozen = $true

foreach ($col in $grid.Columns) {
$col.Resizable = [System.Windows.Forms.DataGridViewTriState]::True
}
} catch {}
})

[void]$form.ShowDialog()
}

# ------------------------------------------------------------------------------------------
# functions
# ------------------------------------------------------------------------------------------

function Import-CSVToDataTable {
$csvDir = Join-Path $PSScriptRoot "samples"
$csvFiles = Get-ChildItem -Path $csvDir -Filter *.csv

$script:table = New-Object System.Data.DataTable
$isColumnsAdded = $false

foreach ($csvFile in $csvFiles) {
$rows = Import-Csv -Path $csvFile.FullName -Encoding UTF8

if ($rows.Count -eq 0) {
continue
}

# Define column with first file
if (-not $isColumnsAdded) {
$rows[0].PSObject.Properties.Name | ForEach-Object {
[void]$table.Columns.Add($_)
}
[void]$table.Columns.Add("Category")
$isColumnsAdded = $true
}

foreach ($row in $rows) {
$datarow = $table.NewRow()
foreach ($col in $row.PSObject.Properties) {
$datarow[$col.Name] = $col.Value
}
$datarow["Category"] = $csvFile.BaseName
$table.Rows.Add($datarow)
}
}

if ($table.Rows.Count -eq 0) {
[System.Windows.Forms.MessageBox]::Show("CSVファイルにデータがありません", "launchan", 'OK', 'Error')
exit 1
}

# return $table
}

function Get-Size([int]$w, [int]$h) {
return @{
FormSize = [System.Drawing.Size]::new( $w , $h )
TextBoxWidth = $w - 150
ComboBoxWidth = 100
GridSize = [System.Drawing.Size]::new( $w - 40 , $h - 90)

TextBoxLocation = [System.Drawing.Point]::new( 10 , 10 )
ComboBoxLocation = [System.Drawing.Point]::new( $w - 130 , 10 )
GridLocation = [System.Drawing.Point]::new( 10 , 40 )
}
}

function ShowRowDetails($row) {
$rawCommand = $row.Cells["Command"].Value
$rawParams = $row.Cells["Params"].Value
$command = [Environment]::ExpandEnvironmentVariables($rawCommand)
$params = [Environment]::ExpandEnvironmentVariables($rawParams)

Write-Host "Command: $command"
Write-Host "Params: $params"

if ($params -eq "") {
Start-Process -FilePath $command
} else {
Start-Process -FilePath $command -ArgumentList $params
}
}

Main
launchan.vbs
Set fso = CreateObject("Scripting.FileSystemObject")
Set shell = CreateObject("WScript.Shell")

ps1Path = fso.GetParentFolderName(WScript.ScriptFullName) & "\launchan.ps1"

shell.Run "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File """ & ps1Path & """", 0, False

関連ツール

ショートカットのCSV化
ps1
$folderPath = "C:\Path\To\Your\Folder"  # ← 適宜変更
$outputCsv = "$folderPath\shortcuts_summary.csv"

$results = @()

# .lnk ファイルの処理(ショートカット)
$lnkFiles = Get-ChildItem -Path $folderPath -Filter *.lnk
foreach ($lnkFile in $lnkFiles) {
$shell = New-Object -ComObject WScript.Shell
$shortcut = $shell.CreateShortcut($lnkFile.FullName)
$targetPath = $shortcut.TargetPath
$exists = if (Test-Path $targetPath) { "Exists" } else { "Not Found" }

$results += [PSCustomObject]@{
Name = $lnkFile.BaseName
Target = $targetPath
Status = $exists
Type = "shortcut"
}
}

# .url ファイルの処理(URLリンク)
$urlFiles = Get-ChildItem -Path $folderPath -Filter *.url
foreach ($urlFile in $urlFiles) {
$lines = Get-Content $urlFile.FullName
$url = ($lines | Where-Object { $_ -match "^URL=" }) -replace "^URL=", ""
$status = if ($url -match '^https?://') { "URL" } else { "Invalid" }

$results += [PSCustomObject]@{
Name = $urlFile.BaseName
Target = $url
Status = $status
Type = "url"
}
}

# CSVに書き出し
$results | Export-Csv -Path $outputCsv -Encoding UTF8 -NoTypeInformation