hack のためのネタ帳, etc,,,

System.Environment.GetFolderPath メソッドとSystem.Environment.SpecialFolder 列挙型を使えば良いらしい。

Tips

コマンドラインからの取得

例えば PowerShell を使って以下のようにするのが簡単なようだ
powershell "[Environment]::GetFolderPath('MyDocuments')"
取り得る値は以下の方法で取得できるらしい
powershell "[Enum]::GetNames([Environment+SpecialFolder])"

なぜか Download フォルダがなくて、これは SHGetKnownFolderPath 使って取るらしいんだけど、
レジストリの "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" 見るとあるらしい。
例えば REG コマンドを使うと以下のようにして一覧を取得できる。
REG QUERY "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
Download フォルダだけとろうと思うとこんな感じ。
REG QUERY "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" /v {374DE290-123F-4565-9164-39C4925E467B}

で、この結果を cmd.exe で使いたい場合はちょっと面倒だが以下のようにする必要がある。
FOR /F "usebackq tokens=*" %i in (`powershell "[Environment]::GetFolderPath('MyDocuments')"`) do @set HOGE=%i
echo %HOGE%
ところが、ps の結果は1行まるまる取れば問題ないが、REG は変数名、型、値になってて、"Start Menu" のように空白が混ざってると token 綺麗に切れなくて死ぬ。
MS のコマンドラインツールは本当に酷過ぎるわ。ゴミかよ。

PowerShell 使うとどうにかなるみたいだが。
powershell "(Get-Item -Path 'Registry::HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders').GetValue('Start Menu')"
FOR /F "usebackq tokens=*" %i in (`powershell "(Get-Item -Path 'Registry::HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders').GetValue('Start Menu')"`) do @set HOGE=%i
echo %HOGE%
cmd の使えなさと来たら
ps は ps でデフォルトでは実行権限出てないので使い難い

Cygwin なら以下のような感じで一覧取れば良い。
for i in /proc/registry/HKEY_CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Explorer/Shell\ Folders/*; do printf "%s\n%4s" "${i##*/}" ""; cat "$i";echo;done
20201205: 追記
20201206: 更新
レジストリ直接読まず SHGetFolderPath か SHGetKnownFolderPath 使うべきだとコメント付いてたんだが、
SHGetFolderPath で済むところはレジストリ見る必要性はそもそもなくて
これは SHGetKnownFolderPath を PowerShell から使おうとすると地味に面倒というのがこの話。

とりあえず、
辺りを参考に書いてみると、
function SHGetKnownFolderPath($guid) {
  $type = ([System.Management.Automation.PSTypeName]'KnownFolder').Type
  if ( -not $type ) {
$sig=@'
[DllImport("shell32.dll")]
public extern static int SHGetKnownFolderPath(ref Guid folderId, uint flags, IntPtr token, [MarshalAs(UnmanagedType.LPWStr)] out string path);
'@
    $type = Add-Type -MemberDefinition $sig -Name 'KnownFolder' -Using System.Text -PassThru
  }
  $path = ""
  $ret=$type::SHGetKnownFolderPath([ref]$guid,0,0,[ref]$path)
  $path
}
もしくは one liner で
function SHGetKnownFolderPath($guid) { $type = ([System.Management.Automation.PSTypeName]'KnownFolder').Type; if ( -not $type ) { $type = Add-Type -MemberDefinition '[DllImport("shell32.dll")] public extern static int SHGetKnownFolderPath(ref Guid folderId, uint flags, IntPtr token, [MarshalAs(UnmanagedType.LPWStr)] out string path);' -Name 'KnownFolder' -Using System.Text -PassThru } $path = ""; $ret=$type::SHGetKnownFolderPath([ref]$guid,0,0,[ref]$path); $path }
みたいにして
Add-Type 使って DllImport した上で更に LPWStr を受け取るため string を事前に用意しとく必要があるようだ。
取得した path は CoTaskMemFree() で解放する必要があって面倒なのだが、これは Marshal が面倒を見てくれるらしい。

以上の準備が出来てれば
SHGetKnownFolderPath("374DE290-123F-4565-9164-39C4925E467B") # Get 'Downloads'
みたいにはできるけど、手軽かと言われるとそうじゃないし、
ここの話は「コマンドラインからの取得 」の文脈、つまりこの結果を Windows の標準機能のみを用いて cmd で使いたいという話なので、出来はするけど、どうやっても面倒だよねという話。

あと、KnownFolders の GUID は別途知ってる必要がある。
20210914: 追記
以下に、上記 PowerShell 実装とほぼ同じやり方のスクリプトを見つけた。 最後のパラメータを [MarshalAs(UnmanagedType.LPWStr)] out string path で取るか、
out IntPtr pszProfilePath で取って、コピー後に [System.Runtime.InteropServices.Marshal]::FreeCoTaskMem($ptr) で開放するかの違いかな?

関連

このページへのコメント

レジストリーに直接書かれていますが、

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders

!Do not use this registry key
Use the SHGetFolderPath or SHGetKnownFolderPath function instead

0
Posted by はじめ 2020年12月04日(金) 12:59:25 返信

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Wiki内検索

フリーエリア

管理人/副管理人のみ編集できます