Fix Clipboard History panel shows corrupted data issue

Fix #13817, fix #13844, close #13833
This commit is contained in:
Don Ho 2023-06-27 04:00:59 +02:00
parent 1943ce87b5
commit d6b5f53a0e
3 changed files with 97 additions and 63 deletions

View File

@ -20,25 +20,22 @@
#include "clipboardFormats.h" #include "clipboardFormats.h"
#define CLIPBOARD_TEXTFORMAT CF_UNICODETEXT
#define MAX_DISPLAY_LENGTH 64 #define MAX_DISPLAY_LENGTH 64
ClipboardData ClipboardHistoryPanel::getClipboadData() ClipboardDataInfo ClipboardHistoryPanel::getClipboadData()
{ {
ClipboardData clipboardData; ClipboardDataInfo clipboardData;
if (!IsClipboardFormatAvailable(CLIPBOARD_TEXTFORMAT)) if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
return clipboardData; return clipboardData;
if (!OpenClipboard(NULL)) if (!OpenClipboard(NULL))
return clipboardData; return clipboardData;
HGLOBAL hglb = GetClipboardData(CLIPBOARD_TEXTFORMAT); HGLOBAL hglb = GetClipboardData(CF_UNICODETEXT);
if (hglb != NULL) if (hglb != NULL)
{ {
char *lpchar = (char *)GlobalLock(hglb); unsigned char* pData = static_cast<unsigned char*>(GlobalLock(hglb));
wchar_t *lpWchar = (wchar_t *)GlobalLock(hglb); if (pData != NULL)
if (lpchar != NULL)
{ {
UINT cf_nppTextLen = RegisterClipboardFormat(CF_NPPTEXTLEN); UINT cf_nppTextLen = RegisterClipboardFormat(CF_NPPTEXTLEN);
if (IsClipboardFormatAvailable(cf_nppTextLen)) if (IsClipboardFormatAvailable(cf_nppTextLen))
@ -46,36 +43,48 @@ ClipboardData ClipboardHistoryPanel::getClipboadData()
HGLOBAL hglbLen = GetClipboardData(cf_nppTextLen); HGLOBAL hglbLen = GetClipboardData(cf_nppTextLen);
if (hglbLen != NULL) if (hglbLen != NULL)
{ {
unsigned long *lpLen = (unsigned long *)GlobalLock(hglbLen); HGLOBAL hglb_binText = GetClipboardData(CF_TEXT);
if (lpLen != NULL) if (hglb_binText != NULL)
{ {
for (size_t i = 0 ; i < (*lpLen) ; ++i) unsigned char* pData_bin = static_cast<unsigned char*>(GlobalLock(hglb_binText));
if (pData_bin != NULL)
{ {
clipboardData.push_back(static_cast<unsigned char>(lpchar[i])); unsigned long* lpLen = (unsigned long*)GlobalLock(hglbLen);
if (lpLen != NULL) // Special copy-paste: Binary data
{
size_t nbBytes = (*lpLen);
for (size_t i = 0; i < nbBytes; ++i)
{
clipboardData._data.push_back(static_cast<unsigned char>(pData_bin[i]));
}
clipboardData._isBinaryContained = true;
GlobalUnlock(hglbLen);
}
} }
GlobalUnlock(hglbLen); GlobalUnlock(hglb_binText);
} }
} }
} }
else if (lpWchar != nullptr) else // Not internal binary clipboard data
{ {
int nbBytes = (lstrlenW(lpWchar) + 1) * sizeof(wchar_t); wchar_t* lpwchar = (wchar_t*)pData;
for (int i = 0 ; i < nbBytes ; ++i) size_t nbBytes = (lstrlenW(lpwchar) + 1) * sizeof(wchar_t);
for (size_t i = 0 ; i < nbBytes ; ++i)
{ {
clipboardData.push_back(static_cast<unsigned char>(lpchar[i])); clipboardData._data.push_back(static_cast<unsigned char>(pData[i]));
} }
} }
GlobalUnlock(hglb); GlobalUnlock(hglb);
GlobalUnlock(hglb);
} }
} }
CloseClipboard(); CloseClipboard();
return clipboardData; return clipboardData;
} }
ByteArray::ByteArray(ClipboardData cd) ByteArray::ByteArray(ClipboardDataInfo cd)
{ {
_length = cd.size(); _length = cd._data.size();
if (!_length) if (!_length)
{ {
_pBytes = NULL; _pBytes = NULL;
@ -84,20 +93,22 @@ ByteArray::ByteArray(ClipboardData cd)
_pBytes = new unsigned char[_length]; _pBytes = new unsigned char[_length];
for (size_t i = 0 ; i < _length ; ++i) for (size_t i = 0 ; i < _length ; ++i)
{ {
_pBytes[i] = cd[i]; _pBytes[i] = cd._data[i];
} }
} }
StringArray::StringArray(ClipboardData cd, size_t maxLen) StringArray::StringArray(ClipboardDataInfo cd, size_t maxLen)
{ {
if (!cd.size()) size_t len = cd._data.size();
if (!len)
{ {
_pBytes = NULL; _pBytes = NULL;
return; return;
} }
bool isCompleted = (cd.size() <= maxLen); bool isCompleted = (len <= maxLen);
_length = isCompleted?cd.size():maxLen; _length = isCompleted?len:maxLen;
_pBytes = new unsigned char[_length+(isCompleted?0:2)]; _pBytes = new unsigned char[_length+(isCompleted?0:2)];
@ -109,7 +120,7 @@ StringArray::StringArray(ClipboardData cd, size_t maxLen)
else if (!isCompleted && (i == _length-6 || i == _length-4 || i == _length-2)) else if (!isCompleted && (i == _length-6 || i == _length-4 || i == _length-2))
_pBytes[i] = '.'; _pBytes[i] = '.';
else else
_pBytes[i] = cd[i]; _pBytes[i] = cd._data[i];
} }
if (!isCompleted) if (!isCompleted)
@ -121,17 +132,17 @@ StringArray::StringArray(ClipboardData cd, size_t maxLen)
// Search clipboard data in internal storage // Search clipboard data in internal storage
// return -1 if not found, else return the index of internal array // return -1 if not found, else return the index of internal array
int ClipboardHistoryPanel::getClipboardDataIndex(ClipboardData cbd) int ClipboardHistoryPanel::getClipboardDataIndex(ClipboardDataInfo cbd)
{ {
int iFound = -1; int iFound = -1;
bool found = false; bool found = false;
for (size_t i = 0, len = _clipboardDataVector.size() ; i < len ; ++i) for (size_t i = 0, len = _clipboardDataInfos.size() ; i < len ; ++i)
{ {
if (cbd.size() == _clipboardDataVector[i].size()) if (cbd._data.size() == _clipboardDataInfos[i]._data.size())
{ {
for (size_t j = 0, len2 = cbd.size(); j < len2 ; ++j) for (size_t j = 0, len2 = cbd._data.size(); j < len2 ; ++j)
{ {
if (cbd[j] == _clipboardDataVector[i][j]) if (cbd._data[j] == _clipboardDataInfos[i]._data[j])
found = true; found = true;
else else
{ {
@ -150,40 +161,49 @@ int ClipboardHistoryPanel::getClipboardDataIndex(ClipboardData cbd)
return iFound; return iFound;
} }
void ClipboardHistoryPanel::addToClipboadHistory(ClipboardData cbd) void ClipboardHistoryPanel::addToClipboadHistory(ClipboardDataInfo cbd)
{ {
int i = getClipboardDataIndex(cbd); int i = getClipboardDataIndex(cbd);
if (i == 0) return; if (i == 0) return;
if (i != -1) if (i != -1)
{ {
_clipboardDataVector.erase(_clipboardDataVector.begin() + i); _clipboardDataInfos.erase(_clipboardDataInfos.begin() + i);
::SendDlgItemMessage(_hSelf, IDC_LIST_CLIPBOARD, LB_DELETESTRING, i, 0); ::SendDlgItemMessage(_hSelf, IDC_LIST_CLIPBOARD, LB_DELETESTRING, i, 0);
} }
_clipboardDataVector.insert(_clipboardDataVector.begin(), cbd); _clipboardDataInfos.insert(_clipboardDataInfos.begin(), cbd);
StringArray sa(cbd, MAX_DISPLAY_LENGTH); ::SendDlgItemMessage(_hSelf, IDC_LIST_CLIPBOARD, LB_INSERTSTRING, 0, reinterpret_cast<LPARAM>(L"")); // String will be added in drawItem()
TCHAR *displayStr = (TCHAR *)sa.getPointer();
::SendDlgItemMessage(_hSelf, IDC_LIST_CLIPBOARD, LB_INSERTSTRING, 0, reinterpret_cast<LPARAM>(displayStr));
} }
void ClipboardHistoryPanel::drawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) void ClipboardHistoryPanel::drawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{ {
if (lpDrawItemStruct->itemID >= _clipboardDataVector.size()) UINT i = lpDrawItemStruct->itemID;
if (i >= _clipboardDataInfos.size())
return; return;
//printStr(TEXT("OK")); //printStr(TEXT("OK"));
COLORREF fgColor = _lbFgColor == -1?black:_lbFgColor; // fg black by default COLORREF fgColor = _lbFgColor == -1?black:_lbFgColor; // fg black by default
COLORREF bgColor = _lbBgColor == -1?white:_lbBgColor; // bg white by default COLORREF bgColor = _lbBgColor == -1?white:_lbBgColor; // bg white by default
StringArray sa(_clipboardDataVector[lpDrawItemStruct->itemID], MAX_DISPLAY_LENGTH); ClipboardDataInfo& cbd = _clipboardDataInfos[i];
TCHAR *ptStr = (TCHAR *)sa.getPointer(); StringArray sa(cbd, MAX_DISPLAY_LENGTH);
TCHAR* displayStr = nullptr;
WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance();
if (cbd._isBinaryContained)
{
char* displayStrA = (char*)sa.getPointer();
displayStr = (TCHAR*)wmc.char2wchar(displayStrA, SC_CP_UTF8);
}
else
{
displayStr = (TCHAR*)sa.getPointer();
}
//printStr(ptStr);
::SetTextColor(lpDrawItemStruct->hDC, fgColor); ::SetTextColor(lpDrawItemStruct->hDC, fgColor);
::SetBkColor(lpDrawItemStruct->hDC, bgColor); ::SetBkColor(lpDrawItemStruct->hDC, bgColor);
::DrawText(lpDrawItemStruct->hDC, ptStr, lstrlen(ptStr), &(lpDrawItemStruct->rcItem), DT_SINGLELINE | DT_VCENTER | DT_LEFT); ::DrawText(lpDrawItemStruct->hDC, displayStr, lstrlen(displayStr), &(lpDrawItemStruct->rcItem), DT_SINGLELINE | DT_VCENTER | DT_LEFT);
} }
intptr_t CALLBACK ClipboardHistoryPanel::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) intptr_t CALLBACK ClipboardHistoryPanel::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam)
@ -212,9 +232,11 @@ intptr_t CALLBACK ClipboardHistoryPanel::run_dlgProc(UINT message, WPARAM wParam
case WM_DRAWCLIPBOARD : case WM_DRAWCLIPBOARD :
{ {
ClipboardData clipboardData = getClipboadData(); ClipboardDataInfo clipboardData = getClipboadData();
if (clipboardData.size()) if (clipboardData._data.size())
{
addToClipboadHistory(clipboardData); addToClipboadHistory(clipboardData);
}
if (_hwndNextCbViewer) if (_hwndNextCbViewer)
::SendMessage(_hwndNextCbViewer, message, wParam, lParam); ::SendMessage(_hwndNextCbViewer, message, wParam, lParam);
return TRUE; return TRUE;
@ -244,18 +266,27 @@ intptr_t CALLBACK ClipboardHistoryPanel::run_dlgProc(UINT message, WPARAM wParam
else else
codepage = SC_CP_UTF8; codepage = SC_CP_UTF8;
ByteArray ba(_clipboardDataVector[i]);
char* c = nullptr; char* c = nullptr;
try { try {
int nbChar = WideCharToMultiByte(codepage, 0, (wchar_t *)ba.getPointer(), static_cast<int32_t>(ba.getLength()), NULL, 0, NULL, NULL); if (_clipboardDataInfos[i]._isBinaryContained)
{
(*_ppEditView)->execute(SCI_REPLACESEL, 0, reinterpret_cast<LPARAM>(""));
(*_ppEditView)->execute(SCI_ADDTEXT, _clipboardDataInfos[i]._data.size(), reinterpret_cast<LPARAM>(&(_clipboardDataInfos[i]._data[0])));
}
else
{
ByteArray ba(_clipboardDataInfos[i]);
int nbChar = WideCharToMultiByte(codepage, 0, (wchar_t*)ba.getPointer(), static_cast<int32_t>(ba.getLength()), NULL, 0, NULL, NULL);
c = new char[nbChar + 1]; c = new char[nbChar + 1];
WideCharToMultiByte(codepage, 0, (wchar_t *)ba.getPointer(), static_cast<int32_t>(ba.getLength()), c, nbChar + 1, NULL, NULL); WideCharToMultiByte(codepage, 0, (wchar_t*)ba.getPointer(), static_cast<int32_t>(ba.getLength()), c, nbChar + 1, NULL, NULL);
(*_ppEditView)->execute(SCI_REPLACESEL, 0, reinterpret_cast<LPARAM>("")); (*_ppEditView)->execute(SCI_REPLACESEL, 0, reinterpret_cast<LPARAM>(""));
(*_ppEditView)->execute(SCI_ADDTEXT, strlen(c), reinterpret_cast<LPARAM>(c)); (*_ppEditView)->execute(SCI_ADDTEXT, strlen(c), reinterpret_cast<LPARAM>(c));
(*_ppEditView)->getFocus(); (*_ppEditView)->getFocus();
delete[] c; delete[] c;
}
} }
catch (...) catch (...)
{ {

View File

@ -23,14 +23,17 @@
#define CH_PROJECTPANELTITLE TEXT("Clipboard History") #define CH_PROJECTPANELTITLE TEXT("Clipboard History")
typedef std::vector<unsigned char> ClipboardData;
class ScintillaEditView; class ScintillaEditView;
struct ClipboardDataInfo {
std::vector<unsigned char> _data;
bool _isBinaryContained = false;
};
class ByteArray { class ByteArray {
public: public:
ByteArray() = default; ByteArray() = default;
explicit ByteArray(ClipboardData cd); explicit ByteArray(ClipboardDataInfo cd);
~ByteArray() { ~ByteArray() {
if (_pBytes) if (_pBytes)
@ -45,7 +48,7 @@ protected:
class StringArray : public ByteArray { class StringArray : public ByteArray {
public: public:
StringArray(ClipboardData cd, size_t maxLen); StringArray(ClipboardDataInfo cd, size_t maxLen);
}; };
class ClipboardHistoryPanel : public DockingDlgInterface { class ClipboardHistoryPanel : public DockingDlgInterface {
@ -61,9 +64,9 @@ public:
_hParent = parent2set; _hParent = parent2set;
}; };
ClipboardData getClipboadData(); ClipboardDataInfo getClipboadData();
void addToClipboadHistory(ClipboardData cbd); void addToClipboadHistory(ClipboardDataInfo cbd);
int getClipboardDataIndex(ClipboardData cbd); int getClipboardDataIndex(ClipboardDataInfo cbd);
virtual void setBackgroundColor(COLORREF bgColour) { virtual void setBackgroundColor(COLORREF bgColour) {
_lbBgColor = bgColour; _lbBgColor = bgColour;
@ -79,7 +82,7 @@ protected:
private: private:
ScintillaEditView **_ppEditView = nullptr; ScintillaEditView **_ppEditView = nullptr;
std::vector<ClipboardData> _clipboardDataVector; std::vector<ClipboardDataInfo> _clipboardDataInfos;
HWND _hwndNextCbViewer = nullptr; HWND _hwndNextCbViewer = nullptr;
int _lbBgColor = -1; int _lbBgColor = -1;
int _lbFgColor= -1; int _lbFgColor= -1;

View File

@ -20,6 +20,6 @@
#define CF_HTML TEXT("HTML Format") #define CF_HTML TEXT("HTML Format")
#define CF_RTF TEXT("Rich Text Format") #define CF_RTF TEXT("Rich Text Format")
#define CF_NPPTEXTLEN TEXT("Notepad++ Binary Text Length") #define CF_NPPTEXTLEN TEXT("Notepad++ Binary Length")
#endif //CLIPBOARDFORMATS_H #endif //CLIPBOARDFORMATS_H