Skip to content

Commit

Permalink
Partially merge branch 'feature-issue115' into master. #115
Browse files Browse the repository at this point in the history
* feature-issue115: (39 commits)
  Enabled building branch "feature-issue115" on AppVeyor.
  Removed "Cached" shell extension from HKEY_CURRENT_USER as per #115.
  Changed "unknown interface" logs in function CContextMenu::QueryInterface() from "WARNINGS" to "INFO".
  Changing implementation for reference counting
  Added another implementation for QueryInterface which might be helpful for invetigating #115
  Added STRICT definition as per new VS2019 ALT project.
  Moved CContextMenu::m_previousMenu from static to a protected member variable.
  Disabled debugging hook.
  Silenced logs from CContextMenu::GetCommandString()
  Greatly simplified IUnknown::QueryInterface() and DllGetClassObject() implementations.
  Updated implementations of QueryInterface()
  Moved dtor scope to protected/private for CClassFactory and CContextMenu classes. Renamed m_cRef to m_refCount. Slightly modified IUnknown::AddRef() implementations.
  Fixed an invalid implementation of GuidToString().
  Added few code optimizations for the Shell Extension.
  Moved code from shellext.h and shellext.cpp to dedicated files.
  restored shellext.h
  duplicate shellext.h to CCriticalSection.h
  restored shellext.h
  duplicate shellext.h to CContextMenu.h
  restored shellext.h
  ...
  • Loading branch information
end2endzone committed May 3, 2023
2 parents 6103716 + 5ddaf71 commit 7ff884f
Show file tree
Hide file tree
Showing 16 changed files with 2,014 additions and 1,210 deletions.
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Changes for 0.8.0
* Fixed issue #101: Deprecate setup.exe installer in favor of the Windows Installer (*.msi).
* Fixed issue #113: Upgrading from v0.7.0 to 0.8.0 installs at the wrong location.
* Fixed issue #114: Implement sa_plugin_terminate() in the C API.
* Fixed issue #118: Registry corruption while registering the extension.


Changes for 0.7.0
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ version: "{branch} (#{build})"
branches:
only:
- master
- feature-issue114
- feature-issue115

# Do not build on tags (GitHub and BitBucket)
skip_tags: true
Expand Down
46 changes: 42 additions & 4 deletions src/shared/Win32Registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@

namespace Win32Registry
{
std::string safe_null(const char* value)
{
if (value)
return value;
return "(null)";
}

bool IsIconEquals(const REGISTRY_ICON& a, const REGISTRY_ICON& b)
{
if (a.path == b.path && a.index == b.index)
Expand Down Expand Up @@ -162,6 +169,19 @@ namespace Win32Registry
return result;
}

inline DWORD GetRelevantDataStorageSize(const DWORD& type, const DWORD& value_size)
{
if (value_size == 0)
return 0;
switch (type)
{
case REG_SZ:
case REG_EXPAND_SZ:
return value_size - 1;
default:
return value_size;
};
}

bool GetValue(const char* key_path,
const char* value_name,
Expand All @@ -183,20 +203,38 @@ namespace Win32Registry
DWORD value_size = 0; //the size of the returned buffer in bytes. This size includes any terminating null character.
RegQueryValueEx(hKey, value_name, NULL, &value_type, NULL, &value_size);

DWORD length = value_size - 1;
//allocate space for storing data value in a string
size_t string_length = GetRelevantDataStorageSize(value_type, value_size);
value.assign(string_length, 0);
bool alloc_success = (value.size() == string_length); // check that allocation worked.

//allocate space for value
if (value_size > 0 && value.assign(length, 0).size())
if (value_size > 0 && alloc_success)
{
//Read the actual data of the value
value_type = 0;
RegQueryValueEx(hKey, value_name, NULL, &value_type, (LPBYTE)value.c_str(), &value_size);

type = ConvertToPublicType(value_type);
success = (length == value.size());

size_t expected_size = GetRelevantDataStorageSize(value_type, value_size);
success = (value.size() == expected_size);
}

RegCloseKey(hKey);

//// DEBUG
//{
// std::string text;
// text += "success=" + ra::strings::ToString((int)success) + "\n";
// text += "alloc_success=" + ra::strings::ToString((int)alloc_success) + "\n";
// text += "key_path=" + safe_null(key_path) + "\n";
// text += "value_name=" + safe_null(value_name) + "\n";
// text += "value_size=" + ra::strings::ToString((uint32_t)value_size) + "\n";
// if (success && (value_type == REG_SZ || value_type == REG_EXPAND_SZ))
// text += "value=" + safe_null(value.c_str()) + "\n";
// MessageBox(NULL, text.c_str(), __FUNCTION__ " [return]", MB_OK | MB_ICONEXCLAMATION);
//}

return success;
}
}
Expand Down
189 changes: 189 additions & 0 deletions src/shellextension/CClassFactory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/**********************************************************************************
* MIT License
*
* Copyright (c) 2018 Antoine Beauchamp
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*********************************************************************************/

