#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif

//---------------------------------------------------------------------------
#ifdef _MSC_VER
	#include <objbase.h>
	#define snprintf _snprintf
	
	// The decision what will get exported is done using fzshellext.def 
	#define STDEXPORTAPI STDAPI
#else
	#define STDEXPORTAPI extern "C" __declspec(dllexport) HRESULT STDAPICALLTYPE

	// Currently, the MinGW w32api has no unicode version of ICopyHook. As such,
	// declare ICopyHookW manually.

	// Use some #define magic to prevent LPCOPYHOOK being declared
	#define LPCOPYHOOK LPCOPYHOOKA
		#include <shlobj.h>
	#undef LPCOPYHOOK

	#define INTERFACE ICopyHookW
	DECLARE_INTERFACE_(ICopyHookW, IUnknown)
	{
		STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;
		STDMETHOD_(ULONG,AddRef)(THIS) PURE;
		STDMETHOD_(ULONG,Release)(THIS) PURE;
		STDMETHOD_(UINT,CopyCallback)(THIS_ HWND,UINT,UINT,LPCWSTR,DWORD,LPCWSTR,DWORD) PURE;
	};
	#undef INTERFACE
	typedef ICopyHookW *LPCOPYHOOK;

#endif

//---------------------------------------------------------------------------
#include <initguid.h>
#include <shlguid.h>
#include <stdio.h>
#include <shlobj.h>
#include <olectl.h>
#include <time.h>
#include "shellext.h"
#include <tchar.h>

//---------------------------------------------------------------------------
#define DEBUG_MSG(MSG) \
  if (GLogOn) \
  { \
    Debug(MSG); \
  }

#define DEBUG_MSG_W(MSG) \
  if (GLogOn) \
  { \
    DebugW(MSG); \
  }
	
//---------------------------------------------------------------------------
#define DRAG_EXT_REG_KEY _T("Software\\FileZilla 3\\fzshellext")
#define DRAG_EXT_REG_KEY_PARENT _T("Software\\FileZilla 3")
#define DRAG_EXT_NAME _T("FileZilla 3 Shell Extension")
#define THREADING_MODEL _T("Apartment")
#define CLSID_SIZE 39

//---------------------------------------------------------------------------
class CShellExtClassFactory : public IClassFactory
{
public:
  CShellExtClassFactory();
  virtual ~CShellExtClassFactory();

  // IUnknown members
  STDMETHODIMP         QueryInterface(REFIID, LPVOID FAR*);
  STDMETHODIMP_(ULONG) AddRef();
  STDMETHODIMP_(ULONG) Release();

  // IClassFactory members
  STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR*);
  STDMETHODIMP LockServer(BOOL);

protected:
  unsigned long FReferenceCounter;
};
//---------------------------------------------------------------------------
class CShellExt : public IShellExtInit, ICopyHookW
{
public:
  CShellExt();
  virtual ~CShellExt();

  // IUnknown members
  STDMETHODIMP         QueryInterface(REFIID, LPVOID FAR*);
  STDMETHODIMP_(ULONG) AddRef();
  STDMETHODIMP_(ULONG) Release();

  // IShellExtInit methods
  STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);

  // ICopyHook method
  STDMETHODIMP_(UINT) CopyCallback(HWND Hwnd, UINT Func, UINT Flags,
    LPCTSTR SrcFile, DWORD SrcAttribs, LPCTSTR DestFile, DWORD DestAttribs);
  
protected:
  unsigned long FReferenceCounter;
  LPDATAOBJECT FDataObj;
  HANDLE FMutex;
  unsigned long FLastTicks;
};
//---------------------------------------------------------------------------
unsigned int GRefThisDll = 0;
bool GEnabled = false;
char GLogFile[MAX_PATH] = "";
bool GLogOn = false;
FILE* GLogHandle = NULL;
HANDLE GLogMutex;
HINSTANCE GInstance;
//---------------------------------------------------------------------------
void Debug(const char* Message)
{
  if (GLogOn)
  {
    unsigned long WaitResult = WaitForSingleObject(GLogMutex, 1000);
    if (WaitResult != WAIT_TIMEOUT)
    {
      try
      {
        if (GLogHandle == NULL)
        {
          if (strlen(GLogFile) == 0)
          {
            GLogOn = false;
          }
          else
          {
            GLogHandle = fopen(GLogFile, "at");
            if (GLogHandle == NULL)
            {
              GLogOn = false;
            }
            else
            {
              setbuf(GLogHandle, NULL);
              fprintf(GLogHandle, "----------------------------\n");
            }
          }
        }

        if (GLogOn)
        {
          SYSTEMTIME Time;
          GetSystemTime(&Time);

          fprintf(GLogHandle, "[%2d/%2d/%4d %2d:%02d:%02d.%03d][%04x] %s\n",
            Time.wDay, Time.wMonth, Time.wYear, Time.wHour, Time.wMinute,
            Time.wSecond, Time.wMilliseconds, GetCurrentThreadId(), Message);
        }
      }
      catch(...)
      {
      }
      ReleaseMutex(GLogMutex);
    }
  }
}

