synergy-plus へファイルのクリップボード共有機能実装
前回の続きで、synergy-plus にタイトルの通りファイルに対するクリップボード共有機能を実装する。実現方法はファイルのデータ本体をやりとりするという方法もあるが、そうすると巨大なファイルの際に問題が発生するのが目に見えているので、Windows間限定になってしまうが、Windowsの管理共有機能を利用する。
CF_HDROP
エクスプローラからファイルがクリップボードにコピーされるとフォーマットIDとしてはCF_HDROPになり、ファイルの取得方法はファイルのDrag&Drop時と同じようにDragQueryFile関数で取得できる。また、Win2000以降はファイル名にUnicode(UTF16)が使われるのでこれらを考慮して実装する。
変更箇所
元々がかなり整理して作られていたので変更しなければならない箇所はすぐにわかる。クリップボードに送られるフォーマットごとにWindows環境用は、CMSWindowsClipboardXXXConverterという名前がついている。そのため、今回はCMSWindowsClipboardFilePathConverter.cpp/hを作成する。また、作成したConverterを利用するためにいくつかの変更を行った。作り方は他のConverterのソースが参考になる。
IClipboard.h
synergyは複数のOS間で利用できるように作られているので、クリップボードのフォーマットを独自に定義してあり、IClipboard.hにある。ここに、ファイルのパスを表すkFilePathを追加し、これを用いて機能を実現することに。もし、ファイルパスではなくファイルのデータ丸ごとやりとりする場合等はここに別の値を追加してやればできるだろう。どうやらUTF8で文字のやりとりが多いようなのでそれに従ってファイルパスもUTF16ではなくUTF8に変換して送ることにした。
diff --git a/lib/synergy/IClipboard.h b/lib/synergy/IClipboard.h --- a/lib/synergy/IClipboard.h +++ b/lib/synergy/IClipboard.h @@ -56,6 +56,7 @@ kText, //!< Text format, UTF-8, newline is LF kBitmap, //!< Bitmap format, BMP 24/32bpp, BI_RGB kHTML, //!< HTML format, HTML fragment, UTF-8, newline is LF + kFilePath, //!< File Path format, UTF-8, separetor is \t kNumFormats //!< The number of clipboard formats };
CMSWindowsClipborad.cpp
MSWindowsとファイル名にあるものはすべてWindows用の実装になっている。ここのコンストラクタで各種コンバータが登録されているので作成したCMSWindowsClipboardFilePathConverterを登録するように追加する。
diff --git a/lib/platform/CMSWindowsClipboard.cpp b/lib/platform/CMSWindowsClipboard.cpp --- a/lib/platform/CMSWindowsClipboard.cpp +++ b/lib/platform/CMSWindowsClipboard.cpp @@ -17,6 +17,7 @@ #include "CMSWindowsClipboardUTF16Converter.h" #include "CMSWindowsClipboardBitmapConverter.h" #include "CMSWindowsClipboardHTMLConverter.h" +#include "CMSWindowsClipboardFilePathConverter.h" #include "CLog.h" #include "CArchMiscWindows.h" @@ -39,6 +40,7 @@ } m_converters.push_back(new CMSWindowsClipboardBitmapConverter); m_converters.push_back(new CMSWindowsClipboardHTMLConverter); + m_converters.push_back(new CMSWindowsClipboardFilePathConverter); } CMSWindowsClipboard::~CMSWindowsClipboard()
CMSWindowsClipboardFilePathConverter.cpp
クリップボードにコピーされたファイルパスは管理共有を使ったUNCパスに変換(複数アイテムの場合はタブ(\t)を区切り文字)し、UTF8にした後で相手先に送信する。受信側は逆順に展開し、クリップボードにデータとしてセットするように実装している。
#include "CMSWindowsClipboardFilePathConverter.h" #include "CLog.h" #include "shellapi.h" #include "shlobj.h" #include "CUnicode.h" #define WCToCString(x) (CString((const char*)(x), wcslen(x)*sizeof(wchar_t))) // // CMSWindowsClipboardFilePathConverter // CMSWindowsClipboardFilePathConverter::CMSWindowsClipboardFilePathConverter() { OSVERSIONINFO os_info; os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); ::GetVersionEx(&os_info); if ( os_info.dwPlatformId == VER_PLATFORM_WIN32_NT ) { // Widechar useWideChar = TRUE; // ARCH->getHostName() DWORD size = sizeof(computerNameWC); if ( ::GetComputerNameW(computerNameWC, &size) == FALSE ) { // Failed computerNameWC[0] = 0; } } else { // Multibyte useWideChar = FALSE; DWORD size = sizeof(computerNameMB); if ( ::GetComputerNameA(computerNameMB, &size) == FALSE ) { // Failed computerNameMB[0] = 0; } } separator = CUnicode::textToUTF8(CString("\t")); } CMSWindowsClipboardFilePathConverter::~CMSWindowsClipboardFilePathConverter() { } IClipboard::EFormat CMSWindowsClipboardFilePathConverter::getFormat() const { return IClipboard::kFilePath; } UINT CMSWindowsClipboardFilePathConverter::getWin32Format() const { return CF_HDROP; } HANDLE CMSWindowsClipboardFilePathConverter::fromIClipboard(const CString& data) const { // data is UTF8 std::vector<CString> fileList; { // separate data with separator('\t') CString s = data; for (int p = 0; (p = s.find(separator)) != s.npos; ) { #ifdef _DEBUG LOG((CLOG_DEBUG "> %s", CUnicode::UTF8ToText(s.substr(0, p)).c_str())); #endif fileList.push_back(s.substr(0, p)); s = s.substr(p + separator.size()); } if ( s.length() > 1 ) { #ifdef _DEBUG LOG((CLOG_DEBUG ">* %s", CUnicode::UTF8ToText(s).c_str())); #endif fileList.push_back(s); } } LOG((CLOG_DEBUG "file: recv count > %d / widechar > %d", fileList.size(), useWideChar)); #ifdef _DEBUG LOG((CLOG_DEBUG " %s", CUnicode::UTF8ToText(data).c_str())); for ( int i = 0; i < fileList.size(); i++ ) { LOG((CLOG_DEBUG "> %s", CUnicode::UTF8ToText(fileList[i]).c_str())); } #endif LPDROPFILES lpDropFile; HDROP hDrop; UINT bufferSize = 0; // calc buffer size & convert encoding if ( useWideChar == TRUE ) { // widechar for ( UINT i = 0;i < fileList.size(); i++ ) { // Widechar is UTF16 in Windows fileList[i] = CUnicode::UTF8ToUTF16(fileList[i]); bufferSize += fileList[i].size() + sizeof(wchar_t); // length + NULL } } else { // multibyte for ( UINT i = 0; i < fileList.size(); i++ ) { fileList[i] = CUnicode::UTF8ToText(fileList[i]); bufferSize += fileList[i].size() + 1; } } hDrop = (HDROP)::GlobalAlloc(GHND,sizeof(DROPFILES) + bufferSize + 2); if (hDrop == NULL) { return NULL; } lpDropFile = (LPDROPFILES) ::GlobalLock(hDrop); lpDropFile->pFiles = sizeof(DROPFILES); lpDropFile->pt.x = 0; lpDropFile->pt.y = 0; lpDropFile->fNC = FALSE; lpDropFile->fWide = useWideChar; if ( useWideChar == TRUE ) { // widechar wchar_t *buf; buf = (wchar_t*)(&lpDropFile[1]); for ( UINT i = 0; i < fileList.size(); i++ ) { memcpy(buf, fileList[i].c_str(), fileList[i].size()); buf += (fileList[i].size()/sizeof(wchar_t)); *buf++ = 0; } *buf = 0; } else { // multibyte char *buf; buf = (char *)(&lpDropFile[1]); for ( UINT i = 0; i < fileList.size(); i++ ) { memcpy(buf, fileList[i].c_str(), fileList[i].size()); buf += fileList[i].size(); *buf++ = 0; } *buf = 0; } ::GlobalUnlock(hDrop); return hDrop; } CString CMSWindowsClipboardFilePathConverter::toIClipboard(HANDLE data) const { CString fileList; UINT fileCount = 0; // FileCount if ( useWideChar == TRUE ) fileCount = ::DragQueryFileW((HDROP)data, (UINT)-1, NULL, 0); else fileCount = ::DragQueryFileA((HDROP)data, (UINT)-1, NULL, 0); LOG((CLOG_DEBUG "file: send count > %d / widechar > %d", fileCount, useWideChar)); if ( fileCount == 0 ) { // nothing return CString(); } // Get file path from clipboard if ( useWideChar == TRUE ) { wchar_t szPath[MAX_PATH]; for ( UINT i = 0; i < fileCount; i++ ) { ::DragQueryFileW((HDROP)data, i, szPath, sizeof(szPath)); CString cnvPath = convertPath(szPath); fileList.append(cnvPath); fileList.append(separator); // separate with '\t' LOG((CLOG_DEBUG " %s", CUnicode::UTF8ToText(cnvPath).c_str())); } } else { char szPath[MAX_PATH]; for ( UINT i = 0; i < fileCount; i++ ) { ::DragQueryFileA((HDROP)data, i, szPath, sizeof(szPath)); CString cnvPath = convertPath(szPath); fileList.append(cnvPath); fileList.append(separator); // separate with '\t' LOG((CLOG_DEBUG " %s", szPath)); LOG((CLOG_DEBUG " -> %s", cnvPath.c_str())); } } return fileList; } // Convert to UNC path using administrative shared folder CString CMSWindowsClipboardFilePathConverter::convertPath(char* szPath) const { CString path = CString(szPath); CString unc; if ( path.substr(0, 2) == "\\\\" ) { // Remote Path unc = CString(path); } else { // Local Path unc = CString("\\\\"); unc.append(computerNameMB); unc.append("\\"); unc.append(path.replace(1, 1, "$")); // Convert 'C:\...' to 'C$\...' } return CUnicode::textToUTF8(unc); } CString CMSWindowsClipboardFilePathConverter::convertPath(wchar_t* szPath) const { size_t lenPath = wcslen(szPath); if ( lenPath < 3 ) return CString(); CString unc; if ( szPath[0] == L'\\' && szPath[1] == L'\\' ) { // Remote Path unc = WCToCString(szPath); } else { // Local Path unc = WCToCString(L"\\\\"); unc.append(WCToCString((wchar_t*)computerNameWC)); unc.append(WCToCString(L"\\")); char* drive = new char[sizeof(wchar_t)]; memcpy(drive, szPath, sizeof(wchar_t)); unc.append(drive, 1 * sizeof(wchar_t)); // Convert 'C:\...' to 'C$\...' delete drive; unc.append(WCToCString(L"$")); unc.append(WCToCString(szPath+2)); } return CUnicode::UTF16ToUTF8(unc); // Widechar is UTF16 in Windows }
CMSWindowsClipboardFilePathConverter.h
#ifndef CMSWindowsClipboardFilePathConverter_H #define CMSWindowsClipboardFilePathConverter_H #include "CMSWindowsClipboard.h" //! Convert to/from some text encoding class CMSWindowsClipboardFilePathConverter : public IMSWindowsClipboardConverter { private: CString separator; BOOL useWideChar; char computerNameMB[MAX_COMPUTERNAME_LENGTH+1]; wchar_t computerNameWC[MAX_COMPUTERNAME_LENGTH+1]; CString convertPath(char* szPath) const; CString convertPath(wchar_t* szPath) const; public: CMSWindowsClipboardFilePathConverter(); virtual ~CMSWindowsClipboardFilePathConverter(); // IMSWindowsClipboardConverter overrides virtual IClipboard::EFormat getFormat() const; virtual UINT getWin32Format() const; virtual HANDLE fromIClipboard(const CString&) const; virtual CString toIClipboard(HANDLE) const; }; #endif