SHGetFileInfoをDllImportからLibraryImportに書き換え
あの、Windowsのファイルアイコンやら色々な情報を獲得する SHGetFileInfo
の宣言を、DllImport
からLibraryImport
に書き換えて、憎きSYSLIB1054
を一つ撲滅する方法をお伝えします。
まず、DllImport
のパターンは、よく見るのはこういう宣言ですね。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttribs, ref SHFILEINFO psfi, uint cbFileInfo, SHGFI uFlags);
そして使い方はこんな感じですよね?
SHFILEINFO shinfo = new();
IntPtr shFileInfoResult;
// ここ
shFileInfoResult = SHGetFileInfo(path, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI.SHGFI_ICON | SHGFI.SHGFI_SMALLICON | SHGFI.SHGFI_TYPENAME);
if (shFileInfoResult == IntPtr.Zero || shinfo.hIcon == IntPtr.Zero)
{
int lastError = Marshal.GetLastWin32Error();
throw new Exception($"SHGetFileInfo Failed with error code {lastError}");
}
BitmapSource icon = Imaging.CreateBitmapSourceFromHIcon(shinfo.hIcon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DestroyIcon(shinfo.hIcon);
// あとはiconとshinfo.szTypeNameでアイコンとファイル種類が手に入る
で、今となってはSHGetFileInfo
に警告SYSLIB1054
が出てくる、と。
そしてVisual Studioの提案のままに、DllImport
からLibraryImport
に書き換えるとこうなります。
/*
// これが
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttribs, ref SHFILEINFO psfi, uint cbFileInfo, SHGFI uFlags);
*/
// ↓こう
[LibraryImport("shell32.dll", EntryPoint = "SHGetFileInfoW", StringMarshalling = StringMarshalling.Utf16)]
private static partial IntPtr SHGetFileInfo(string pszPath, uint dwFileAttribs, ref SHFILEINFO psfi, uint cbFileInfo, SHGFI uFlags);
そして、悲惨なことに第三引数psfi
でエラーになってしまうんです。
その回避方法となりますが、エラーになったSHFILEINFO
の書き換えが必要になります。できあがりがこちら。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public unsafe struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
public fixed ushort szDisplayName[260];
public fixed ushort szTypeName[80];
};
これで、エラー自体は抑制できますが、今度は別の場所が問題になります。szDisplayName
や szTypeName
が string
型から ushort
配列になったのですから当然ですね。
szDisplayName
やszTypeName
をstring
型に変換する必要があります。ここではszTypeName
を対象にします。
string typeName = String.Empty;
unsafe
{
typeName = Utf16StringMarshaller.ConvertToManaged(shinfo.szTypeName) ?? String.Empty;
}
とunsafe
で括ってUtf16StringMarshaller.ConvertToManaged
を使えばいいです。
以上。
追記:Prism調べるの止めました。関連記事も近日中に削除します。