EG/core/drag_drop/windows_detector.py
2026-01-21 09:59:13 +08:00

221 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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')