【问题标题】:Sending WM_DROPFILES with C++ and WinAPI to third-party application使用 C++ 和 WinAPI 将 WM_DROPFILES 发送到第三方应用程序
【发布时间】:2014-03-08 16:21:30
【问题描述】:

所以我一直在拼命地尝试自动化拖放功能,并将我的解决方案搜索范围缩小到一段相当精致的代码:

// DragAndDrop.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <Shlobj.h>
#include <tchar.h>


int main(int argc, char* argv[]) {

    for (int i = 0; i <= WM_DROPFILES; i++)
    {
        ChangeWindowMessageFilter (i, MSGFLT_ADD);
    }

    if (HWND hwnd = FindWindow ("OpusApp", NULL)) {

    //HGLOBAL hGlobal = GlobalAlloc (GMEM_FIXED,
    //sizeof ("d:\\DragMe.txt") + 2);
    //char *strFile = (char*) GlobalLock
    //(hGlobal);
    //strcpy (strFile, "d:\\DragMe.txt");
    //strFile [strlen ("d:\\DragMe.txt") +
    //1] = NULL;
    char filename[] = "d:\\DragMe.txt";

    POINT point;
    point.x = 480;
    point.y = 480;

    HGLOBAL hMem = GlobalAlloc(GHND, sizeof(DROPFILES) + strlen(filename)+2);

    DROPFILES *dfiles = (DROPFILES*) GlobalLock(hMem);
    if (!dfiles)
    {
        GlobalFree(hMem);
        return NULL;
    }

    dfiles->pFiles = sizeof(DROPFILES);
    dfiles->pt = point;
    dfiles->fNC = TRUE;
    dfiles->fWide = FALSE;
    memcpy(&dfiles[1], filename, strlen(filename));
    GlobalUnlock(hMem);

    printf ("Sending Message...\n");

    if (!PostMessage(hwnd, WM_DROPFILES, (WPARAM)hMem, 0)) {
        printf("Error Posting Message!");
        GlobalFree(hMem);
    }
    }

    int temp = 0;
    scanf("&d", temp);
    return 0;
}

...对于我的代码中的任何不好的词,我深表歉意...它们仅用于调试目的。无论如何,以上内容非常简单,它适用于 Microsoft Word、Excel 和记事本......但对于许多应用程序它根本不起作用(Spy++ 甚至不记录 WM_DROPFILES 消息系统范围 在这些情况下,这很奇怪......)。我什至尝试将问题应用程序的代码编译为 x64 或 x86,但没有任何变化...

我觉得我可能错误地使用了 FindWindow(我正在使用与 AutoIT 捆绑的窗口信息工具来获取窗口类,因为我发现 Spy++ 非常混乱)。无论如何,我设置了一个赏金,因为我真的需要弄清楚这一点。

我需要使用它的应用程序名为 Dartfish,它是 Windows 7 上的 32 位应用程序...我需要将视频文件列表发送到其界面的特定区域(特定窗格) ,我正在尝试用上面的代码来做到这一点。

有什么帮助吗?非常感谢!!

【问题讨论】:

  • 你的目标窗口有 WS_EX_ACCEPTFILES 样式吗?
  • 看,这就是问题所在……它是第三方的闭源应用程序。我不知道。所以完全有可能我可能会叫错树,并且应用程序甚至可能没有 WM_DROPFILES 的事件处理程序?还是所有 Windows 应用程序都默认处理此消息?
  • 要检测 WS_EX_ACCEPTFILES 样式,您可以使用 GetWindowLong 和 GWL_EXSTYLE 参数。
  • WS_EX_ACCEPTFILES 只是避免调用DragAcceptFiles() 的捷径。但是不,所有应用程序默认不处理WM_DROPFILESDefWindowProc() 丢弃它。各个应用程序必须处理 WM_DROPFILES 来决定它们要如何使用拖动的文件名。
  • 好的,感谢您解决这个问题。见下文;我一直在研究您的解决方案。

标签: c++ winapi drag-and-drop automation windows-messages


【解决方案1】:

ChangeWindowMessageFilter/Ex() 不授予您将指定消息发送到其他进程的权利。它授予其他进程(特别是低完整性进程)将该消息发送给您的权利。所以摆脱它,它对你没有好处。

接下来,尝试发送 dfiles-&gt;fWide 设置为 TRUE 的 Unicode 文件名,看看是否会有所不同。某些应用程序不处理 Ansi 数据。 Windows 是基于 Unicode 的操作系统。使用IsWindowUnicode() 了解给定的 HWND 是否需要 Ansi 或 Unicode 窗口消息。

