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

如何将文件作为OLE对象复制到剪贴板,实现第三方应用插入功能?

问题描述

我开发的插件所在的第三方应用支持在文档中插入OLE对象,想复现/自动化从Windows资源管理器复制文件并粘贴到该应用的功能——比如复制.doc文件时,应用会将其作为Word OLE对象插入。

目前已能获取文件名,并成功将其作为OLE"Package"插入,但遇到瓶颈:OLE相关函数的文档极少。推测需要向剪贴板添加特定信息来识别OLE对象类型,而这个类型应该可以通过文件扩展名推导出来。

以下是从网上示例中获取的现有代码:

if (AfxOleInit())
{
    CComPtr<IDataObject> spdto;
    if (SUCCEEDED(GetUIObjectOfFile(nullptr, m_sFileName, IID_PPV_ARGS(&spdto))) &&
        SUCCEEDED(OleSetClipboard(spdto)) &&
        SUCCEEDED(OleFlushClipboard()))
    {
        // success
    }
}

//------------------------------------------------------------------------------
HRESULT GetUIObjectOfFile(HWND hwnd, LPCWSTR pszPath, REFIID riid, void** ppv)
//------------------------------------------------------------------------------
{
    *ppv = NULL;
    HRESULT hr;
    LPITEMIDLIST pidl;
    SFGAOF sfgao;
    //printf_s("%s", pszPath);
    if (SUCCEEDED(hr = SHParseDisplayName(pszPath, NULL, &pidl, 0, &sfgao)))
    {
        IShellFolder* psf;
        LPCITEMIDLIST pidlChild;
        if (SUCCEEDED(hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&psf, &pidlChild)))
        {
            hr = psf->GetUIObjectOf(hwnd, 1, &pidlChild, riid, NULL, ppv);
            psf->Release();
        }
        CoTaskMemFree(pidl);
    }
    return hr;
}
解决方案

你的思路正确:剪贴板需要包含对应文件类型的OLE对象信息,而非仅通用的Package容器。当前代码通过GetUIObjectOfFile获取的IDataObject仅包含文件基础剪贴板格式,未关联到对应OLE服务器程序,因此被识别为Package。

要实现和资源管理器复制一致的效果,需按以下步骤调整:

核心步骤

  1. 通过文件扩展名获取关联OLE的CLSID
    使用AssocQueryString从文件扩展名获取关联ProgID,再通过CLSIDFromProgID得到对应CLSID(比如.doc对应Word.Document.8的CLSID)。

  2. 从文件创建对应类型的OLE对象
    使用OleCreateFromFile直接从目标文件创建对应类型的OLE对象,而非通过Shell的GetUIObjectOfFile获取基础数据。

  3. 将OLE对象数据写入剪贴板
    从OLE对象获取IDataObject接口,通过OleSetClipboard和OleFlushClipboard写入剪贴板,确保目标应用识别为对应类型的OLE对象。

修改后代码示例

#include <Shlwapi.h>
#include <Ole2.h>
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "Ole32.lib")

HRESULT CopyFileAsSpecificOLEObject(LPCWSTR pszFilePath)
{
    HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
    if (FAILED(hr))
        return hr;

    // 1. 获取文件关联的ProgID
    WCHAR szProgID[MAX_PATH] = {0};
    hr = AssocQueryStringW(ASSOCF_INIT_DEFAULTTOSTAR, ASSOCSTR_PROGID, pszFilePath, nullptr, szProgID, nullptr);
    if (FAILED(hr))
    {
        CoUninitialize();
        return hr;
    }

    // 2. 从ProgID获取CLSID
    CLSID clsidOle;
    hr = CLSIDFromProgIDW(szProgID, &clsidOle);
    if (FAILED(hr))
    {
        CoUninitialize();
        return hr;
    }

    // 3. 从文件创建对应类型的OLE对象
    CComPtr<IOleObject> spOleObj;
    hr = OleCreateFromFile(clsidOle, pszFilePath, IID_IOleObject, OLERENDER_DRAW,
                           nullptr, nullptr, (void**)&spOleObj);
    if (FAILED(hr))
    {
        CoUninitialize();
        return hr;
    }

    // 4. 设置宿主信息(可选,但部分OLE服务器需要)
    hr = spOleObj->SetHostNames(L"PluginHost", L"OLEContainer");
    if (FAILED(hr))
    {
        CoUninitialize();
        return hr;
    }

    // 5. 获取IDataObject并写入剪贴板
    CComPtr<IDataObject> spDataObj;
    hr = spOleObj->QueryInterface(IID_IDataObject, (void**)&spDataObj);
    if (SUCCEEDED(hr))
    {
        hr = OleSetClipboard(spDataObj);
        if (SUCCEEDED(hr))
        {
            OleFlushClipboard();
        }
    }

    CoUninitialize();
    return hr;
}

注意事项

  • 确保目标文件类型已正确注册OLE服务器(比如安装了Microsoft Word才能处理.doc文件)
  • 必须使用单线程公寓(STA)模型初始化COM(COINIT_APARTMENTTHREADED),OLE对象操作依赖该模型
  • 若AssocQueryString获取ProgID失败,可尝试直接指定已知ProgID(比如硬编码Word.Document.8处理.doc)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 05:47:40