ããã¾ã§ P/Invoke å®ç¾©ã¯ããã¥ã¡ã³ãããããã¼ãã¡ã¤ã«ããææ¸ããããã¨ãå¤ãã£ãã®ã§ãããæè¿ã¯å¿ è¦ãª Win32 API ã COM ã¤ã³ã¿ã¼ãã§ã¼ã¹ãå¤ããªã£ã¦ããã®ã§ CsWin32 ã使ã£ã¦èªåçæãã¦ãã¾ãã
CsWin32 èªä½ã¯ win32metadata ã¨ããããã¸ã§ã¯ãã®ææç©ã¨ãªã winmd ãã¡ã¤ã«ãããå¿ è¦ãªãã®ã ãã Source Generator ã§çæãããã¼ã«ã«ãªã£ã¦ãã¾ãã
çæãã¦ã»ãã Win32 API 㨠COM ã¤ã³ã¿ã¼ãã§ã¼ã¹ã«é¢ä¿ããæ§é ä½ã¨å®æ°ãèªåçæãããã®ã§ãä¾åé¢ä¿ãèªåã§ãã§ãã¯ããå¿ è¦ããªãã®ãæé«ã§ãã
詳ãã説æãã»ããã¢ããæ¹æ³ã¯å ¬å¼ã® GitHub ãªãã¸ããªã«ä»»ãã¦ãããããã¯å®éã« CsWin32 ã使ã£ã¦ã¢ããªã±ã¼ã·ã§ã³ãéçºããéã®æ³¨æç¹ã¨å¹çåã®ãã¤ã³ããæ¸ãã¦ããã¾ãã
CsWin32 ç¨ã®ããã¸ã§ã¯ããå¥éä½ã
ææ¸ã㧠P/Invoke å®ç¾©ãæ¸ãã¦ããæã¯åºæ¥ãã ããã¤ã³ã¿ã¼ã使ããªãããã«æ¸ãã¦ããã¨æãã¾ãããCsWin32 ã§ã¯ããã©ã¼ãã³ã¹ã¨å¹çãéè¦ããã¦ããã®ã§çµæ§ãã¤ã³ã¿ã¼ãåºã¦ãããã¨ãå¤ãã§ãã
ãã¤ã³ã¿ã¼ãå«ãã ã³ã¼ãããã«ãããã«ã¯ unsafe ã追å ããå¿ è¦ãããã®ã§ãã¢ããªã±ã¼ã·ã§ã³ãã使ãå ´åã«ã¯æèããããªãé¨åã§ãããªã®ã§ä»¥ä¸ã®ããã« CsWin32 㨠unsafe ãªã³ã¼ãã¯ã¯ã©ã¹ã©ã¤ãã©ãªããåãåºãã¦ãã¾ãã¨ã¹ãããªãã¾ãã
ããã¸ã§ã¯ãã® RootNamespace
ã Windows.Win32
ã«ãã¦ããã°è¿½å ã®ã³ã¼ããåãåå空éã«å®ç¾©ã§ããã®ã§ãèªåçæããã partial ã¯ã©ã¹ã«ã¡ã½ããã追å ããéã«ã便å©ã§ãã
æ大ã®ã¡ãªããã¯ã¡ã¤ã³ããã¸ã§ã¯ãã§ãã«ãã¨ã©ã¼ãçºçãã¦ããCsWin32 㨠Source Generator ã¯ã¯ã©ã¹ã©ã¤ãã©ãªã«éãã¦ããã®ã§ P/Invoke å¨ãã®å®ç¾©ãå·»ãè¾¼ã¾ããã«æ¸ããã¨ã§ãããã«ãã¨ã©ã¼ã«ãªã㨠Source Generator ã§çæãããã³ã¼ããã¨ã©ã¼æ±ãã«ãªãã®ãã¹ãã¬ã¹ãã«ã§ããã
PWSTR ãªå¼æ°ã¯ Span<char> ã«ç½®ãæãã
Win32 API ã使ã£ã¦ããã¨é »çºãããã¿ã¼ã³ã¨ãã¦ãå¼ã³åºãå´ãå¿ è¦ãµã¤ãºã®ã¡ã¢ãªã確ä¿ãã¦ããã®ãã¤ã³ã¿ã¼ã API ã«æ¸¡ããã¨ã§çµæãåãåãã¨ããæåãããã¾ãã
ç¹ã«æååãåãåãå ´åã«ã¯ StringBuilder
ã使ããã¨ãå¤ãã£ãã®ã§ãããæè¿ã§ã¯éæ¨å¥¨ã«ãªã£ã¦ããã®ã§å¥ã®æ¹æ³ã使ãå¿
è¦ãããã¾ãã
ããã¥ã¡ã³ãã«ã¯ãã¤ã³ã¿ã¼ã char
ã®é
åã使ãä¾ãããã¾ãããä»ã®æ代ã§ã¯ Span<T>
ã使ã£ã¦è§£æ±ºããã®ããã¹ãã§ããP/Invoke å®ç¾©ã«ç´æ¥æ¸¡ããã¨ã¯åºæ¥ã¾ããããfixed
ã使ã£ã¦ãã¤ã³ã¿ã¼ãåå¾ã§ããã®ã§ CsWin32 ã¨ã®ç¸æ§ãè¯ãã§ãã
ä¾ã¨ã㦠AssocQueryString
ã«å¯¾ã㦠Span<T>
ã使ããªã¼ãã¼ãã¼ãã追å ãã¾ãã
å¼æ°ã«ãã pszOut
ãçµæãæ¸ãè¾¼ã¾ãããã¤ã³ã¿ã¼ã«ãªãã®ã§ãããã Span<T>
ã«æ¸ãæãããªã¼ãã¼ãã¼ãã¨ãã¦ä»¥ä¸ã®ãããªå®ç¾©ã追å ãã¾ãã
public static unsafe HRESULT AssocQueryString(uint flags, ASSOCSTR str, string pszAssoc, string pszExtra, Span<char> pszOut, ref uint pcchOut) { fixed (char* pszOutLocal = pszOut) { return AssocQueryString(flags, str, pszAssoc, pszExtra, new PWSTR(pszOutLocal), ref pcchOut); } }
ãã£ã¦ãããã¨ã¯é£ãããªããåç´ã« Span<T>
ã®ãã¤ã³ã¿ã¼ãåºå®ãã¦æ¸¡ãã¦ããã ãã§ãã
å®ç¾©ããã¡ã½ãããå¼ã³åºããµã³ãã«ã¯ä»¥ä¸ã®ããã«ãªãã¾ãããã®ä¾ã§ã¯ C# æ¡å¼µåã«é¢é£ã¥ããã¢ããªã±ã¼ã·ã§ã³ã®ãã¹ãåå¾ãã¦ãã¾ãã
// stackalloc ã§æ¸ãè¾¼ã¿å ã®ã¡ã¢ãªã確ä¿ã㦠Span<T> ã«å ¥ãã Span<char> pszOut = stackalloc char[260]; // 確ä¿ããã¡ã¢ãªã®ãµã¤ãº uint pcchOut = 260; PInvoke.AssocQueryString(ASSOCF.ASSOCF_NOTRUNCATE, ASSOCSTR.ASSOCSTR_EXECUTABLE, ".cs", null, pszOut, ref pcchOut); // string 㯠\0 ããã£ã¦ãç¡è¦ãã¦ãµã¤ãºå確ä¿ãã¦ãã¾ãã®ã§ \0 ã§åã var result = new string(pszOut.TrimEnd('\0'));
æå¾ã«æåååããå¿
è¦ãããå StringBuilder
ã®æããããã¨è¦ã¯åé·ã«è¦ãã¾ããããã¡ãã®æ¹ãå§åçã«ããã©ã¼ãã³ã¹ãåªãã¦ãã¾ããã¡ã¢ãªç¢ºä¿ã« stackalloc
ã使ããã®ã§å¹ççã§ãããã¾ãã
ã¡ãªã¿ã« PWSTR
ã«ã¯ AsSpan
ã¨ããã¡ã½ãããç¨æããã¦ããã®ã§ãéæ¹åã®å ´å㯠unsafe ã³ã¼ãã使ããã¨ãªãå®å
¨ã«æ±ããããã«ãªã£ã¦ãã¾ãã
COM ãªãã¸ã§ã¯ãã¯ç¹å®ã®åã«ãã£ã¹ããã¦è¿ã
CsWin32 ã¯ããã©ã«ã㧠COM ãªãã¸ã§ã¯ãã®ãã¼ã·ã£ãªã³ã°ãæå¹åããã¦ããã®ã§ãCOM ãªãã¸ã§ã¯ããè¿ãé¢æ°ã¯ãã¼ã·ã£ãªã³ã°å¯¾å¿ã®ãªã¼ãã¼ãã¼ããåæã«çæããã¾ãããåã object
åºå®ã«ãªã£ã¦ãã¾ãã®ã§ä½¿ãåæãããããã¾ããã
ããã§ä»¥ä¸ã®ãããªãªã¼ãã¼ãã¼ãã追å ããã¨ãCOM ã¤ã³ã¿ã¼ãã§ã¼ã¹ãæå®ããã ã㧠IID ã®è§£æ±ºã¨ãé©åãªåã¸ã®ãã£ã¹ãæ¸ã¿ãªãã¸ã§ã¯ããåãåããããã«ãªãã¾ã
public static HRESULT SHCreateItemFromParsingName<T>(string pszPath, System.Com.IBindCtx pbc, out T ppv) { var hr = SHCreateItemFromParsingName(pszPath, pbc, typeof(T).GUID, out var o); ppv = (T)o; return hr; }
ã¡ã½ãããå¼ã³åºãéã« out
å¼æ°ã§åãæ示çã«æå®ããã°ããã£ã¹ãä¸è¦ã§ã¨ã¦ãã¹ãããªãã¾ãã
PInvoke.SHCreateItemFromParsingName("...", null, out IShellItem? shellItem);
æ®ã©ã®ã±ã¼ã¹ã§ã¯èªåçã«ãã¼ã·ã£ãªã³ã°ç¨ã®ãªã¼ãã¼ãã¼ãã追å ããã¾ããããã¾ã«ãã¼ã·ã£ãªã³ã°ããã¦ããããã¤ã³ã¿ãç´æ¥è¿ãã¡ã½ããããçæãããªããã¨ãããã¾ãã
æè¿ã 㨠SHGetPropertyStoreFromParsingName
ã¨ããé¢æ°ã¯ void**
㨠out void*
ã®ãªã¼ãã¼ãã¼ãããçæããã¾ããã§ããããããã£ãå ´åã¯ä»¥ä¸ã®ãããªãªã¼ãã¼ãã¼ãã追å ããã¨ä½¿ãããããªãã¾ãã
public static unsafe HRESULT SHGetPropertyStoreFromParsingName<T>(string pszPath, System.Com.IBindCtx pbc, GETPROPERTYSTOREFLAGS flags, out T ppv) { var hr = SHGetPropertyStoreFromParsingName(pszPath, pbc, flags, typeof(T).GUID, out var o); ppv = (T)Marshal.GetUniqueObjectForIUnknown(new IntPtr(o)); return hr; }
è¿ã£ã¦ãã COM ãªãã¸ã§ã¯ãã¸ã®ãã¤ã³ã¿ã Marshal.GetUniqueObjectForIUnknown
ãå¼ã³åºã㦠RCW ã§å
ã¿ã¾ãããã¨ã¯ãã£ã¹ãããã ãã§æ¬²ãããªãã¸ã§ã¯ããå¾ããã¨ãåºæ¥ã¾ãã
preserveSigMethods
ã¯æå®ããæ¹ãåããããã
CsWin32 ã®ã³ã¼ãçæã«é¢ããè¨å®ã¯ NativeMethods.json
ã«æ¸ããã¨ã«ãªã£ã¦ãã¦ãããã©ã«ãã§ã大ä½ããæãã®è¨å®ã«ãªã£ã¦ããã®ã§ãã preserveSigMethods
ã ãã¯è¨å®ããæ¹ãæ±ãããããªãã¾ãã
å
·ä½çã«ã¯ HRESULT
ãèªåçã« COMException
ã«å¤æãããã©ããã¨ããè¨å®ãªã®ã§ãããHRESULT
ã®ã¾ã¾å¤å®ããæ¹ãä¾å¤ã®ãã³ããªã³ã°ãè¡ãããã³ã¼ããã¹ãããªãã¾ãããä¾å¤ãå¿
è¦ãªå ´å㯠HRESULT
ã« ThrowOnFailure
ã¡ã½ãããç¨æããã¦ããã®ã§åçã®å¦çã¯ç°¡åã«æ¸ãã¾ãã
if (PInvoke.SHCreateItemFromParsingName("...", null, out IShellItem? shellItem).Succeeded) { // IShellItem ã®ä½æã«æåããå ´åã¯ããã«ãã // RCW ã®è§£æ¾ãã¹ã³ã¼ãã決ã¾ãã®ã§ãããããã Marshal.ReleaseComObject(shellItem); } // COMException ãå¿ è¦ãªã ThrowOnFailure ãæ示çã«å¼ã³åºã PInvoke.SHCreateItemFromParsingName("...", null, out IShellItem? shellItem).ThrowOnFailure();
1 ã¤ãããã®å¼ã³åºããªãã¨ãããããããè¤æ°ã® COM ãªãã¸ã§ã¯ããå¿
è¦ãªå ´åã¯ä¾å¤ãã³ããªã³ã°ãããªãã«ãªã¹ã«ãªãã®ã§ããã㦠HRESULT
ã®ã¾ã¾ä½¿ãã®ãç°¡åã§ãã
å¯è½ãªéããã³ãã«ç³»ã¯ SafeHandle
ã¨ãã¦æ±ã
Win32 API ã使ã£ã¦ããéãé¿ãã¦ã¯éããªãã®ã HWND
ã HBITMAP
ã¨ãã£ããã³ãã«ç³»ã§ããããããå ´é¢ã§å¿
è¦ã«ãªãã¾ãã CsWin32 ã¯èªåçã« SafeHandle
ãå®è£
ããã¯ã©ã¹ãçæãã¦ããã¾ãã
ä»åã¯ã¡ãã»ã¼ã¸ããã¯ãè¡ãããã« SetWindowsHookEx
ãå¼ã³åºãä¾ãæãã¦ã¿ã¾ãã
é常ãªã HHOOK
ãè¿ãã¦ããã®ã IntPtr
ã«ç½®ãæã㦠P/Invoke å®ç¾©ãç¨æããã±ã¼ã¹ã§ãããCsWin32 ã§ã¯ UnhookWindowsHookExSafeHandle
ã¨ããã¯ã©ã¹ãèªåçæãã¦ãããã®ã§ã以ä¸ã®ãããªã·ã³ãã«ãªã³ã¼ãã§ããã¯ã®è§£æ¾ã¾ã§è¡ãã¾ãã
// ãã®å ´å㯠GetModuleHandle ã SafeHandle ãè¿ãã¦ãã var hook = PInvoke.SetWindowsHookEx(_idHook, HookProc, PInvoke.GetModuleHandle((string)null!), 0); // Close ã¡ã½ããã UnhookWindowsHookEx ãå¼ã³åºãã¦ããã hook.Close();
ãã³ãã«ã§ããã°èªåçã«ãªã¼ãã¼ãã¼ãå«ãã¦çæãããã®ã§ Bitmap å¨ããæ±ãå ´åã«ã便å©ã§ãããã ã WPF ã®ä¸çã«æã¡è¾¼ãéã«ã¯ DangerousGetHandle
ãå¼ã³åºãå¿
è¦ãããã¾ãã
ARM64 åãã®ãã«ãæã«ä¸é¨ã®å®ç¾©ãçæãããªãåé¡
æã ã§ãã Win32 API ã«ã¯ 32bit 㨠64bit ã§æ§é ä½ã®ãµã¤ãºãå¤ããã¨ããã±ã¼ã¹ãåå¨ãã¾ãã
ãã®ãããªã±ã¼ã¹ã« CsWin32 èªä½ã¯åé¡ãªã対å¿ããã¦ããã®ã§ãããARM64 åãã«é¢ãã¦ã¯ä»¥ä¸ã®ãããªè¦åãåºã¦ãç¹å®ã®å®ç¾©ãçæãããªãåé¡ãããã¾ãã
ã¡ãªã¿ã« x86 㨠x64 ã§ã¯åé¡ãªãã®ã§ Issue ãä½æããã¨ãããMSBuild å´ã®åé¡ã§ãããã¨ãçºè¦ãã¾ãããARM64 åãã®å ´åã®ã¿ PlatformTarget
ãå®ç¾©ãããã«æ£ããã³ã¼ãçæãè¡ãããªãããã§ãã
ãã¤ã㯠MSBuild å´ã§å¯¾å¿ãããã¯ãã§ãããããã¾ã§ã¯ CsWin32 ã®ããã¸ã§ã¯ããã¡ã¤ã«ã«ä»¥ä¸ã®ãããªå®ç¾©ã追å ãããã¨ã§ãæ¬ ãã PlatformTarget
ãè£ã£ã¦æ£ããã³ã¼ãçæåºæ¥ãããã«ãªãã¾ãã
<PropertyGroup Condition=" '$(_PlatformWithoutConfigurationInference)' == 'ARM64' "> <PlatformTarget Condition=" '$(PlatformTarget)' == '' ">ARM64</PlatformTarget> </PropertyGroup>
æå¾ã« ARM64 åãã«ãã«ããã¦ãã人ãã»ã¼å± ãªããã¨ãçºè¦ãã¦ãã¾ã£ãæãããã¾ãããCsWin32 ã使ã㨠P/Invoke ã§ã®é¢åãªå®ç¾©ãèªååãã¤æ´çãããåå空éã§å©ç¨ã§ããããã«ãªãã®ã§ãå§ãã§ãã
Win32 API ã COM èªä½ãè¨å¤§ãããã®ã§ãçæãããã³ã¼ããæ£ããåä½ããªãã¨ããåé¡ãé »çºããã®ã§ãããGitHub ã« Issue ãç«ã¦ãã°è§£æ±ºåºæ¥ããã®ãå¤ãã®ã§æ¹åã«è²¢ç®ãã¦ããããã§ããã