C# 使用Win32 API进行文件拖放在某些应用程序(如文件资源管理器)上不起作用

C# 使用Win32 API进行文件拖放在某些应用程序(如文件资源管理器)上不起作用,c#,winapi,.net-core,pinvoke,ole,C#,Winapi,.net Core,Pinvoke,Ole,我已经在.NET核心应用程序上实现了。问题是,它适用于某些应用程序,而不适用于其他一些应用程序 作品:记事本++,记事本,写字板,MS Word,Gimp,7Zip 不工作:(Windows)文件浏览器、Chrome、FireFox、VS代码、Libre Writer、Android Studio 为什么会这样 我的实现IDataObject的“DataObject”为EnumFormatEtc()返回这个FORMATETC。其中我只返回一个表单等 new FORMATETC() {

我已经在.NET核心应用程序上实现了。问题是,它适用于某些应用程序,而不适用于其他一些应用程序

  • 作品:记事本++,记事本,写字板,MS Word,Gimp,7Zip
  • 不工作:(Windows)文件浏览器、Chrome、FireFox、VS代码、Libre Writer、Android Studio
为什么会这样

我的实现IDataObject的“DataObject”为
EnumFormatEtc()
返回这个
FORMATETC
。其中我只返回一个表单等

new FORMATETC()
{
    cfFormat = CF_HDROP,
    ptd = IntPtr.Zero,
    dwAspect = DVASPECT.DVASPECT_ICON,
    lindex = -1,
    tymed = TYMED.TYMED_HGLOBAL
}
QueryGetData()
上,如果格式的tymed是
tymed\u HGLOBAL
,则返回s\u OK表示我正在拖动文件。否则返回DV_E___TYMED,表示我没有该类型的数据

GetData()
GetDataHere()
上,如果
format.cfFormat==CF_HDROP
format.tymed==tymed.tymed_HGLOBAL
,我会像这样填充
介质:

medium = new STGMEDIUM();
medium.tymed = TYMED.TYMED_HGLOBAL;
medium.unionmember = mem;
medium.pUnkForRelease = IntPtr.Zero;
IntPtr CreateDropFiles(string[] strFiles)
{
    var buffer = new MemoryStream();
    var df = new DROPFILES();
    IntPtr ipGlobal = IntPtr.Zero;
    int bufferLength;

    foreach(var file in strFiles)
    {
        var b = Encoding.Unicode.GetBytes(file);
        buffer.Write(b, 0, b.Length);
        buffer.WriteByte(0);
        buffer.WriteByte(0);
    }
    buffer.WriteByte(0);
    bufferLength = (int)buffer.Length;

    // Allocate and get pointer to global memory
    int intTotalLen = Marshal.SizeOf(df) + bufferLength;
    ipGlobal = Marshal.AllocHGlobal(intTotalLen);

    df.pFiles = Marshal.SizeOf(df);
    df.fWide = true;

    Marshal.StructureToPtr(df, ipGlobal, true);
    var ipNew = new IntPtr(ipGlobal.ToInt64() + Marshal.SizeOf(df));
    Marshal.Copy(buffer.ToArray(), 0, ipNew, bufferLength);

    return ipGlobal;
}
,其中
mem
是分配给
DROPFILES
的内存,如下所示:

medium = new STGMEDIUM();
medium.tymed = TYMED.TYMED_HGLOBAL;
medium.unionmember = mem;
medium.pUnkForRelease = IntPtr.Zero;
IntPtr CreateDropFiles(string[] strFiles)
{
    var buffer = new MemoryStream();
    var df = new DROPFILES();
    IntPtr ipGlobal = IntPtr.Zero;
    int bufferLength;

    foreach(var file in strFiles)
    {
        var b = Encoding.Unicode.GetBytes(file);
        buffer.Write(b, 0, b.Length);
        buffer.WriteByte(0);
        buffer.WriteByte(0);
    }
    buffer.WriteByte(0);
    bufferLength = (int)buffer.Length;

    // Allocate and get pointer to global memory
    int intTotalLen = Marshal.SizeOf(df) + bufferLength;
    ipGlobal = Marshal.AllocHGlobal(intTotalLen);

    df.pFiles = Marshal.SizeOf(df);
    df.fWide = true;

    Marshal.StructureToPtr(df, ipGlobal, true);
    var ipNew = new IntPtr(ipGlobal.ToInt64() + Marshal.SizeOf(df));
    Marshal.Copy(buffer.ToArray(), 0, ipNew, bufferLength);

    return ipGlobal;
}
相比之下,使用WinForm的方法将文件拖放到文件资源管理器或VS代码中(如下所示)效果很好

var data = new System.Windows.Forms.DataObject(
    System.Windows.Forms.DataFormats.FileDrop,
    new string[] { file1, file2 });
dummy.DoDragDrop(data, System.Windows.Forms.DragDropEffects.Copy);

您是否考虑过安全问题(UAC)?不在同一UAC级别运行的两个应用程序无法通信(使用D&D)。例如,如果您以标准方式运行Explorer(而非admin),则无法使用以admin身份运行的应用程序进行D&D。请进行一些调试,查看您尝试拖动到这些应用程序时调用了哪些方法,以及它们的要求。并非所有应用程序都能处理
CF\u HDROP
。要支持广泛的应用程序,请实现尽可能多的应用程序。实现的格式越多,可以与之交换数据的应用程序就越多。也就是说,
DVASPECT\u图标
是错误的
dwAspect
对于
CF\u HDROP
,请使用
DVASPECT\u内容
。而您的
缓冲区
缺少一个字节,它需要用一个2字节的空Unicode字符终止字符串列表,但您要用一个空字节终止它。您需要2个空字节,就像每个字符串的结尾一样。如果您仍然有问题,我建议查看,看看它是如何实现
System.Windows.Forms.DataFormats.FileDrop