void DebugW(const wchar_t* Message)
{
	int bytes = WideCharToMultiByte(CP_UTF8, 0, Message, -1, 0, 0, 0, 0);
	if (bytes <= 0)
	{
		Debug("WideCharToMultiByte failed");
		return;
	}
	char *buffer = new char[bytes + 1];
	int written = WideCharToMultiByte(CP_UTF8, 0, Message, -1, buffer, bytes, 0, 0);
	if (!written)
		Debug("WideCharToMultiByte failed");
	else
	{
        buffer[written] = 0;
		Debug(buffer);
	}
	delete [] buffer;
}

//---------------------------------------------------------------------------
void LogVersion(HINSTANCE HInstance)
{
  if (GLogOn)
  {
    char FileName[MAX_PATH];
    if (GetModuleFileNameA(HInstance, FileName, sizeof(FileName)) > 0)
    {
      Debug(FileName);
      
      unsigned long InfoHandle, Size;
      Size = GetFileVersionInfoSizeA(FileName, &InfoHandle);
      if (Size > 0)
      {
        void* Info;
        Info = new char[Size];
        if (GetFileVersionInfoA(FileName, InfoHandle, Size, Info) != 0)
        {
          VS_FIXEDFILEINFO* VersionInfo;
          unsigned int VersionInfoSize;
          if (VerQueryValueA(Info, "\\", reinterpret_cast<void**>(&VersionInfo),
                &VersionInfoSize) != 0)
          {
            char VersionStr[100];
            snprintf(VersionStr, sizeof(VersionStr), "LogVersion %d.%d.%d.%d",
              HIWORD(VersionInfo->dwFileVersionMS),
              LOWORD(VersionInfo->dwFileVersionMS),
              HIWORD(VersionInfo->dwFileVersionLS),
              LOWORD(VersionInfo->dwFileVersionLS));
            Debug(VersionStr);
          }
          else
          {
            Debug("LogVersion no fixed version info");
          }
        }
        else
        {
          Debug("LogVersion cannot read version info");
        }
      }
      else
      {
        Debug("LogVersion no version info");
      }
    }
  }
}
//---------------------------------------------------------------------------
extern "C" int APIENTRY
DllMain(HINSTANCE HInstance, DWORD Reason, LPVOID Reserved)
{
  if (Reason == DLL_PROCESS_ATTACH)
  {
    GInstance = HInstance;
  }

  if (GRefThisDll == 0)
  {
    GLogMutex = CreateMutex(NULL, false, _T("FileZilla3DragDropExtLogMutex"));

    for (int Root = 0; Root <= 1; Root++)
    {
      HKEY Key;
      if (RegOpenKeyEx(Root == 0 ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
            DRAG_EXT_REG_KEY, 0,
            STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
            &Key) == ERROR_SUCCESS)
      {
        unsigned long Type;
        unsigned long Value;
        unsigned long Size;
        char Buf[MAX_PATH];

        Size = sizeof(Value);
        if ((RegQueryValueEx(Key, _T("Enable"), NULL, &Type,
               reinterpret_cast<LPBYTE>(&Value), &Size) == ERROR_SUCCESS) &&
            (Type == REG_DWORD))
        {
          GEnabled = (Value != 0);
        }

        Size = sizeof(Buf);
        if ((RegQueryValueExA(Key, "LogFile", NULL, &Type,
               reinterpret_cast<LPBYTE>(&Buf), &Size) == ERROR_SUCCESS) &&
            (Type == REG_SZ))
        {
          strncpy(GLogFile, Buf, sizeof(GLogFile));
          GLogFile[sizeof(GLogFile) - 1] = '\0';
          GLogOn = true;
        }

        RegCloseKey(Key);
      }
    }
    DEBUG_MSG("DllMain loaded settings");
    DEBUG_MSG(GEnabled ? "DllMain enabled" : "DllMain disabled");
    LogVersion(HInstance);
  }
  else
  {
    DEBUG_MSG("DllMain settings already loaded");
  }

  DEBUG_MSG("DllMain leave");

  return 1;   // ok
}
//---------------------------------------------------------------------------
STDEXPORTAPI DllCanUnloadNow(void)
{
  bool CanUnload = (GRefThisDll == 0);
  DEBUG_MSG(CanUnload ? "DllCanUnloadNow can" : "DllCanUnloadNow cannot");
  return (CanUnload ? S_OK : S_FALSE);
}
//---------------------------------------------------------------------------
STDEXPORTAPI DllGetClassObject(REFCLSID Rclsid, REFIID Riid, LPVOID* PpvOut)
{
  DEBUG_MSG("DllGetClassObject");

  *PpvOut = NULL;

  if (IsEqualIID(Rclsid, CLSID_ShellExtension))
  {
    DEBUG_MSG("DllGetClassObject is ShellExtension");

    CShellExtClassFactory* Pcf = new CShellExtClassFactory;

    return Pcf->QueryInterface(Riid, PpvOut);
  }

  return CLASS_E_CLASSNOTAVAILABLE;
}
//---------------------------------------------------------------------------
bool RegisterServer(bool AllUsers)
{
  DEBUG_MSG("RegisterServer enter");

  DEBUG_MSG(AllUsers ? "RegisterServer all users" : "RegisterServer current users");

  bool Result = false;
  HKEY RootKey = AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  HKEY HKey;
  DWORD Unused;
  wchar_t ClassID[CLSID_SIZE];

  StringFromGUID2(CLSID_ShellExtension, ClassID, CLSID_SIZE);

  if ((RegOpenKeyEx(RootKey, _T("Software\\Classes"), 0, KEY_WRITE, &HKey) ==
         ERROR_SUCCESS) &&
      (RegCreateKeyEx(HKey, _T("CLSID"), 0, NULL,
         REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey, &Unused) ==
           ERROR_SUCCESS))
  {
    if (RegCreateKey(HKey, ClassID, &HKey) == ERROR_SUCCESS)
    {
      RegSetValueEx(HKey, NULL, 0, REG_SZ,
        reinterpret_cast<const unsigned char*>(DRAG_EXT_NAME), sizeof(DRAG_EXT_NAME));

      if (RegCreateKey(HKey, _T("InProcServer32"), &HKey) == ERROR_SUCCESS)
      {
        wchar_t Filename[MAX_PATH];
        GetModuleFileName(GInstance, Filename, MAX_PATH);
        RegSetValueEx(HKey, NULL, 0, REG_SZ,
          reinterpret_cast<LPBYTE>(Filename), (_tcslen(Filename) + 1) * sizeof(TCHAR));

        RegSetValueEx(HKey, _T("ThreadingModel"), 0, REG_SZ,
          reinterpret_cast<const unsigned char*>(THREADING_MODEL),
          sizeof(THREADING_MODEL));
      }
    }
    RegCloseKey(HKey);

    if ((RegOpenKeyEx(RootKey, _T("Software\\Classes"),
           0, KEY_WRITE, &HKey) == ERROR_SUCCESS) &&
        (RegCreateKeyEx(HKey,
           _T("directory\\shellex\\CopyHookHandlers\\FileZilla3CopyHook"),
           0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey,
           &Unused) == ERROR_SUCCESS))
    {
      RegSetValueEx(HKey, NULL, 0, REG_SZ,
        reinterpret_cast<LPBYTE>(ClassID), (_tcslen(ClassID) + 1) * 2);
      RegCloseKey(HKey);

      if ((RegCreateKeyEx(RootKey, DRAG_EXT_REG_KEY,
             0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey,
             &Unused) == ERROR_SUCCESS))
      {
        unsigned long Value = 1;
        RegSetValueEx(HKey, _T("Enable"), 0, REG_DWORD,
          reinterpret_cast<unsigned char*>(&Value), sizeof(Value));
        
        RegCloseKey(HKey);
        
        Result = true;
      }
      
      SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
    }
  }

  DEBUG_MSG("RegisterServer leave");

  return Result;
}
//---------------------------------------------------------------------------
STDEXPORTAPI DllRegisterServer()
{
  DEBUG_MSG("DllRegisterServer enter");

  HRESULT Result;
  if (RegisterServer(true) || RegisterServer(false))
  {
    Result = S_OK;
  }
  else
  {
    Result = SELFREG_E_CLASS;
  }
  
  DEBUG_MSG("DllRegisterServer leave");

  return Result;
}
//---------------------------------------------------------------------------
static bool RegDeleteEmptyKey(HKEY root, LPCTSTR name)
{
	HKEY key;
	
	// Can't use SHDeleteEmptyKey, it gives a linking error
	if (RegOpenKeyEx(root, name, 0, KEY_READ, &key) != ERROR_SUCCESS)
		return false;

	DWORD subKeys, values;
	int ret = RegQueryInfoKey(key, 0, 0, 0, &subKeys, 0, 0, &values, 0, 0, 0,0);
	
	RegCloseKey(key);
	
	if (ret != ERROR_SUCCESS)
		return false;
	
	if (subKeys || values)
		return false;

	RegDeleteKey(root, name);
	
	return true;
}

