You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何将文件路径转为IDataObject指针调用IAssocHandler::Invoke

解决IAssocHandler.Invoke所需的IDataObject构建问题

你的核心问题在于直接传递字符串路径给DataObject无法被IAssocHandler正确识别——IAssocHandler::Invoke期望的是包含CF_HDROP格式数据的IDataObject,这是Shell用于传递文件列表的标准格式,而非简单的字符串。下面是兼容Windows 7和10的正确实现方案:

关键原理

CF_HDROP是Windows Shell定义的剪贴板格式,用于表示一个或多个文件路径。WinForms的DataObject内置了对该格式的支持,通过SetFileDropList方法可以自动构建符合要求的IDataObject,无需手动调用底层Win32 API构建HDROP结构。

修改后的核心代码

替换你原来的DataObject构建部分,以下是完整的修正后代码片段:

using System;
using System.Collections.Specialized;
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)
            {
                const string filePath = @"c:\devtest\htmlTest.html";
                // 构建包含文件路径的StringCollection
                StringCollection fileList = new StringCollection();
                fileList.Add(filePath);

                // 创建符合CF_HDROP格式的DataObject
                DataObject dataObject = new DataObject();
                dataObject.SetFileDropList(fileList);

                // 获取IDataObject的COM接口指针
                IntPtr pDataObject = Marshal.GetComInterfaceForObject(dataObject, typeof(IDataObject));

                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));
                    // 释放字符串指针(IAssocHandler::GetUiName返回的字符串需要手动释放)
                    Marshal.FreeCoTaskMem(pUiName);

                    // 调用Invoke方法
                    IntPtr pFuncInvoke = Marshal.ReadIntPtr(Marshal.ReadIntPtr(pAssocHandler) + 5 * sizeof(int));
                    FuncInvoke invoke = (FuncInvoke)Marshal.GetDelegateForFunctionPointer(pFuncInvoke, typeof(FuncInvoke));
                    int resInvoke = invoke(pAssocHandler, pDataObject);
                    Console.WriteLine($"Invoke返回值: {resInvoke}");
                    Console.WriteLine("-----");

                    Marshal.Release(pArrayAssocHandlers[i]);
                }

                // 释放IDataObject指针
                Marshal.Release(pDataObject);
            }

            Marshal.Release(pEnumAssocHandlers);
            Console.ReadLine();
        }
    }
}

关键修正点说明

  1. 正确构建CF_HDROP格式数据:
    使用DataObject.SetFileDropList方法传入StringCollection(可以包含多个文件路径),该方法会自动将数据打包为CF_HDROP格式,完全符合Shell接口的要求。

  2. 正确获取IDataObject指针:
    使用Marshal.GetComInterfaceForObject直接获取IDataObject的COM接口指针,而不是GetIUnknownForObject——虽然IUnknown可以转换为IDataObject,但直接获取目标接口指针更简洁可靠。

  3. 内存泄漏修复:

    • IAssocHandler::GetUiName返回的字符串指针需要调用Marshal.FreeCoTaskMem手动释放(因为它是由Shell分配的内存)。
    • 手动释放IDataObject的COM指针,避免内存泄漏。

兼容性说明

该方案完全兼容Windows 7和Windows 10:

  • SetFileDropList是.NET Framework 2.0以来的标准API,在Win7及以上系统均支持。
  • CF_HDROP是Windows Shell的原生格式,所有版本的Shell接口都能正确识别。

内容的提问来源于stack exchange,提问作者Patrick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:33:54