using System; using System.Runtime.InteropServices; using System.Text; using System.Windows.Threading; namespace AxCopilot.Services; public class FileDialogWatcher : IDisposable { private delegate void WinEventDelegate(nint hWinEventHook, uint eventType, nint hwnd, int idObject, int idChild, uint idEventThread, uint dwmsEventTime); private const uint EVENT_OBJECT_SHOW = 32770u; private const uint EVENT_OBJECT_CREATE = 32768u; private const uint WINEVENT_OUTOFCONTEXT = 0u; private const uint WINEVENT_SKIPOWNPROCESS = 2u; private nint _hook; private WinEventDelegate? _delegate; private bool _disposed; private readonly Dispatcher _dispatcher; public event EventHandler? FileDialogOpened; [DllImport("user32.dll")] private static extern nint SetWinEventHook(uint eventMin, uint eventMax, nint hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); [DllImport("user32.dll")] private static extern bool UnhookWinEvent(nint hWinEventHook); [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetClassName(nint hWnd, StringBuilder lpClassName, int nMaxCount); [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetWindowText(nint hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll")] private static extern bool IsWindowVisible(nint hWnd); public FileDialogWatcher() { _dispatcher = Dispatcher.CurrentDispatcher; } public void Start() { if (_hook == IntPtr.Zero) { _delegate = OnWinEvent; _hook = SetWinEventHook(32770u, 32770u, IntPtr.Zero, _delegate, 0u, 0u, 2u); } } public void Stop() { if (_hook != IntPtr.Zero) { UnhookWinEvent(_hook); _hook = IntPtr.Zero; } } private void OnWinEvent(nint hWinEventHook, uint eventType, nint hwnd, int idObject, int idChild, uint idEventThread, uint dwmsEventTime) { if (hwnd == IntPtr.Zero || idObject != 0 || !IsWindowVisible(hwnd)) { return; } StringBuilder stringBuilder = new StringBuilder(256); GetClassName(hwnd, stringBuilder, 256); string text = stringBuilder.ToString(); if (text != "#32770") { return; } StringBuilder stringBuilder2 = new StringBuilder(256); GetWindowText(hwnd, stringBuilder2, 256); string text2 = stringBuilder2.ToString(); if (text2.Contains("열기") || text2.Contains("Open") || text2.Contains("저장") || text2.Contains("Save") || text2.Contains("다른 이름") || text2.Contains("Browse") || text2.Contains("폴더") || text2.Contains("Folder")) { _dispatcher.BeginInvoke((Delegate)(Action)delegate { this.FileDialogOpened?.Invoke(this, hwnd); }, Array.Empty()); } } public void Dispose() { if (!_disposed) { _disposed = true; Stop(); } } }