在 C# 中使用 SHAssocEnumHandlers
Use SHAssocEnumHandlers in C#
我很难在 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 时我只得到一个
"Unexpected error: HRESULT: 0x80004005 (E_FAIL)"
堆栈跟踪显示
处的异常
System.StubHelpers.InterfaceMarshaler.ConvertToManaged(IntPtr pUnk, IntPtr itfMT, IntPtr classMT, Int32 flags)
我想我可能缺少一些实现。但是我想不通是什么。
更新 1
此错误只发生在 Windows 7 上。在 Windows 10 机器上它工作正常。
(在各种win7和win10机器上测试过)
我找到了一种在 Windows 7 机器上获得结果的方法。
这样你就可以复习指针了。
我不是 100% 确定它为什么有效。而且我确信这不是最安全的方法。
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和IAssocHandler继承自IUnknown。所以他们总是实现三个方法。这就是为什么您必须将指向第一个函数的指针移动 3。第二个函数的指针移动 + 4,依此类推。
不确定这个解释是否正确,但它有效:)
IntPtr[] pArrayAssocHandlers = new IntPtr[255];
Next 函数 returns IAssocHandler 数组。我发现无法找出给定扩展的 IAssocHandler 数量。所以我将数组的大小设置为 255.
Next 函数获取三个参数(在 NumberOfElementsToRetrieve 中,在 ArrayOfIAssocHandlers 中,在 NumberOfRetrievedElements 中)听起来有点奇怪,但这就是文档中的内容。
相关链接:
SHAssocEnumHandlers
IEnumAssocHandlers
IAssocHandler
注:
此示例中缺少各种错误处理。
我很难在 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 时我只得到一个
"Unexpected error: HRESULT: 0x80004005 (E_FAIL)"
堆栈跟踪显示
处的异常System.StubHelpers.InterfaceMarshaler.ConvertToManaged(IntPtr pUnk, IntPtr itfMT, IntPtr classMT, Int32 flags)
我想我可能缺少一些实现。但是我想不通是什么。
更新 1
此错误只发生在 Windows 7 上。在 Windows 10 机器上它工作正常。 (在各种win7和win10机器上测试过)
我找到了一种在 Windows 7 机器上获得结果的方法。
这样你就可以复习指针了。
我不是 100% 确定它为什么有效。而且我确信这不是最安全的方法。
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和IAssocHandler继承自IUnknown。所以他们总是实现三个方法。这就是为什么您必须将指向第一个函数的指针移动 3。第二个函数的指针移动 + 4,依此类推。 不确定这个解释是否正确,但它有效:)
IntPtr[] pArrayAssocHandlers = new IntPtr[255];
Next 函数 returns IAssocHandler 数组。我发现无法找出给定扩展的 IAssocHandler 数量。所以我将数组的大小设置为 255.
Next 函数获取三个参数(在 NumberOfElementsToRetrieve 中,在 ArrayOfIAssocHandlers 中,在 NumberOfRetrievedElements 中)听起来有点奇怪,但这就是文档中的内容。
相关链接:
SHAssocEnumHandlers
IEnumAssocHandlers
IAssocHandler
注:
此示例中缺少各种错误处理。