在C#中使用SHAssocEnumHandlers

我正在努力在C#中调用WinAPI SHAssocEnumHandlers。

using System; using System.Runtime.InteropServices; namespace AssocHandlerTest { [Flags] public enum ASSOC_FILTER { ASSOC_FILTER_NONE = 0x0, ASSOC_FILTER_RECOMMENDED = 0x1 }; [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("F04061AC-1659-4a3f-A954-775AA57FC083")] public interface IAssocHandler { int GetName([Out, MarshalAs(UnmanagedType.LPWStr)] out string ppsz); int GetUIName([Out, MarshalAs(UnmanagedType.LPWStr)] out string ppsz); int GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] out string ppszPath, [Out] out int pIndex); int IsRecommended(); int MakeDefault([In, MarshalAs(UnmanagedType.LPWStr)] string pszDescription); int Invoke([In, MarshalAs(UnmanagedType.IUnknown)] object pdo); int CreateInvoker([In, MarshalAs(UnmanagedType.IUnknown)] object pdo, [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppInvoker); }; [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("973810ae-9599-4b88-9e4d-6ee98c9552da")] public interface IEnumAssocHandlers { int Next([In, MarshalAs(UnmanagedType.U4)] int celt, [Out, MarshalAs(UnmanagedType.Interface)] out IAssocHandler rgelt, [Out, MarshalAs(UnmanagedType.U4)] out int pceltFetched); }; class Program { [DllImport("Shell32.dll", CharSet = CharSet.Auto)] static extern bool SHAssocEnumHandlers( [In, MarshalAs(UnmanagedType.LPWStr)] string pszExtra, [In] ASSOC_FILTER afFilter, [Out, MarshalAs(UnmanagedType.Interface)] out IEnumAssocHandlers ppEnumHandler); static void Main(string[] args) { const string extension = ".html"; try { IEnumAssocHandlers enumAssocHandlers = null; SHAssocEnumHandlers(extension, ASSOC_FILTER.ASSOC_FILTER_NONE, out enumAssocHandlers); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadLine(); } } } 

调用SHAssocEnumHandlers时我只得到一个

“意外错误:HRESULT:0x80004005(E_FAIL)”

stacktrace显示的是exeption

 System.StubHelpers.InterfaceMarshaler.ConvertToManaged(IntPtr pUnk, IntPtr itfMT, IntPtr classMT, Int32 flags) 

我想我可能错过了一些实现。 但我无法弄清楚是什么。

更新1

此错误仅发生在Windows 7上。在Windows 10计算机上,它可以正常工作。 (在各种win7和win10机器上测试过)

我找到了一种在Windows 7 maschine上获得结果的方法。

通过这种方式你可以超越指针。

我不是百分之百确定它为什么有效。 我相信这不是最安全的方式。

 using System; using System.Runtime.InteropServices; namespace AssocHandlerWithPointer { [Flags] public enum ASSOC_FILTER { ASSOC_FILTER_NONE = 0x00000000, ASSOC_FILTER_RECOMMENDED = 0x00000001 } public class Test { [DllImport("Shell32", EntryPoint = "SHAssocEnumHandlers", PreserveSig = false)] public extern static void SHAssocEnumHandlers([MarshalAs(UnmanagedType.LPWStr)] string pszExtra, ASSOC_FILTER afFilter, [Out] out IntPtr ppEnumHandler); // IEnumAssocHandlers [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)] private delegate int FuncNext(IntPtr refer, int celt, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Interface, SizeParamIndex = 1)] IntPtr[] rgelt, [Out] out int pceltFetched); // IAssocHandler [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)] private delegate int FuncGetName(IntPtr refer, out IntPtr ppsz); [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)] private delegate int FuncGetUiName(IntPtr refer, out IntPtr ppsz); static void Main(string[] args) { const string extension = ".html"; IntPtr pEnumAssocHandlers; SHAssocEnumHandlers(extension, ASSOC_FILTER.ASSOC_FILTER_RECOMMENDED, out pEnumAssocHandlers); IntPtr pFuncNext = Marshal.ReadIntPtr(Marshal.ReadIntPtr(pEnumAssocHandlers) + 3 * sizeof(int)); FuncNext next = (FuncNext)Marshal.GetDelegateForFunctionPointer(pFuncNext, typeof(FuncNext)); IntPtr[] pArrayAssocHandlers = new IntPtr[255]; int num; int resNext = next(pEnumAssocHandlers, 255, pArrayAssocHandlers, out num); if (resNext == 0) { for (int i = 0; i < num; i++) { IntPtr pAssocHandler = pArrayAssocHandlers[i]; IntPtr pFuncGetName = Marshal.ReadIntPtr(Marshal.ReadIntPtr(pAssocHandler) + 3 * sizeof(int)); FuncGetName getName = (FuncGetName)Marshal.GetDelegateForFunctionPointer(pFuncGetName, typeof(FuncGetName)); IntPtr pName; int resGetName = getName(pAssocHandler, out pName); Console.WriteLine("Path: " + Marshal.PtrToStringUni(pName)); IntPtr pFuncGetUiName = Marshal.ReadIntPtr(Marshal.ReadIntPtr(pAssocHandler) + 4 * sizeof(int)); FuncGetUiName getUiName = (FuncGetUiName)Marshal.GetDelegateForFunctionPointer(pFuncGetUiName, typeof(FuncGetUiName)); IntPtr pUiName; int resGetUiName = getUiName(pAssocHandler, out pUiName); Console.WriteLine("UIName: " + Marshal.PtrToStringUni(pUiName)); Marshal.Release(pArrayAssocHandlers[i]); } } Marshal.Release(pEnumAssocHandlers); Console.ReadLine(); } } } 

一些解释:

 + 3 * sizeof(int) 

接口IEnumAssocHandlers和IAssocHandlerinheritance自IUnknown。 所以他们总是实施三种方法。 这就是为什么你必须将指针移动到你的第一个函数3.和+ 4为你的第二个函数,依此类推。 不确定这个解释是否正确,但它的工作原理:)

 IntPtr[] pArrayAssocHandlers = new IntPtr[255]; 

Next funtion返回一个IAssocHandlers数组。 我发现没有办法找出给定扩展名的IAssocHandler的数量。 所以我将数组的大小设置为255。

Next函数得到三个参数(在NumberOfElementsToRetrieve中,输出ArrayOfIAssocHandlers,输出NumberOfRetrievedElements)声音有点奇怪,但那是文档中的内容。

相关链接:
SHAssocEnumHandlers
IEnumAssocHandlers
IAssocHandler

注意:
此示例中缺少各种error handling。