bool UnregisterServer(bool AllUsers)
{
	DEBUG_MSG("UnregisterServer enter");

	DEBUG_MSG(AllUsers ? "UnregisterServer all users" : "UnregisterServer current users");

	bool Result = false;
	wchar_t ClassID[CLSID_SIZE];

	StringFromGUID2(CLSID_ShellExtension, ClassID, CLSID_SIZE);

	HKEY RootKey = AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
	HKEY HKey;

	if ((RegOpenKeyEx(RootKey, _T("Software\\Classes"), 0, KEY_WRITE, &HKey) ==
		ERROR_SUCCESS) &&
		(RegOpenKeyEx(HKey, _T("directory\\shellex\\CopyHookHandlers"),
		0, KEY_WRITE, &HKey) == ERROR_SUCCESS))
	{
		RegDeleteKey(HKey, _T("FileZilla3CopyHook"));

		RegCloseKey(HKey);
	}

	if ((RegOpenKeyEx(RootKey, _T("Software\\Classes"), 0, KEY_WRITE, &HKey) ==
		ERROR_SUCCESS) &&
		(RegOpenKeyEx(HKey, _T("CLSID"), 0, KEY_WRITE, &HKey) ==
		ERROR_SUCCESS))
	{
		if (RegOpenKeyEx(HKey, ClassID, 0, KEY_WRITE, &HKey) == ERROR_SUCCESS)
		{
			RegDeleteKey(HKey, _T("InProcServer32"));

			RegCloseKey(HKey);

			if ((RegOpenKeyEx(RootKey, _T("Software\\Classes"), 0, KEY_WRITE, &HKey) ==
				ERROR_SUCCESS) &&
				(RegOpenKeyEx(HKey, _T("CLSID"), 0, KEY_WRITE, &HKey) ==
				ERROR_SUCCESS))
			{
				RegDeleteKey(HKey, ClassID);

				RegCloseKey(HKey);

				Result = true;
			}
		}
	}

	if ((RegOpenKeyEx(RootKey, DRAG_EXT_REG_KEY, 0, KEY_WRITE, &HKey) ==
		ERROR_SUCCESS))
	{
		RegDeleteValue(HKey, _T("Enable"));
		RegCloseKey(HKey);

		Result = true;
	}
	RegDeleteEmptyKey(RootKey, DRAG_EXT_REG_KEY);
	RegDeleteEmptyKey(RootKey, DRAG_EXT_REG_KEY_PARENT);

	SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);

	DEBUG_MSG("UnregisterServer leave");

	return Result;
}
//---------------------------------------------------------------------------
STDEXPORTAPI DllUnregisterServer()
{
  DEBUG_MSG("DllUnregisterServer enter");

  HRESULT Result = SELFREG_E_CLASS;
  if (UnregisterServer(true))
  {
    Result = S_OK;
  }

  if (UnregisterServer(false))
  {
    Result = S_OK;
  }

  DEBUG_MSG("DllUnregisterServer leave");

  return Result;
}
//---------------------------------------------------------------------------
CShellExtClassFactory::CShellExtClassFactory()
{
  DEBUG_MSG("CShellExtClassFactory");

  FReferenceCounter = 0;

  GRefThisDll++;
}
//---------------------------------------------------------------------------
CShellExtClassFactory::~CShellExtClassFactory()
{
  DEBUG_MSG("~CShellExtClassFactory");

  GRefThisDll--;
}
//---------------------------------------------------------------------------
STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID Riid, LPVOID FAR* Ppv)
{
  DEBUG_MSG("QueryInterface");

  *Ppv = NULL;

  // Any interface on this object is the object pointer

  if (IsEqualIID(Riid, IID_IUnknown) || IsEqualIID(Riid, IID_IClassFactory))
  {
    DEBUG_MSG("QueryInterface is IUnknown or IClassFactory");

    *Ppv = (LPCLASSFACTORY)this;

    AddRef();

    return NOERROR;
  }

  return E_NOINTERFACE;
}
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef()
{
  DEBUG_MSG("AddRef");
  return ++FReferenceCounter;
}
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CShellExtClassFactory::Release()
{
  DEBUG_MSG("Release");

  if (--FReferenceCounter)
  {
    return FReferenceCounter;
  }

  delete this;

  return 0;
}
//---------------------------------------------------------------------------
STDMETHODIMP CShellExtClassFactory::CreateInstance(LPUNKNOWN UnkOuter,
  REFIID Riid, LPVOID* PpvObj)
{
  DEBUG_MSG("CreateInstance");

  *PpvObj = NULL;

  // Shell extensions typically don't support aggregation (inheritance)

  if (UnkOuter)
  {
    return CLASS_E_NOAGGREGATION;
  }

  // Create the main shell extension object.  The shell will then call
  // QueryInterface with IID_IShellExtInit--this is how shell extensions are
  // initialized.

  CShellExt* ShellExt = new CShellExt();  //Create the CShellExt object

  if (NULL == ShellExt)
  {
    return E_OUTOFMEMORY;
  }

  return ShellExt->QueryInterface(Riid, PpvObj);
}
//---------------------------------------------------------------------------
STDMETHODIMP CShellExtClassFactory::LockServer(BOOL Lock)
{
  DEBUG_MSG("LockServer");

  return NOERROR;
}
//---------------------------------------------------------------------------
// CShellExt
CShellExt::CShellExt()
{
  DEBUG_MSG("CShellExt enter");

  FReferenceCounter = 0L;
  FDataObj = NULL;

  FMutex = CreateMutex(NULL, false, DRAG_EXT_MUTEX);
  FLastTicks = 0;

  GRefThisDll++;

  DEBUG_MSG("CShellExt leave");
}
//---------------------------------------------------------------------------
CShellExt::~CShellExt()
{
  DEBUG_MSG("~CShellExt enter");

  if (FDataObj)
  {
    FDataObj->Release();
  }

  CloseHandle(FMutex);

  GRefThisDll--;

  DEBUG_MSG("~CShellExt leave");
}
//---------------------------------------------------------------------------
STDMETHODIMP CShellExt::QueryInterface(REFIID Riid, LPVOID FAR* Ppv)
{
  DEBUG_MSG("CShellExt::QueryInterface enter");

  HRESULT Result = E_NOINTERFACE;
  *Ppv = NULL;

  if (!GEnabled)
  {
    DEBUG_MSG("CShellExt::QueryInterface shelext disabled");
  }
  else
  {
    if (IsEqualIID(Riid, IID_IShellExtInit) || IsEqualIID(Riid, IID_IUnknown))
    {
      DEBUG_MSG("CShellExt::QueryInterface is IShellExtInit or IUnknown");
      *Ppv = (LPSHELLEXTINIT)this;
    }
    else if (IsEqualIID(Riid, IID_IShellCopyHook))
    {
      DEBUG_MSG("CShellExt::QueryInterface is IShellCopyHook");
      *Ppv = (LPCOPYHOOK)this;
    }

    if (*Ppv)
    {
      AddRef();

      Result = NOERROR;
    }
  }

  DEBUG_MSG("CShellExt::QueryInterface leave");

  return Result;
}
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CShellExt::AddRef()
{
  DEBUG_MSG("CShellExt::AddRef");

  return ++FReferenceCounter;
}
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CShellExt::Release()
{
  DEBUG_MSG("CShellExt::Release");
  if (--FReferenceCounter)
  {
    return FReferenceCounter;
  }

  delete this;

  return 0;
}
//---------------------------------------------------------------------------
STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST IDFolder,
  LPDATAOBJECT DataObj, HKEY RegKey)
{
  DEBUG_MSG("CShellExt::Initialize enter");

  if (FDataObj != NULL)
  {
    FDataObj->Release();
    FDataObj = NULL;
  }

  // duplicate the object pointer and registry handle

  if (DataObj != NULL)
  {
    FDataObj = DataObj;
    DataObj->AddRef();
  }

  DEBUG_MSG("CShellExt::Initialize leave");

  return NOERROR;
}
//---------------------------------------------------------------------------
STDMETHODIMP_(UINT) CShellExt::CopyCallback(HWND Hwnd, UINT Func, UINT Flags,
											LPCTSTR SrcFile, DWORD SrcAttribs, LPCTSTR DestFile, DWORD DestAttribs)
{
	DEBUG_MSG("CShellExt::CopyCallback enter");

	UINT Result = IDYES;

	if (GEnabled && ((Func == FO_COPY) || (Func == FO_MOVE)))
	{
		DEBUG_MSG("CShellExt::CopyCallback copy or move");

		unsigned long Ticks = GetTickCount();
		if (((Ticks - FLastTicks) >= 100) ||
			(FLastTicks > Ticks))
		{
			//USES_CONVERSION;
			DEBUG_MSG("CShellExt::CopyCallback interval elapsed");

			DEBUG_MSG("CShellExt::CopyCallback source / dest:");
			DEBUG_MSG_W(SrcFile);
			DEBUG_MSG_W(DestFile);

			FLastTicks = Ticks;
			LPCTSTR BackPtr = _tcsrchr(SrcFile, '\\');

			if ((BackPtr != NULL) &&
				(_tcsncmp(BackPtr + 1, DRAG_EXT_DUMMY_DIR_PREFIX,
				DRAG_EXT_DUMMY_DIR_PREFIX_LEN) == 0))
			{
				DEBUG_MSG("CShellExt::CopyCallback filename has prefix");

				HANDLE MapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, DRAG_EXT_MAPPING);

				if (MapFile != NULL)
				{
					DEBUG_MSG("CShellExt::CopyCallback mapfile found");

					char* data = reinterpret_cast<char *>(MapViewOfFile(MapFile, FILE_MAP_ALL_ACCESS, 0, 0, DRAG_EXT_MAPPING_LENGTH));

					if (data != NULL)
					{
						DEBUG_MSG("CShellExt::CopyCallback mapview created");
						unsigned long WaitResult = WaitForSingleObject(FMutex, 1000);
						if (WaitResult != WAIT_TIMEOUT)
						{
							DEBUG_MSG("CShellExt::CopyCallback mutex got");
							if (*data >= DRAG_EXT_VERSION)
							{
								DEBUG_MSG("CShellExt::CopyCallback supported structure version");
								if (data[1] == 1)
								{
									DEBUG_MSG("CShellExt::CopyCallback dragging");

								wchar_t* file = reinterpret_cast<wchar_t *>(data + 2);
								DEBUG_MSG("Dragged file:");
								DEBUG_MSG_W(file);
							
							  if (wcscmp(file, SrcFile) == 0)
							  {
								  data[1] = 2;
								  if (_tcslen(DestFile) > MAX_PATH)
								  {
									  DEBUG_MSG("CShellExt::CopyCallback length of DestFile exceeding MAX_PATH");
								  }
								  else
								  {
									  wcsncpy(file, DestFile, MAX_PATH);
									  file[MAX_PATH] = 0;
									  DEBUG_MSG("CShellExt::CopyCallback destination written into buffer");
								  }
								  Result = IDNO;
								  DEBUG_MSG("CShellExt::CopyCallback dragging refused");
							  }
							  else
							  {
								  data[1] = 3;
								  DEBUG_MSG("CShellExt::CopyCallback dragged file does NOT match");
									}
								}
								else
								{
									DEBUG_MSG("CShellExt::CopyCallback NOT dragging");
								}
							}
							else
							{
								DEBUG_MSG("CShellExt::CopyCallback unsupported structure version");
							}
							ReleaseMutex(FMutex);
							DEBUG_MSG("CShellExt::CopyCallback mutex released");
						}
						else
						{
							DEBUG_MSG("CShellExt::CopyCallback mutex timeout");
						}
						UnmapViewOfFile(data);
					}
					else
					{
						DEBUG_MSG("CShellExt::CopyCallback mapview NOT created");
					}

					CloseHandle(MapFile);
				}
				else
				{
					DEBUG_MSG("CShellExt::CopyCallback mapfile NOT found");
				}
			}
			else
			{
				DEBUG_MSG("CShellExt::CopyCallback filename has NOT prefix");
			}
		}
		else
		{
			DEBUG_MSG("CShellExt::CopyCallback interval NOT elapsed");
		}
	}
	else
	{
		DEBUG_MSG("CShellExt::CopyCallback NOT copy nor move");
	}

	DEBUG_MSG("CShellExt::CopyCallback leave");

	return Result;
}


syntax highlighted by Code2HTML, v. 0.9.1