Skip to content

Instantly share code, notes, and snippets.

@learn-more
Last active June 16, 2018 12:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save learn-more/eb1f56b4be51ac9d5ea784ae5b97cf29 to your computer and use it in GitHub Desktop.
Save learn-more/eb1f56b4be51ac9d5ea784ae5b97cf29 to your computer and use it in GitHub Desktop.
#include <windows.h>
#include <objbase.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <shellapi.h>
#include <atlbase.h>
#include <atlcom.h>
#include <atlwin.h>
#include <atlsimpcoll.h>
# define IID_PPV_ARG(Itype, ppType) IID_##Itype, reinterpret_cast<void**>((static_cast<Itype**>(ppType)))
#define PRINT_FAILURE_HR(expr, hr) wprintf(L"%S failed with 0x%x\n", expr, hr)
#define ABORT_IF_FAILED(expr) \
do { \
HRESULT _hr = (expr);\
if (!SUCCEEDED(_hr)) { \
PRINT_FAILURE_HR(#expr, _hr); \
return; \
} \
} while (0);
#define ABORT_IF_FAILED_HR(expr) \
do { \
HRESULT _hr = (expr);\
if (!SUCCEEDED(_hr)) { \
PRINT_FAILURE_HR(#expr, _hr); \
return _hr; \
} \
} while (0);
static CSimpleArray<HPROPSHEETPAGE> g_Pages;
static BOOL CALLBACK cb_AddPage(HPROPSHEETPAGE page, LPARAM lParam)
{
g_Pages.Add(page);
return TRUE;
}
static HRESULT CreateIDataObject(CComHeapPtr<ITEMIDLIST>& pidl, CComPtr<IDataObject>& dataObject, PCWSTR FileName)
{
CComPtr<IShellFolder> desktopFolder;
ABORT_IF_FAILED_HR(SHGetDesktopFolder(&desktopFolder));
ABORT_IF_FAILED_HR(desktopFolder->ParseDisplayName(NULL, NULL, (LPWSTR)FileName, NULL, &pidl, NULL));
CComPtr<IShellFolder> shellFolder;
PCUITEMID_CHILD childs;
ABORT_IF_FAILED_HR(SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shellFolder), &childs));
ABORT_IF_FAILED_HR(shellFolder->GetUIObjectOf(NULL, 1, &childs, IID_IDataObject, NULL, (PVOID*)&dataObject));
return S_OK;
}
class CDllWrapper
{
public:
HMODULE hMod;
CDllWrapper(PCWSTR ModuleName)
{
hMod = ::LoadLibraryW(ModuleName);
}
~CDllWrapper()
{
if (hMod)
::FreeLibrary(hMod);
}
};
typedef HRESULT(STDAPICALLTYPE *tDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID *ppv);
void run_shellext(PCWSTR ShellextModule, REFGUID ShellextGuid, PCWSTR ShellextTarget)
{
CDllWrapper dll(ShellextModule);
if (!dll.hMod)
{
wprintf(L"Failed to load %s with 0x%x\n", ShellextModule, GetLastError());
return;
}
tDllGetClassObject DllGet = (tDllGetClassObject)GetProcAddress(dll.hMod, "DllGetClassObject");
if (!DllGet)
{
wprintf(L"Failed to retrieve DllGetClassObject from %s\n", ShellextModule);
return;
}
{
CComPtr<IShellPropSheetExt> pSheetExt;
CComPtr<IClassFactory> pClassFactory;
ABORT_IF_FAILED(DllGet(ShellextGuid, IID_PPV_ARG(IClassFactory, &pClassFactory)));
ABORT_IF_FAILED(pClassFactory->CreateInstance(NULL, IID_PPV_ARG(IShellPropSheetExt, &pSheetExt)));
CComPtr<IShellExtInit> pShellext;
ABORT_IF_FAILED(pSheetExt->QueryInterface(IID_PPV_ARG(IShellExtInit, &pShellext)));
CComPtr<IDataObject> dataObject;
CComHeapPtr<ITEMIDLIST> pidl;
HRESULT hr = CreateIDataObject(pidl, dataObject, ShellextTarget);
if (!SUCCEEDED(hr))
return;
ABORT_IF_FAILED(pShellext->Initialize(pidl, dataObject, NULL));
ABORT_IF_FAILED(pSheetExt->AddPages(cb_AddPage, NULL));
}
PROPSHEETHEADER psh = { 0 };
psh.dwSize = sizeof(psh);
psh.dwFlags = PSH_PROPTITLE;
psh.pszCaption = ShellextTarget;
psh.phpage = g_Pages.GetData();
psh.nPages = g_Pages.GetSize();
PropertySheetW(&psh);
}
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' ""version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
class CCoInitialize
{
public:
CCoInitialize(DWORD dwCoInit = COINIT_APARTMENTTHREADED) : m_hr(CoInitializeEx(NULL, dwCoInit)) { }
~CCoInitialize() { if (SUCCEEDED(m_hr)) CoUninitialize(); }
operator HRESULT() const { return m_hr; }
HRESULT m_hr;
};
bool match_arg(wchar_t* arg, const wchar_t* option)
{
if (arg && (*arg == '/' || *arg == '-'))
{
return !_wcsicmp(arg + 1, option);
}
return false;
}
int wmain(int argc, wchar_t* argv[])
{
INITCOMMONCONTROLSEX icc = { sizeof(icc), ICC_LINK_CLASS | ICC_STANDARD_CLASSES };
InitCommonControlsEx(&icc);
CCoInitialize coinit;
bool show_help = false;
for (int n = 1; n < argc;)
{
wchar_t* arg = argv[n];
if (match_arg(arg, L"shellext"))
{
if (n + 3 < argc)
{
const wchar_t* ext_dll = argv[n + 1];
const wchar_t* ext_clsid = argv[n + 2];
const wchar_t* target = argv[n + 3];
GUID clsid;
HRESULT hr = CLSIDFromString(ext_clsid, &clsid);
if (SUCCEEDED(hr))
{
run_shellext(ext_dll, clsid, target);
}
else
{
PRINT_FAILURE_HR("CLSIDFromString", hr);
show_help = true;
}
}
else
{
show_help = true;
}
n += 4;
}
else if (match_arg(arg, L"h") || match_arg(arg, L"help"))
{
show_help = true;
++n;
}
else
{
++n;
}
}
if (show_help)
{
wprintf(L"Usage: %s [/shellext extdll {GUID} target] [/help]\n", argv[0]);
wprintf(L" /shellext - Create {GUID} from 'extdll', and display the properties for 'target'\n");
wprintf(L" /help - display this help\n");
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment