本文
Windows 10ではデスクトップのPCアイコンのプロパティから「コンピューターの基本的な情報の表示」を表示できます。C#ではSHGetKnownFolderIDList
にFOLDERID_ComputerFolder
を与えてLPITEMIDLIST
を取得した後、SEE_MASK_INVOKEIDLIST
を指定してShellExecuteExW
を呼び出すことでこれを表示できます。
using System;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
var pidlComputer = default(IntPtr);
try
{
pidlComputer = NativeMethods.SHGetKnownFolderIDList(
FOLDERID_ComputerFolder,
KF_FLAG_DEFAULT,
IntPtr.Zero);
var sei = new SHELLEXECUTEINFOW
{
cbSize = (uint)Marshal.SizeOf<SHELLEXECUTEINFOW>(),
fMask = SEE_MASK_INVOKEIDLIST,
lpVerb = "properties",
lpIDList = pidlComputer
};
NativeMethods.ShellExecuteExW(ref sei);
}
finally
{
Marshal.FreeCoTaskMem(pidlComputer);
}
}
private static class NativeMethods
{
[DllImport("shell32.dll", ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShellExecuteExW(
ref SHELLEXECUTEINFOW pExecInfo);
[DllImport("shell32.dll", ExactSpelling = true, PreserveSig = false)]
public static extern IntPtr SHGetKnownFolderIDList(
[MarshalAs(UnmanagedType.LPStruct)] Guid rfid,
uint dwFlags,
IntPtr hToken);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct SHELLEXECUTEINFOW
{
public uint cbSize;
public uint fMask;
public IntPtr hwnd;
public string lpVerb;
public string lpFile;
public string lpParameters;
public string lpDirectory;
public int nShow;
public IntPtr hInstApp;
public IntPtr lpIDList;
public IntPtr lpClass;
public IntPtr hkeyClass;
public uint dwHotKey;
public IntPtr hIconOrMonitor;
public IntPtr hProcess;
}
private static readonly Guid FOLDERID_ComputerFolder = Guid.Parse(
"{0AC0837C-BBF8-452A-850D-79D08E667CA7}");
private const uint KF_FLAG_DEFAULT = 0;
private const uint SEE_MASK_INVOKEIDLIST = 0x0000000C;
}
}
Windows 10以外ではおそらく普通にPCのプロパティが表示されます。
アンマネージリソースとtry...finally
var pidlComputer = default(IntPtr);
try
{
pidlComputer = NativeMethods.SHGetKnownFolderIDList(...);
...
}
finally
{
Marshal.FreeCoTaskMem(pidlComputer);
}
SHGetKnownFolderIDList
の戻り値であるpidlComputer
はアンマネージリソースのポインタであり、Marshal.FreeCoTaskMem
で解放する必要があります。try
の中でvar pidlComputer = ...
とするとfinally
のスコープからはpidlComputer
を参照できないので、try
の外側でpidlComputer
を宣言しています。
この冗長さを回避するためにはSafeHandle
の派生クラスを作成してusing
と組み合わせます。
オブジェクト初期化子による構造体の初期化
var sei = new SHELLEXECUTEINFOW
{
cbSize = ...
};
構造体はnew 型名 {field1 = ..., field2 = ..., ...}
の形式で初期化できます。
構造体中のstring
のP/Invoke
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct SHELLEXECUTEINFOW
{
...
public string lpVerb;
...
]
P/Invoke時(DllImport
属性を持つ関数の呼び出し時)、構造体中の文字列は自動的にIntPtr
型へ変換・復元されます。IntPtr
を指定して毎回Marshal.PtrToStringAnsi/Uni
を呼び出すこともできますが、文字列を再利用しなければこちらの方が簡潔です。変換時のANSI/UNICODEはStructLayout
属性のCharSet
で指定します。
UnmanagedType.LPStruct
C#側で値渡しした構造体(Guid
等)をP/Invoke時にそのポインタとして渡します。C++のREFCLSID
やREFIID
にC#のtype(...).GUID
等を渡すとき、ref Guid ...
とすると必要になる一時的な変数に省略に使います。