最后,一些应用根本不实现WM_DROPFILES(它们不调用DragAcceptFiles()或启用WS_EX_ACCEPTFILES)。在现代 Windows 版本中处理拖放的首选方法是实现 IDropTarget 接口,并使用 RegisterDragDrop() 将其与 HWND 关联。没有 API 可以检索 HWND 的 IDropTarget,但可以手动完成:

(改编自本次讨论:How to receive for HWND it IDropTarget?

IDropTarget* GetRegisteredDropTargetFromWnd (HWND hWnd) 
{ 
    IUnknown *pBuffer = (IUnknown *) GetProp (hWnd, TEXT("OleDropTargetInterface")); 
    if (pBuffer != NULL) 
    { 
        IDropTarget *pRetVal = NULL; 
        if (SUCCEEDED(pBuffer->QueryInterface(IID_IDropTarget, (void **) &pRetVal)))
            return pRetVal;     
    }
    return NULL; 
}

如果 HWND 具有 IDropTarget,您可以使用 IDataObject 包装您的 DROPFILES 数据并将其传递给 IDropTarget::Drop() 方法。如果Drop() 接受数据,请不要发布WM_DROPFILES 消息。然而,诀窍是 GetProp() 返回的 IDropTarget* 指针与拥有 HWND 的进程相关,因此您必须将其编组到您的进程中,或者将您的代码注入到 HWND 的进程中,以便实际使用接口指针。

【讨论】:

  • 检索IDropTarget 的代码看起来不能跨进程工作(至少,检索到的值没有意义)。如果目标应用注册了一个放置目标(如here 所述),您可以使用它。
  • 如果您阅读了我链接到的讨论,IDropTarget 检索代码是通过分析 DoDragDrop() 的内部工作原理确定的,它与 IDropTarget 跨进程边界一起工作。其他在线资源,包括herehere,确认这一点。
  • 在您的第一个链接中,建议使用此方法的评论者还说(在稍后的评论中)您需要在相同的地址空间中运行。我知道DoDragDrop 可以跨进程工作;我也知道它使用在每个进程中运行的 OLE 代码。最后,every注册的IDropTarget指针怎么可能在every进程的地址空间有效?
  • Remy,您的回答与我在另一个相关问题中的建议一致;我有一个“半生不熟”的尝试在我的家用电脑上做这件事......也许我应该走这条路,不是吗?即使只是为了增加与现代应用程序的兼容性?到目前为止,我使用的代码使用“Createremotethread”方法将 DLL 注入目标应用程序(这只是一个应用程序 - 在一台计算机上 - 所以我不担心 Createremotethread 不向后兼容)。这是一个合适的载体来实现这个解决方案?然后在进程内传递IDataObject?
  • @araisbec:我已经described 简单的注入算法在回答您之前的问题。为什么不用它?
【解决方案2】:

我使用的代码与您的代码几乎相同,并且在许多应用程序中都取得了不错的效果。我认为您的问题是您使用 FindWindow() 找到的窗口是一个 顶级 窗口,它可能 不是 是允许放入目标应用程序的窗口。某些应用程序仅启用选定的子窗口以进行删除。问题当然是找到那个窗口。我还没有找到任何简单的解决方案来解决这个问题。您可以使用 EnumChildWindows() 递归地枚举顶部窗口的所有子窗口并尝试识别正确的窗口(即,按类、ID、窗口样式或其他参数),但这很糟糕。我使用 SpyXX,但这也不是一个很好的解决方案。
祝你好运。

【讨论】:

  • 这个问题(寻找放置目标)激起了我的好奇心。我想出了一个解决方案。这是我所做的: 1. 查找顶级窗口(例如 FindWindow())。 2. 获取那个窗口偏移量(屏幕)和大小。 3. 在窗口内创建一个坐标网格(比如 10x10)。 4. 对于每个网格点,调用 WindowFromPoint() 以检索该点的窗口(或子窗口)。 5. 获取 Ex 样式(即 GetWindowLong(GWL_EXSTYLE))。 6. 使用 WS_EX_ACCEPTFILES 进行掩码。 7. 如果设置,这是将接受文件的(第一个)窗口。只会找到第一个目标。如果目标 wnd 非常小,请使用较小的网格间距。
猜你喜欢
  • 1970-01-01
  • 2016-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多