接收拖拽到其它窗口的文件路径
想做一个无窗口后台程序,可以获取指定窗口的拖拽事件,得到拖拽文件路径
(只要是获取拖拽其它窗口的文件,所以窗口组件的拖拽事件self.setAcceptDrops(True)是不适应的)
import win32gui, win32ui, win32con
hwnd = win32gui.FindWindow(None, "Test Dialogs")#获取窗口句柄
win32gui.DragAcceptFiles(hwnd, True) #定义一个窗口句柄接受拖拽
以下找到的示例无效
wnd = win32ui.CreateWindowFromHandle(hwnd)
wnd.HookMessage(self.test, win32con.WM_DROPFILES)
请教有可以实现的方法吗?
谢谢啦! 本帖最后由 hrpzcf 于 2022-3-11 12:13 编辑
看你还没解决,我按照楼上大佬的代码修改了做成模块了,你自己看看吧
下载附件,把里面三个文件解压至你项目里能import 到的地方,用法:
import time
from GouZi import *
def test1():
# 实例化一个文件拖放事件钩子客户端
# 当然,你想直接调用dll也行,可导出的函数跟DragHookClient类里的函数同名
dh = DragHookClient()
print(f"启用钩子:{dh.HookDrag()}")# 安装拖放事件钩子
print(f"钩子是否启用:{dh.IsHooked()}")# 检查钩子是否已安装
count = 0
while count < 60:# while True:
count += 1
if dh.HasNewDropFiles():# 查询是否有新的拖放事件
print(dh.GetNewDropFiles())# 获取新的拖放事件生成的路径列表
time.sleep(1)
dh.UnHookDrag()# 程序结束不要忘记卸载钩子
def test2():
with DragHookClient() as dh:
if dh.IsHooked():# 检查钩子是否已安装
print("钩子已启用")
else:
print("钩子未启用")
return
count = 0
while count < 60:# while True:
count += 1
if dh.HasNewDropFiles():# 查询是否有新的拖放事件
print(dh.GetNewDropFiles())# 获取新的拖放事件生成的路径列表
time.sleep(1)
if __name__ == "__main__":
# test1()
test2()
GouZi.py
import sys
from ctypes import c_bool, c_wchar_p, windll
from os import path
__all__ = ["DragHookClient"]
if sys.maxsize > 0xFFFFFFFF:
dllName = "GouZi_vc143_x64.dll"
else:
dllName = "GouZi_vc143_x86.dll"
rootPath = path.dirname(path.realpath(__file__))
dllFullPath = path.join(rootPath, dllName)
class DragHookClient:
"""WIN全局文件拖放事件钩子客户端"""
def __init__(self):
self.__hook = windll.LoadLibrary(dllFullPath)
self.__hook.IsHooked.restype = c_bool
self.__hook.HasNewDropFiles.restype = c_bool
self.__hook.HookDrag.restype = c_bool
self.__hook.GetNewDropFiles.restype = c_wchar_p
def IsHooked(self) -> bool:
"""检查是否已启用全局文件拖放事件钩子"""
return self.__hook.IsHooked()
def HasNewDropFiles(self) -> bool:
"""检查是否有新的文件拖放事件"""
return self.__hook.HasNewDropFiles()
def UnHookDrag(self) -> None:
"""停止全局文件拖放事件钩子"""
self.__hook.UnHookDrag()
def HookDrag(self) -> bool:
"""启用全局文件拖放事件钩子"""
return self.__hook.HookDrag()
def GetNewDropFiles(self) -> list:
"""以列表形式返回检测到的全局文件拖放事件的文件路径"""
string = self.__hook.GetNewDropFiles()
return
# def __del__(self) -> None:
# self.__hook.UnHookDrag()
def __enter__(self) -> "DragHookClient":
self.__hook.HookDrag()
return self
def __exit__(self, excType, excValue, excTB) -> None:
self.__hook.UnHookDrag()
Hook.c
#include <Windows.h>
#include <strsafe.h>
#define MAX_STRLEN 131072
#define LOCKER TEXT("j5u7ld6va0b489sj1y3k28r1n3b1")
#define IDRAGFILE INFINITE
typedef struct {
INT len;
TCHAR str;
} cp_t;
#pragma data_seg("shared")
static BOOL hasNewDrops = FALSE;
static cp_t dropedFiles = { 0 };
static HHOOK hHookHnd = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:shared,rws")
static HINSTANCE hLibInst;
BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
switch (reason) {
case DLL_PROCESS_ATTACH:
hLibInst = instance;
}
return TRUE;
}
static void TargetWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_DROPFILES: {
HDROP hDrop = (HDROP)wParam;
UINT reqSize = 0;
TCHAR buffer;
UINT count = DragQueryFile(hDrop, IDRAGFILE, NULL, 0);
HANDLE hMutex = CreateMutex(NULL, FALSE, LOCKER);
if (!hMutex) break;
WaitForSingleObject(hMutex, INFINITE);
if (!hasNewDrops) {
hasNewDrops = TRUE;
dropedFiles.len = 0;
dropedFiles.str = 0;
}
for (UINT i = 0; i < count; ++i) {
reqSize = DragQueryFile(hDrop, i, NULL, 0);
if (reqSize >= MAX_PATH)
continue;
if (dropedFiles.len + reqSize + 1 >= MAX_STRLEN)
break;
DragQueryFile(hDrop, i, buffer, MAX_PATH);
StringCchCat(dropedFiles.str, MAX_STRLEN, buffer);
StringCchCat(dropedFiles.str, MAX_STRLEN, TEXT("\n"));
dropedFiles.len += (reqSize + 1);
}
dropedFiles.str = 0;
ReleaseMutex(hMutex);
break;
}
}
}
static LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
switch (nCode) {
case HC_ACTION: {
MSG *pMsg = (MSG *)lParam;
TargetWndProc(pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam);
break;
}
}
return CallNextHookEx(hHookHnd, nCode, wParam, lParam);
}
__declspec(dllexport) void UnHookDrag(void) {
if (hHookHnd)
UnhookWindowsHookEx(hHookHnd);
hHookHnd = NULL;
}
__declspec(dllexport) BOOL HookDrag(void) {
if (hHookHnd) return TRUE;
hHookHnd = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hLibInst, 0);
return hHookHnd != NULL;
}
__declspec(dllexport) BOOL IsHooked(void) {
return hHookHnd != NULL;
}
__declspec(dllexport) BOOL HasNewDropFiles(void) {
BOOL newDrops = FALSE;
if (!IsHooked()) return newDrops;
HANDLE hMutex = CreateMutex(NULL, FALSE, LOCKER);
if (!hMutex) return newDrops;
WaitForSingleObject(hMutex, INFINITE);
newDrops = hasNewDrops;
ReleaseMutex(hMutex);
return newDrops;
}
__declspec(dllexport) TCHAR *GetNewDropFiles(void) {
static TCHAR dropedTemp;
dropedTemp = 0;
if (!IsHooked()) return dropedTemp;
HANDLE hMutex = CreateMutex(NULL, FALSE, LOCKER);
if (!hMutex) return dropedTemp;
WaitForSingleObject(hMutex, INFINITE);
hasNewDrops = FALSE;
if (dropedFiles.len > 0) {
StringCchCopy(dropedTemp, MAX_STRLEN, dropedFiles.str);
dropedFiles.len = 0;
dropedFiles.str = 0;
}
ReleaseMutex(hMutex);
return dropedTemp;
}
@人造人
本帖最后由 人造人 于 2022-3-1 02:43 编辑
现学现卖,耗时4天,^_^
就我所知,目前拖放文件有两种完全不同的方法
一种是通过消息机制,使用 WM_DROPFILES 消息
另一种是通过 com 接口,使用 IDropSource IDataObject IDropTarget 这3个接口
第一种我弄懂了,写了一个C代码
第二种我找不到能用的资料,这方面的资料太少了
而且现在大部分软件已经不使用消息机制了,使用的是 com 接口
下面的代码是C语言版本的,核心原理就是一个全局钩子,在python中我还不知道怎么用全局钩子
如果你使用第一种方法的话,这方面的资料很多,你完全可以参考这个代码,把这个代码修改成python版本的
实在不行,你可以 python + c 呀,全局钩子的部分在 C 中做,就是在 python 中调用一下,这完全没有问题
对于第二种方法,我真的没办法了,这方面的资料真的太少了,^_^
drag.c
#include <windows.h>
#include <wchar.h>
#include <strsafe.h>
#define BUFF_SIZE 4096
static TCHAR video_buff = TEXT("hello world!");
static LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch(message) {
case WM_CREATE: {
DragAcceptFiles(hwnd, TRUE);
return 0;
}
case WM_DROPFILES: {
HDROP hdrop = (HDROP)wParam;
POINT point; DragQueryPoint(hdrop, &point);
StringCchPrintf(video_buff, BUFF_SIZE, TEXT("point: (%ld, %ld)"), point.x, point.y);
UINT count = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
TCHAR filename;
filename = TEXT('\n');
for(UINT i = 0; i < count; ++i) {
DragQueryFile(hdrop, i, filename + 1, MAX_PATH - 1);
StringCchCat(video_buff, BUFF_SIZE, filename);
}
DragFinish(hdrop);
RECT rect;
GetClientRect(hwnd, &rect);
InvalidateRect(hwnd, &rect, TRUE);
return 0;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect; GetClientRect(hwnd, &rect);
DrawText(hdc, video_buff, -1, &rect, DT_LEFT);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY: {
DragAcceptFiles(hwnd, FALSE);
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
const TCHAR szAppName[] = TEXT("MyWindows");
WNDCLASS wndclass = {0};
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = MainWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if(!RegisterClass(&wndclass)) {
MessageBox(NULL, TEXT("这个程序需要在 Windows NT 才能执行!"), szAppName, MB_ICONERROR);
return 0;
}
HWND hwnd = CreateWindow(szAppName, TEXT("鱼C工作室"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
common.h
#ifndef _COMMON_H_
#define _COMMON_H_
#include <windows.h>
typedef struct {
POINT point;
UINT count;
TCHAR path;
} shared_t;
#define SHARED_STRUCT_SIZE(n) (sizeof(shared_t) + n * sizeof(TCHAR ))
#endif
hook.c
#include "common.h"
static HHOOK hhook;
static HINSTANCE hinstLib;
static HWND hwndCaller __attribute((section(TEXT("shared")), shared));
static void TargetWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch(message) {
case WM_DROPFILES: {
HDROP hdrop = (HDROP)wParam;
UINT count = DragQueryFile(hdrop, 0xffffffff, NULL, 0);
UINT block_size = SHARED_STRUCT_SIZE(count);
shared_t *shared = malloc(block_size);
DragQueryPoint(hdrop, &shared->point);
shared->count = count;
for(UINT i = 0; i < shared->count; ++i) {
DragQueryFile(hdrop, i, shared->path, MAX_PATH);
}
COPYDATASTRUCT cds = {0, block_size, shared};
HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("mutex_hook_dll"));
WaitForSingleObject(hMutex, INFINITE);
if(hwndCaller) SendMessage(hwndCaller, WM_COPYDATA, (WPARAM)hwnd, (LPARAM)&cds);
ReleaseMutex(hMutex);
free(shared);
break;
}
}
}
static LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
switch(nCode) {
case HC_ACTION: {
MSG *pMsg = (MSG *)lParam;
TargetWndProc(pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam);
break;
}
}
return CallNextHookEx(hhook, nCode, wParam, lParam);
}
void InstallHook(HWND hwnd) {
HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("mutex_hook_dll"));
WaitForSingleObject(hMutex, INFINITE);
hwndCaller = hwnd;
ReleaseMutex(hMutex);
hhook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hinstLib, 0);
}
void UninstallHook(void) {
HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("mutex_hook_dll"));
WaitForSingleObject(hMutex, INFINITE);
hwndCaller = NULL;
ReleaseMutex(hMutex);
UnhookWindowsHookEx(hhook);
hhook = NULL;
}
BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
switch(reason) {
case DLL_PROCESS_ATTACH:
hinstLib = instance;
break;
}
return TRUE;
}
main.c
#include "common.h"
#include <wchar.h>
#include <strsafe.h>
#define BUFF_SIZE 4096
static TCHAR video_buff = TEXT("hello world!");
static HINSTANCE hinstLib;
static LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch(message) {
case WM_CREATE: {
hinstLib = LoadLibrary(TEXT("hook.dll"));
void (*InstallHook)(HWND hwnd) = (void *)GetProcAddress(hinstLib, TEXT("InstallHook"));
InstallHook(hwnd);
return 0;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect; GetClientRect(hwnd, &rect);
DrawText(hdc, video_buff, -1, &rect, DT_LEFT);
EndPaint(hwnd, &ps);
return 0;
}
case WM_COPYDATA: {
COPYDATASTRUCT *pcds = (COPYDATASTRUCT *)lParam;
shared_t *shared = (shared_t *)pcds->lpData;
StringCchPrintf(video_buff, BUFF_SIZE, TEXT("hwnd: %p\npoint: (%ld, %ld)"), wParam, shared->point.x, shared->point.y);
for(UINT i = 0; i < shared->count; ++i) {
StringCchCat(video_buff, BUFF_SIZE, TEXT("\n"));
StringCchCat(video_buff, BUFF_SIZE, shared->path);
}
RECT rect;
GetClientRect(hwnd, &rect);
InvalidateRect(hwnd, &rect, TRUE);
return 0;
}
case WM_DESTROY: {
void (*UninstallHook)(void) = (void *)GetProcAddress(hinstLib, TEXT("UninstallHook"));
UninstallHook();
FreeLibrary(hinstLib);
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
const TCHAR szAppName[] = TEXT("MyWindows");
WNDCLASS wndclass = {0};
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = MainWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if(!RegisterClass(&wndclass)) {
MessageBox(NULL, TEXT("这个程序需要在 Windows NT 才能执行!"), szAppName, MB_ICONERROR);
return 0;
}
HWND hwnd= CreateWindow(szAppName, TEXT("鱼C工作室"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
人造人 发表于 2022-3-1 02:36
现学现卖,耗时4天,^_^
就我所知,目前拖放文件有两种完全不同的方法
一种是通过消息机制,使用 WM_DROP ...
首先感谢你的回复及给出的建议,因为是想在python里做出最终效果
{:10_266:}也不会C改PY,只能暂时用其它方式做测试,还不知道能不能行{:10_319:} 新手报道 人造人 发表于 2022-3-1 02:36
现学现卖,耗时4天,^_^
就我所知,目前拖放文件有两种完全不同的方法
一种是通过消息机制,使用 WM_DROP ...
请问大佬,hook.c里开头的static HWND hwndCaller __attribute((section(TEXT("shared")), shared));是什么意思,起什么作用?搜索都搜不到这样的用法{:10_250:}
疑问来源:
1. 我用msys2里的GCC编译器编译你的代码,需要把上面那句TEXT("shared")改成"shared"才能正常编译,也可以达到你演示的效果。
2. 当我改用VS创建工程后,VC好像不支持__attribute(xxx),我试着只保留了static HWND hwndCaller;,各种调试后发现hook安装正常但是往drag.c创建的窗口拖放文件时,main.c创建的窗口收不到WM_COPYDATA消息(给case WM_COPYDATA分支下加了MessageBox发现没被触发)。一顿查后好像发现HWND不支持跨模块传递?? hrpzcf 发表于 2022-3-9 17:56
请问大佬,hook.c里开头的是什么意思,起什么作用?搜索都搜不到这样的用法
疑问来源:
https://www.baidu.com/baidu?tn=monline_7_dg&ie=utf-8&wd=dll%E5%85%B1%E4%BA%AB%E6%95%B0%E6%8D%AE%E6%AE%B5
这是gcc的写法,用来共享数据段
全局钩子会把dll加载到其他进程地址空间,不共享数据段的话,其他进程不知道应该把拦截下来的数据发送到哪里
人造人 发表于 2022-3-9 18:13
https://www.baidu.com/baidu?tn=monline_7_dg&ie=utf-8&wd=dll%E5%85%B1%E4%BA%AB%E6%95%B0%E6%8D%AE%E6 ...
感谢大佬,VC下改成#pragma data_seg("shared")
static HWND hwndCaller = NULL;
#pragma data_seg()
#pragma comment(linker,"/section:shared,rws")成功。
页:
[1]