【发布时间】:2018-01-26 10:54:14
【问题描述】:
我想重建一个 OpenWith 上下文菜单。
到目前为止,我使用SHAssocEnumHandler 来检索给定文件扩展名的所有可能应用程序。
现在我想调用所选AssocHandler 的Invoke method。此方法需要一个指向IDataObject 的指针来调用具有给定对象的应用程序。
在我的情况下,我有一个要打开的文件的路径:
c:\devtest\htmlTest.html
我的问题是如何从这条路径到达IDataObject。然后是一个指向这个对象的指针。
到目前为止我的代码:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
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 FuncGetUiName(IntPtr refer, out IntPtr ppsz);
[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)]
private delegate int FuncInvoke(IntPtr refer, IntPtr pdo);
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 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("UI: " + Marshal.PtrToStringUni(pUiName));
const string filePath = "c:\\devtest\\htmlTest.html";
DataObject dataObject = new DataObject();
dataObject.SetData(filePath);
IntPtr pDataObject = Marshal.GetIUnknownForObject(dataObject);
IntPtr pFuncInvoke = Marshal.ReadIntPtr(Marshal.ReadIntPtr(pAssocHandler) + 5 * sizeof(int));
FuncInvoke invoke = (FuncInvoke) Marshal.GetDelegateForFunctionPointer(pFuncInvoke, typeof (FuncInvoke));
int resInvoke = invoke(pAssocHandler, Marshal.ReadIntPtr(pDataObject));
Console.WriteLine(resInvoke);
Console.WriteLine("-----");
Marshal.Release(pArrayAssocHandlers[i]);
}
}
Marshal.Release(pEnumAssocHandlers);
Console.ReadLine();
}
}
}
我使用来自System.Windows.Forms 的DataObject。应该没问题,因为 DataObject 继承自 IDataObject。
我知道在循环内调用Invoke 方法对用例没有意义。而且也没有用户交互。
但是要让它工作应该没问题。
注意:
该解决方案必须适用于 Windows 7 和 Windows 10。
手动解析注册表并检索 shell/open/command 是不可能的解决方案。
【问题讨论】:
标签: c# winapi pinvoke marshalling