PAYATHON 2026

如何获取 System.Windows.Forms.IDataObject 中的 innerData?

排障赵师傅

要获取 innerData,先调用 GetType() 取得数据对象的实际运行时类型,再用 BindingFlags.Instance 和 BindingFlags.NonPublic 查找字段。找到字段后,调用 GetValue(),并保存返回值。

前提是当前对象的实现中确实有名为 innerData 的字段。System.Windows.Forms.IDataObject 接口没有定义这个成员,因此这种读取方式不一定适用于所有实现或 .NET 版本。

为什么原来的代码有问题

GetField() 应通过 System.Type 调用

在标准 .NET 反射 API 中,GetField() 是 System.Type 的方法。要查找数据对象实例的字段,应这样调用:

comDataObject:GetType():GetField(...)

将 e:Data 转换为 System.Runtime.InteropServices.ComTypes.IDataObject,只会改变访问对象时使用的接口,不会改变对象的实际运行时类型,也不会暴露私有字段。

缺少 BindingFlags.Instance

NonPublic 只指定查找非公开成员,查找实例字段时还必须加上 Instance:

BindingFlags.Instance | BindingFlags.NonPublic

ABL 可以用 OR 组合这两个枚举值,具体规则见 Type.GetField 文档和 ABL 的位运算 OR 文档。

应先检查字段,再读取字段值

原代码先调用 innerDataField:GetValue(...),再检查 innerDataField 是否有效。如果没有找到字段,程序在读取时就会出错,无法执行后面的检查。

GetValue() 返回的是字段中保存的对象,需要将返回值赋给变量,才能继续使用。

ABL 修改示例

将下面的代码放在能访问事件参数 e 的拖放事件处理过程中。示例假定当前实现中的字段名是 innerData:

DEFINE VARIABLE dataObject     AS System.Object
    NO-UNDO.
DEFINE VARIABLE dataType       AS System.Type
    NO-UNDO.
DEFINE VARIABLE innerDataField AS System.Reflection.FieldInfo
    NO-UNDO.
DEFINE VARIABLE innerData      AS System.Object
    NO-UNDO.

dataObject = e:Data.

IF VALID-OBJECT(dataObject) THEN DO:
    dataType = dataObject:GetType().

    innerDataField = dataType:GetField(
        "innerData",
        System.Reflection.BindingFlags:Instance OR
        System.Reflection.BindingFlags:NonPublic
    ).

    IF VALID-OBJECT(innerDataField) THEN DO:
        innerData = innerDataField:GetValue(dataObject).

        MESSAGE
            "innerData is valid:"
            VALID-OBJECT(innerData)
            VIEW-AS ALERT-BOX INFORMATION BUTTONS OK.
    END.
    ELSE DO:
        MESSAGE
            "Field innerData was not found."
            "Runtime type:"
            dataType:FullName
            VIEW-AS ALERT-BOX INFORMATION BUTTONS OK.
    END.
END.

这里可以直接使用 e:Data,无需先转换为 COM 接口。传给 GetValue() 的参数必须是拥有该字段的对象实例。

加上 Instance 后仍然找不到怎么办

先查看 dataType:FullName,确认拿到的对象类型,再检查对应程序集中的字段定义。

innerData 属于实现细节。WinForms 的 v3.1.0 源码中有这个字段,当前主分支源码使用的则是 _innerData,字段类型和内部结构也已不同。查找时应以当前实现为准,不能直接沿用旧示例中的字段名。

查找和读取时还需注意:

  • 字段名默认区分大小写。
  • 如果字段是基类声明的私有字段,需要沿 BaseType 查找,并在声明该字段的类型上调用 GetField()。
  • innerDataField 有效,只说明找到了字段,字段值是否为空还要单独检查。
  • 取到的 innerData 可能仍是一层包装对象,不能假定它就是底层 COM 对象。

如果后续需要调用 GetDataFromHGLOBLAL,还要核对实际程序集中的方法名称、参数类型和所属类型。找到字段,并不能保证这个私有方法也存在。

从 WhatsApp 获取拖放数据是否必须读取 innerData

仅凭这段代码,无法判断是否必须访问私有字段。可以先调用 e:Data:GetFormats(FALSE) 查看拖放源提供的格式,再根据格式使用 GetDataPresent() 和 GetData() 读取数据。这些方法属于 System.Windows.Forms.IDataObject 的公开接口。

如果需要直接使用 OLE 数据传输接口,可以在对象支持的情况下,将其转换为 System.Runtime.InteropServices.ComTypes.IDataObject,再通过公开方法访问数据。是否还需要读取内部对象,取决于拖放源提供的格式和当前 WinForms 实现。题目中的信息不足以确定,通过反射读取 innerData 就能完成 WhatsApp 文件拖放。

备注:内容仅供参考。