-1

I would like to hook Windows Explorer paste event to copy files from a remote connection.

Description: The goal is remote copy/paste files. Like Team Viewer or Remote Desktop. Ctrl+C file on one computer, and Ctrl+V on another...

Service 7
  • 35
  • 4
  • When you copy on one computer, put a FILEGROUPDESCRIPTOR on the other computer. Then you can generate the data on the fly during the paste. You get a free copy progress dialog. – Raymond Chen Oct 22 '22 at 15:43
  • @Raymond Chen I have read your articles on MS Support. Do you have any full working example for Windows Explorer Shell Dll Extension that can handle IFileOperation::CopyItem or other... – Service 7 Oct 23 '22 at 11:01
  • https://stackoverflow.com/questions/73185376/transfer-files-remotelly-via-delayed-renderingcf-hdrop – Raymond Chen Oct 23 '22 at 13:31

1 Answers1

2

Well let's break this problem into 3 parts:

1. Detect for clipboard changes:

This is pretty easy, by registering a hook using SetClipboardViewer, Windows will nicely send us an WM_DRAWCLIPBOARD message:

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. Get the active Windows Explorer directory

In the HandleClipboardChanges function above, we should iterate through all the opened Windows Explorer, check if any of them is focused, and get their current directory, thanks to zett42's answer, we could do this fairly easily:

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. Handle the copy operation and show a progress dialog

For the progress dialog, we will use IProgressDialog, although IOperationsProgressDialog has more features, but it is also more difficult to use, you can consider switching to it.

The hWndParent passed into IProgressDialog::StartProgressDialog could be nullptr, but we will use the explorer's hWnd for consistency.

The below code doesn't check for errors for readability.

// 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();

Related:

  1. Monitoring clipboard
  2. How to get the path of an active file explorer window in c++ winapi
thedemons
  • 1,139
  • 2
  • 9
  • 25
  • Thanks. Your answer hits the spot. But there is a problem with with PASTE event. I have hooked GetClipoboardData, and it is called much more times then user explicitly calls PASTE. For example when new directory is opend and etc. Any suggestion how to hook explicitly PASTE. I was looking with IFileOperation::CopyItem, but I didn not find full working example inside Shell Dll Extension for Exporer. Thanks again very for usefull code. :) – Service 7 Oct 23 '22 at 10:52
  • @Service7 Oh yeah I was confused here, `SetClipboardViewer` can only monitor copy events, not paste. I will do some research and update the answer. – thedemons Oct 23 '22 at 10:59
  • Also, I wouldn't recommend injecting into explorer.exe and hooking its APIs, it would get really ugly really fast. – thedemons Oct 23 '22 at 11:03
  • "Also, I wouldn't recommend injecting into explorer.exe and hooking its APIs, it would get really ugly really fast. ". Ok. Any other useful solution? – Service 7 Oct 23 '22 at 11:05
  • @Service7 it seems like there is no way to hook paste events besides checking for `Ctrl+V, Shift+Insert`, it is also impossible to hook the `Right click -> Paste` in explorer without invasive methods like DLL injection. – thedemons Oct 23 '22 at 11:23
  • @Service7 I'm trying to reverse engineer explorer.exe and noticed that [SHGetAttributesFromDataObject](https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetattributesfromdataobject) got called on actual user copy and paste events, it even got called when there are no files in the clipboard, you might wanna look at that to see if you could filter out the paste events. One problem is how to block the paste event if there are files in the client clipboard and paste from the remote connection instead – thedemons Oct 23 '22 at 12:01
  • If you went with the injection option, I think it should be moved to another question, this problem is too broad after all, explaining the whole injection, hooking, and inter-communication process wouldn't fit into one answer. – thedemons Oct 23 '22 at 12:04