#include <Windows.h>
#include <shlobj.h>
#include "ContextMenu.h"
#include "resource.h"
#include <tchar.h>
#include "shlwapi.h"
#include <string>
using namespace std;

#define ID_ABOUTSHELLHASH	0x0001
#define ID_CALCMD5			0x0002
#define ID_CALCED2K			0x0003
#define ID_CALCSHA1			0x0004
#define ID_CALCCRC32		0x0005
#define ID_CALCMD2			0x0006
#define ID_CALCMD4			0x0007
#define ID_CALCGOSTHASH		0x0008
#define ID_CALCSHA2256		0x0009
#define ID_CALCSHA2384		0x00010
#define ID_CALCSHA2512		0x00011

extern HINSTANCE g_hInstance;
extern UINT g_uiRefThisDll;

CContextMenu::CContextMenu()
{
    g_uiRefThisDll++;
    m_uiRef = 1;
	m_pszSource = NULL;

	GetModuleFileName(g_hInstance,m_pszModulePath,MAX_PATH);
	PathRemoveFileSpec(m_pszModulePath);
	m_count = 0;
	m_files = NULL;
}

CContextMenu::~CContextMenu()
{
	if(m_pszSource)
		delete m_pszSource;
    g_uiRefThisDll--;
}

STDMETHODIMP CContextMenu::QueryInterface (REFIID riid, LPVOID FAR *ppv)
{
    if (IsEqualIID (riid, IID_IUnknown)) 
	{
        *ppv = this;
        m_uiRef++;
        return NOERROR;
    }
    else if (IsEqualIID (riid, IID_IShellExtInit)) 
	{
        *ppv = (LPSHELLEXTINIT) this;
        m_uiRef++;
        return NOERROR;
    }
    else if (IsEqualIID (riid, IID_IContextMenu)) 
	{
        *ppv = (IContextMenu*) this;
        m_uiRef++;
        return NOERROR;
    }
    else 
	{
        *ppv = NULL;
        return ResultFromScode (E_NOINTERFACE);
    }
}

STDMETHODIMP_(ULONG) CContextMenu::AddRef ()
{
    return ++m_uiRef;
}
 
STDMETHODIMP_(ULONG) CContextMenu::Release ()
{
    if (--m_uiRef == 0)
	{
        delete this;
		return 0;
	}
    return m_uiRef;
}

STDMETHODIMP CContextMenu::Initialize (LPCITEMIDLIST pidlFolder,
    LPDATAOBJECT lpdobj, HKEY hKeyProgID)
{
	HRESULT retVal=S_OK;
	char fileName[MAX_PATH];
	UINT iCount;
	UINT i;
	//LPTSTR pEndingSlash;

	if (lpdobj == NULL)
		return E_INVALIDARG;

	// get all the files into our data structures
	STGMEDIUM med;
	FORMATETC fe={CF_HDROP,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL};
	if (SUCCEEDED(lpdobj->GetData(&fe,&med)))
	{
		// get count so i can resize my array
		iCount=DragQueryFile((HDROP)med.hGlobal,0xFFFFFFFF,NULL,0);
		// we have to have 2+ things
		// resize it
		m_files = new FILENAME[iCount];
		if (m_files)
		{
			m_count = 0;//iCount;
			// go through all files, add them
			for (i=0;i<iCount;++i)
			{
				if (DragQueryFile((HDROP)med.hGlobal,i,fileName,MAX_PATH) != 0)
				{
					// parse out directory name/file name and store them separately
					//MessageBox(NULL, fileName, NULL, MB_OK);
					if (::PathIsDirectory(fileName))
						continue ;

					lstrcpy(m_files[m_count].szFilePath,fileName);
					m_count++;
					//// get file name
					//pEndingSlash = _tcsrchr(fileName,'\\');
					//if (!pEndingSlash)
					//{
					//	lstrcpy(m_files[i].szFilePath,fileName);
					//}
					//else
					//{
					//	*pEndingSlash = 0; // break it up

					//	lstrcpy(m_files[i].szFilePath,fileName);
					//}
				}
			}
		}
		else
		{
			m_count = 0;
			retVal = E_OUTOFMEMORY;
		}
		ReleaseStgMedium(&med);	// don't forget to clean up

		if (m_count < 1)
		{
			delete []m_files;
			m_files = NULL;
			return E_INVALIDARG;
		}
	}
	return retVal;
	////DWORD dwErrcode = 0L;
	////FORMATETC   fe;
	////STGMEDIUM   stgmed;

	////fe.cfFormat   = CF_HDROP;
	////fe.ptd        = NULL;
	////fe.dwAspect   = DVASPECT_CONTENT;
	////fe.lindex     = -1;
	////fe.tymed      = TYMED_HGLOBAL;

	////// Get the storage medium from the data object.
	////HRESULT hr = lpdobj->GetData(&fe, &stgmed);
	////if (SUCCEEDED(hr))
	////{
	////	if(stgmed.hGlobal)
	////	{
	////		int iSize = 0;
	////		LPDROPFILES pDropFiles = (LPDROPFILES)GlobalLock(stgmed.hGlobal);

	////		LPTSTR pszFiles = NULL, pszTemp = NULL;
	////		LPWSTR pswFiles = NULL, pswTemp = NULL;

	////		if (pDropFiles->fWide)
	////		{
	////			pswFiles	=	(LPWSTR) ((BYTE*) pDropFiles + pDropFiles->pFiles);
	////			pswTemp		=	(LPWSTR) ((BYTE*) pDropFiles + pDropFiles->pFiles);
	////		}
	////		else
	////		{
	////			pszFiles	=	(LPTSTR) pDropFiles + pDropFiles->pFiles;
	////			pszTemp		=	(LPTSTR) pDropFiles + pDropFiles->pFiles;
	////		}

	////		while(pszFiles && *pszFiles || pswFiles && *pswFiles)
	////		{
	////			if(pDropFiles->fWide)
	////			{
	////				//Get size of first file/folders path
	////				iSize += WideCharToMultiByte(CP_ACP,	0, pswFiles,	-1,	NULL, 0, NULL, NULL);

	////				pswFiles += (wcslen(pswFiles) + 1 );
	////			}
	////			else
	////			{
	////				//Get size of first file/folders path
	////				iSize += strlen(pszFiles) + 1;

	////				pszFiles += (strlen(pszFiles) +1);
	////			}
	////		}
	////		if(iSize)
	////		{
	////			++iSize;
	////			m_pszSource = new char[iSize];
	////			memset(m_pszSource, 0, iSize);
	////			if(pswTemp)
	////			{
	////				WideCharToMultiByte(CP_ACP,	0, pswTemp,	iSize,	m_pszSource, iSize, NULL, NULL);
	////			}
	////			else
	////			{
	////				memcpy(m_pszSource, pszTemp, iSize);
	////			}
	////		}
	////	}
	////	GlobalUnlock(stgmed.hGlobal);
	////	ReleaseStgMedium(&stgmed);
	////}
	////else
	////	dwErrcode = GetLastError();

	////return NOERROR;
}

