最低限DllImportの使い方を理解している方を対象にしています。この記事をコピペで利用した際の責任は一切負いません

あの、C# で Windowsのファイルアイコンやら色々な情報を獲得する SHGetFileInfo の宣言を、DllImportからLibraryImportに書き換えて、憎きSYSLIB1054を一つ撲滅する方法をお伝えします。

まず、C# での 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);

そして SHGETFILEINFO の使い方はこんな感じですよね?

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でアイコンとファイル種類が手に入る

で、今となっては C# の DllImport を使った 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でエラーになってしまうんです。

その 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];
};

これで、エラー自体は抑制できますが、今度は別の場所が問題になります。szDisplayNameszTypeNamestring型からushort配列になったのですから当然の話ですね。

szDisplayNameszTypeNamestring型に変換する必要があります。

ここではszTypeNameを対象にしますが、szDisplayNameも同様の処理となります。

string typeName = String.Empty;
unsafe
{
    typeName = Utf16StringMarshaller.ConvertToManaged(shinfo.szTypeName) ?? String.Empty;
}

unsafeで括ってUtf16StringMarshaller.ConvertToManagedを使えばいいです。

以上。