зеркало из https://github.com/nextcloud/desktop.git
Windows shell extension: port to the new protocol
This commit is contained in:
Родитель
ebfac84c69
Коммит
883080b557
|
@ -34,7 +34,7 @@ using namespace std;
|
|||
#define PIPE_TIMEOUT 5*1000 //ms
|
||||
#define SOCK_BUFFER 4096
|
||||
|
||||
OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
||||
OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo(const std::wstring &files)
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
|
||||
|
@ -45,7 +45,8 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
|||
if (!socket.Connect(pipename)) {
|
||||
return {};
|
||||
}
|
||||
socket.SendMsg(L"GET_STRINGS\n");
|
||||
socket.SendMsg(L"GET_STRINGS:CONTEXT_MENU_TITLE\n");
|
||||
socket.SendMsg((L"GET_MENU_ITEMS:" + files + L"\n").data());
|
||||
|
||||
ContextMenuInfo info;
|
||||
std::wstring response;
|
||||
|
@ -60,16 +61,14 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
|||
wstring stringName, stringValue;
|
||||
if (!StringUtil::extractChunks(response, stringName, stringValue))
|
||||
continue;
|
||||
if (stringName == L"SHARE_MENU_TITLE")
|
||||
info.shareMenuTitle = move(stringValue);
|
||||
else if (stringName == L"CONTEXT_MENU_TITLE")
|
||||
if (stringName == L"CONTEXT_MENU_TITLE")
|
||||
info.contextMenuTitle = move(stringValue);
|
||||
else if (stringName == L"COPY_PRIVATE_LINK_MENU_TITLE")
|
||||
info.copyLinkMenuTitle = move(stringValue);
|
||||
else if (stringName == L"EMAIL_PRIVATE_LINK_MENU_TITLE")
|
||||
info.emailLinkMenuTitle = move(stringValue);
|
||||
}
|
||||
else if (StringUtil::begins_with(response, wstring(L"GET_STRINGS:END"))) {
|
||||
} else if (StringUtil::begins_with(response, wstring(L"MENU_ITEM:"))) {
|
||||
wstring commandName, flags, title;
|
||||
if (!StringUtil::extractChunks(response, commandName, flags, title))
|
||||
continue;
|
||||
info.menuItems.push_back({ commandName, flags, title });
|
||||
} else if (StringUtil::begins_with(response, wstring(L"GET_MENU_ITEMS:END"))) {
|
||||
break; // Stop once we completely received the last sent request
|
||||
}
|
||||
}
|
||||
|
@ -81,22 +80,7 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
|||
return info;
|
||||
}
|
||||
|
||||
void OCClientInterface::RequestShare(const std::wstring &path)
|
||||
{
|
||||
SendRequest(L"SHARE", path);
|
||||
}
|
||||
|
||||
void OCClientInterface::RequestCopyLink(const std::wstring &path)
|
||||
{
|
||||
SendRequest(L"COPY_PRIVATE_LINK", path);
|
||||
}
|
||||
|
||||
void OCClientInterface::RequestEmailLink(const std::wstring &path)
|
||||
{
|
||||
SendRequest(L"EMAIL_PRIVATE_LINK", path);
|
||||
}
|
||||
|
||||
void OCClientInterface::SendRequest(wchar_t *verb, const std::wstring &path)
|
||||
void OCClientInterface::SendRequest(const wchar_t *verb, const std::wstring &path)
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
|
||||
|
|
|
@ -46,18 +46,14 @@ public:
|
|||
struct ContextMenuInfo {
|
||||
std::vector<std::wstring> watchedDirectories;
|
||||
std::wstring contextMenuTitle;
|
||||
std::wstring shareMenuTitle;
|
||||
std::wstring copyLinkMenuTitle;
|
||||
std::wstring emailLinkMenuTitle;
|
||||
struct MenuItem
|
||||
{
|
||||
std::wstring command, flags, title;
|
||||
};
|
||||
std::vector<MenuItem> menuItems;
|
||||
};
|
||||
static ContextMenuInfo FetchInfo();
|
||||
|
||||
static void RequestShare(const std::wstring &path);
|
||||
static void RequestCopyLink(const std::wstring &path);
|
||||
static void RequestEmailLink(const std::wstring &path);
|
||||
|
||||
private:
|
||||
static void SendRequest(wchar_t *verb, const std::wstring &path);
|
||||
static ContextMenuInfo FetchInfo(const std::wstring &files);
|
||||
static void SendRequest(const wchar_t *verb, const std::wstring &path);
|
||||
};
|
||||
|
||||
#endif //ABSTRACTSOCKETHANDLER_H
|
||||
|
|
|
@ -22,13 +22,8 @@
|
|||
#include <shellapi.h>
|
||||
#include <StringUtil.h>
|
||||
|
||||
extern HINSTANCE g_hInst;
|
||||
extern long g_cDllRef;
|
||||
|
||||
#define IDM_SHARE 0
|
||||
#define IDM_COPYLINK 1
|
||||
#define IDM_EMAILLINK 2
|
||||
|
||||
OCContextMenu::OCContextMenu(void)
|
||||
: m_cRef(1)
|
||||
{
|
||||
|
@ -40,23 +35,6 @@ OCContextMenu::~OCContextMenu(void)
|
|||
InterlockedDecrement(&g_cDllRef);
|
||||
}
|
||||
|
||||
|
||||
void OCContextMenu::OnVerbShare(HWND hWnd)
|
||||
{
|
||||
OCClientInterface::RequestShare(std::wstring(m_szSelectedFile));
|
||||
}
|
||||
|
||||
void OCContextMenu::OnVerbCopyLink(HWND hWnd)
|
||||
{
|
||||
OCClientInterface::RequestCopyLink(std::wstring(m_szSelectedFile));
|
||||
}
|
||||
|
||||
void OCContextMenu::OnVerbEmailLink(HWND hWnd)
|
||||
{
|
||||
OCClientInterface::RequestEmailLink(std::wstring(m_szSelectedFile));
|
||||
}
|
||||
|
||||
|
||||
#pragma region IUnknown
|
||||
|
||||
// Query to the interface the component supported.
|
||||
|
@ -97,12 +75,12 @@ IFACEMETHODIMP_(ULONG) OCContextMenu::Release()
|
|||
IFACEMETHODIMP OCContextMenu::Initialize(
|
||||
LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hKeyProgID)
|
||||
{
|
||||
m_selectedFiles.clear();
|
||||
|
||||
if (!pDataObj) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
STGMEDIUM stm;
|
||||
|
||||
|
@ -110,14 +88,19 @@ IFACEMETHODIMP OCContextMenu::Initialize(
|
|||
// Get an HDROP handle.
|
||||
HDROP hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal));
|
||||
if (hDrop) {
|
||||
// Ignore multi-selections
|
||||
UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
|
||||
if (nFiles == 1) {
|
||||
for (int i = 0; i < nFiles; ++i) {
|
||||
// Get the path of the file.
|
||||
if (0 != DragQueryFile(hDrop, 0, m_szSelectedFile, ARRAYSIZE(m_szSelectedFile)))
|
||||
{
|
||||
hr = S_OK;
|
||||
wchar_t buffer[MAX_PATH];
|
||||
|
||||
if (!DragQueryFile(hDrop, i, buffer, ARRAYSIZE(buffer))) {
|
||||
m_selectedFiles.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
if (i)
|
||||
m_selectedFiles += L'\x1e';
|
||||
m_selectedFiles += buffer;
|
||||
}
|
||||
|
||||
GlobalUnlock(stm.hGlobal);
|
||||
|
@ -128,7 +111,7 @@ IFACEMETHODIMP OCContextMenu::Initialize(
|
|||
|
||||
// If any value other than S_OK is returned from the method, the context
|
||||
// menu item is not displayed.
|
||||
return hr;
|
||||
return m_selectedFiles.empty() ? E_FAIL : S_OK;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
@ -153,17 +136,8 @@ IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT
|
|||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
||||
}
|
||||
|
||||
OCClientInterface::ContextMenuInfo info = OCClientInterface::FetchInfo();
|
||||
bool skip = true;
|
||||
size_t selectedFileLength = wcslen(m_szSelectedFile);
|
||||
for (const std::wstring path : info.watchedDirectories) {
|
||||
if (StringUtil::isDescendantOf(m_szSelectedFile, selectedFileLength, path)) {
|
||||
skip = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
m_info = OCClientInterface::FetchInfo(m_selectedFiles);
|
||||
if (m_info.menuItems.empty()) {
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
||||
}
|
||||
|
||||
|
@ -175,7 +149,7 @@ IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT
|
|||
mii.fMask = MIIM_SUBMENU | MIIM_FTYPE | MIIM_STRING;
|
||||
mii.hSubMenu = hSubmenu;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &info.contextMenuTitle[0];
|
||||
mii.dwTypeData = &m_info.contextMenuTitle[0];
|
||||
|
||||
if (!InsertMenuItem(hMenu, indexMenu++, TRUE, &mii))
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
|
@ -183,133 +157,59 @@ IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT
|
|||
InsertSeperator(hMenu, indexMenu++);
|
||||
|
||||
UINT indexSubMenu = 0;
|
||||
{
|
||||
assert(!info.shareMenuTitle.empty());
|
||||
for (auto &item : m_info.menuItems) {
|
||||
bool disabled = item.flags.find(L'd') != std::string::npos;
|
||||
|
||||
MENUITEMINFO mii = { sizeof(mii) };
|
||||
mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING;
|
||||
mii.wID = idCmdFirst + IDM_SHARE;
|
||||
mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING | MIIM_STATE;
|
||||
mii.wID = indexSubMenu;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &info.shareMenuTitle[0];
|
||||
mii.dwTypeData = &item.title[0];
|
||||
mii.fState = disabled ? MFS_DISABLED : MFS_ENABLED;
|
||||
|
||||
if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii))
|
||||
if (!InsertMenuItem(hSubmenu, indexSubMenu, true, &mii))
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
indexSubMenu++;
|
||||
}
|
||||
{
|
||||
assert(!info.copyLinkMenuTitle.empty());
|
||||
MENUITEMINFO mii = { sizeof(mii) };
|
||||
mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING;
|
||||
mii.wID = idCmdFirst + IDM_COPYLINK;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &info.copyLinkMenuTitle[0];
|
||||
|
||||
if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii))
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
{
|
||||
assert(!info.emailLinkMenuTitle.empty());
|
||||
MENUITEMINFO mii = { sizeof(mii) };
|
||||
mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING;
|
||||
mii.wID = idCmdFirst + IDM_EMAILLINK;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &info.emailLinkMenuTitle[0];
|
||||
|
||||
if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii))
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
|
||||
// Return an HRESULT value with the severity set to SEVERITY_SUCCESS.
|
||||
// Set the code value to the offset of the largest command identifier
|
||||
// that was assigned, plus one (1).
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_EMAILLINK + 1));
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(indexSubMenu));
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
|
||||
{
|
||||
std::wstring command;
|
||||
|
||||
// For the Unicode case, if the high-order word is not zero, the
|
||||
// command's verb string is in lpcmi->lpVerbW.
|
||||
if (HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW))
|
||||
{
|
||||
// Is the verb supported by this context menu extension?
|
||||
if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"ocshare") == 0) {
|
||||
OnVerbShare(pici->hwnd);
|
||||
}
|
||||
else if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"occopylink") == 0) {
|
||||
OnVerbCopyLink(pici->hwnd);
|
||||
}
|
||||
else if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"ocemaillink") == 0) {
|
||||
OnVerbEmailLink(pici->hwnd);
|
||||
}
|
||||
else {
|
||||
// If the verb is not recognized by the context menu handler, it
|
||||
// must return E_FAIL to allow it to be passed on to the other
|
||||
// context menu handlers that might implement that verb.
|
||||
command = ((CMINVOKECOMMANDINFOEX *)pici)->lpVerbW;
|
||||
} else {
|
||||
// If the command cannot be identified through the verb string, then
|
||||
// check the identifier offset.
|
||||
|
||||
auto offset = LOWORD(pici->lpVerb);
|
||||
if (offset < m_info.menuItems.size())
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// If the command cannot be identified through the verb string, then
|
||||
// check the identifier offset.
|
||||
else
|
||||
{
|
||||
// Is the command identifier offset supported by this context menu
|
||||
// extension?
|
||||
if (LOWORD(pici->lpVerb) == IDM_SHARE) {
|
||||
OnVerbShare(pici->hwnd);
|
||||
}
|
||||
else if (LOWORD(pici->lpVerb) == IDM_COPYLINK) {
|
||||
OnVerbCopyLink(pici->hwnd);
|
||||
}
|
||||
else if (LOWORD(pici->lpVerb) == IDM_EMAILLINK) {
|
||||
OnVerbEmailLink(pici->hwnd);
|
||||
}
|
||||
else {
|
||||
// If the verb is not recognized by the context menu handler, it
|
||||
// must return E_FAIL to allow it to be passed on to the other
|
||||
// context menu handlers that might implement that verb.
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
command = m_info.menuItems[offset].command;
|
||||
}
|
||||
|
||||
OCClientInterface::SendRequest(command.data(), m_selectedFiles);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::GetCommandString(UINT_PTR idCommand,
|
||||
UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
|
||||
{
|
||||
HRESULT hr = E_INVALIDARG;
|
||||
|
||||
switch (idCommand) {
|
||||
case IDM_SHARE:
|
||||
if (uFlags == GCS_VERBW) {
|
||||
// GCS_VERBW is an optional feature that enables a caller to
|
||||
// discover the canonical name for the verb passed in through
|
||||
// idCommand.
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
L"OCShareViaOC");
|
||||
}
|
||||
break;
|
||||
case IDM_COPYLINK:
|
||||
if (uFlags == GCS_VERBW) {
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
L"OCCopyLink");
|
||||
}
|
||||
break;
|
||||
case IDM_EMAILLINK:
|
||||
if (uFlags == GCS_VERBW) {
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
L"OCEmailLink");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (idCommand < m_info.menuItems.size() && uFlags == GCS_VERBW) {
|
||||
return StringCchCopyW(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
m_info.menuItems[idCommand].command.data());
|
||||
}
|
||||
|
||||
// If the idCommand or uFlags is not supported by this context menu
|
||||
// extension handler, return E_INVALIDARG.
|
||||
|
||||
return hr;
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
#pragma endregion
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#pragma once
|
||||
#include <shlobj.h> // For IShellExtInit and IContextMenu
|
||||
#include <string>
|
||||
#include "OCClientInterface.h"
|
||||
|
||||
class OCContextMenu : public IShellExtInit, public IContextMenu
|
||||
{
|
||||
|
@ -43,21 +45,9 @@ private:
|
|||
// Reference count of component.
|
||||
long m_cRef;
|
||||
|
||||
// The name of the selected file.
|
||||
wchar_t m_szSelectedFile[MAX_PATH];
|
||||
|
||||
// The method that handles the "ocshare" verb.
|
||||
void OnVerbShare(HWND hWnd);
|
||||
void OnVerbCopyLink(HWND hWnd);
|
||||
void OnVerbEmailLink(HWND hWnd);
|
||||
|
||||
PWSTR m_pszMenuText;
|
||||
PCSTR m_pszVerb;
|
||||
PCWSTR m_pwszVerb;
|
||||
PCSTR m_pszVerbCanonicalName;
|
||||
PCWSTR m_pwszVerbCanonicalName;
|
||||
PCSTR m_pszVerbHelpText;
|
||||
PCWSTR m_pwszVerbHelpText;
|
||||
// The name of the selected files (separated by '\x1e')
|
||||
std::wstring m_selectedFiles;
|
||||
OCClientInterface::ContextMenuInfo m_info;
|
||||
};
|
||||
|
||||
#endif //OCCONTEXTMENU_H
|
||||
|
|
|
@ -61,6 +61,30 @@ public:
|
|||
thirdChunk = source.substr(statusEnd + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool extractChunks(const std::wstring &source, std::wstring &secondChunk, std::wstring &thirdChunk, std::wstring &forthChunk)
|
||||
{
|
||||
auto statusBegin = source.find(L':', 0);
|
||||
assert(statusBegin != std::wstring::npos);
|
||||
|
||||
auto statusEnd = source.find(L':', statusBegin + 1);
|
||||
if (statusEnd == std::wstring::npos) {
|
||||
// the command do not contains two colon?
|
||||
return false;
|
||||
}
|
||||
|
||||
auto thirdColon = source.find(L':', statusEnd + 1);
|
||||
if (statusEnd == std::wstring::npos) {
|
||||
// the command do not contains three colon?
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assume the caller extracted the chunk before the first colon.
|
||||
secondChunk = source.substr(statusBegin + 1, statusEnd - statusBegin - 1);
|
||||
thirdChunk = source.substr(statusEnd + 1, thirdColon - statusEnd - 1);
|
||||
forthChunk = source.substr(thirdColon + 1);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // STRINGUTIL_H
|
||||
|
|
Загрузка…
Ссылка в новой задаче