#include "stdafx.h"

#include "CClassFactory.h"
#include "CContextMenu.h"
#include "shellext.h"

#pragma warning( push )
#pragma warning( disable: 4355 ) // glog\install_dir\include\glog/logging.h(1167): warning C4355: 'this' : used in base member initializer list
#include <glog/logging.h>
#pragma warning( pop )

// Constructeur de l'interface IClassFactory:
CClassFactory::CClassFactory()
{
LOG(INFO) << __FUNCTION__ << "(), new instance " << ToHexString(this);

#if SA_QUERYINTERFACE_IMPL == 0
m_refCount = 0; // reference counter must be initialized to 0 even if we are actually creating an instance. A reference to this instance will be added when the instance will be queried by explorer.exe.
#elif SA_QUERYINTERFACE_IMPL == 1
m_refCount = 1;
#endif

// Increment the dll's reference counter.
DllAddRef();
}

// Destructeur de l'interface IClassFactory:
CClassFactory::~CClassFactory()
{
LOG(INFO) << __FUNCTION__ << "(), delete instance " << ToHexString(this);

// Decrement the dll's reference counter.
DllRelease();
}

HRESULT STDMETHODCALLTYPE CClassFactory::QueryInterface(REFIID riid, LPVOID FAR* ppv)
{
std::string riid_str = GuidToInterfaceName(riid);
LOG(INFO) << __FUNCTION__ << "(), riid=" << riid_str << ", this=" << ToHexString(this);

HRESULT hr = E_NOINTERFACE;

#if SA_QUERYINTERFACE_IMPL == 0
//https://docs.microsoft.com/en-us/office/client-developer/outlook/mapi/implementing-iunknown-in-c-plus-plus

// Always set out parameter to NULL, validating it first.
if (!ppv)
return E_INVALIDARG;
*ppv = NULL;

//https://stackoverflow.com/questions/1742848/why-exactly-do-i-need-an-explicit-upcast-when-implementing-queryinterface-in-a
if (IsEqualGUID(riid, IID_IUnknown)) *ppv = (LPVOID)this;
if (IsEqualGUID(riid, IID_IClassFactory)) *ppv = (LPCLASSFACTORY)this;

if (*ppv)
{
AddRef();
hr = S_OK;
}
else
hr = E_NOINTERFACE;
#elif SA_QUERYINTERFACE_IMPL == 1
static const QITAB qit[] =
{
QITABENT(CClassFactory, IClassFactory),
{ 0, 0 }
};
hr = QISearch(this, qit, riid, ppv);
#endif

if (SUCCEEDED(hr))
LOG(INFO) << __FUNCTION__ << "(), found interface " << riid_str << ", ppv=" << ToHexString(*ppv);
else
LOG(WARNING) << __FUNCTION__ << "(), unknown interface " << riid_str;
return hr;
}

