【问题标题】:C# drag-drop problemC#拖放问题
【发布时间】:2011-04-23 04:41:11
【问题描述】:

我有一个 C# .NET 3.5 应用程序,它允许从树中拖动项目并将它们作为文件拖放到文件夹中。这是我的代码

String absolutePathToFile=...get absolute path
DataObject dataObject = new DataObject();
StringCollection paths = new StringCollection();
paths.Add(absolutePathToFile);
dataObject.SetFileDropList(paths);
DoDragDrop(dataObject, DragDropEffects.Copy);

这很好用,除非与另一个通过拖放接受文件的 C# 应用程序交互时,另一个 C# 应用程序具有以下 DragOver 处理程序

if ((e.Data is DataObject) && (e.Data as DataObject).ContainsFileDropList())
{
    e.Effect = DragDropEffects.Copy;
}

块永远不会被执行,因为 e.Data 是 __ComObject 而不是 DataObject。有趣的是,当我将文件从文件夹中拖到第二个应用程序上时,它会将其视为 DataObject。

如何使第一个 C# 应用程序中的 DataObject 在第二个 C# 应用程序中显示为 DataObject?

【问题讨论】:

  • 尝试将 e.Data 转换为 DataObject。例如:DataObject obj = e.Data as DataObject; if (obj != null) { //Your code }
  • @fardjad 如果(e.Data is DataObject) 为假,那么您尝试将 e.Data 安全地转换为 DataObject,它将为空。
  • e.Data 是一个 COM 对象,In the Microsoft .NET Framework, the GetType.Type operator for a COM object will return the System.__ComObject class. In some coding scenarios, you may have to know the specific class for an object. [ support.microsoft.com/kb/320523 ] 当然,您应该将此 e.Data 转换为 DataObject 或其他类。
  • 让我明确一点:第二个接收文件的应用程序是我们需要集成的外部应用程序。我无法修改它。我使用 Reflector 提取代码然后构建测试应用程序。测试应用程序显示,当我从资源管理器中拖动文件时,应用程序会看到 DataObject,但是当我从 C# 应用程序启动拖动时,它会看到 __ComObject。此外,C# 应用程序中的本地拖放从 e.Data 返回 DataObject,而跨进程边界拖放则生成 __ComObject。从资源管理器和本机应用程序拖放产生 DataObject。我正在尝试

标签: c# drag-and-drop


【解决方案1】:

我决定使用 .NET 互操作实现 IDataObject,从 IDataObject 开始,从 here 实现。然后我定义了DROPFILES结构

[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct _DROPFILES
{
    public Int32 pFiles;
    public Int32 X;
    public Int32 Y;
    public bool fNC;
    public bool fWide;
}

并实现填充所有 OLE 结构的代码。下面的函数返回我在 DoDragDrop 中使用的 IDataObject:

DoDragDrop(GetDataObject(new String[] { file name }), DragDropEffects.Copy);

DataObject2 GetDataObject(String[] strFiles)
{
    byte[] bData;
    _DROPFILES df = new _DROPFILES();
    int intChar, intFile, intDataLen, intPos;
    IntPtr ipGlobal = IntPtr.Zero;

    // Calculate total data length
    intDataLen = 0;
    for (intFile = 0; intFile <= strFiles.GetUpperBound(0);intFile++)
    {
        intDataLen += strFiles[intFile].Length + 1;
    }
    // Terminating double zero
    intDataLen++;

    bData = new Byte[intDataLen];
    intPos = 0;

    // Build null terminated list of files
    for (intFile = 0; intFile <= strFiles.GetUpperBound(0); intFile++)
    {
        for (intChar = 0; intChar < strFiles[intFile].Length;intChar++)
        {
            bData[intPos++] = (byte)strFiles[intFile][intChar];
        }
        bData[intPos++] = 0;
    }
    // Terminating double zero
    bData[intPos++] = 0;

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

    if (ipGlobal == IntPtr.Zero)
    {
        return null;
    }

    // Build DROPFILES structure in global memory.
    df.pFiles = Marshal.SizeOf(df);
    df.fWide = false;
    Marshal.StructureToPtr(df, ipGlobal, true);
    IntPtr ipNew = new IntPtr(ipGlobal.ToInt32() + Marshal.SizeOf(df));
    Marshal.Copy(bData, 0, ipNew, intDataLen);

    short CF_HDROP = 15;
    System.Runtime.InteropServices.ComTypes.FORMATETC formatEtc;
    System.Runtime.InteropServices.ComTypes.STGMEDIUM stgMedium;

    formatEtc = new System.Runtime.InteropServices.ComTypes.FORMATETC();
    formatEtc.cfFormat = CF_HDROP;
    formatEtc.dwAspect = System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT;
    formatEtc.lindex = -1;
    formatEtc.tymed = System.Runtime.InteropServices.ComTypes.TYMED.TYMED_HGLOBAL;

    stgMedium = new System.Runtime.InteropServices.ComTypes.STGMEDIUM();
    stgMedium.unionmember = ipGlobal;
    stgMedium.tymed = System.Runtime.InteropServices.ComTypes.TYMED.TYMED_HGLOBAL;
    DataObject2 dobj = new DataObject2();
    dobj.SetData(ref formatEtc, ref stgMedium, false);
    return dobj;

}

使用新代码,第二个应用程序在e.Data 中看到DataObject,我可以将文件拖放到任何应用程序。除了现在资源管理器不接受文件。我在实施过程中是否遗漏了什么?

【讨论】:

  • 我的应用是 64 位的。它不起作用。经过一些跟踪,我发现代码在IntPtr ipNew = new IntPtr(ipGlobal.ToInt32() + Marshal.SizeOf(df)); 处崩溃并溢出。我把ToInt32()改成ToInt64(),然后就成功了。
猜你喜欢
  • 1970-01-01
  • 2018-03-09
  • 2011-06-27
  • 2023-04-10
  • 2015-08-27
  • 2013-10-19
  • 1970-01-01
相关资源
最近更新 更多