【问题标题】:WebView2 - Selecting text and copying to clipboardWebView2 - 选择文本并复制到剪贴板
【发布时间】:2021-06-30 04:24:02
【问题描述】:

使用CHtmlView 类,我可以选择所有文本并复制到剪贴板,如下所示:

void CChristianLifeMinistryHtmlView::CopyToClipboard()
{
    ExecWB(OLECMDID_COPY, OLECMDEXECOPT_DONTPROMPTUSER, nullptr, nullptr);
}

void CChristianLifeMinistryHtmlView::SelectAll()
{
    ExecWB(OLECMDID_SELECTALL, OLECMDEXECOPT_DONTPROMPTUSER, nullptr, nullptr);
}

我正在尝试了解如何使用新的 WebView2 API 做同样的事情。


更新

WebView2 控件设计支持:

  • CTRL + A 选择所有内容。
  • CTRL + C 将选中的文本复制到剪贴板。

我找到了一个 VB.NET 解决方案,以编程方式将所有页面复制到剪贴板:

Sub Async GetText()
  v = Await wv.ExecuteScriptAsync("document.body.innerText")
End Sub

但是我使用的是 Visual C++,我没有看到这个方法被暴露。另外,我不确定这是否是我想要的,因为我不想像使用热键一样复制为纯文本数据而是 HTML 数据(适合在 Word 中粘贴)。我也为此提出了GitHub 问题。


更新

所以我现在尝试了这段代码,但它似乎没有做任何事情:

void CWebBrowser::CopyToClipboard()
{
    if (m_pImpl->m_webView != nullptr)
    {
        m_pImpl->m_webView->ExecuteScript(_T("document.body.innerText"), nullptr);

    }
}

更新

据此article 声明:

或者,对于ICoreWebView2::ExecuteScript,您提供一个具有Invoke 方法的实例,该方法为您提供ExecuteScript 请求的成功或失败代码。还要提供第二个参数,即脚本运行结果的JSON

试图找到例子。


更新

我现在明白我需要这样做:

void CWebBrowser::CopyToClipboard()
{
    if (m_pImpl->m_webView != nullptr)
    {
        m_pImpl->m_webView->ExecuteScript(L"document.body.innerText",
            Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
                [](HRESULT error, PCWSTR result) -> HRESULT
                {
                    if (error != S_OK) {
                        ShowFailure(error, L"ExecuteScript failed");
                    }
                    SetClipboardText(result);
                    AfxMessageBox(L"HTML copied to clipboard!", MB_OK);
                    return S_OK;
                }).Get());


    }
}

变量result 包含HTML 页面的内容。但它不喜欢我现在打电话给SetClipboardText。这是错误:

这是函数:

void CWebBrowser::SetClipboardText(CString strText)
{
    BYTE* pbyText;
    TCHAR* pcBuffer;
    HANDLE  hText;
    UINT    uLength;

    //USES_CONVERSION ;

    if (::OpenClipboard(nullptr))
    {
        // Empty it of all data first.
        ::EmptyClipboard();

        // Replace previous text contents.
        uLength = strText.GetLength();
        //pcBuffer = T2A( (LPTSTR)(LPCTSTR)strText);
        pcBuffer = strText.GetBuffer(uLength);
        if (pcBuffer != nullptr)
        {
            hText = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (uLength + 1) * sizeof(TCHAR));
            if (hText != nullptr)
            {
                pbyText = (BYTE*)::GlobalLock(hText);
                if (pbyText != nullptr)
                {
                    // Deliberately not _tcscpy().
                    //strcpy_s( (char *)pbyText, uLength+1, pcBuffer);
                    _tcscpy_s((TCHAR*)pbyText, uLength + 1, pcBuffer);
                    ::GlobalUnlock(hText);

                    ::SetClipboardData(CF_UNICODETEXT, hText);

                    // Don't free this memory, clipboard owns it now.
                }
            }
        }
        ::CloseClipboard();

        strText.ReleaseBuffer(uLength);
    }
}

这是我知道(迄今为止)复制到剪贴板的唯一方法。所以我这里有两个问题:

  1. 它不会让我使用这个功能。
  2. 我希望它只会复制文本 (CF_UNICODETEXT),我不知道这是否足以将数据作为 HTML 粘贴到 Word 中?

关于问题 1,我需要创建方法 static。然后它编译并将信息复制到剪贴板。

关于问题 2,正如预期的那样,粘贴的数据被剥离了 HTML,我只剩下文本。并且换行符在文本中显示为"\n"。这是一个巨大的段落。所以我确实需要CF_HTML 格式。

很遗憾WebView2 没有公开它已经具备的复制到剪贴板功能。


更新

这是我在网上找到的clipboard methodCF_HTML

