windows剪切板暫存

其實最初是由於在項目中使用了html網頁編輯器,經過ie的com組件和javascript通信完成一些事情,其中有一個功能是插入表格,咱們本來使用的range.pasteHTML(HTMLstr);根據用戶傳入的行和列等參數在javascript端建立好用戶想要的表格的html字串,而後的而後,測試人員就發現一個bug,由於這種pasterHTML會破壞TextArea本來的剪切板內容,會直接致使沒法撤銷到插入表格以前(包含插入表格)的狀態。javascript

有一個同事想出一個方法,使用剪切板來代替pasterHTML這樣的操做,由於粘貼一個html有格式的內容是能夠撤銷的,這樣就又會涉及到一個問題,若是借用了剪切板的內容就須要備份以前的內容,並在使用完以後恢復,無論原來是圖片、純文本、仍是word、表格、帶格式的複雜的內容。html

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

由於我須要加入的是一個html表格因此找來了它所須要的格式,並使用SetClipboardData進行設置。windows

官方格式示例:安全

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類型記錄它們的偏移量。ide

接下來須要在insert table的以前深拷貝剪切板的內容到內存中,在時候恢復內存的數據到剪切板中,這其中對用戶都是不可知的而且速度也是至關快的。測試

剛開始咱們覺得GlobalLock剪切板所得到的內存塊是安全的,因此寫了代碼在push的時候lock,而後在pop的時候unlock,誰知道調用EmptyClipboard後就照樣清空了,只能老老實實的拷貝所有的內存數據了,咱們採用了map來暫存剪切板中各類類型的數據,針對不一樣的format的clipboard分別存到不一樣的map中。spa

 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 }

切記第十、11行必須這樣寫,必須在CloseClipboard以後來GlobalFree,不然就沒辦法恢復到備份clipboard以前的狀態了。code

差很少到這裏就是今天一天的奇遇的所有內容了。。。

相信不少人碰到這個問題的時候最開始都受到

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早點回家~

相關文章
相關標籤/搜索