• windows剪切板暂存


    其实最初是因为在项目中使用了html网页编辑器,通过ie的com组件和javascript通讯完成一些事情,其中有一个功能是插入表格,我们原本使用的range.pasteHTML(HTMLstr);根据用户传入的行和列等参数在javascript端创建好用户想要的表格的html字串,然后的然后,测试人员就发现一个bug,因为这种pasterHTML会破坏TextArea原本的剪切板内容,会直接导致无法撤销到插入表格之前(包含插入表格)的状态。

    有一个同事想出一个方法,使用剪切板来代替pasterHTML这样的操作,因为粘贴一个html有格式的内容是可以撤销的,这样就又会涉及到一个问题,如果借用了剪切板的内容就需要备份之前的内容,并在使用完之后恢复,不管原来是图片、纯文本、还是word、表格、带格式的复杂的内容。

    首先在MSDN上找到这篇文章:http://msdn.microsoft.com/en-us/library/windows/desktop/ms649015%28v=vs.85%29.aspx

    因为我需要加入的是一个html表格所以找来了它所需要的格式,并使用SetClipboardData进行设置。

    官方格式示例:

    Version:0.9
    StartHTML:71
    EndHTML:170
    StartFragment:140
    EndFragment:160
    StartSelection:140
    EndSelection:160
    <!DOCTYPE>
    <HTML>
    <HEAD>
    <TITLE> The HTML Clipboard</TITLE>
    <BASE HREF="http://sample/specs">
    </HEAD>
    <BODY>
    <UL>
    <!--StartFragment -->
    <LI> The Fragment </LI>
    <!--EndFragment -->
    </UL>
    </BODY>
    </HTML>
    View Code

    构造header代码:

     1 int ClipboardHTMLHeader::size() const
     2 {
     3     const int numSpaces = 8;
     4     int headerSIZE = 8/*strlen("Version:")*/ + strlen(version);
     5     headerSIZE += 10/*strlen("StartHTML:")*/ + numSpaces;
     6     headerSIZE += 8/*strlen("EndHTML:")*/ + numSpaces;
     7     headerSIZE += 14/*strlen("StartFargment:")*/ + numSpaces;
     8     headerSIZE += 12/*strlen("EndFargment:")*/ + numSpaces;
     9     //headerSIZE += 15/*strlen("StartSelection:")*/ + numSpaces;
    10     //headerSIZE += 13/*strlen("EndSelection:")*/ + numSpaces;
    11     headerSIZE += 5/*fields*/ * 1;
    12     return headerSIZE;
    13 }
    14 
    15 std::ostream& operator <<(std::ostream& os, const ClipboardHTMLHeader& header) 
    16 {
    17     using namespace std;
    18     const int numSpaces = 8;
    19     const int headerSIZE = header.size();
    20 
    21     os  << "Version:" << header.version << endl
    22         << "StartHTML:" << setw(numSpaces) << setfill('0') << (header.StartHTML < 0 ? -1 : headerSIZE + header.StartHTML) << endl
    23         << "EndHTML:" << setw(numSpaces) << setfill('0') << (header.EndHTML < 0 ? -1 : headerSIZE + header.EndHTML) << endl
    24         << "StartFragment:" << setw(numSpaces) << setfill('0') << (header.StartFragment < 0 ? -1 : headerSIZE + header.StartFragment) << endl
    25         << "EndFragment:" << setw(numSpaces) << setfill('0') << (header.EndFragment < 0 ? -1 : headerSIZE + header.EndFragment) << endl;
    26         //<< "StartSelection:" << setw(numSpaces) << setfill('0') << (header.StartSelection < 0 ? -1 : headerSIZE + header.StartSelection) << endl
    27         //<< "EndSelection:" << setw(numSpaces) << setfill('0') << (header.EndSelection < 0 ? -1 : headerSIZE + header.EndSelection) << endl;
    28     return os;
    29 }
    30 
    31 bool CRichEditor::CopyHTMLToClipboard(LPCWSTR lpszWide)
    32 {
    33     using namespace std;
    34 
    35     string html = "<!--StartFragment-->" + decode(lpszWide, CP_UTF8) + "<!--EndFragment-->";
    36     char docBegin[] = "<HTML><HEAD><TITLE>*</TITLE></HEAD><BODY>";
    37     char docEnd[]="</BODY></HTML>";
    38     
    39     ClipboardHTMLHeader h;
    40     h.version = "0.9";
    41     h.StartHTML = 0;
    42     h.EndHTML = sizeof(docBegin) + html.length() + sizeof(docEnd);
    43     h.StartFragment = sizeof(docBegin);
    44     h.EndFragment = sizeof(docBegin) + html.length();
    45     //h.StartSelection = h.StartFragment;
    46     //h.EndSelection = h.EndFragment;
    47 
    48     stringstream ss;
    49     ss << h;
    50     ss << docBegin;
    51     ss << html;
    52     ss << docEnd;
    53 
    54     
    55 
    56     // Get clipboard id for HTML format...
    57     static int cfid = 0;
    58     cfid = RegisterClipboardFormat(L"HTML Format");
    59     // Open the clipboard...
    60     if(::OpenClipboard(0)) {
    61         EmptyClipboard();
    62         
    63         HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE/* |GMEM_DDESHARE*/, (int)ss.tellp() + 1);
    64         char* buf = (char*)GlobalLock(hMem);
    65         ss.read( buf, ss.tellp());
    66         buf[ss.tellp()] = 0;
    67         GlobalUnlock(hMem);
    68 
    69         ::SetClipboardData(cfid, hMem);
    70         CloseClipboard();
    71         GlobalFree(hMem);
    72     }
    73 
    74     return false;
    75 }

    其中selection是可选的,重载了运算符<<,且定义了很多int类型记录它们的偏移量。

    接下来需要在insert table的之前深拷贝剪切板的内容到内存中,在时候恢复内存的数据到剪切板中,这其中对用户都是不可知的并且速度也是相当快的。

    刚开始我们以为GlobalLock剪切板所获得的内存块是安全的,所以写了代码在push的时候lock,然后在pop的时候unlock,谁知道调用EmptyClipboard后就照样清空了,只能老老实实的拷贝全部的内存数据了,我们采用了map来暂存剪切板中各种类型的数据,针对不同的format的clipboard分别存到不同的map中。

     1 std::map<UINT, HGLOBAL> _clipdata;
     2 void CRichEditor::popClipboardData()
     3 {
     4     ::OpenClipboard(0);
     5     ::EmptyClipboard();
     6     for (auto it = _clipdata.begin(); it != _clipdata.end(); ++it)
     7         ::SetClipboardData(it->first, it->second);
     8     ::CloseClipboard();
     9 
    10     for (auto it = _clipdata.begin(); it != _clipdata.end(); ++it)
    11         ::GlobalFree(it->second);
    12 
    13     _clipdata.clear();
    14 }
    15 
    16 void CRichEditor::pushClipboardData()
    17 {
    18     ::OpenClipboard(0);
    19 
    20     for (UINT next = ::EnumClipboardFormats(0); next != 0; next = ::EnumClipboardFormats(next))
    21     {
    22         if(::IsClipboardFormatAvailable(next))
    23         {
    24             HGLOBAL hmem = ::GetClipboardData(next);
    25             void* src = ::GlobalLock(hmem);
    26             SIZE_T bytes = ::GlobalSize(hmem);
    27 
    28             _clipdata[next] = ::GlobalAlloc( GMEM_MOVEABLE, bytes );
    29 
    30             void* dst = ::GlobalLock( _clipdata[next] );
    31             memcpy(dst, src, bytes);
    32             ::GlobalUnlock(_clipdata[next]);
    33 
    34             ::GlobalUnlock(hmem);
    35         }
    36     }
    37 
    38     ::EmptyClipboard();
    39     ::CloseClipboard();
    40 }

    切记第10、11行必须这样写,必须在CloseClipboard之后来GlobalFree,否则就没办法恢复到备份clipboard之前的状态了。

    差不多到这里就是今天一天的奇遇的全部内容了。。。

    相信很多人碰到这个问题的时候最开始都受到

    http://stackoverflow.com/questions/15962982/how-to-set-html-unicode-text-to-clipboard-in-vc

    http://social.msdn.microsoft.com/Forums/es-ES/acc07c85-d0d3-4c4d-83e9-08f1a239758c/how-to-set-html-unicode-text-to-clipboard-in-vc?forum=vcgeneral

    所误导,根本就不能用,问题是代码写的乱七八糟,怕是只有自己能看得懂。例如105这个数值是怎么来的?length+4又为什么?

    希望祖国的花朵们不要再受到外国人的毒代码摧残了。。.今天就写到这里,希望black早点回家~

  • 相关阅读:
    虚拟化资料
    Windows线程+进程通信
    Linux进程+进程间通信IPC
    COM/DOM/COM+
    C# 2.0新特性与C# 3.5新特性[转]
    [CruiseControl]配置文件config.xml
    C#的Delegate和Event
    [BuildRelease Management]Visual Build
    软件培训机构
    可以自动输入密码的Runas
  • 原文地址:https://www.cnblogs.com/hyb1/p/3378065.html
Copyright © 2020-2023  润新知