其實最初是由於在項目中使用了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>
構造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
所誤導,根本就不能用,問題是代碼寫的亂七八糟,怕是隻有本身能看得懂。例如105這個數值是怎麼來的?length+4又爲何?
但願祖國的花朵們不要再受到外國人的毒代碼摧殘了。。.今天就寫到這裏,但願black早點回家~