首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC++ 與資源管理器互通:剪貼板實現檔案剪切/複製/貼上

     編輯:關於VC++

前言

Windows 剪貼板不僅能傳遞文字、圖片,更支援檔案物件的跨程式傳遞。本文詳細解析如何在 VC++ 應用程式中:

  1. 偵測使用者在資源管理器 剪切/複製 檔案後,在自家程式 貼上;
  2. 在自家程式 剪切/複製 檔案,讓資源管理器 貼上。

核心在於理解剪貼板中 CF_HDROP 格式與 Preferred DropEffect 格式的資料結構與操作流程。

資料結構解析

CF_HDROP - 拖放檔案清單

剪貼板不存檔名字串,而是存一個 HDROP 控制碼,指向內部 DROPFILES 結構:

typedef struct _DROPFILES {
    DWORD pFiles;   // 檔案清單相對結構起始位移 (通常 = sizeof(DROPFILES))
    POINT pt;       // 滑鼠座標 (客戶區/螢幕座標由 fNC 決定)
    BOOL  fNC;      // TRUE=客戶區座標, FALSE=螢幕座標
    BOOL  fWide;    // TRUE=Unicode 檔名, FALSE=ANSI
} DROPFILES;

緊接著結構體之後是雙重空字元結尾的檔名陣列 (Unicode 時為 WCHAR):檔名1\0檔名2\0...\0\0。

Preferred DropEffect - 操作類型旗標

非標準格式,需動態註冊:

UINT uDropEffect = RegisterClipboardFormat(_T("Preferred DropEffect"));

資料為單一 DWORD 值,定義於 <oleidl.h>:

#define DROPEFFECT_COPY  1  // 複製
#define DROPEFFECT_MOVE  2  // 移動 (剪切即為移動)
#define DROPEFFECT_LINK  4  // 建立連結
#define DROPEFFECT_SCROLL 0x80000000 // 捲動

情境一:讀取資源管理器放入的檔案

#include <Shellapi.h>
#include <Oleidl.h>

void CheckClipboardFiles(HWND hWnd)
{
    UINT uDropEffect = RegisterClipboardFormat(_T("Preferred DropEffect"));
    if (!OpenClipboard(hWnd)) return;

    HDROP hDrop = (HDROP)GetClipboardData(CF_HDROP);
    if (hDrop)
    {
        // 取得操作類型
        DWORD* pdwEffect = (DWORD*)GetClipboardData(uDropEffect);
        DWORD dwEffect = pdwEffect ? *pdwEffect : DROPEFFECT_COPY;

        // 列舉檔案
        UINT cFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
        TCHAR szFile[MAX_PATH];
        CString strList;
        for (UINT i = 0; i < cFiles; ++i)
        {
            DragQueryFile(hDrop, i, szFile, MAX_PATH);
            strList += szFile;
            strList += _T("\n");
        }

        // 依類型執行複製或移動
        if (dwEffect & DROPEFFECT_MOVE)
        {
            // MoveFileEx(...)
            MessageBox(NULL, strList, _T("Move Files"), MB_OK);
        }
        else if (dwEffect & DROPEFFECT_COPY)
        {
            // CopyFile(...)
            MessageBox(NULL, strList, _T("Copy Files"), MB_OK);
        }
    }
    CloseClipboard();
}

情境二:向剪貼板寫入檔案供資源管理器貼上

需準備兩組全域記憶體物件 (HGLOBAL) 並 SetClipboardData:

完整函式:CutOrCopyFiles

#include <Shellapi.h>
#include <Shlobj.h>
#include <Oleidl.h>

VOID CutOrCopyFiles(LPCSTR lpBuffer, UINT uBufLen, BOOL bCopy)
{
    // 1. 註冊格式
    UINT uDropEffect = RegisterClipboardFormat(_T("Preferred DropEffect"));

    // 2. 準備 DropEffect 資料
    HGLOBAL hGblEffect = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(DWORD));
    DWORD* pdwEffect = (DWORD*)GlobalLock(hGblEffect);
    *pdwEffect = bCopy ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
    GlobalUnlock(hGblEffect);

    // 3. 準備 HDROP (DROPFILES + 檔名清單)
    DROPFILES df = {0};
    df.pFiles = sizeof(DROPFILES);
    df.pt.x = df.pt.y = 0;
    df.fNC = FALSE;   // 螢幕座標
    df.fWide = TRUE;  // Unicode

    // 計算緩衝區大小:結構 + 雙倍字元長度 (Unicode) + 雙重結尾
    UINT uGblLen = sizeof(DROPFILES) + uBufLen * 2 + 8;
    HGLOBAL hGblFiles = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, uGblLen);
    LPBYTE pData = (LPBYTE)GlobalLock(hGblFiles);

    // 複製 DROPFILES 結構
    memcpy(pData, &df, sizeof(DROPFILES));
    LPWSTR pFileList = (LPWSTR)(pData + sizeof(DROPFILES));

    // ANSI/UTF-8 轉 Unicode (lpBuffer 為 "path1\0path2\0" 格式)
    MultiByteToWideChar(CP_ACP, MB_COMPOSITE, lpBuffer, uBufLen, pFileList, uBufLen);

    GlobalUnlock(hGblFiles);

    // 4. 寫入剪貼板
    if (OpenClipboard(NULL))
    {
        EmptyClipboard();
        SetClipboardData(CF_HDROP, hGblFiles);
        SetClipboardData(uDropEffect, hGblEffect);
        CloseClipboard();
    }
    // 注意:剪貼板接管 HGLOBAL 所有權,不可在此釋放
}

呼叫範例

// 複製兩個檔案
char szFiles[] = "c:\\1.txt\0c:\\2.txt\0";
CutOrCopyFiles(szFiles, sizeof(szFiles), TRUE);  // TRUE = Copy

// 剪切兩個檔案
CutOrCopyFiles(szFiles, sizeof(szFiles), FALSE); // FALSE = Cut (Move)

關鍵技術細節

  • GMEM_MOVEABLE | GMEM_DDESHARE:剪貼板資料必須可移動且可跨行程共享。
  • GlobalLock/Unlock:Win32 雖然虛擬記憶體獨立,但剪貼板/DDE 仍依賴全域控制碼機制。
  • fWide = TRUE:現代系統建議一律使用 Unicode 檔名,避免編碼問題。
  • EmptyClipboard:寫入前必須清空,否則資料不會更新。
  • 資料所有權轉移:SetClipboardData 成功後,記憶體由系統管理,程式不得釋放。

附帶 Demo 程式

原始碼包含 CutCopy.exe:

  • 點 [CheckClipboard] 分析剪貼板內容,顯示 Copy/Move 及檔案清單。
  • 點 [Browser] 選取檔案後,按 [Cut]/[Copy] 寫入剪貼板。
  • 可在資源管理器貼上驗證,或再次點 [CheckClipboard] 確認。

編譯環境:VC6.0 + Platform SDK (無需 MFC) / Windows 2000 測試通過。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved