diff --git a/shell_integration/windows/NCContextMenu/NCClientInterface.cpp b/shell_integration/windows/NCContextMenu/NCClientInterface.cpp index 4c5d665139aae..4b06b987d3c9a 100644 --- a/shell_integration/windows/NCContextMenu/NCClientInterface.cpp +++ b/shell_integration/windows/NCContextMenu/NCClientInterface.cpp @@ -26,44 +26,55 @@ #include #include #include +#include +#include using namespace std; #define PIPE_TIMEOUT 5*1000 //ms -NCClientInterface::ContextMenuInfo NCClientInterface::FetchInfo(const std::wstring &files) +NCClientInterface::ContextMenuInfo NCClientInterface::FetchInfo(const std::wstring &files, std::ofstream &logger) { auto pipename = CommunicationSocket::DefaultPipePath(); CommunicationSocket socket; if (!WaitNamedPipe(pipename.data(), PIPE_TIMEOUT)) { + logger << "error with WaitNamedPipe" << std::endl; return {}; } if (!socket.Connect(pipename)) { + logger << "error with Connect" << std::endl; return {}; } - socket.SendMsg(L"GET_STRINGS:CONTEXT_MENU_TITLE\n"); - socket.SendMsg((L"GET_MENU_ITEMS:" + files + L"\n").data()); + socket.SendMsg(L"GET_STRINGS:CONTEXT_MENU_TITLE\n", logger); + socket.SendMsg((L"GET_MENU_ITEMS:" + files + L"\n").data(), logger); ContextMenuInfo info; std::wstring response; int sleptCount = 0; + constexpr auto noReplyTimeout = 20; constexpr auto replyTimeout = 200; bool receivedReplyFromDesktopClient = false; while ((!receivedReplyFromDesktopClient && sleptCount < noReplyTimeout) || (receivedReplyFromDesktopClient && sleptCount < replyTimeout)) { - if (socket.ReadLine(&response)) { + logger << "trying to read a line" << std::endl; + + if (socket.ReadLine(&response, logger)) { + logger << "received: " << StringUtil::toUtf8(response.c_str()) << std::endl; if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) { + logger << "received: REGISTER_PATH" << std::endl; wstring responsePath = response.substr(14); // length of REGISTER_PATH info.watchedDirectories.push_back(responsePath); } else if (StringUtil::begins_with(response, wstring(L"STRING:"))) { + logger << "received: STRING" << std::endl; wstring stringName, stringValue; if (!StringUtil::extractChunks(response, stringName, stringValue)) continue; if (stringName == L"CONTEXT_MENU_TITLE") info.contextMenuTitle = move(stringValue); } else if (StringUtil::begins_with(response, wstring(L"MENU_ITEM:"))) { + logger << "received: MENU_ITEM" << std::endl; wstring commandName, flags, title; if (!StringUtil::extractChunks(response, commandName, flags, title)) continue; @@ -72,18 +83,22 @@ NCClientInterface::ContextMenuInfo NCClientInterface::FetchInfo(const std::wstri receivedReplyFromDesktopClient = true; continue; } else if (StringUtil::begins_with(response, wstring(L"GET_MENU_ITEMS:END"))) { + logger << "received: GET_MENU_ITEMS:END" << std::endl; break; // Stop once we completely received the last sent request + } else { + logger << "received: another reply" << std::endl; } - } - else { + } else { + logger << "received nothing" << std::endl; Sleep(50); ++sleptCount; } } + return info; } -void NCClientInterface::SendRequest(const wchar_t *verb, const std::wstring &path) +void NCClientInterface::SendRequest(const wchar_t *verb, const std::wstring &path, std::ofstream &logger) { auto pipename = CommunicationSocket::DefaultPipePath(); @@ -95,5 +110,5 @@ void NCClientInterface::SendRequest(const wchar_t *verb, const std::wstring &pat return; } - socket.SendMsg((verb + (L":" + path + L"\n")).data()); + socket.SendMsg((verb + (L":" + path + L"\n")).data(), logger); } diff --git a/shell_integration/windows/NCContextMenu/NCClientInterface.h b/shell_integration/windows/NCContextMenu/NCClientInterface.h index a76cab5af7f76..c0d145c38c03d 100644 --- a/shell_integration/windows/NCContextMenu/NCClientInterface.h +++ b/shell_integration/windows/NCContextMenu/NCClientInterface.h @@ -37,6 +37,7 @@ #include #include #include +#include class CommunicationSocket; @@ -52,8 +53,8 @@ class NCClientInterface }; std::vector menuItems; }; - static ContextMenuInfo FetchInfo(const std::wstring &files); - static void SendRequest(const wchar_t *verb, const std::wstring &path); + static ContextMenuInfo FetchInfo(const std::wstring &files, std::ofstream &logger); + static void SendRequest(const wchar_t *verb, const std::wstring &path, std::ofstream &logger); }; #endif //ABSTRACTSOCKETHANDLER_H diff --git a/shell_integration/windows/NCContextMenu/NCContextMenu.cpp b/shell_integration/windows/NCContextMenu/NCContextMenu.cpp index b35576900fe7a..72f91cc347649 100644 --- a/shell_integration/windows/NCContextMenu/NCContextMenu.cpp +++ b/shell_integration/windows/NCContextMenu/NCContextMenu.cpp @@ -21,16 +21,22 @@ #include #include +#include +#include + extern long g_cDllRef; NCContextMenu::NCContextMenu(void) : m_cRef(1) { InterlockedIncrement(&g_cDllRef); + m_logger.open("c:\\test.log"); + m_logger << "hello world" << std::endl; } NCContextMenu::~NCContextMenu(void) { + m_logger << "NCContextMenu::~NCContextMenu" << std::endl; InterlockedDecrement(&g_cDllRef); } @@ -39,6 +45,7 @@ NCContextMenu::~NCContextMenu(void) // Query to the interface the component supported. IFACEMETHODIMP NCContextMenu::QueryInterface(REFIID riid, void **ppv) { + m_logger << "NCContextMenu::QueryInterface" << std::endl; static const QITAB qit[] = { QITABENT(NCContextMenu, IContextMenu), @@ -51,12 +58,14 @@ IFACEMETHODIMP NCContextMenu::QueryInterface(REFIID riid, void **ppv) // Increase the reference count for an interface on an object. IFACEMETHODIMP_(ULONG) NCContextMenu::AddRef() { + m_logger << "NCContextMenu::AddRef" << std::endl; return InterlockedIncrement(&m_cRef); } // Decrease the reference count for an interface on an object. IFACEMETHODIMP_(ULONG) NCContextMenu::Release() { + m_logger << "NCContextMenu::Release" << std::endl; ULONG cRef = InterlockedDecrement(&m_cRef); if (0 == cRef) { delete this; @@ -74,6 +83,7 @@ IFACEMETHODIMP_(ULONG) NCContextMenu::Release() IFACEMETHODIMP NCContextMenu::Initialize( LPCITEMIDLIST, LPDATAOBJECT pDataObj, HKEY) { + m_logger << "NCContextMenu::Initialize" << std::endl; m_selectedFiles.clear(); if (!pDataObj) { @@ -129,17 +139,20 @@ void InsertSeperator(HMENU hMenu, UINT indexMenu) IFACEMETHODIMP NCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { + m_logger << "NCContextMenu::QueryContextMenu" << std::endl; // If uFlags include CMF_DEFAULTONLY then we should not do anything. if (CMF_DEFAULTONLY & uFlags) { return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0)); } - m_info = NCClientInterface::FetchInfo(m_selectedFiles); + m_info = NCClientInterface::FetchInfo(m_selectedFiles, m_logger); if (m_info.menuItems.empty()) { + m_logger << "NCContextMenu::QueryContextMenu " << "empty info" << std::endl; return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0)); } + m_logger << "NCContextMenu::QueryContextMenu" << "insert separator" << std::endl; InsertSeperator(hMenu, indexMenu++); HMENU hSubmenu = CreateMenu(); @@ -153,6 +166,7 @@ IFACEMETHODIMP NCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT if (!InsertMenuItem(hMenu, indexMenu++, TRUE, &mii)) return HRESULT_FROM_WIN32(GetLastError()); } + m_logger << "NCContextMenu::QueryContextMenu" << "insert separator" << std::endl; InsertSeperator(hMenu, indexMenu++); UINT indexSubMenu = 0; @@ -179,6 +193,7 @@ IFACEMETHODIMP NCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT IFACEMETHODIMP NCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici) { + m_logger << "NCContextMenu::InvokeCommand" << std::endl; std::wstring command; CMINVOKECOMMANDINFOEX *piciEx = nullptr; @@ -215,13 +230,14 @@ IFACEMETHODIMP NCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici) return E_FAIL; } - NCClientInterface::SendRequest(command.data(), m_selectedFiles); + NCClientInterface::SendRequest(command.data(), m_selectedFiles, m_logger); return S_OK; } IFACEMETHODIMP NCContextMenu::GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax) { + m_logger << "NCContextMenu::GetCommandString" << std::endl; if (idCommand < m_info.menuItems.size() && uFlags == GCS_VERBW) { return StringCchCopyW(reinterpret_cast(pszName), cchMax, m_info.menuItems[idCommand].command.data()); diff --git a/shell_integration/windows/NCContextMenu/NCContextMenu.h b/shell_integration/windows/NCContextMenu/NCContextMenu.h index b5b2a3eb7f8f0..730b4d4eee6a6 100644 --- a/shell_integration/windows/NCContextMenu/NCContextMenu.h +++ b/shell_integration/windows/NCContextMenu/NCContextMenu.h @@ -16,9 +16,13 @@ #define NCCONTEXTMENU_H #pragma once +#include "NCClientInterface.h" + #include // For IShellExtInit and IContextMenu + #include -#include "NCClientInterface.h" +#include +#include class NCContextMenu : public IShellExtInit, public IContextMenu { @@ -48,6 +52,8 @@ class NCContextMenu : public IShellExtInit, public IContextMenu // The name of the selected files (separated by '\x1e') std::wstring m_selectedFiles; NCClientInterface::ContextMenuInfo m_info; + + std::ofstream m_logger; }; #endif //NCCONTEXTMENU_H diff --git a/shell_integration/windows/NCOverlays/NCOverlay.cpp b/shell_integration/windows/NCOverlays/NCOverlay.cpp index a70f38b23248d..a7846e827042c 100644 --- a/shell_integration/windows/NCOverlays/NCOverlay.cpp +++ b/shell_integration/windows/NCOverlays/NCOverlay.cpp @@ -35,22 +35,25 @@ namespace { unique_ptr s_instance; -RemotePathChecker *getGlobalChecker() +RemotePathChecker *getGlobalChecker(ofstream &logger) { // On Vista we'll run into issue #2680 if we try to create the thread+pipe connection // on any DllGetClassObject of our registered classes. // Work around the issue by creating the static RemotePathChecker only once actually needed. static once_flag s_onceFlag; - call_once(s_onceFlag, [] { s_instance.reset(new RemotePathChecker); }); + call_once(s_onceFlag, [&logger] { s_instance.reset(new RemotePathChecker{logger}); }); return s_instance.get(); } } -NCOverlay::NCOverlay(int state) +NCOverlay::NCOverlay(int state) : _referenceCount(1) , _state(state) { + // it is saved under %userprofile% + m_logger.open("C:\\overlay.log"); + m_logger << "debug" << std::endl; } NCOverlay::~NCOverlay(void) @@ -120,10 +123,11 @@ IFACEMETHODIMP NCOverlay::GetPriority(int *pPriority) IFACEMETHODIMP NCOverlay::IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib) { - RemotePathChecker* checker = getGlobalChecker(); + auto checker = getGlobalChecker(m_logger); std::shared_ptr> watchedDirectories = checker->WatchedDirectories(); if (watchedDirectories->empty()) { + m_logger << "list of watched directories are empty" << std::endl; return MAKE_HRESULT(S_FALSE, 0, 0); } @@ -136,13 +140,16 @@ IFACEMETHODIMP NCOverlay::IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib) } if (!watched) { + m_logger << "watched is false" << std::endl; return MAKE_HRESULT(S_FALSE, 0, 0); } int state = 0; if (!checker->IsMonitoredPath(pwszPath, &state)) { + m_logger << "not monitored path: " << pwszPath << std::endl; return MAKE_HRESULT(S_FALSE, 0, 0); } + return MAKE_HRESULT(state == _state ? S_OK : S_FALSE, 0, 0); } diff --git a/shell_integration/windows/NCOverlays/NCOverlay.h b/shell_integration/windows/NCOverlays/NCOverlay.h index 688a6b0950287..45966c9f51d88 100644 --- a/shell_integration/windows/NCOverlays/NCOverlay.h +++ b/shell_integration/windows/NCOverlays/NCOverlay.h @@ -18,12 +18,13 @@ #pragma once #include +#include class NCOverlay : public IShellIconOverlayIdentifier { public: - NCOverlay(int state); + explicit NCOverlay(int state); IFACEMETHODIMP_(ULONG) AddRef(); IFACEMETHODIMP GetOverlayInfo(PWSTR pwszIconFile, int cchMax, int *pIndex, DWORD *pdwFlags); @@ -38,6 +39,7 @@ class NCOverlay : public IShellIconOverlayIdentifier private: long _referenceCount; int _state; + std::ofstream m_logger; }; -#endif \ No newline at end of file +#endif diff --git a/shell_integration/windows/NCUtil/CommunicationSocket.cpp b/shell_integration/windows/NCUtil/CommunicationSocket.cpp index 6f9cdbb983c63..60d7bd4e923db 100644 --- a/shell_integration/windows/NCUtil/CommunicationSocket.cpp +++ b/shell_integration/windows/NCUtil/CommunicationSocket.cpp @@ -28,6 +28,8 @@ using namespace std; namespace { +constexpr DWORD timeoutC = 100; + std::wstring getUserName() { DWORD len = DEFAULT_BUFLEN; TCHAR buf[DEFAULT_BUFLEN]; @@ -52,6 +54,7 @@ std::wstring CommunicationSocket::DefaultPipePath() CommunicationSocket::CommunicationSocket() : _pipe(INVALID_HANDLE_VALUE) { + _overlapped.hEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); } CommunicationSocket::~CommunicationSocket() @@ -72,7 +75,7 @@ bool CommunicationSocket::Close() bool CommunicationSocket::Connect(const std::wstring &pipename) { - _pipe = CreateFile(pipename.data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); + _pipe = CreateFile(pipename.data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr); if (_pipe == INVALID_HANDLE_VALUE) { return false; @@ -81,28 +84,36 @@ bool CommunicationSocket::Connect(const std::wstring &pipename) return true; } -bool CommunicationSocket::SendMsg(const wchar_t* message) const +bool CommunicationSocket::SendMsg(const wchar_t* message, std::ofstream &logger) const { + logger << "CommunicationSocket::SendMsg: " << (*message) << std::endl; + auto utf8_msg = StringUtil::toUtf8(message); DWORD numBytesWritten = 0; - auto result = WriteFile( _pipe, utf8_msg.c_str(), DWORD(utf8_msg.size()), &numBytesWritten, nullptr); - if (result) { - return true; - } else { - const_cast(this)->Close(); + if (!WriteFile(_pipe, utf8_msg.c_str(), static_cast(utf8_msg.size()), &numBytesWritten, &_overlapped)) { + if (GetLastError() == ERROR_IO_PENDING) { - return false; + if (WaitForSingleObject(_overlapped.hEvent, timeoutC) != WAIT_OBJECT_0) { + logger << "SendMsg timed out" << std::endl; + return false; + } + + if (!GetOverlappedResult(_pipe, &_overlapped, &numBytesWritten, FALSE)) { + logger << "GetOverlappedResult failed" << std::endl; + return false; + } + + } } + + return true; } -bool CommunicationSocket::ReadLine(wstring* response) +bool CommunicationSocket::ReadLine(wstring* response, std::ofstream &logger) const { - if (!response) { - return false; - } - + assert(response); response->clear(); if (_pipe == INVALID_HANDLE_VALUE) { @@ -118,26 +129,34 @@ bool CommunicationSocket::ReadLine(wstring* response) return true; } - std::array resp_utf8 = {}; + std::array resp_utf8; DWORD numBytesRead = 0; DWORD totalBytesAvailable = 0; - if (!PeekNamedPipe(_pipe, nullptr, 0, nullptr, &totalBytesAvailable, nullptr)) { - Close(); + if (!PeekNamedPipe(_pipe, NULL, 0, 0, &totalBytesAvailable, 0)) { return false; } if (totalBytesAvailable == 0) { - return false; + return true; } - if (!ReadFile(_pipe, resp_utf8.data(), DWORD(resp_utf8.size()), &numBytesRead, nullptr)) { - Close(); - return false; + if (!ReadFile(_pipe, resp_utf8.data(), DWORD(resp_utf8.size()), &numBytesRead, &_overlapped)) { + if (GetLastError() == ERROR_IO_PENDING) { + if (WaitForSingleObject(_overlapped.hEvent, timeoutC) != WAIT_OBJECT_0) { + logger << "ReadLine timed out" << std::endl; + } + if (!GetOverlappedResult(_pipe, &_overlapped, &numBytesRead, FALSE)) { + logger << "GetOverlappedResult failedt" << std::endl; + return false; + } + } else { + return false; + } } if (numBytesRead <= 0) { return false; } - _buffer.insert(_buffer.end(), resp_utf8.begin(), resp_utf8.begin()+numBytesRead); + _buffer.insert(_buffer.end(), resp_utf8.begin(), resp_utf8.begin() + numBytesRead); continue; } } diff --git a/shell_integration/windows/NCUtil/CommunicationSocket.h b/shell_integration/windows/NCUtil/CommunicationSocket.h index bc55c2d933de2..eca3ea00f2787 100644 --- a/shell_integration/windows/NCUtil/CommunicationSocket.h +++ b/shell_integration/windows/NCUtil/CommunicationSocket.h @@ -21,6 +21,7 @@ #include #include +#include #include class __declspec(dllexport) CommunicationSocket @@ -34,15 +35,17 @@ class __declspec(dllexport) CommunicationSocket bool Connect(const std::wstring& pipename); bool Close(); - bool SendMsg(const wchar_t*) const; - bool ReadLine(std::wstring*); + bool SendMsg(const wchar_t *, std::ofstream &logger) const; + bool ReadLine(std::wstring *, std::ofstream &logger) const; HANDLE Event() { return _pipe; } private: HANDLE _pipe; - std::vector _buffer; + mutable std::vector _buffer; bool _connected = false; + + mutable OVERLAPPED _overlapped = {}; }; #endif diff --git a/shell_integration/windows/NCUtil/RemotePathChecker.cpp b/shell_integration/windows/NCUtil/RemotePathChecker.cpp index 5d004d567d286..2d3693990b17a 100644 --- a/shell_integration/windows/NCUtil/RemotePathChecker.cpp +++ b/shell_integration/windows/NCUtil/RemotePathChecker.cpp @@ -30,7 +30,7 @@ using namespace std; // This code is run in a thread -void RemotePathChecker::workerThreadLoop() +void RemotePathChecker::workerThreadLoop(ofstream &logger) { auto pipename = CommunicationSocket::DefaultPipePath(); bool connected = false; @@ -62,14 +62,32 @@ void RemotePathChecker::workerThreadLoop() lock.unlock(); if (!asked.count(filePath)) { asked.insert(filePath); - socket.SendMsg(wstring(L"RETRIEVE_FILE_STATUS:" + filePath + L'\n').data()); + if (!socket.SendMsg(wstring(L"RETRIEVE_FILE_STATUS:" + filePath + L'\n').data(), logger)) { + //MessageBox(nullptr, L"stopping thread - socket.SendMsg RETRIEVE_FILE_STATUS returned false", L"Debugging", MB_OK); + _stop = true; + logger << "stopping thread - socket.SendMsg RETRIEVE_FILE_STATUS returned false" << std::endl; + break; + } } lock.lock(); } } std::wstring response; - while (!_stop && socket.ReadLine(&response)) { + while (!_stop) { + if (!socket.ReadLine(&response, logger)) { + socket.Close(); + logger << "closing socket, no more data to read" << std::endl; + //MessageBox(nullptr, L"closing socket, no more data to read", L"Debugging", MB_OK); + break; + } + + if (response.empty()) { + logger << "closing socket, response is empty" << std::endl; + //MessageBox(nullptr, L"closing socket,response is empty", L"Debugging", MB_OK); + break; + } + if (StringUtil::begins_with(response, wstring(L"REGISTER_PATH:"))) { wstring responsePath = response.substr(14); // length of REGISTER_PATH: @@ -162,12 +180,12 @@ void RemotePathChecker::workerThreadLoop() -RemotePathChecker::RemotePathChecker() +RemotePathChecker::RemotePathChecker(ofstream &logger) : _stop(false) , _watchedDirectories(make_shared>()) , _connected(false) , _newQueries(CreateEvent(nullptr, FALSE, FALSE, nullptr)) - , _thread([this]{ this->workerThreadLoop(); }) + , _thread([this, &logger]{ this->workerThreadLoop(logger); }) { } diff --git a/shell_integration/windows/NCUtil/RemotePathChecker.h b/shell_integration/windows/NCUtil/RemotePathChecker.h index 134138f137244..63afba079f928 100644 --- a/shell_integration/windows/NCUtil/RemotePathChecker.h +++ b/shell_integration/windows/NCUtil/RemotePathChecker.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #pragma once @@ -36,7 +37,7 @@ class RemotePathChecker { StateWarning, StateNone }; - RemotePathChecker(); + explicit RemotePathChecker(std::ofstream &logger); ~RemotePathChecker(); std::shared_ptr> WatchedDirectories() const; bool IsMonitoredPath(const wchar_t* filePath, int* state); @@ -64,7 +65,7 @@ class RemotePathChecker { HANDLE _newQueries; std::thread _thread; - void workerThreadLoop(); + void workerThreadLoop(std::ofstream &logger); }; -#endif \ No newline at end of file +#endif