221 lines
7.6 KiB
Python
221 lines
7.6 KiB
Python
"""
|
||
Windows平台拖拽检测器
|
||
|
||
使用Windows API实现系统级拖拽检测。
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import time
|
||
import threading
|
||
from typing import List, Optional
|
||
from .base_detector import BaseDragDetector
|
||
|
||
|
||
class WindowsDragDetector(BaseDragDetector):
|
||
"""Windows平台拖拽检测器"""
|
||
|
||
def __init__(self, supported_formats: List[str] = None):
|
||
"""
|
||
初始化Windows拖拽检测器
|
||
|
||
Args:
|
||
supported_formats: 支持的文件格式列表
|
||
"""
|
||
super().__init__(supported_formats)
|
||
self._hwnd = None
|
||
self._old_win_proc = None
|
||
self._is_com_initialized = False
|
||
self._ole_initialized = False
|
||
|
||
def _initialize_com(self) -> bool:
|
||
"""初始化COM"""
|
||
try:
|
||
import ctypes
|
||
from ctypes import wintypes
|
||
|
||
# 初始化COM
|
||
ctypes.windll.ole32.CoInitializeEx(None, 0) # COINIT_APARTMENTTHREADED
|
||
self._is_com_initialized = True
|
||
return True
|
||
except Exception as e:
|
||
print(f"COM初始化失败: {e}")
|
||
return False
|
||
|
||
def _initialize_ole(self) -> bool:
|
||
"""初始化OLE"""
|
||
try:
|
||
import ctypes
|
||
from ctypes import wintypes
|
||
|
||
# 初始化OLE
|
||
ctypes.windll.ole32.OleInitialize(None)
|
||
self._ole_initialized = True
|
||
return True
|
||
except Exception as e:
|
||
print(f"OLE初始化失败: {e}")
|
||
return False
|
||
|
||
def _get_window_handle(self) -> Optional[int]:
|
||
"""获取Panda3D窗口句柄"""
|
||
try:
|
||
# 这里需要获取Panda3D窗口的句柄
|
||
# 可能需要通过Panda3D的API获取
|
||
# 暂时返回None,需要进一步实现
|
||
return None
|
||
except Exception as e:
|
||
print(f"获取窗口句柄失败: {e}")
|
||
return None
|
||
|
||
def _setup_drag_drop(self) -> bool:
|
||
"""设置拖拽接收"""
|
||
try:
|
||
import ctypes
|
||
from ctypes import wintypes
|
||
|
||
hwnd = self._get_window_handle()
|
||
if not hwnd:
|
||
return False
|
||
|
||
# 注册拖拽接收窗口
|
||
# 这里需要实现Windows拖拽API的调用
|
||
# 暂时返回False,需要进一步实现
|
||
return False
|
||
except Exception as e:
|
||
print(f"设置拖拽接收失败: {e}")
|
||
return False
|
||
|
||
def _monitor_clipboard_drag(self):
|
||
"""通过剪贴板监控拖拽"""
|
||
try:
|
||
import ctypes
|
||
from ctypes import wintypes
|
||
|
||
user32 = ctypes.windll.user32
|
||
kernel32 = ctypes.windll.kernel32
|
||
|
||
# 监控剪贴板变化
|
||
while self.is_running:
|
||
# 检查剪贴板是否包含文件
|
||
if user32.OpenClipboard(None):
|
||
try:
|
||
# 检查是否是文件格式
|
||
format_id = 15 # CF_HDROP
|
||
if user32.IsClipboardFormatAvailable(format_id):
|
||
# 获取拖拽文件列表
|
||
h_drop = user32.GetClipboardData(format_id)
|
||
if h_drop:
|
||
files = self._parse_drop_files(h_drop)
|
||
for file_path in files:
|
||
self.add_dropped_file(file_path)
|
||
finally:
|
||
user32.CloseClipboard()
|
||
|
||
time.sleep(0.1)
|
||
|
||
except Exception as e:
|
||
print(f"剪贴板监控错误: {e}")
|
||
|
||
def _parse_drop_files(self, h_drop) -> List[str]:
|
||
"""解析拖拽文件列表"""
|
||
try:
|
||
import ctypes
|
||
from ctypes import wintypes
|
||
|
||
kernel32 = ctypes.windll.kernel32
|
||
shell32 = ctypes.windll.shell32
|
||
|
||
# 获取文件数量
|
||
file_count = shell32.DragQueryFileW(h_drop, 0xFFFFFFFF, None, 0)
|
||
files = []
|
||
|
||
# 获取每个文件路径
|
||
for i in range(file_count):
|
||
# 获取文件路径长度
|
||
length = shell32.DragQueryFileW(h_drop, i, None, 0)
|
||
if length > 0:
|
||
# 分配缓冲区
|
||
buffer = ctypes.create_unicode_buffer(length + 1)
|
||
# 获取文件路径
|
||
shell32.DragQueryFileW(h_drop, i, buffer, length + 1)
|
||
files.append(buffer.value)
|
||
|
||
return files
|
||
except Exception as e:
|
||
print(f"解析拖拽文件失败: {e}")
|
||
return []
|
||
|
||
def _monitor_file_drag(self):
|
||
"""通过文件系统变化监控拖拽"""
|
||
# 监控用户桌面和下载目录
|
||
import winreg
|
||
|
||
try:
|
||
# 获取桌面路径
|
||
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER,
|
||
r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
|
||
desktop_path = winreg.QueryValueEx(key, "Desktop")[0]
|
||
downloads_path = winreg.QueryValueEx(key, "{374DE290-123F-4565-9164-39C4925E467B}")[0] # 下载文件夹
|
||
winreg.CloseKey(key)
|
||
except Exception:
|
||
# 降级到默认路径
|
||
desktop_path = os.path.expanduser("~/Desktop")
|
||
downloads_path = os.path.expanduser("~/Downloads")
|
||
|
||
watch_dirs = [desktop_path, downloads_path]
|
||
|
||
while self.is_running:
|
||
try:
|
||
current_time = time.time()
|
||
for watch_dir in watch_dirs:
|
||
if not os.path.exists(watch_dir):
|
||
continue
|
||
|
||
for filename in os.listdir(watch_dir):
|
||
filepath = os.path.join(watch_dir, filename)
|
||
try:
|
||
file_time = os.path.getmtime(filepath)
|
||
# 如果文件是最近1秒内创建的
|
||
if current_time - file_time < 1.0:
|
||
if self._is_supported_format(filepath):
|
||
self.add_dropped_file(filepath)
|
||
except OSError:
|
||
continue
|
||
|
||
except Exception as e:
|
||
print(f"文件拖拽监控错误: {e}")
|
||
|
||
time.sleep(0.5)
|
||
|
||
def _monitor_loop(self):
|
||
"""主监控循环"""
|
||
# 尝试初始化COM和OLE
|
||
com_ready = self._initialize_com()
|
||
ole_ready = self._initialize_ole()
|
||
|
||
if com_ready and ole_ready:
|
||
# 尝试设置真正的拖拽接收
|
||
if self._setup_drag_drop():
|
||
# 使用Windows API拖拽检测
|
||
pass
|
||
else:
|
||
# 降级到剪贴板监控
|
||
self._monitor_clipboard_drag()
|
||
else:
|
||
# 降级到文件系统监控
|
||
self._monitor_file_drag()
|
||
|
||
# 清理COM和OLE
|
||
try:
|
||
if self._ole_initialized:
|
||
import ctypes
|
||
ctypes.windll.ole32.OleUninitialize()
|
||
if self._is_com_initialized:
|
||
import ctypes
|
||
ctypes.windll.ole32.CoUninitialize()
|
||
except Exception:
|
||
pass
|
||
|
||
def is_supported(self) -> bool:
|
||
"""检查Windows平台是否支持"""
|
||
return sys.platform.startswith('win') |