【问题标题】:Handling drag and drop files in a running Windows console application在正在运行的 Windows 控制台应用程序中处理拖放文件
【发布时间】:2014-02-16 04:31:31
【问题描述】:

首先,澄清一下,我不是询问如何将文件拖放到 exe 的图标上。我想知道如何处理拖放到已经运行 win32 控制台应用程序。我也询问如何通过 Windows 消息泵处理基于 WinMain 的应用程序内部的拖放。我想在一个入口点 int main() 的程序内部执行此操作,该入口点没有 WndProc(还)或任何东西。

也就是说,我想知道我的目标是否可以实现(并希望它可以实现)。

我有一个在控制台窗口中运行的服务器应用程序。由于庞大的代码库和许多奇怪的耦合,它是一个用于所有密集目的的“仅输出”控制台。尽管如此,我仍然可以处理诸如按键之类的事情,因为我有一个更新循环滴答作响。我希望能够将充满命令(使用自定义语法)的文件拖放到我正在运行的应用程序上并让它处理它们。

这可能吗?我在想我可能会得到一个指向控制台的 HWND 的指针(希望是一个东西?),然后可能子类化该窗口以使用自定义 WndProc 来侦听 WM_DROPFILES 消息。

我从未真正尝试在 int main() 程序而不是 WinMain 程序中设置对 Windows 消息的处理,但我希望它以某种方式可能。

任何帮助将不胜感激! 奇怪的解决方案很好。

【问题讨论】:

    标签: c++ winapi drag-and-drop console-application


    【解决方案1】:
    #include <vector>
    #include <string>
    #include <iostream>
    #include <conio.h>
    
    int main()
    {
        std::cout << "Please drop files and press [Enter] when done ...\n";
    
        std::vector< std::string > files;
    
        for( int ch = _getch(); ch != '\r'; ch = _getch() ) {
    
            std::string file_name;
    
            if( ch == '\"' ) {  // path containing spaces. read til next '"' ...
    
                while( ( ch = _getch() ) != '\"' )
                    file_name += ch;
    
            } else { // path not containing spaces. read as long as chars are coming rapidly.
    
                file_name += ch;
    
                while( _kbhit() )
                    file_name += _getch();
            }
    
            files.push_back( file_name );
        }
    
        std::cout << "You dropped these files:\n";
    
        for( auto & i : files )
            std::cout << i << '\n';
    }
    

    【讨论】:

    • 添加一些描述,以便其他人可以理解您的代码并回答这个问题。
    • @Charlie,因为 Win32 控制台窗口实现有这个内置的放置处理程序例程——对用户代码完全不透明。 (注意:这不仅仅是一些“默认”行为,因为无法自定义它,AFAIK。例如窗口子类化不起作用,因为控制台进程无法更改运行其窗口的窗口过程(基本上是因为它由控制台主机进程(“CSRSS”或它的一些亲戚)管理,而不是应用程序本身)。)
    【解决方案2】:

    AFAIK,默认情况下控制台窗口不支持拖放。您始终可以使用自己的消息循环创建自己的单独弹出窗口,以便用户可以将项目拖到上面。

    要在控制台窗口本身上使用拖放,请尝试使用GetConsoleWindow() 获取控制台 HWND,然后:

    1. 使用SetWindowLong/Ptr()SetWindowSubClass() 子类化HWND,然后使用DragAcceptFiles() 注册HWND 以开始接收WM_DROPFILES 消息。请务必再次致电DragAcceptFiles() 以停止接收消息,然后在退出应用程序之前取消挂钩您的子类。

    2. 实现IDropTarget接口,然后使用RegisterDragDrop()注册HWND,开始接收通知。退出应用前请务必致电RevokeDragDrop()

    WM_DROPFILES 更容易编写代码,但IDropTarget 更灵活,因为它可以处理虚拟物品和物理文件。

    【讨论】:

    • 嗯,不,控制台窗口实际上并不归控制台程序所有。因此,将其子类化是一个完全的死胡同。也被this question 覆盖。
    • 如果程序是控制台中运行的第一个进程,那么它确实拥有控制台窗口(@98​​7654340@ 返回与 GetCurrentProcessId()GetCurrentThreadId() 相同的 ID)但奇怪的是子类化失败(@987654343 @ 返回错误 5)。如果程序不是控制台中的第一个进程,则它不会拥有控制台窗口,尽管它会使用现有的控制台窗口执行 I/O。
    • 我什至试过SetWindowsHookEx(),虽然它能够安装消息挂钩,但它没有检测到WM_DROPFILES消息。我确实发现控制台窗口默认启用了WS_EX_ACCEPTFILES 样式,因此控制台必须有自己的内部拖放处理程序,特别是因为我可以在窗口上拖动文件并且光标更改以显示允许放置。
    • 如果您在 cmd.exe 控制台上放置文件,文件路径将粘贴到命令行中,所以我想说这就是放置处理程序的用途。但是,我认为没有办法解决这个问题。尽管对于正在运行的控制台进程来说,它可能看起来就像用户输入了文件名,但对于 OP 想要实现的目标来说,这可能已经足够了。
    • 感谢您的调查。看来我得暂时停止这项调查了……创建一个辅助侦听器窗口可能是我唯一的希望。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-20
    • 1970-01-01
    • 2021-01-09
    • 1970-01-01
    相关资源
    最近更新 更多