System.Environment.GetFolderPath メソッドとSystem.Environment.SpecialFolder 列挙型を使えば良いらしい。
- msdn
- Microsoft Docs / PowerShell / Scripting / レジストリ エントリの操作
- @IT / Insider.NET / .NET TIPS / 2003-05-30: Windowsのシステム・フォルダのパスを取得するには?
- ほそぼそプログラミング日記 / 2016-12-23: 【PowerShell】特殊フォルダを取得する
- stackoverflow / 2013-05-20: How can I use powershell to call SHGetKnownFolderPath?
- gist / josy1024 / powershell set shell folders! Use the SHGetFolderPath or SHGetKnownFolderPath function instead Shell Folders
- 西海岸より / 2010-02-07: バッチファイルで環境変数にコマンドの実行結果を格納(複数行)
例えば PowerShell を使って以下のようにするのが簡単なようだ
なぜか Download フォルダがなくて、これは SHGetKnownFolderPath 使って取るらしいんだけど、
レジストリの "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" 見るとあるらしい。
例えば REG コマンドを使うと以下のようにして一覧を取得できる。
で、この結果を cmd.exe で使いたい場合はちょっと面倒だが以下のようにする必要がある。
MS のコマンドラインツールは本当に酷過ぎるわ。ゴミかよ。
PowerShell 使うとどうにかなるみたいだが。
ps は ps でデフォルトでは実行権限出てないので使い難い
Cygwin なら以下のような感じで一覧取れば良い。
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
20201206: 更新
レジストリ直接読まず SHGetFolderPath か SHGetKnownFolderPath 使うべきだとコメント付いてたんだが、
SHGetFolderPath で済むところはレジストリ見る必要性はそもそもなくて
これは SHGetKnownFolderPath を PowerShell から使おうとすると地味に面倒というのがこの話。
とりあえず、
辺りを参考に書いてみると、
Add-Type 使って DllImport した上で更に LPWStr を受け取るため string を事前に用意しとく必要があるようだ。
取得した path は CoTaskMemFree() で解放する必要があって面倒なのだが、これは Marshal が面倒を見てくれるらしい。
以上の準備が出来てれば
ここの話は「コマンドラインからの取得 」の文脈、つまりこの結果を Windows の標準機能のみを用いて cmd で使いたいという話なので、出来はするけど、どうやっても面倒だよねという話。
あと、KnownFolders の GUID は別途知ってる必要がある。
レジストリ直接読まず SHGetFolderPath か SHGetKnownFolderPath 使うべきだとコメント付いてたんだが、
SHGetFolderPath で済むところはレジストリ見る必要性はそもそもなくて
これは SHGetKnownFolderPath を PowerShell から使おうとすると地味に面倒というのがこの話。
とりあえず、
- Gist / josy1024 / SH-Set-KnownFolderPath.ps1
- IDERA / Database Tools / PowerShell / Ask the Experts / PowerShell for Windows / 2011-09-30: Looking for P/Invoke for SHGetKnownFolderPath in shell.dll
- @IT / アジャイル/DevOps / Insider.NET / .NET TIPS / 2003-05-09: Win32 APIやDLL関数を呼び出すには?
- Microsoft / Docs / .NET / .NET Framework / 2019-03-20: アンマネージド コードとの相互運用
- CodeZine / Windows PowerShell 入門 / 2008-05-29: Windows PowerShell 入門(6)−関数編1
- stackoverflow
辺りを参考に書いてみると、
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 は別途知ってる必要がある。
以下に、上記 PowerShell 実装とほぼ同じやり方のスクリプトを見つけた。
out IntPtr pszProfilePath で取って、コピー後に [System.Runtime.InteropServices.Marshal]::FreeCoTaskMem($ptr) で開放するかの違いかな?
- stackoverflow / 2013-05-20: How can I use powershell to call SHGetKnownFolderPath? # 2021-06-22: comment 68082196
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