// CopyHtml() - Copies given HTML to the clipboard.
// The HTML/BODY blanket is provided, so you only need to
// call it like CopyHtml("<b>This is a test</b>");
void CopyHTML(char* html)
{
    // Create temporary buffer for HTML header...
    char* buf = new char[400 + strlen(html)];
    if (!buf) return;

    // Get clipboard id for HTML format...
    static int cfid = 0;
    if (!cfid) cfid = RegisterClipboardFormat("HTML Format");

    // Create a template string for the HTML header...
    strcpy(buf,
        "Version:0.9\r\n"
        "StartHTML:00000000\r\n"
        "EndHTML:00000000\r\n"
        "StartFragment:00000000\r\n"
        "EndFragment:00000000\r\n"
        "<html><body>\r\n"
        "<!--StartFragment -->\r\n");

    // Append the HTML...
    strcat(buf, html);
    strcat(buf, "\r\n");
    // Finish up the HTML format...
    strcat(buf,
        "<!--EndFragment-->\r\n"
        "</body>\r\n"
        "</html>");

    // Now go back, calculate all the lengths, and write out the
    // necessary header information. Note, wsprintf() truncates the
    // string when you overwrite it so you follow up with code to replace
    // the 0 appended at the end with a '\r'...
    char* ptr = strstr(buf, "StartHTML");
    wsprintf(ptr + 10, "%08u", strstr(buf, "<html>") - buf);
    *(ptr + 10 + 8) = '\r';

    ptr = strstr(buf, "EndHTML");
    wsprintf(ptr + 8, "%08u", strlen(buf));
    *(ptr + 8 + 8) = '\r';

    ptr = strstr(buf, "StartFragment");
    wsprintf(ptr + 14, "%08u", strstr(buf, "<!--StartFrag") - buf);
    *(ptr + 14 + 8) = '\r';

    ptr = strstr(buf, "EndFragment");
    wsprintf(ptr + 12, "%08u", strstr(buf, "<!--EndFrag") - buf);
    *(ptr + 12 + 8) = '\r';

    // Now you have everything in place ready to put on the clipboard.
    // Open the clipboard...
    if (OpenClipboard(0))
    {
        // Empty what's in there...
        EmptyClipboard();

        // Allocate global memory for transfer...
        HGLOBAL hText = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, strlen(buf) + 4);

        // Put your string in the global memory...
        char* ptr = (char*)GlobalLock(hText);
        strcpy(ptr, buf);
        GlobalUnlock(hText);

        ::SetClipboardData(cfid, hText);

        CloseClipboard();
        // Free memory...
        GlobalFree(hText);
    }
    // Clean up...
    delete[] buf;
}

但它与PCWSTR 变量不兼容。


更新

我现在在进行调试后意识到,如果我使用 document.body.innerText,dpes 是否会返回实际的 HTML 数据。生成的字符串只是带有\n 字符的新行文本。它不是HTML 格式。所以我回到第一方。

【问题讨论】:

  • ExecuteScript 返回文本,您所要做的就是将其复制到剪贴板(我不是 C++ 人)。在这里搜索如何做到这一点。
  • @PoulBak 但ExecuteScript 的返回值是HRESULT 值。
  • PCWSRT 转换为UTF-8 - 这可行吗? docs.microsoft.com/en-us/windows/win32/api/stringapiset/…

标签: visual-c++ mfc webview2


【解决方案1】:

只需使用ICoreWebView2::ExecuteScript

【讨论】:

  • 我假设脚本返回文本?而不是复制到剪贴板。我不知道如何获取该文本对象。希望它更容易?
  • 我想我现在知道该做什么了。将测试。
  • 我现在已经取得了一些进展,并且对如何使用 ExecuteScript 有了更多的了解。但是我现在将 HTMl 格式的数据设置到剪贴板时遇到了难题。
  • 您必须选择正确的剪贴板格式。您可以解析 HTML 并将其转换为某种文本格式。否则选择 CF_HTML(你可以在网上找到足够的)
  • 查看我的问题更新。我找到了复制 char * 但不是 PCWSTR 的代码。它失败了。
【解决方案2】:

我将其显示为替代方法,因为当前方法不包括实际的 HTML 内容。我在互联网上找到的信息对使用document.body.innerText具有误导性。

一个解决方案是使用document.execCommand() 路由。我在浏览器类中添加了以下方法:

void CWebBrowser::SelectAll()
{
    if (m_pImpl->m_webView != nullptr)
    {
        m_pImpl->m_webView->ExecuteScript(L"document.execCommand(\"SelectAll\")", nullptr);
    }
}


void CWebBrowser::CopyToClipboard2()
{
    if (m_pImpl->m_webView != nullptr)
    {
        m_pImpl->m_webView->ExecuteScript(L"document.execCommand(\"Copy\")", nullptr);
    }
}

然后我将以下按钮处理程序添加到我的对话框中进行测试:

void CMFCTestWebView2Dlg::OnBnClickedButtonSelectAll()
{
    if (m_pWebBrowser != nullptr)
    {
        m_pWebBrowser->SelectAll();
    }
}


void CMFCTestWebView2Dlg::OnBnClickedButtonCopyToClipboard()
{
    if (m_pWebBrowser != nullptr)
    {
        m_pWebBrowser->CopyToClipboard2();
    }
}

这确实有效,并且似乎是目前最简单的解决方案。我看到引用 Calendar API 作为替代方案,但我还不确定如何实现该方法。

我对@9​​87654327@ 的唯一担忧是我看到它被记录为已弃用

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多