好吧,让我们把这个问题分成 3 个部分:
1.检测剪贴板更改:
这很简单,通过使用SetClipboardViewer 注册一个钩子,Windows 会很好地向我们发送WM_DRAWCLIPBOARD 消息:
HWND nextClipboardViewer = nullptr;
void HandleClipboardChanges()
{
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
nextClipboardViewer = SetClipboardViewer(hwnd);
break;
case WM_CHANGECBCHAIN:
if (reinterpret_cast<HWND>(wParam) == nextClipboardViewer)
{
nextClipboardViewer = reinterpret_cast<HWND>(lParam);
}
else if (nextClipboardViewer != nullptr)
{
SendMessage(nextClipboardViewer, msg, wParam, lParam);
}
break;
case WM_DRAWCLIPBOARD:
HandleClipboardChanges();
SendMessage(nextClipboardViewer, msg, wParam, lParam);
break;
}
}
2. 获取活动的 Windows Explorer 目录
在上面的 HandleClipboardChanges 函数中,我们应该遍历所有打开的 Windows 资源管理器,检查其中是否有焦点,并获取它们的当前目录,感谢 zett42's answer,我们可以很容易地做到这一点:
HWND hWndExplorer = nullptr;
HWND hWndFocused = GetActiveWindow();
std::wstring explorerDir;
for (const auto& info : GetCurrentExplorerFolders())
{
if (hWndFocused == info.hwnd)
{
CComHeapPtr<wchar_t> pPath;
if (SUCCEEDED(::SHGetNameFromIDList(info.pidl.get(), SIGDN_FILESYSPATH, &pPath)))
{
hWndExplorer = info.hwnd;
explorerDir = pPath;
}
break;
}
}
3.处理复制操作并显示进度对话框
对于进度对话框,我们会使用IProgressDialog,虽然IOperationsProgressDialog的功能比较多,但是也比较难用,可以考虑换用。
传递给IProgressDialog::StartProgressDialog 的hWndParent 可以是nullptr,但我们将使用资源管理器的hWnd 以保持一致性。
下面的代码不检查可读性错误。
// don't forget the include and CoInitialize
#include <atlbase.h>
#include <shlobj_core.h>
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
CComPtr<IProgressDialog> pDialog;
pDialog.CoCreateInstance(CLSID_ProgressDialog);
pDialog->StartProgressDialog(hWndExplorer, nullptr, PROGDLG_AUTOTIME, nullptr);
pDialog->SetTitle(L"Copying from network");
pDialog->SetLine(1, L"Copying 69 files", false, nullptr);
// Do your copy operation here
for (DWORD i = 0; i < 1'000'000; i++)
{
pDialog->SetProgress(i, 1'000'000);
pDialog->SetLine(2, L"Copying file_a.txt", false, nullptr);
// Check if the user had cancelled the operation
// See also: pDialog->SetCancelMsg()
// BOOL isUserCancelled = pDialog->HasUserCancelled();
}
pDialog->StopProgressDialog();