You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Win32 API拖拽文本时预览图片无法显示问题求助

自定义Win32文本拖拽预览图片不显示问题排查

开发自定义UI框架时,Win32 API文本拖拽功能正常,但拖拽预览图片始终无法显示。对比示例代码和文档未发现明显差异,无函数返回错误。环境为C++17、MSVC编译器、x64平台,已提前调用OleInitialize和DragSource::CreateInstance。

DragSource实现代码

class DragSource : public IDropSource {
    UINT m_refCount;
    HWND m_hwnd;

    DragSource() {
        m_refCount = 0;
    }
    virtual ~DragSource() {}

public:
    static HRESULT STDMETHODCALLTYPE CreateInstance(IDropSource** pDropSource) {
        HRESULT hr = E_OUTOFMEMORY;

        if (pDropSource != NULL) {
            DragSource* pCDropSrc = new DragSource();

            hr = pCDropSrc->QueryInterface(IID_IDropSource, reinterpret_cast<void**>(pDropSource));

            if (SUCCEEDED(hr))
                return S_OK;
        }

        return hr;
    }

    HRESULT STDMETHODCALLTYPE QueryInterface(_In_ REFIID riid, _Out_ void** ppvObject) {
        if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDropSource)) {
            *ppvObject = static_cast<void*>(this);
        }
        else {
            *ppvObject = NULL; return E_NOINTERFACE;
        }

        AddRef();

        return S_OK;
    }
    ULONG STDMETHODCALLTYPE AddRef() {
        return InterlockedIncrement(&m_refCount);
    }
    ULONG STDMETHODCALLTYPE Release() {
        ULONG refCount = InterlockedDecrement(&m_refCount);
        if (refCount == 0) {
            delete this;
            return 0;
        }

        return refCount;
    }

    HRESULT GiveFeedback(DWORD dwEffect) override {
        return DRAGDROP_S_USEDEFAULTCURSORS;
    }

    HRESULT QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) override {
        if (fEscapePressed)
            return DRAGDROP_S_CANCEL;
        if (!(grfKeyState & MK_LBUTTON))
            return DRAGDROP_S_DROP;

        return S_OK;
    }
};

启动拖拽操作的代码

void DragNDropManager::startDragText(HWND windowHandle, std::string text) {
    HRESULT hr;

    const wchar_t* testText = L"Hello from drag!";
    HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, (wcslen(testText) + 1) * sizeof(wchar_t));
    memcpy(GlobalLock(hGlobal), testText, (wcslen(testText) + 1) * sizeof(wchar_t));
    GlobalUnlock(hGlobal);

    FORMATETC fmt = { CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    STGMEDIUM stg = { TYMED_HGLOBAL };
    stg.hGlobal = hGlobal;

    SIZE imageSize = { 100, 30 };

    HDC hdc = GetDC(nullptr);
    HDC memDC = CreateCompatibleDC(hdc);
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc, imageSize.cx, imageSize.cy);
    SelectObject(memDC, hBitmap);

    HBRUSH bg = CreateSolidBrush(RGB(255, 0, 255));
    RECT rc = { 0, 0, imageSize.cx, imageSize.cy };
    FillRect(memDC, &rc, bg);
    DeleteObject(bg);
    SetTextColor(memDC, RGB(0, 0, 0));
    DrawTextW(memDC, L"Drag text", -1, &rc, DT_CENTER | DT_VCENTER | DT_SINGLELINE);

    DeleteDC(memDC);
    ReleaseDC(nullptr, hdc);

    SHDRAGIMAGE dragImage = {};
    dragImage.sizeDragImage.cx = imageSize.cx;
    dragImage.sizeDragImage.cy = imageSize.cy;
    dragImage.hbmpDragImage = hBitmap;
    dragImage.crColorKey = RGB(255, 255, 255);
    dragImage.ptOffset = { 0, 0 };

    hr = SHCreateDataObject(NULL, NULL, NULL, NULL, IID_PPV_ARGS(&textObject));
    textObject->SetData(&fmt, &stg, TRUE);

    // Set drag image
    IDragSourceHelper* pHelper = nullptr;
    if (SUCCEEDED(CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&pHelper)))) {
        hr = pHelper->InitializeFromBitmap(&dragImage, textObject);
        std::cout << "InitializeFromBitmap result: " << std::hex << hr << "\n";
        pHelper->Release();
    }

    // Set CDataSource pointer pDs to NULL if you want to use the Shell DropSource class
    DWORD dwEffect;
    DoDragDrop(textObject, sourceObject, DROPEFFECT_COPY, &dwEffect);

    textObject->Release();
    DeleteObject(hBitmap);
}

问题排查与修复要点

  • 颜色键值不匹配:拖拽图片背景色为RGB(255,0,255),但SHDRAGIMAGE.crColorKey设为RGB(255,255,255),导致透明区域错误。需将crColorKey改为与背景一致的RGB(255,0,255)。
  • DragSource的GiveFeedback实现:当前返回DRAGDROP_S_USEDEFAULTCURSORS会让系统使用默认光标覆盖预览图,需修改为返回S_OK,让拖拽助手处理预览和光标:
    HRESULT GiveFeedback(DWORD dwEffect) override {
        return S_OK;
    }
    
  • DC状态未恢复:创建位图后未保存并恢复DC中的旧位图,可能导致绘制异常。修改绘制代码:
    HBITMAP oldBitmap = (HBITMAP)SelectObject(memDC, hBitmap);
    // 绘制逻辑...
    SelectObject(memDC, oldBitmap); // 恢复DC原有位图
    DeleteDC(memDC);
    
  • 拖拽偏移设置:ptOffset是鼠标相对于图片左上角的偏移,设为(0,0)可能导致图片位置偏离鼠标,建议根据起始拖拽位置调整,例如设为(imageSize.cx/2, imageSize.cy/2)让图片中心跟随鼠标。
  • 确认IDataObject初始化:SHCreateDataObject参数全为NULL虽合法,但需确保textObject正确初始化,可添加SUCCEEDED(hr)判断避免后续操作失效。

内容的提问来源于stack exchange,提问作者Astryl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 22:53:12