source: filezilla/trunk/fuentes/src/fzshellext/shellext.cpp @ 130

Last change on this file since 130 was 130, checked in by jrpelegrina, 4 years ago

First release to xenial

File size: 21.4 KB
Line 
1#ifndef UNICODE
2#define UNICODE
3#endif
4#ifndef _UNICODE
5#define _UNICODE
6#endif
7
8
9//---------------------------------------------------------------------------
10#ifdef _MSC_VER
11        #include <objbase.h>
12        #define snprintf _snprintf
13
14        // The decision what will get exported is done using fzshellext.def
15        #define STDEXPORTAPI STDAPI
16#else
17        #define STDEXPORTAPI extern "C" __declspec(dllexport) HRESULT STDAPICALLTYPE
18
19        #include "config.h"
20#if !HAVE_ICOPYHOOKW
21        // Some versions of the MinGW w32api have no unicode version of ICopyHook. As such,
22        // declare ICopyHookW manually.
23
24        // Use some #define magic to prevent LPCOPYHOOK being declared
25        #define LPCOPYHOOK LPCOPYHOOKA
26                #include <shlobj.h>
27        #undef LPCOPYHOOK
28
29        #define INTERFACE ICopyHookW
30        DECLARE_INTERFACE_(ICopyHookW, IUnknown)
31        {
32                STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;
33                STDMETHOD_(ULONG,AddRef)(THIS) PURE;
34                STDMETHOD_(ULONG,Release)(THIS) PURE;
35                STDMETHOD_(UINT,CopyCallback)(THIS_ HWND,UINT,UINT,LPCWSTR,DWORD,LPCWSTR,DWORD) PURE;
36        };
37        #undef INTERFACE
38        typedef ICopyHookW *LPCOPYHOOK;
39#endif // !HAVE_ICOPYHOOKW
40
41#endif
42
43//---------------------------------------------------------------------------
44#include <initguid.h>
45#include <shlguid.h>
46#include <stdio.h>
47#include <shlobj.h>
48#include <olectl.h>
49#include <time.h>
50#include "shellext.h"
51#include <tchar.h>
52#include <cstddef>
53#include <cstdint>
54
55//---------------------------------------------------------------------------
56#ifdef DEBUG
57#define DEBUG_MSG(MSG) Debug(MSG)
58#define DEBUG_MSG_W(MSG) DebugW(MSG)
59#define DEBUG_LOG_VERSION(MSG) LogVersion(MSG)
60#define DEBUG_INIT(KEY) DebugInit(KEY)
61#else
62#define DEBUG_MSG(MSG)
63#define DEBUG_MSG_W(MSG)
64#define DEBUG_LOG_VERSION(MSG)
65#define DEBUG_INIT(KEY)
66#endif
67
68//---------------------------------------------------------------------------
69#define DRAG_EXT_REG_KEY _T("Software\\FileZilla 3\\fzshellext")
70#define DRAG_EXT_REG_KEY_PARENT _T("Software\\FileZilla 3")
71#define DRAG_EXT_NAME   _T("FileZilla 3 Shell Extension")
72#define THREADING_MODEL _T("Apartment")
73#define CLSID_SIZE 39
74
75void* operator new(std::size_t count)
76{
77        return malloc(count);
78}
79
80void* operator new[](std::size_t count)
81{
82        return malloc(count);
83}
84
85void operator delete(void* ptr)
86{
87        if (ptr) {
88                free(ptr);
89        }
90}
91
92void operator delete[](void* ptr)
93{
94        if (ptr) {
95                free(ptr);
96        }
97}
98
99void operator delete(void* ptr, std::size_t)
100{
101        if (ptr) {
102                free(ptr);
103        }
104}
105
106void operator delete[](void* ptr, std::size_t)
107{
108        if (ptr) {
109                free(ptr);
110        }
111}
112
113//---------------------------------------------------------------------------
114class CShellExtClassFactory : public IClassFactory
115{
116public:
117        CShellExtClassFactory();
118        virtual ~CShellExtClassFactory();
119
120        // IUnknown members
121        STDMETHODIMP         QueryInterface(REFIID, LPVOID FAR*);
122        STDMETHODIMP_(ULONG) AddRef();
123        STDMETHODIMP_(ULONG) Release();
124
125        // IClassFactory members
126        STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR*);
127        STDMETHODIMP LockServer(BOOL);
128
129protected:
130        unsigned long FReferenceCounter;
131};
132
133//---------------------------------------------------------------------------
134class CShellExt final : public IShellExtInit, ICopyHookW
135{
136public:
137        CShellExt();
138        virtual ~CShellExt();
139
140        // IUnknown members
141        STDMETHODIMP         QueryInterface(REFIID, LPVOID FAR*);
142        STDMETHODIMP_(ULONG) AddRef();
143        STDMETHODIMP_(ULONG) Release();
144
145        // IShellExtInit methods
146        STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);
147
148        // ICopyHook method
149        STDMETHODIMP_(UINT) CopyCallback(HWND Hwnd, UINT Func, UINT Flags,
150                                                                         LPCTSTR SrcFile, DWORD SrcAttribs, LPCTSTR DestFile, DWORD DestAttribs);
151
152protected:
153        unsigned long FReferenceCounter;
154        LPDATAOBJECT FDataObj;
155        HANDLE FMutex;
156        unsigned long FLastTicks;
157};
158
159//---------------------------------------------------------------------------
160namespace {
161unsigned int GRefThisDll = 0;
162bool GEnabled = false;
163HINSTANCE GInstance;
164
165//---------------------------------------------------------------------------
166#ifdef DEBUG
167bool GLogOn = false;
168FILE* GLogHandle = NULL;
169char GLogFile[MAX_PATH] = "";
170HANDLE GLogMutex = 0;
171
172void Debug(const char* Message)
173{
174        if (!GLogOn)
175                return;
176
177        unsigned long WaitResult = WaitForSingleObject(GLogMutex, 1000);
178        if (WaitResult == WAIT_TIMEOUT)
179                return;
180
181        if (GLogHandle == NULL) {
182                if (strlen(GLogFile) == 0) {
183                        GLogOn = false;
184                        ReleaseMutex(GLogMutex);
185                        return;
186                }
187
188                GLogHandle = fopen(GLogFile, "at");
189                if (GLogHandle == NULL) {
190                        GLogOn = false;
191                        ReleaseMutex(GLogMutex);
192                        return;
193                }
194
195                setbuf(GLogHandle, NULL);
196                fprintf(GLogHandle, "----------------------------\n");
197        }
198
199        SYSTEMTIME Time;
200        GetSystemTime(&Time);
201
202        fprintf(GLogHandle, "[%4d-%02d-%02d %2d:%02d:%02d.%03d][%04x:%04x] %s\n",
203                Time.wYear, Time.wMonth, Time.wDay, Time.wHour, Time.wMinute,
204                Time.wSecond, Time.wMilliseconds,
205                (unsigned int)GetCurrentProcessId(), (unsigned int)GetCurrentThreadId(),
206                Message);
207
208        ReleaseMutex(GLogMutex);
209}
210
211void DebugW(const wchar_t* Message)
212{
213        if (!GLogOn)
214                return;
215
216        int bytes = WideCharToMultiByte(CP_UTF8, 0, Message, -1, 0, 0, 0, 0);
217        if (bytes <= 0) {
218                DEBUG_MSG("WideCharToMultiByte failed");
219                return;
220        }
221        char *buffer = new char[bytes + 1];
222        if (buffer) {
223                int written = WideCharToMultiByte(CP_UTF8, 0, Message, -1, buffer, bytes, 0, 0);
224                if (!written)
225                        DEBUG_MSG("WideCharToMultiByte failed");
226                else {
227                        buffer[written] = 0;
228                        Debug(buffer);
229                }
230                delete[] buffer;
231        }
232}
233
234//---------------------------------------------------------------------------
235void LogVersion(HINSTANCE HInstance)
236{
237        if (!GLogOn)
238                return;
239
240        char FileName[MAX_PATH];
241        if (!GetModuleFileNameA(HInstance, FileName, sizeof(FileName))) {
242                return;
243        }
244
245        Debug(FileName);
246
247        DWORD InfoHandle;
248        DWORD Size = GetFileVersionInfoSizeA(FileName, &InfoHandle);
249        if (!Size) {
250                Debug("LogVersion return: No version info");
251                return;
252        }
253
254        char* Info = new char[Size];
255        if (!Info) {
256                return;
257        }
258
259        if (!GetFileVersionInfoA(FileName, InfoHandle, Size, Info)) {
260                Debug("LogVersion return: cannot read version info");
261                delete [] Info;
262                return;
263        }
264
265        VS_FIXEDFILEINFO* VersionInfo;
266        unsigned int VersionInfoSize;
267        if (!VerQueryValueA(Info, "\\", reinterpret_cast<void**>(&VersionInfo), &VersionInfoSize)) {
268                delete [] Info;
269                Debug("LogVersion return: no fixed version info");
270                return;
271        }
272
273        char VersionStr[100];
274        snprintf(VersionStr, sizeof(VersionStr), "LogVersion %d.%d.%d.%d",
275                HIWORD(VersionInfo->dwFileVersionMS),
276                LOWORD(VersionInfo->dwFileVersionMS),
277                HIWORD(VersionInfo->dwFileVersionLS),
278                LOWORD(VersionInfo->dwFileVersionLS));
279        Debug(VersionStr);
280
281        delete [] Info;
282}
283
284void DebugInit(HKEY Key)
285{
286        unsigned long Type;
287        unsigned long Size;
288
289        Size = sizeof(GLogFile);
290        if ((RegQueryValueExA(Key, "LogFile", NULL, &Type,
291                reinterpret_cast<LPBYTE>(&GLogFile), &Size) == ERROR_SUCCESS) &&
292                (Type == REG_SZ))
293        {
294                GLogFile[sizeof(GLogFile) - 1] = '\0';
295                GLogOn = true;
296        }
297}
298#endif
299}
300
301//---------------------------------------------------------------------------
302extern "C" int APIENTRY
303DllMain(HINSTANCE HInstance, DWORD Reason, LPVOID Reserved)
304{
305        if (Reason == DLL_PROCESS_ATTACH) {
306                GInstance = HInstance;
307
308#ifdef DEBUG
309                if (!GLogMutex)
310                        GLogMutex = CreateMutex(NULL, false, _T("FileZilla3DragDropExtLogMutex"));
311#endif
312
313                if (GRefThisDll != 0) {
314                        DEBUG_MSG("DllMain return: settings already loaded");
315                        return 1;
316                }
317
318                for (int Root = 0; Root <= 1; Root++) {
319                        HKEY Key;
320                        if (RegOpenKeyEx(Root == 0 ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
321                                DRAG_EXT_REG_KEY, 0,
322                                STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
323                                &Key) == ERROR_SUCCESS)
324                        {
325                                unsigned long Type;
326                                unsigned long Value;
327                                unsigned long Size;
328
329                                Size = sizeof(Value);
330                                if ((RegQueryValueEx(Key, _T("Enable"), NULL, &Type,
331                                        reinterpret_cast<LPBYTE>(&Value), &Size) == ERROR_SUCCESS) &&
332                                        (Type == REG_DWORD))
333                                {
334                                        GEnabled = (Value != 0);
335                                }
336
337                                DEBUG_INIT(Key);
338
339                                RegCloseKey(Key);
340                        }
341                }
342                if (GEnabled) {
343                        DEBUG_MSG("DllMain loaded settings, extension is enabled");
344                }
345                else {
346                        DEBUG_MSG("DllMain loaded settings, extension is disabled");
347                }
348                DEBUG_LOG_VERSION(HInstance);
349
350                DEBUG_MSG("DllMain leave");
351        }
352        else if (Reason == DLL_PROCESS_DETACH) {
353                DEBUG_MSG("DllMain detaching process");
354#ifdef DEBUG
355                if (GLogMutex) {
356                        CloseHandle(GLogMutex);
357                        GLogMutex = 0;
358                }
359#endif
360        }
361
362        return 1;   // ok
363}
364
365//---------------------------------------------------------------------------
366STDEXPORTAPI DllCanUnloadNow(void)
367{
368        bool CanUnload = (GRefThisDll == 0);
369        DEBUG_MSG(CanUnload ? "DllCanUnloadNow can" : "DllCanUnloadNow cannot");
370        return (CanUnload ? S_OK : S_FALSE);
371}
372
373//---------------------------------------------------------------------------
374STDEXPORTAPI DllGetClassObject(REFCLSID Rclsid, REFIID Riid, LPVOID* PpvOut)
375{
376        DEBUG_MSG("DllGetClassObject");
377
378        *PpvOut = NULL;
379
380        if (IsEqualIID(Rclsid, CLSID_ShellExtension))
381        {
382                DEBUG_MSG("DllGetClassObject is ShellExtension");
383
384                CShellExtClassFactory* pcf = new CShellExtClassFactory;
385                if (pcf) {
386                        return pcf->QueryInterface(Riid, PpvOut);
387                }
388        }
389
390        return CLASS_E_CLASSNOTAVAILABLE;
391}
392
393//---------------------------------------------------------------------------
394bool RegisterServer(bool AllUsers)
395{
396        DEBUG_MSG("RegisterServer enter");
397
398        DEBUG_MSG(AllUsers ? "RegisterServer all users" : "RegisterServer current users");
399
400        bool Result = false;
401        HKEY RootKey = AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
402        HKEY HKey;
403        DWORD Unused;
404        wchar_t ClassID[CLSID_SIZE];
405
406        StringFromGUID2(CLSID_ShellExtension, ClassID, CLSID_SIZE);
407
408        if ((RegOpenKeyEx(RootKey, _T("Software\\Classes"), 0, KEY_WRITE, &HKey) ==
409                ERROR_SUCCESS) &&
410                (RegCreateKeyEx(HKey, _T("CLSID"), 0, NULL,
411                REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey, &Unused) ==
412                ERROR_SUCCESS))
413        {
414                if (RegCreateKey(HKey, ClassID, &HKey) == ERROR_SUCCESS)
415                {
416                        RegSetValueEx(HKey, NULL, 0, REG_SZ,
417                                reinterpret_cast<const unsigned char*>(DRAG_EXT_NAME), sizeof(DRAG_EXT_NAME));
418
419                        if (RegCreateKey(HKey, _T("InProcServer32"), &HKey) == ERROR_SUCCESS)
420                        {
421                                wchar_t Filename[MAX_PATH];
422                                GetModuleFileName(GInstance, Filename, MAX_PATH);
423                                RegSetValueEx(HKey, NULL, 0, REG_SZ,
424                                        reinterpret_cast<LPBYTE>(Filename), (_tcslen(Filename) + 1) * sizeof(TCHAR));
425
426                                RegSetValueEx(HKey, _T("ThreadingModel"), 0, REG_SZ,
427                                        reinterpret_cast<const unsigned char*>(THREADING_MODEL),
428                                        sizeof(THREADING_MODEL));
429                        }
430                }
431                RegCloseKey(HKey);
432
433                if ((RegOpenKeyEx(RootKey, _T("Software\\Classes"),
434                        0, KEY_WRITE, &HKey) == ERROR_SUCCESS) &&
435                        (RegCreateKeyEx(HKey,
436                        _T("directory\\shellex\\CopyHookHandlers\\FileZilla3CopyHook"),
437                        0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey,
438                        &Unused) == ERROR_SUCCESS))
439                {
440                        RegSetValueEx(HKey, NULL, 0, REG_SZ,
441                                reinterpret_cast<LPBYTE>(ClassID), (_tcslen(ClassID) + 1) * 2);
442                        RegCloseKey(HKey);
443
444                        if ((RegCreateKeyEx(RootKey, DRAG_EXT_REG_KEY,
445                                0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey,
446                                &Unused) == ERROR_SUCCESS))
447                        {
448                                unsigned long Value = 1;
449                                RegSetValueEx(HKey, _T("Enable"), 0, REG_DWORD,
450                                        reinterpret_cast<unsigned char*>(&Value), sizeof(Value));
451
452                                RegCloseKey(HKey);
453
454                                Result = true;
455                        }
456
457                        SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
458                }
459        }
460
461        DEBUG_MSG("RegisterServer leave");
462
463        return Result;
464}
465
466//---------------------------------------------------------------------------
467STDEXPORTAPI DllRegisterServer()
468{
469        DEBUG_MSG("DllRegisterServer enter");
470
471        HRESULT Result;
472        if (RegisterServer(true) || RegisterServer(false))
473                Result = S_OK;
474        else
475                Result = SELFREG_E_CLASS;
476
477        DEBUG_MSG("DllRegisterServer leave");
478
479        return Result;
480}
481
482//---------------------------------------------------------------------------
483namespace {
484static bool RegDeleteEmptyKey(HKEY root, LPCTSTR name)
485{
486        HKEY key;
487
488        // Can't use SHDeleteEmptyKey, it gives a linking error
489        if (RegOpenKeyEx(root, name, 0, KEY_READ, &key) != ERROR_SUCCESS)
490                return false;
491
492        DWORD subKeys, values;
493        int ret = RegQueryInfoKey(key, 0, 0, 0, &subKeys, 0, 0, &values, 0, 0, 0,0);
494
495        RegCloseKey(key);
496
497        if (ret != ERROR_SUCCESS)
498                return false;
499
500        if (subKeys || values)
501                return false;
502
503        RegDeleteKey(root, name);
504
505        return true;
506}
507}
508
509bool UnregisterServer(bool AllUsers)
510{
511        DEBUG_MSG("UnregisterServer enter");
512
513        DEBUG_MSG(AllUsers ? "UnregisterServer all users" : "UnregisterServer current users");
514
515        bool Result = false;
516        wchar_t ClassID[CLSID_SIZE];
517
518        StringFromGUID2(CLSID_ShellExtension, ClassID, CLSID_SIZE);
519
520        HKEY RootKey = AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
521        HKEY HKey;
522
523        if ((RegOpenKeyEx(RootKey, _T("Software\\Classes"), 0, KEY_WRITE, &HKey) ==
524                ERROR_SUCCESS) &&
525                (RegOpenKeyEx(HKey, _T("directory\\shellex\\CopyHookHandlers"),
526                0, KEY_WRITE, &HKey) == ERROR_SUCCESS))
527        {
528                RegDeleteKey(HKey, _T("FileZilla3CopyHook"));
529
530                RegCloseKey(HKey);
531        }
532
533        if ((RegOpenKeyEx(RootKey, _T("Software\\Classes"), 0, KEY_WRITE, &HKey) ==
534                ERROR_SUCCESS) &&
535                (RegOpenKeyEx(HKey, _T("CLSID"), 0, KEY_WRITE, &HKey) ==
536                ERROR_SUCCESS))
537        {
538                if (RegOpenKeyEx(HKey, ClassID, 0, KEY_WRITE, &HKey) == ERROR_SUCCESS)
539                {
540                        RegDeleteKey(HKey, _T("InProcServer32"));
541
542                        RegCloseKey(HKey);
543
544                        if ((RegOpenKeyEx(RootKey, _T("Software\\Classes"), 0, KEY_WRITE, &HKey) ==
545                                ERROR_SUCCESS) &&
546                                (RegOpenKeyEx(HKey, _T("CLSID"), 0, KEY_WRITE, &HKey) ==
547                                ERROR_SUCCESS))
548                        {
549                                RegDeleteKey(HKey, ClassID);
550
551                                RegCloseKey(HKey);
552
553                                Result = true;
554                        }
555                }
556        }
557
558        if ((RegOpenKeyEx(RootKey, DRAG_EXT_REG_KEY, 0, KEY_WRITE, &HKey) ==
559                ERROR_SUCCESS))
560        {
561                RegDeleteValue(HKey, _T("Enable"));
562                RegCloseKey(HKey);
563
564                Result = true;
565        }
566        RegDeleteEmptyKey(RootKey, DRAG_EXT_REG_KEY);
567        RegDeleteEmptyKey(RootKey, DRAG_EXT_REG_KEY_PARENT);
568
569        SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
570
571        DEBUG_MSG("UnregisterServer leave");
572
573        return Result;
574}
575
576//---------------------------------------------------------------------------
577STDEXPORTAPI DllUnregisterServer()
578{
579        DEBUG_MSG("DllUnregisterServer enter");
580
581        HRESULT Result = SELFREG_E_CLASS;
582        if (UnregisterServer(true))     {
583                Result = S_OK;
584        }
585
586        if (UnregisterServer(false)) {
587                Result = S_OK;
588        }
589
590        DEBUG_MSG("DllUnregisterServer leave");
591
592        return Result;
593}
594
595//---------------------------------------------------------------------------
596CShellExtClassFactory::CShellExtClassFactory()
597{
598        DEBUG_MSG("CShellExtClassFactory");
599
600        FReferenceCounter = 0;
601
602        GRefThisDll++;
603}
604
605//---------------------------------------------------------------------------
606CShellExtClassFactory::~CShellExtClassFactory()
607{
608        DEBUG_MSG("~CShellExtClassFactory");
609
610        GRefThisDll--;
611}
612
613//---------------------------------------------------------------------------
614STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID Riid, LPVOID FAR* Ppv)
615{
616        DEBUG_MSG("QueryInterface");
617
618        *Ppv = NULL;
619
620        // Any interface on this object is the object pointer
621
622        if (IsEqualIID(Riid, IID_IUnknown) || IsEqualIID(Riid, IID_IClassFactory)) {
623                DEBUG_MSG("QueryInterface is IUnknown or IClassFactory");
624
625                *Ppv = (LPCLASSFACTORY)this;
626
627                AddRef();
628
629                return NOERROR;
630        }
631
632        return E_NOINTERFACE;
633}
634
635//---------------------------------------------------------------------------
636STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef()
637{
638        DEBUG_MSG("AddRef");
639        return ++FReferenceCounter;
640}
641
642//---------------------------------------------------------------------------
643STDMETHODIMP_(ULONG) CShellExtClassFactory::Release()
644{
645        DEBUG_MSG("Release");
646
647        if (--FReferenceCounter) {
648                return FReferenceCounter;
649        }
650
651        delete this;
652
653        return 0;
654}
655
656//---------------------------------------------------------------------------
657STDMETHODIMP CShellExtClassFactory::CreateInstance(LPUNKNOWN UnkOuter,
658                                                                                                   REFIID Riid, LPVOID* PpvObj)
659{
660        DEBUG_MSG("CreateInstance");
661
662        *PpvObj = NULL;
663
664        // Shell extensions typically don't support aggregation (inheritance)
665
666        if (UnkOuter) {
667                return CLASS_E_NOAGGREGATION;
668        }
669
670        // Create the main shell extension object.  The shell will then call
671        // QueryInterface with IID_IShellExtInit--this is how shell extensions are
672        // initialized.
673
674        CShellExt* ShellExt = new CShellExt();  //Create the CShellExt object
675
676        if (NULL == ShellExt) {
677                return E_OUTOFMEMORY;
678        }
679
680        return ShellExt->QueryInterface(Riid, PpvObj);
681}
682
683//---------------------------------------------------------------------------
684STDMETHODIMP CShellExtClassFactory::LockServer(BOOL Lock)
685{
686        DEBUG_MSG("LockServer");
687
688        return NOERROR;
689}
690
691//---------------------------------------------------------------------------
692// CShellExt
693CShellExt::CShellExt()
694{
695        DEBUG_MSG("CShellExt enter");
696
697        FReferenceCounter = 0L;
698        FDataObj = NULL;
699
700        FMutex = CreateMutex(NULL, false, DRAG_EXT_MUTEX);
701        FLastTicks = 0;
702
703        GRefThisDll++;
704
705        DEBUG_MSG("CShellExt leave");
706}
707
708//---------------------------------------------------------------------------
709CShellExt::~CShellExt()
710{
711        DEBUG_MSG("~CShellExt enter");
712
713        if (FDataObj) {
714                FDataObj->Release();
715        }
716
717        CloseHandle(FMutex);
718
719        GRefThisDll--;
720
721        DEBUG_MSG("~CShellExt leave");
722}
723
724//---------------------------------------------------------------------------
725STDMETHODIMP CShellExt::QueryInterface(REFIID Riid, LPVOID FAR* Ppv)
726{
727        DEBUG_MSG("CShellExt::QueryInterface enter");
728
729        HRESULT Result = E_NOINTERFACE;
730        *Ppv = NULL;
731
732        if (!GEnabled) {
733                DEBUG_MSG("CShellExt::QueryInterface shelext disabled");
734        }
735        else {
736                if (IsEqualIID(Riid, IID_IShellExtInit) || IsEqualIID(Riid, IID_IUnknown)) {
737                        DEBUG_MSG("CShellExt::QueryInterface is IShellExtInit or IUnknown");
738                        *Ppv = (LPSHELLEXTINIT)this;
739                }
740                else if (IsEqualIID(Riid, IID_IShellCopyHook)) {
741                        DEBUG_MSG("CShellExt::QueryInterface is IShellCopyHook");
742                        *Ppv = (LPCOPYHOOK)this;
743                }
744
745                if (*Ppv) {
746                        AddRef();
747
748                        Result = NOERROR;
749                }
750        }
751
752        DEBUG_MSG("CShellExt::QueryInterface leave");
753
754        return Result;
755}
756
757//---------------------------------------------------------------------------
758STDMETHODIMP_(ULONG) CShellExt::AddRef()
759{
760        DEBUG_MSG("CShellExt::AddRef");
761
762        return ++FReferenceCounter;
763}
764
765//---------------------------------------------------------------------------
766STDMETHODIMP_(ULONG) CShellExt::Release()
767{
768        DEBUG_MSG("CShellExt::Release");
769        if (--FReferenceCounter) {
770                return FReferenceCounter;
771        }
772
773        delete this;
774
775        return 0;
776}
777
778//---------------------------------------------------------------------------
779STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST IDFolder,
780                                                                   LPDATAOBJECT DataObj, HKEY RegKey)
781{
782        DEBUG_MSG("CShellExt::Initialize enter");
783
784        if (FDataObj != NULL) {
785                FDataObj->Release();
786                FDataObj = NULL;
787        }
788
789        // duplicate the object pointer and registry handle
790
791        if (DataObj != NULL) {
792                FDataObj = DataObj;
793                DataObj->AddRef();
794        }
795
796        DEBUG_MSG("CShellExt::Initialize leave");
797
798        return NOERROR;
799}
800
801//---------------------------------------------------------------------------
802STDMETHODIMP_(UINT) CShellExt::CopyCallback(HWND Hwnd, UINT wFunc, UINT Flags,
803                                                                                        LPCTSTR SrcFile, DWORD SrcAttribs, LPCTSTR DestFile, DWORD DestAttribs)
804{
805        UINT Result = IDYES;
806
807        if (!GEnabled) {
808                DEBUG_MSG("CShellExt::CopyCallback return: Not enabled");
809                return Result;
810        }
811
812        if (wFunc != FO_COPY && wFunc != FO_MOVE) {
813                char buffer[100];
814                sprintf(buffer, "CShellExt::CopyCallback return: wFunc is %u, NOT FO_COPY nor FO_MOVE", (unsigned int)wFunc);
815                DEBUG_MSG(buffer);
816                return Result;
817        }
818        else if (wFunc == FO_COPY) {
819                DEBUG_MSG("CShellExt::CopyCallback: wFunc is FO_COPY");
820        }
821        else {
822                DEBUG_MSG("CShellExt::CopyCallback: wFunc is FO_MOVE");
823        }
824
825        unsigned long Ticks = GetTickCount();
826        if ((Ticks - FLastTicks) < 100 && FLastTicks <= Ticks) {
827                DEBUG_MSG("CShellExt::CopyCallback return; interval NOT elapsed");
828                return Result;
829        }
830
831        FLastTicks = Ticks;
832
833        DEBUG_MSG("CShellExt::CopyCallback source / dest:");
834        DEBUG_MSG_W(SrcFile);
835        DEBUG_MSG_W(DestFile);
836
837        LPCTSTR BackPtr = _tcsrchr(SrcFile, '\\');
838
839        if (BackPtr == NULL || (_tcsncmp(BackPtr + 1, DRAG_EXT_DUMMY_DIR_PREFIX, DRAG_EXT_DUMMY_DIR_PREFIX_LEN) != 0)) {
840                DEBUG_MSG("CShellExt::CopyCallback return: filename has NOT prefix");
841                return Result;
842        }
843
844        HANDLE MapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, DRAG_EXT_MAPPING);
845
846        if (MapFile == NULL) {
847                DEBUG_MSG("CShellExt::CopyCallback return: mapfile NOT found");
848                return Result;
849        }
850
851        char* data = reinterpret_cast<char *>(MapViewOfFile(MapFile, FILE_MAP_ALL_ACCESS, 0, 0, DRAG_EXT_MAPPING_LENGTH));
852
853        if (data != NULL) {
854                DEBUG_MSG("CShellExt::CopyCallback mapview created");
855                unsigned long WaitResult = WaitForSingleObject(FMutex, 1000);
856                if (WaitResult != WAIT_TIMEOUT) {
857                        DEBUG_MSG("CShellExt::CopyCallback mutex got");
858                        if (*data >= DRAG_EXT_VERSION) {
859                                DEBUG_MSG("CShellExt::CopyCallback supported structure version");
860                                if (data[1] == 1) {
861                                        DEBUG_MSG("CShellExt::CopyCallback dragging");
862
863                                        wchar_t* file = reinterpret_cast<wchar_t *>(data + 2);
864                                        DEBUG_MSG("Dragged file:");
865                                        DEBUG_MSG_W(file);
866
867                                        if (_wcsicmp(file, SrcFile) == 0) {
868                                                data[1] = 2;
869                                                if (_tcslen(DestFile) > MAX_PATH) {
870                                                        DEBUG_MSG("CShellExt::CopyCallback length of DestFile exceeding MAX_PATH");
871                                                }
872                                                else {
873                                                        wcsncpy(file, DestFile, MAX_PATH);
874                                                        file[MAX_PATH] = 0;
875                                                        DEBUG_MSG("CShellExt::CopyCallback destination written into buffer");
876                                                }
877                                                Result = IDNO;
878                                                DEBUG_MSG("CShellExt::CopyCallback dragging refused");
879                                        }
880                                        else {
881                                                data[1] = 3;
882                                                DEBUG_MSG("CShellExt::CopyCallback dragged file does NOT match");
883                                        }
884                                }
885                                else {
886                                        DEBUG_MSG("CShellExt::CopyCallback NOT dragging");
887                                }
888                        }
889                        else {
890                                DEBUG_MSG("CShellExt::CopyCallback unsupported structure version");
891                        }
892                        ReleaseMutex(FMutex);
893                        DEBUG_MSG("CShellExt::CopyCallback mutex released");
894                }
895                else {
896                        DEBUG_MSG("CShellExt::CopyCallback mutex timeout");
897                }
898                UnmapViewOfFile(data);
899        }
900        else {
901                DEBUG_MSG("CShellExt::CopyCallback mapview NOT created");
902        }
903
904        CloseHandle(MapFile);
905
906        return Result;
907}
Note: See TracBrowser for help on using the repository browser.