STDMETHODIMP CContextMenu::GetCommandString(UINT idCmd, UINT uFlags, UINT *pwReserved,
											LPSTR pszName, UINT cchMax)
{
	HRESULT hr = S_OK;

	switch(uFlags)
	{
	case GCS_HELPTEXTA:
		switch(idCmd)
		{
		case ID_ABOUTSHELLHASH:
			lstrcpynA((LPSTR)pszName,"关于 ShellHash..", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCMD5:
			lstrcpynA((LPSTR)pszName,"计算MD5校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCED2K:
			lstrcpynA((LPSTR)pszName,"计算ED2K校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCSHA1:
			lstrcpynA((LPSTR)pszName,"计算SHA-1校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCCRC32:
			lstrcpynA((LPSTR)pszName,"计算CRC32校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCMD2:
			lstrcpynA((LPSTR)pszName,"计算MD2校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCMD4:
			lstrcpynA((LPSTR)pszName,"计算MD4校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCGOSTHASH:
			lstrcpynA((LPSTR)pszName,"计算GostHash校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCSHA2256:
			lstrcpynA((LPSTR)pszName,"计算SHA-2 256位校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCSHA2384:
			lstrcpynA((LPSTR)pszName,"计算SHA-2 384位校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCSHA2512:
			lstrcpynA((LPSTR)pszName,"计算SHA-2 512位校验码", cchMax);
			hr = NOERROR;
			break;
		default:
			hr = E_NOTIMPL;
		}
		break;
	case GCS_HELPTEXTW:
		switch(idCmd)
		{
		case ID_ABOUTSHELLHASH:
			lstrcpynW((LPWSTR) pszName,L"关于 ShellHash..", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCMD5:
			lstrcpynW((LPWSTR) pszName,L"计算MD5校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCED2K:
			lstrcpynW((LPWSTR)pszName,L"计算ED2K校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCSHA1:
			lstrcpynW((LPWSTR)pszName,L"计算SHA-1校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCCRC32:
			lstrcpynW((LPWSTR)pszName,L"计算CRC32校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCMD2:
			lstrcpynW((LPWSTR)pszName,L"计算MD2校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCMD4:
			lstrcpynW((LPWSTR)pszName,L"计算MD4校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCGOSTHASH:
			lstrcpynW((LPWSTR)pszName,L"计算GostHash校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCSHA2256:
			lstrcpynW((LPWSTR)pszName,L"计算SHA-2 256位校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCSHA2384:
			lstrcpynW((LPWSTR)pszName,L"计算SHA-2 384位校验码", cchMax);
			hr = NOERROR;
			break;
		case ID_CALCSHA2512:
			lstrcpynW((LPWSTR)pszName,L"计算SHA-2 512位校验码", cchMax);
			hr = NOERROR;
			break;
		default:
			hr = E_NOTIMPL;
		}
		break;
	}
	return hr;
}

STDMETHODIMP CContextMenu::QueryContextMenu(HMENU hMenu, UINT uiIndexMenu, UINT idCmdFirst,
											UINT idCmdLast, UINT uFlags)
{
	if (m_count < 1)
		return E_NOTIMPL;

	UINT idCmd = idCmdFirst;

	HMENU hPopMenu = CreatePopupMenu ();

	InsertMenu(hMenu, uiIndexMenu++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);
	InsertMenu (hMenu, uiIndexMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hPopMenu, __TEXT("ShellHash") );	

	InsertMenu(hPopMenu, 0, MF_STRING | MF_BYPOSITION, (idCmdFirst + ID_ABOUTSHELLHASH), __TEXT("关于 ShellHash..."));
	InsertMenu(hPopMenu, 1, MF_STRING | MF_BYPOSITION, (idCmdFirst + ID_CALCMD5), __TEXT("计算MD5..."));
	InsertMenu(hPopMenu, 2, MF_STRING | MF_BYPOSITION, (idCmdFirst + ID_CALCED2K), __TEXT("计算ed2k HashID..."));
	InsertMenu(hPopMenu, 3, MF_STRING | MF_BYPOSITION, (idCmdFirst + ID_CALCSHA1), __TEXT("计算SHA-1..."));
	InsertMenu(hPopMenu, 4, MF_STRING | MF_BYPOSITION, (idCmdFirst + ID_CALCCRC32), __TEXT("计算CRC32..."));
	InsertMenu(hPopMenu, 5, MF_STRING | MF_BYPOSITION, (idCmdFirst + ID_CALCMD2), __TEXT("计算MD2..."));
	InsertMenu(hPopMenu, 6, MF_STRING | MF_BYPOSITION, (idCmdFirst + ID_CALCMD4), __TEXT("计算MD4..."));
	InsertMenu(hPopMenu, 7, MF_STRING | MF_BYPOSITION, (idCmdFirst + ID_CALCGOSTHASH), __TEXT("计算GostHash..."));
	InsertMenu(hPopMenu, 8, MF_STRING | MF_BYPOSITION, (idCmdFirst + ID_CALCSHA2256), __TEXT("计算SHA2-256..."));
	InsertMenu(hPopMenu, 9, MF_STRING | MF_BYPOSITION, (idCmdFirst + ID_CALCSHA2384), __TEXT("计算SHA2-384..."));
	InsertMenu(hPopMenu, 9, MF_STRING | MF_BYPOSITION, (idCmdFirst + ID_CALCSHA2512), __TEXT("计算SHA2-512..."));


	InsertMenu(hMenu, uiIndexMenu++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL);

	return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, USHORT(ID_CALCSHA2512 + 1));
}

STDMETHODIMP CContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
{
	if (m_count < 1)
		return E_NOTIMPL;

	HRESULT hr = S_OK;
	int iMethod = -1;
	CoInitialize(NULL);
	switch(LOWORD(lpici->lpVerb))
	{
	case ID_ABOUTSHELLHASH:
		MessageBox (NULL, "欢迎使用ShellHash！", "ShellHash", MB_OK);
		break;
	case ID_CALCMD5:
		iMethod = 0;
		break;
	case ID_CALCED2K:
		iMethod = 1;
		break;
	case ID_CALCSHA1:
		iMethod = 2;
		break;
	case ID_CALCCRC32:
		iMethod = 3;
		break;
	case ID_CALCMD2:
		iMethod = 4;
		break;
	case ID_CALCMD4:
		iMethod = 5;
		break;
	case ID_CALCGOSTHASH:
		iMethod = 6;
		break;
	case ID_CALCSHA2256:
		iMethod = 7;
		break;
	case ID_CALCSHA2384:
		iMethod = 8;
		break;
	case ID_CALCSHA2512:
		iMethod = 9;
		break;
	default:
		hr = E_FAIL;
	}
	CoUninitialize();

	if (iMethod >= 0)
	{
		SHELLEXECUTEINFO shi;
		memset ((void *)&shi, 0, sizeof(SHELLEXECUTEINFO));

		int iAllocLen = 128;
		char *pszParams = new char[iAllocLen];
		memset ( (void *)pszParams, 0, iAllocLen );
		sprintf ( pszParams, "-m=%d", iMethod );
		int iUsedCnt = strlen ( pszParams );

		for (int i = 0; i < m_count; i++)
		{
			pszParams[iUsedCnt++] = ' ';
			pszParams[iUsedCnt++] = '\"';

			for (int j = 0; j < strlen( m_files[i].szFilePath ); j++)
			{
				pszParams[iUsedCnt++] = m_files[i].szFilePath[j];

				if (iUsedCnt + 10 > iAllocLen)
				{
					char *pszNewBuf = new char[iAllocLen + 100];
					strcpy (pszNewBuf, pszParams);
					delete []pszParams;
					iAllocLen += 100;
					pszParams = pszNewBuf;
				}
			}

			pszParams[iUsedCnt++] = '\"';
		}
		pszParams[iUsedCnt++] = '\0';

		char pszExeFile[MAX_PATH];
		sprintf (pszExeFile, _T("%s\\HashCalc.exe"), m_pszModulePath);


		shi.cbSize = sizeof(SHELLEXECUTEINFO);
		shi.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI | SEE_MASK_FLAG_DDEWAIT;
		shi.hwnd = ::GetDesktopWindow();
		shi.lpVerb = _T("open");
		shi.lpFile = pszExeFile;//_T("E:\\Tools\\HashCalc\\HashCalc.exe");
		shi.lpParameters = pszParams;

		shi.lpDirectory = NULL;
		shi.nShow = SW_NORMAL;

		ShellExecuteEx (&shi);

		delete []pszParams;
	}

	delete []m_files;
	m_files = NULL;
	m_count = 0;
	return hr;
}

void CContextMenu::ErrMessage(DWORD dwErrcode)
{
	void* pMsgBuf;
	TCHAR szMessage[MAX_PATH] = {0};

	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		dwErrcode,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR)&pMsgBuf,
		0,
		NULL);
	lstrcpy(szMessage, (LPCTSTR)pMsgBuf);

	MessageBox(GetForegroundWindow(), szMessage, "Oops! Error", MB_ICONERROR);
	LocalFree(pMsgBuf);
}
//EOF
