トップ回答者
PowerShellからCOMインターフェイスのIShellLinkを呼び出してショートカットファイルのターゲットパスを取得できません。

質問
-
まず、COMインターフェイスのIShellLinkを呼び出すクラスを定義した以下のC#のコードを用意し、IShellLink.csという名前で保存します。
using System; using System.IO; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.Text; namespace ShortcutOperation { public class Shortcut { private IShellLink Link; private IPersistFile File; public Shortcut() { this.Link = (IShellLink)( new ShellLink() ); } public void Load( string lnkPath ) { this.File = (IPersistFile)this.Link; this.File.Load( lnkPath, 0 ); } public void Save( string lnkPath ) { this.File.Save( lnkPath, false ); } public string GetPath( int flags ) { StringBuilder sb = new StringBuilder( 65536 ); IntPtr finfData; this.Link.GetPath( sb, sb.Capacity, out finfData, flags ); return sb.ToString(); } public void SetPath( string path ) { this.Link.SetPath( path ); } public string GetDescription() { StringBuilder sb = new StringBuilder( 65536 ); this.Link.GetDescription( sb, sb.Capacity ); return sb.ToString(); } public void SetDescription( string description ) { this.Link.SetDescription( description ); } public string GetWorkingDirectory() { StringBuilder sb = new StringBuilder( 65536 ); this.Link.GetWorkingDirectory( sb, sb.Capacity ); return sb.ToString(); } public void SetWorkingDirectory( string workingDirectory ) { this.Link.SetWorkingDirectory( workingDirectory ); } public string GetArguments() { StringBuilder sb = new StringBuilder( 65536 ); this.Link.GetArguments( sb, sb.Capacity ); return sb.ToString(); } public void SetArguments( string arguments ) { this.Link.SetArguments( arguments ); } public short GetHotkey() { short hotKey; this.Link.GetHotkey( out hotKey ); return hotKey; } public void SetHotkey( short hotKey ) { this.Link.SetHotkey( hotKey ); } public int GetShowCmd() { int showCmd; this.Link.GetShowCmd( out showCmd ); return showCmd; } public void SetShowCmd( int showCmd ) { this.Link.SetShowCmd( showCmd ); } public string GetIconLocation() { StringBuilder iconPathSB = new StringBuilder( 65536 ); int iconIndex; this.Link.GetIconLocation( iconPathSB, iconPathSB.Capacity, out iconIndex ); return ( iconPathSB.ToString() + "," + iconIndex.ToString() ); } public void SetIconLocation( string iconPath, int iconIndex ) { this.Link.SetIconLocation( iconPath, iconIndex ); } public void Resolve( int flags ) { this.Link.Resolve( (IntPtr)0, flags ); } } [ComImport] [Guid("00021401-0000-0000-C000-000000000046")] public class ShellLink { } [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("000214F9-0000-0000-C000-000000000046")] public interface IShellLink { void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out IntPtr pfd, int fFlags); void GetIDList(out IntPtr ppidl); void SetIDList(IntPtr pidl); void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); void GetHotkey(out short pwHotkey); void SetHotkey(short wHotkey); void GetShowCmd(out int piShowCmd); void SetShowCmd(int iShowCmd); void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); void Resolve(IntPtr hwnd, int fFlags); void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); } }
次に、適当なショートカットファイルtest.lnkをあらかじめ作っておきます。
そして、PowerShellから
$Source = Get-Content -LiteralPath '.\IShellLink.cs' -Force -Encoding 'UTF8' -Raw $Types = Add-Type -TypeDefinition $Source -Language 'CSharp' -PassThru $Type = ( $Types | Where-Object -Property 'Name' -Value 'Shortcut' -EQ )[ 0 ] $Shortcut = New-Object -TypeName $Type.FullName $Shortcut.Load( '.\test.lnk' )
と入力して、このクラスのインスタンスを生成してtest.lnkを読み込みます。
この状態で、以下のショートカットの情報を取得するメソッドを実行すれば正常に動作します。
$Shortcut.GetArguments() #コマンドライン引数 $Shortcut.GetDescription() #説明 $Shortcut.GetHotkey() #ホットキー $Shortcut.GetIconLocation() #アイコンのパスとインデックス番号 $Shortcut.GetShowCmd() #ウィンドウスタイル $Shortcut.GetWorkingDirectory() #作業フォルダー
ところが、以下のようにして肝心要のターゲットパスを取得しようとすると、PowerShellのプロセスがアプリケーションエラーで落ちてしまいます。
$Shortcut.GetPath( 4 )
C#およびPowerShellのコードを何度見直しても、どこがおかしいのか分かりません。どなたかご教示お願いします。
当方では今までスタートメニューなどのショートカットファイルの管理をWSHで行ってきましたが、これからPowerShellでの管理に移行しようと検討しているところです。しかし、もしこれがWindowsの不具合ならば移行を断念せざるを得ません。
環境はWindows 10 Pro 64bit 1709、PowerShell 5.1.16299.64です。
- 編集済み fzok4234 2018年11月2日 10:20 PowerShellのバージョンの訂正