ULONG STDMETHODCALLTYPE CClassFactory::AddRef()
{
//https://docs.microsoft.com/en-us/office/client-developer/outlook/mapi/implementing-iunknown-in-c-plus-plus

// Increment the object's internal counter.
return InterlockedIncrement(&m_refCount);
}

ULONG STDMETHODCALLTYPE CClassFactory::Release()
{
//https://docs.microsoft.com/en-us/office/client-developer/outlook/mapi/implementing-iunknown-in-c-plus-plus

// Decrement the object's internal counter.
LONG refCount = InterlockedDecrement(&m_refCount);
if (refCount == 0)
{
delete this;
}
return refCount;
}

HRESULT STDMETHODCALLTYPE CClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID FAR* ppv)
{
std::string riid_str = GuidToInterfaceName(riid);
LOG(INFO) << __FUNCTION__ << "(), pUnkOuter=" << pUnkOuter << ", riid=" << riid_str << " this=" << ToHexString(this);

#if SA_QUERYINTERFACE_IMPL == 0
// Always set out parameter to NULL, validating it first.
if (!ppv)
return E_INVALIDARG;
*ppv = NULL;

if (pUnkOuter) return CLASS_E_NOAGGREGATION;
CContextMenu* pContextMenu = new CContextMenu();
if (!pContextMenu) return E_OUTOFMEMORY;
HRESULT hr = pContextMenu->QueryInterface(riid, ppv);
if (FAILED(hr))
{
pContextMenu->Release();
}
#elif SA_QUERYINTERFACE_IMPL == 1
HRESULT hr = CLASS_E_NOAGGREGATION;

// pUnkOuter is used for aggregation. We do not support it in the sample.
if (pUnkOuter == NULL)
{
hr = E_OUTOFMEMORY;

// Create the COM component.
CContextMenu* pExt = new (std::nothrow) CContextMenu();
if (pExt)
{
// Query the specified interface.
hr = pExt->QueryInterface(riid, ppv);
pExt->Release();
}
}
#endif

if (SUCCEEDED(hr))
LOG(INFO) << __FUNCTION__ << "(), found interface " << riid_str << ", ppv=" << ToHexString(*ppv);
else
LOG(ERROR) << __FUNCTION__ << "(), failed creating interface " << riid_str;
return hr;
}

HRESULT STDMETHODCALLTYPE CClassFactory::LockServer(BOOL bLock)
{
LOG(INFO) << __FUNCTION__ << "(), bLock=" << (int)bLock << " this=" << ToHexString(this);

//https://docs.microsoft.com/en-us/windows/desktop/api/unknwnbase/nf-unknwnbase-iclassfactory-lockserver
//https://docs.microsoft.com/en-us/windows/desktop/api/combaseapi/nf-combaseapi-colockobjectexternal

// Note:
// Previous implementations was blindly returning S_OK without doing anything else. This is probably a bad idea. Examples: git-for-windows/7-Zip
// Other implementations just return E_NOTIMPL to let explorer know. Examples: TortoiseGit.
// Finaly, most other implementations resolves on adding a lock on the DLL. This is better but not it does not follow the official microsoft documentation. Examples: chrdavis/SmartRename, owncloud/client, https://github.com/microsoft/Windows-classic-samples/

if (bLock)
{
DllAddRef();
}
else
{
DllRelease();
}
return S_OK;
}
46 changes: 46 additions & 0 deletions src/shellextension/CClassFactory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**********************************************************************************
* MIT License
*
* Copyright (c) 2018 Antoine Beauchamp
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*********************************************************************************/

#pragma once

#include "stdafx.h"

class CClassFactory : public IClassFactory
{
public:
CClassFactory();

//IUnknown interface
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID FAR*);
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();

//IClassFactory interface
HRESULT STDMETHODCALLTYPE CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR*);
HRESULT STDMETHODCALLTYPE LockServer(BOOL);

private:
~CClassFactory();
ULONG m_refCount;
};
Loading

0 comments on commit 7ff884f

Please sign in to comment.