用程序来模拟文件拖放

这个课题属于没苦硬吃,实际价值很难说有多大,但既然奋战一番,记录一下也好。

一开始的直觉就是使用 WM_DROPFILES 消息,没什么其他原因,简单嘛。实现完成后测试,当目标窗口是记事本的时候工作正常,但是换成三太爷想要操作的 360 加固助手就崴泥。稍微观察了一下,该程序的界面是使用 Java 技术栈实现的,而且在从资源管理器向其中投放文件时,也根本收不到 WM_DROPFILES 消息。加之还可以观察到,尽管由于 Java 的原因,主界面在 Windows 世界里只体现为唯一的一个 Window(因为其中的子窗口或者控件是 Java 自己管理的非标准 Windows 窗口实现),但是接收放下文件的区域又并非整个 Window 所占用的区域,可以推测出,接收拖放应该是使用了 OLE 的 drag and drop。

攻坚之旅无须多言,共三个阶段:一开始时尝试用 Groovy 直接搞定,未果;接下来和 ChatGPT 合作写 Windows SDK 程序,程序写出来了,效果没达到;最后跟 DeepSeek 合作写 Windows SDK 程序,达到了预期效果。在实践中发现,ChatGPT 和 DeepSeek 写程序的优缺点各有千秋,在反复提示、反复修改的输出过程中,ChatGPT 输出较为简洁,会减少大量的与上一次回答中相同的部分,而 DeepSeek 则动不动全量输出,不利于前后翻阅浏览。而且,在这个小程序的编制过程中,ChatGPT 在关键问题上(很可能)犯了大错误,以至于没能实现目标,而 DeepSeek 则表现为在很多地方的输出都有“毛刺”(例如调用 API 时,已经默认全局设定为 Unicode 宽字符情境,却使用了不带明确的 W 后缀的 Unicode API 版本,以至于编译时又被默认替换成了带 A 后缀的 ANSI 版本),只不过无伤大雅,手动纠正过来后就好了,关键地方没有掉链子。这里只是如实记录一下过程,并非要论定二者的优劣,毕竟这中间还存在老夫的思路切换对它们造成的影响。

三个源文件的内容附上。首先是 resource.h

接着是 resource.rc

最后是 main.cpp

最后是构建命令,

仅此而已。

后记:当然了,上面最后可以说“仅此而已”这四个字的前提是我们的需求确实很简单,事实上 OLE 拖放相关的知识非常繁杂,MFC 封装了许多以简化使用,而如果你恰好是一个 SDK 级别的开发员的话,就要痛苦不少。有一个前辈程序员在很多年前写过一个助力 SDK 开发者开发拖放相关功能的库,文章在这儿,代码在这儿。CSDN 上有个博主,看上去应该是以 Delphi 作为主力技术的,他的博客转载了一系列 OLE 拖放相关的博文

思考:在 OLE 的世界里,信息庞杂但多浮于表层,有很多都是人云亦云的传抄。在和 ChatGPT 的合作当中,由于 IDropSource 的 GetData 方法不会被调用,程序被挂起,在对接口进行近距离观测后,感觉很可能是因为没有实现 EnumFormatEtc 方法而导致的。推测的依据是,如果没有实现这个方法,拖放接收端就很可能因无法获知有待接收的数据的格式,从而无法反馈拖放源是否可以放落。转而去增加这个方法的实现,结果又引入了一大坨与 IEnumFORMATETC 接口关联的实现和周边。最后到头来再看 DeepSeek 的实现,会发现不实现 EnumFormatEtc 方法其实也没有问题。那么,ChatGPT 的版本为什么挂起了呢,目前的推测是,老夫当时实现的是一个控制台程序,因为没有创建自己的窗口(以及消息循环),所以进入 DoDragDrop 模态后,它的内部消息循环接收不到任何事件,就僵住了。而和 DeepSeek 合作时,起手就有意改变了程序的类型,实现了一个对话框,进展就很顺利(这也是为什么前面并没有敢妄下结论,说它们两者存在着显著的优劣差别)。当然,这个推测出的结论还有待验证。想说明的是,即便在前面给出的那个系列文章里,其中也写道,“庆幸的是,为了简化 OLE 拖放,我们仅仅需要实现 GetDataQueryGetDataEnumFormatEtc”,而我们已经用实际代码证实了,连 EnumFormatEtc 也不是必须的。而这些必须经过实践才能得来的关键知识,网络上其实并不很多。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注