
#define INC_OLE2

#include <windows.h>
#include <initguid.h>
#include <shlobj.h>
#include <shlguid.h>
#include <tchar.h>
#include <stdlib.h>
#include "ShellHash.h"
#include "ContextMenu.h"

UINT		g_uiRefThisDll = 0;		// Reference count for this DLL
HINSTANCE	g_hInstance;			// Instance handle for this DLL

class CShellHashClassFactory : public IClassFactory
{
protected:
	ULONG   m_ulRef;         // Object reference count
public:
	CShellHashClassFactory();
	~CShellHashClassFactory();
        
    // IUnknown methods
	STDMETHODIMP            QueryInterface (REFIID, LPVOID FAR *);
	STDMETHODIMP_(ULONG)    AddRef ();
	STDMETHODIMP_(ULONG)    Release ();
    
	// IClassFactory methods
	STDMETHODIMP    CreateInstance (LPUNKNOWN, REFIID, LPVOID FAR *);
	STDMETHODIMP    LockServer (BOOL);
};

extern "C" int APIENTRY DllMain (HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
	if (dwReason == DLL_PROCESS_ATTACH)
	{
		g_hInstance = hInstance;
	}
	return TRUE;
}


STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
	*ppv = NULL;

	if (!IsEqualCLSID (rclsid, CLSID_Shell_Extension_ShellHash))
	{
		return ResultFromScode (CLASS_E_CLASSNOTAVAILABLE);
	}
    
	CShellHashClassFactory *pClassFactory = new CShellHashClassFactory;

	if (pClassFactory == NULL)
	{
		return ResultFromScode (E_OUTOFMEMORY);
	}

	HRESULT hr = pClassFactory->QueryInterface (riid, ppv);
	pClassFactory->Release ();
	return hr;
}

STDAPI DllCanUnloadNow (void)
{
	return ResultFromScode ((g_uiRefThisDll == 0) ? S_OK : S_FALSE);
}

#define SZ_CLSID				_T("CLSID\\{56E050FF-82E8-4741-9FFC-F6C95AD095C0}")
#define SZ_INPROCSERVER32		_T("CLSID\\{56E050FF-82E8-4741-9FFC-F6C95AD095C0}\\InprocServer32")
#define SZ_DEFAULT				_T("")
#define SZ_THREADINGMODEL		_T("ThreadingModel")
#define SZ_APARTMENT			_T("Apartment")
#define SZ_APPROVED				_T("Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved")
#define SZ_ERRMSG				_T("Unable to add ClassId {56E050FF-82E8-4741-9FFC-F6C95AD095C0} to Registry\nAdministrative Privileges Needed")
#define SZ_ERROR				_T("Error")
#define SZ_DIRSHELLHASH	_T("Directory\\shellex\\ContextMenuHandlers\\ShellHash")
#define SZ_FILESHELLHASH	_T("*\\shellex\\ContextMenuHandlers\\ShellHash")
#define SZ_FOLDERSHELLHASH	_T("Folder\\shellex\\ContextMenuHandlers\\ShellHash")

