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

使用IAdviseSink监听剪贴板变更并获取UWP进程ID失败问题求助

如何获取修改剪贴板的WinRT UWP应用进程ID?

我需要获取修改剪贴板内容的WinRT UWP应用的进程ID。最初我通过剪贴板查看器和监听器响应WM_DRAWCLIPBOARD与WM_CLIPBOARDUPDATE事件,在事件处理中调用::GetClipboardOwner()和::GetWindowThreadProcessId()来获取进程ID,代码如下:

bool IsHwndWinContainer(HWND hwnd)
{
    wchar_t wszClassName[MAX_PATH] = { 0 };
    ::GetClassName(hwnd, wszClassName, _countof(wszClassName));

    return _wcsicmp(wszClassName, L"ApplicationFrameWindow") == 0;
}

HWND GetHwndWinRTApp(HWND winRTcontainer)
{
    return FindWindowEx(winRTcontainer, NULL, L"Windows.UI.Core.CoreWindow", NULL);
}

//....

HWND hwndClipboardOwner = ::GetClipboardOwner();

if (!IsHwndWinContainer(hwndClipboardOwner))
{
    HWND realHwndProcess = GetHwndWinRTApp(hwndClipboardOwner);

    hwndClipboardOwner = realHwndProcess ? realHwndProcess : hwndClipboardOwner;
}

DWORD dwClipboardOwnerWindowProcessId = 0;
::GetWindowThreadProcessId(hwndClipboardOwner, &dwClipboardOwnerWindowProcessId);

但获取到的ID并非目标WinRT应用的进程ID,我认为原因在于UWP应用的容器化特性。我还制作了挂钩SetClipboardData()、GetClipboardData()以及OLE相关方法OleSetClipboard()和OleGetClipboard()的DLL,发现该UWP应用复制文本时仅调用OLE的相关方法。

经过调研,我尝试实现IAdviseSink接口,但调用IDataObject的DAdvise()时出现问题,示例代码如下:

class MyAdviseSink : public IAdviseSink
{
    LONG _cRef = 0;

public:
    virtual void STDMETHODCALLTYPE OnDataChange(FORMATETC* pFormatetc, STGMEDIUM* pStgmed)
    {
        DWORD processId = GetCurrentProcessId();
        std::cout << processId;
    }

    virtual void STDMETHODCALLTYPE OnClose() {}
    virtual void STDMETHODCALLTYPE OnRename(IMoniker* pmk) {}
    virtual void STDMETHODCALLTYPE OnSave() {}
    virtual void STDMETHODCALLTYPE OnViewChange(DWORD dwAspect, LONG lindex) {}

    ULONG STDMETHODCALLTYPE AddRef()
    {
        return InterlockedIncrement(&_cRef);
    }


    ULONG STDMETHODCALLTYPE Release()
    {
        ULONG ulRef = InterlockedDecrement(&_cRef);

        if (0 == ulRef)
        {
            delete this;
        }

        return ulRef;
    }


    HRESULT STDMETHODCALLTYPE QueryInterface(
        REFIID riid, VOID** ppvInterface)
    {
        if (IID_IUnknown == riid)
        {
            AddRef();
            *ppvInterface = (IUnknown*)this;
        }
        else if (__uuidof(IAdviseSink) == riid)
        {
            AddRef();
            *ppvInterface = (IAdviseSink*)this;
        }
        else
        {
            *ppvInterface = NULL;
            return E_NOINTERFACE;
        }

        return S_OK;
    }
};


int main()
{
    HRESULT res = ::CoInitialize(NULL);
    MyAdviseSink* pAdviseSink = new MyAdviseSink();

    IDataObject* pDataObject;
    res = ::OleGetClipboard(&pDataObject);

    FORMATETC formatetc;
    ZeroMemory(&formatetc, sizeof(FORMATETC));

    formatetc.cfFormat = CF_UNICODETEXT;
    formatetc.tymed = TYMED_HGLOBAL;
    formatetc.dwAspect = DVASPECT_CONTENT;
    formatetc.lindex = -1;

    DWORD adviseCookie = 0;
    res = pDataObject->DAdvise(&formatetc, ADVF_PRIMEFIRST, pAdviseSink, &adviseCookie);

    MessageBox(NULL, L"Waiting clipboard changing...", L"Example", MB_OK);

    pDataObject->DUnadvise(adviseCookie);
    pDataObject->Release();
    delete pAdviseSink;

    CoUninitialize();

    return 0;
}

调试发现DAdvise()调用返回错误0x80040003: This implementation doesn't take advise.。请问我哪里出错了?或者有没有其他方法可以监听剪贴板内容变更并获取发起变更的进程ID?


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 16:19:51