1

我正在尝试支持从本机 Windows 应用程序拖放到 Java 应用程序中。我的问题是我没有文档,所以我一直在对其进行逆向工程。我的第一个问题是我不知道剪贴板格式的名称。在调试器中工作,我能够找到应用程序使用系统调用注册的自定义剪贴板格式的标识符RegisterClipboardFormat()。在 中sun.awt.windows.WDataTransferer,有一个系统调用的本机接口GetClipboardFormatName(),通过它我设法提取了注册名称。(作为参考,虽然它不直接涉及问题,但应用程序是Eudora 7.1,名称是EudoraTransferClipboardFormat。)

现在我预计需要再次执行此操作,我宁愿现在编写一个小实用程序,因为我肯定会在以后需要时忘记库的内部数据结构。问题是相关的剪贴板格式标识符被隐藏在SunDropTargetContextPeeras private member的子类实例中currentT。从拖放事件到该对象的导航同样通过受保护的成员。并且系统调用接口同样被声明为私有的。这在调试器中都是可见的,但在普通代码中不可见。

我的目标是提取 的值currentT(因为这是拖放子系统实际看到的)并在其中显示已注册剪贴板格式的名称。我怀疑类加载器和安全系统的各种滥用对于让它作为一个用 Java 编写的可发布实用程序工作是必要的。

4

1 回答 1

0

我通过使用 Java 反射调用来获取相关的内部成员解决了这个问题。这其中的关键部分是对象setAccessible上的功能Field。此函数关闭 Field 对象的访问控制,允许公共访问,尽管不是通过普通语法。(注意实际的函数名称;它不是subvertAccessControls.) 此调用在普通 JRE 环境中成功,并且可能无法在例如 applet 中工作。我没有费心检查这个,因为这是一个开发工具来检查原生类型的拖动事件,因为 (1) 应用程序不知道原生类型和 (2) 没有数据将本机数据传输转换为 Java 对象。因此,对于每个具有拖动类型的应用程序,我只需要它运行一次,我是逆向工程;我只需要在普通的 JRE 中运行。

谢天谢地,我不必突破java.lang.instrument甚至 JVMTI。然而,我确实不得不滥用安全系统。

测试代码片段如下。变量long native_types[]是输出。DropTargetDragEvent dtde是输入;它是事件处理程序的参数。

long native_types[];
try {
    /*
     * Retrieve the drop target context.
     *
     * We can retrieve this member directly, because it's a public field.
     */
    DropTargetContext dtc = ( ( DropTarget ) dtde.getSource() ).getDropTargetContext();
    /*
     * Retrieve the drop target context peer.
     *
     * We cannot retrieve this member without reflection, because it's a private field. We get the class
     * object from the public class declaration. The field "dropTargetContextPeer" is private, so we
     * have to retrieve with a reflection call and then set it to be accessible so that we don't get
     * IllegalAccessException when we call get() on the field. Since we're only going to use reflection
     * on the drop target context peer, we don't bother trying to cast it and just leave it declared
     * as Object.
     */
    Class<DropTargetContext> DTC_class = DropTargetContext.class;
    Field DTCP_field = DTC_class.getDeclaredField( "dropTargetContextPeer" );
    DTCP_field.setAccessible( true );
    Object dtcp = DTCP_field.get( dtc );
    /*
     * Retrieve the array of native types.
     *
     * This is almost exactly analogous to the previous retrieval, but with the exception that the field
     * is defined in the superclass, not the class itself. Because the field is declared private, we can't
     * simply use getField(), since that only works on public fields. So we get the class object for the
     * parent class and proceed as before.
     *
     * As a bonus, this routine is cross-platform. The class sun.awt.windows.WDropTargetContextPeer is
     * the implementation class for sun.awt.dnd.SunDropTargetContextPeer for Windows. The type identifiers,
     * though, all fit into a long for every platform.
     */
    Class<?> DTCP_class = dtcp.getClass();
    Class<?> DTCP_superclass = DTCP_class.getSuperclass();
    Field CT_field = DTCP_superclass.getDeclaredField( "currentT" );
    CT_field.setAccessible( true );
    native_types = ( long[] ) CT_field.get( dtcp );
} catch ( Exception e ) {
    throw new RuntimeException( "Did not retrieve native types.", e );
}
于 2012-11-13T01:06:47.530 回答