STDAPI DllRegisterServer(void)
{
	HRESULT hr = E_UNEXPECTED;

	static TCHAR szDescr[] = _T("ShellHash Extension");

	TCHAR szFilePath[_MAX_PATH];
	GetModuleFileName(g_hInstance, szFilePath, _MAX_PATH);
	
	HKEY hKeyCLSID = NULL;
	if (::RegCreateKey(HKEY_CLASSES_ROOT, SZ_CLSID, &hKeyCLSID) != ERROR_SUCCESS)
	{
		return E_UNEXPECTED;
	}
	if (::RegSetValueEx(hKeyCLSID, SZ_DEFAULT, 0, REG_SZ,  (const BYTE*)szDescr, 
				(lstrlen(szDescr)+1) * sizeof(TCHAR)) != ERROR_SUCCESS)
	{
		return E_UNEXPECTED;
	}
	::RegCloseKey(hKeyCLSID);

	HKEY hkeyInprocServer32 = NULL;
	if (::RegCreateKey(HKEY_CLASSES_ROOT, SZ_INPROCSERVER32, &hkeyInprocServer32) == ERROR_SUCCESS)
	{
		static TCHAR szApartment[] = SZ_APARTMENT;
		if (::RegSetValueEx(hkeyInprocServer32, SZ_DEFAULT, 0, REG_SZ, 
				(const BYTE*)szFilePath, (lstrlen(szFilePath)+1) * sizeof(TCHAR)) == ERROR_SUCCESS)
		{
			if (::RegSetValueEx(hkeyInprocServer32, SZ_THREADINGMODEL, 0, REG_SZ, 
				(const BYTE*)szApartment, (lstrlen(szApartment)+1) * sizeof(TCHAR)) == ERROR_SUCCESS)
			{
				hr = S_OK;
			}
		}
		::RegCloseKey(hkeyInprocServer32);
	}


	HKEY hKeyApproved = NULL;
	LONG lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_APPROVED, 0, KEY_SET_VALUE, &hKeyApproved);
	if (lRet == ERROR_ACCESS_DENIED)
	{
		MessageBox(NULL, SZ_ERRMSG, SZ_ERROR, MB_OK);
		hr = E_UNEXPECTED;
	}
	else if (lRet == ERROR_FILE_NOT_FOUND)
	{
		// Its okay.. The key doesn't exist. May be its Win95/98 or older verion of NT
	}
	if (hKeyApproved)
	{
		if (RegSetValueEx(hKeyApproved, SZ_GUID, 0, REG_SZ, 
			(const BYTE*) szDescr, (lstrlen(szDescr) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS)
		{
			hr = S_OK;
		}
		else
		{
			hr = E_UNEXPECTED;
		}
	}
	//Create CtxMenuExtKey 3 keys
	HKEY hkeyDirCtx = NULL;
	if (::RegCreateKey(HKEY_CLASSES_ROOT, SZ_DIRSHELLHASH, &hkeyDirCtx) == ERROR_SUCCESS)
	{
		static TCHAR szCLSID[] = _T("{56E050FF-82E8-4741-9FFC-F6C95AD095C0}");
		if (::RegSetValueEx(hkeyDirCtx, SZ_DEFAULT, 0, REG_SZ, 
				(const BYTE*)szCLSID, (lstrlen(szCLSID)+1) * sizeof(TCHAR)) == ERROR_SUCCESS)
		{
			hr = S_OK;
		}
			::RegCloseKey(hkeyDirCtx);
	}
	HKEY hkeyFileCtx = NULL;
	if (::RegCreateKey(HKEY_CLASSES_ROOT, SZ_FILESHELLHASH, &hkeyFileCtx) == ERROR_SUCCESS)
	{
		static TCHAR szCLSID[] = _T("{56E050FF-82E8-4741-9FFC-F6C95AD095C0}");
		if (::RegSetValueEx(hkeyFileCtx, SZ_DEFAULT, 0, REG_SZ, 
				(const BYTE*)szCLSID, (lstrlen(szCLSID)+1) * sizeof(TCHAR)) == ERROR_SUCCESS)
		{
			hr = S_OK;
		}
		::RegCloseKey(hkeyFileCtx);
	}
	HKEY hkeyFolderCtx = NULL;
	if (::RegCreateKey(HKEY_CLASSES_ROOT, SZ_FOLDERSHELLHASH, &hkeyFolderCtx) == ERROR_SUCCESS)
	{
		static TCHAR szCLSID[] = _T("{56E050FF-82E8-4741-9FFC-F6C95AD095C0}");
		if (::RegSetValueEx(hkeyFolderCtx, SZ_DEFAULT, 0, REG_SZ, 
				(const BYTE*)szCLSID, (lstrlen(szCLSID)+1) * sizeof(TCHAR)) == ERROR_SUCCESS)
		{
			hr = S_OK;
		}
			::RegCloseKey(hkeyFolderCtx);
	}	

	return hr;
}

STDAPI DllUnregisterServer(void)
{
	HRESULT hr = E_UNEXPECTED;

	::RegDeleteKey(HKEY_CLASSES_ROOT, SZ_INPROCSERVER32);

	if (::RegDeleteKey(HKEY_CLASSES_ROOT, SZ_CLSID) == ERROR_SUCCESS)
	{
		hr = S_OK;
	}
	HKEY hKeyApproved = NULL;
	LONG lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, SZ_APPROVED, 0, KEY_SET_VALUE, &hKeyApproved);
	if (lRet == ERROR_ACCESS_DENIED)
	{
		MessageBox(NULL, SZ_ERRMSG, SZ_ERROR, MB_OK);
		hr = E_UNEXPECTED;
	}
	else if (lRet == ERROR_FILE_NOT_FOUND)
	{
		// Its okay.. The key doesn't exist. May be its Win95 or older verion of NT
	}
	if (hKeyApproved)
	{
		if (::RegDeleteValue(hKeyApproved, SZ_GUID) != ERROR_SUCCESS)
		{
			hr &= E_UNEXPECTED;
		}
		::RegCloseKey(hKeyApproved);
	}

	return hr;
}

CShellHashClassFactory::CShellHashClassFactory()
{
	m_ulRef = 1;
	g_uiRefThisDll++;
}

CShellHashClassFactory::~CShellHashClassFactory()
{
	g_uiRefThisDll--;
}

STDMETHODIMP CShellHashClassFactory::QueryInterface (REFIID riid, LPVOID FAR *ppv)
{
	if (IsEqualIID (riid, IID_IUnknown)) 
	{
		*ppv = (LPUNKNOWN) (LPCLASSFACTORY) this;
		m_ulRef++;
		return NOERROR;
	}

	else if (IsEqualIID (riid, IID_IClassFactory)) 
	{
		*ppv = (LPCLASSFACTORY) this;
		m_ulRef++;
		return NOERROR;
	}
	else 
	{  
		*ppv = NULL;
		return ResultFromScode (E_NOINTERFACE);
	}
}

STDMETHODIMP_(ULONG) CShellHashClassFactory::AddRef ()
{
	return ++m_ulRef;
}

STDMETHODIMP_(ULONG) CShellHashClassFactory::Release ()
{
	if (--m_ulRef == 0)
	{
		delete this;
		return 0;
	}
	return m_ulRef;
}

STDMETHODIMP CShellHashClassFactory::CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid,
    LPVOID FAR *ppvObj)
{
    *ppvObj = NULL;

    if (pUnkOuter != NULL)
        return ResultFromScode (CLASS_E_NOAGGREGATION);

	if (IsEqualIID (riid, IID_IShellExtInit)) 
	{
		CContextMenu *pCtxMenu = new CContextMenu();
		if (NULL == pCtxMenu)
		{
			return E_OUTOFMEMORY;
		}
		HRESULT hr = pCtxMenu->QueryInterface(riid, ppvObj);
		
		pCtxMenu->Release();
		return hr;
	}
	else if (IsEqualIID (riid, IID_IContextMenu)) 
	{
		*ppvObj = (LPCONTEXTMENU) this;
		m_ulRef++;
		return NOERROR;
	}
	return E_NOTIMPL;
}

STDMETHODIMP CShellHashClassFactory::LockServer (BOOL fLock)
{
	return ResultFromScode (E_NOTIMPL);
}

//EOF
