1 // Support back to Vista
2 #define _WIN32_WINNT _WIN32_WINNT_VISTA
3 #include <sdkddkver.h>
4 
5 // Use WRL to define a classic COM class
6 #define __WRL_CLASSIC_COM__
7 #include <wrl.h>
8 
9 #include <windows.h>
10 #include <shlobj.h>
11 #include <shlwapi.h>
12 #include <olectl.h>
13 #include <strsafe.h>
14 
15 #include "pyshellext_h.h"
16 
17 #define DDWM_UPDATEWINDOW (WM_USER+3)
18 
19 static HINSTANCE hModule;
20 static CLIPFORMAT cfDropDescription;
21 static CLIPFORMAT cfDragWindow;
22 
23 static const LPCWSTR CLASS_SUBKEY = L"Software\\Classes\\CLSID\\{BEA218D2-6950-497B-9434-61683EC065FE}";
24 static const LPCWSTR DRAG_MESSAGE = L"Open with %1";
25 
26 using namespace Microsoft::WRL;
27 
FilenameListCchLengthA(LPCSTR pszSource,size_t cchMax,size_t * pcchLength,size_t * pcchCount)28 HRESULT FilenameListCchLengthA(LPCSTR pszSource, size_t cchMax, size_t *pcchLength, size_t *pcchCount) {
29     HRESULT hr = S_OK;
30     size_t count = 0;
31     size_t length = 0;
32 
33     while (pszSource && pszSource[0]) {
34         size_t oneLength;
35         hr = StringCchLengthA(pszSource, cchMax - length, &oneLength);
36         if (FAILED(hr)) {
37             return hr;
38         }
39         count += 1;
40         length += oneLength + (strchr(pszSource, ' ') ? 3 : 1);
41         pszSource = &pszSource[oneLength + 1];
42     }
43 
44     *pcchCount = count;
45     *pcchLength = length;
46     return hr;
47 }
48 
FilenameListCchLengthW(LPCWSTR pszSource,size_t cchMax,size_t * pcchLength,size_t * pcchCount)49 HRESULT FilenameListCchLengthW(LPCWSTR pszSource, size_t cchMax, size_t *pcchLength, size_t *pcchCount) {
50     HRESULT hr = S_OK;
51     size_t count = 0;
52     size_t length = 0;
53 
54     while (pszSource && pszSource[0]) {
55         size_t oneLength;
56         hr = StringCchLengthW(pszSource, cchMax - length, &oneLength);
57         if (FAILED(hr)) {
58             return hr;
59         }
60         count += 1;
61         length += oneLength + (wcschr(pszSource, ' ') ? 3 : 1);
62         pszSource = &pszSource[oneLength + 1];
63     }
64 
65     *pcchCount = count;
66     *pcchLength = length;
67     return hr;
68 }
69 
FilenameListCchCopyA(STRSAFE_LPSTR pszDest,size_t cchDest,LPCSTR pszSource,LPCSTR pszSeparator)70 HRESULT FilenameListCchCopyA(STRSAFE_LPSTR pszDest, size_t cchDest, LPCSTR pszSource, LPCSTR pszSeparator) {
71     HRESULT hr = S_OK;
72     size_t count = 0;
73     size_t length = 0;
74 
75     while (pszSource[0]) {
76         STRSAFE_LPSTR newDest;
77 
78         hr = StringCchCopyExA(pszDest, cchDest, pszSource, &newDest, &cchDest, 0);
79         if (FAILED(hr)) {
80             return hr;
81         }
82         pszSource += (newDest - pszDest) + 1;
83         pszDest = PathQuoteSpacesA(pszDest) ? newDest + 2 : newDest;
84 
85         if (pszSource[0]) {
86             hr = StringCchCopyExA(pszDest, cchDest, pszSeparator, &newDest, &cchDest, 0);
87             if (FAILED(hr)) {
88                 return hr;
89             }
90             pszDest = newDest;
91         }
92     }
93 
94     return hr;
95 }
96 
FilenameListCchCopyW(STRSAFE_LPWSTR pszDest,size_t cchDest,LPCWSTR pszSource,LPCWSTR pszSeparator)97 HRESULT FilenameListCchCopyW(STRSAFE_LPWSTR pszDest, size_t cchDest, LPCWSTR pszSource, LPCWSTR pszSeparator) {
98     HRESULT hr = S_OK;
99     size_t count = 0;
100     size_t length = 0;
101 
102     while (pszSource[0]) {
103         STRSAFE_LPWSTR newDest;
104 
105         hr = StringCchCopyExW(pszDest, cchDest, pszSource, &newDest, &cchDest, 0);
106         if (FAILED(hr)) {
107             return hr;
108         }
109         pszSource += (newDest - pszDest) + 1;
110         pszDest = PathQuoteSpacesW(pszDest) ? newDest + 2 : newDest;
111 
112         if (pszSource[0]) {
113             hr = StringCchCopyExW(pszDest, cchDest, pszSeparator, &newDest, &cchDest, 0);
114             if (FAILED(hr)) {
115                 return hr;
116             }
117             pszDest = newDest;
118         }
119     }
120 
121     return hr;
122 }
123 
124 
125 class PyShellExt : public RuntimeClass<
126     RuntimeClassFlags<ClassicCom>,
127     IDropTarget,
128     IPersistFile
129 >
130 {
131     LPOLESTR target, target_dir;
132     DWORD target_mode;
133 
134     IDataObject *data_obj;
135 
136 public:
PyShellExt()137     PyShellExt() : target(NULL), target_dir(NULL), target_mode(0), data_obj(NULL) {
138         OutputDebugString(L"PyShellExt::PyShellExt");
139     }
140 
~PyShellExt()141     ~PyShellExt() {
142         if (target) {
143             CoTaskMemFree(target);
144         }
145         if (target_dir) {
146             CoTaskMemFree(target_dir);
147         }
148         if (data_obj) {
149             data_obj->Release();
150         }
151     }
152 
153 private:
UpdateDropDescription(IDataObject * pDataObj)154     HRESULT UpdateDropDescription(IDataObject *pDataObj) {
155         STGMEDIUM medium;
156         FORMATETC fmt = {
157             cfDropDescription,
158             NULL,
159             DVASPECT_CONTENT,
160             -1,
161             TYMED_HGLOBAL
162         };
163 
164         auto hr = pDataObj->GetData(&fmt, &medium);
165         if (FAILED(hr)) {
166             OutputDebugString(L"PyShellExt::UpdateDropDescription - failed to get DROPDESCRIPTION format");
167             return hr;
168         }
169         if (!medium.hGlobal) {
170             OutputDebugString(L"PyShellExt::UpdateDropDescription - DROPDESCRIPTION format had NULL hGlobal");
171             ReleaseStgMedium(&medium);
172             return E_FAIL;
173         }
174         auto dd = (DROPDESCRIPTION*)GlobalLock(medium.hGlobal);
175         if (!dd) {
176             OutputDebugString(L"PyShellExt::UpdateDropDescription - failed to lock DROPDESCRIPTION hGlobal");
177             ReleaseStgMedium(&medium);
178             return E_FAIL;
179         }
180         StringCchCopy(dd->szMessage, sizeof(dd->szMessage) / sizeof(dd->szMessage[0]), DRAG_MESSAGE);
181         StringCchCopy(dd->szInsert, sizeof(dd->szInsert) / sizeof(dd->szInsert[0]), PathFindFileNameW(target));
182         dd->type = DROPIMAGE_MOVE;
183 
184         GlobalUnlock(medium.hGlobal);
185         ReleaseStgMedium(&medium);
186 
187         return S_OK;
188     }
189 
GetDragWindow(IDataObject * pDataObj,HWND * phWnd)190     HRESULT GetDragWindow(IDataObject *pDataObj, HWND *phWnd) {
191         HRESULT hr;
192         HWND *pMem;
193         STGMEDIUM medium;
194         FORMATETC fmt = {
195             cfDragWindow,
196             NULL,
197             DVASPECT_CONTENT,
198             -1,
199             TYMED_HGLOBAL
200         };
201 
202         hr = pDataObj->GetData(&fmt, &medium);
203         if (FAILED(hr)) {
204             OutputDebugString(L"PyShellExt::GetDragWindow - failed to get DragWindow format");
205             return hr;
206         }
207         if (!medium.hGlobal) {
208             OutputDebugString(L"PyShellExt::GetDragWindow - DragWindow format had NULL hGlobal");
209             ReleaseStgMedium(&medium);
210             return E_FAIL;
211         }
212 
213         pMem = (HWND*)GlobalLock(medium.hGlobal);
214         if (!pMem) {
215             OutputDebugString(L"PyShellExt::GetDragWindow - failed to lock DragWindow hGlobal");
216             ReleaseStgMedium(&medium);
217             return E_FAIL;
218         }
219 
220         *phWnd = *pMem;
221 
222         GlobalUnlock(medium.hGlobal);
223         ReleaseStgMedium(&medium);
224 
225         return S_OK;
226     }
227 
GetArguments(IDataObject * pDataObj,LPCWSTR * pArguments)228     HRESULT GetArguments(IDataObject *pDataObj, LPCWSTR *pArguments) {
229         HRESULT hr;
230         DROPFILES *pdropfiles;
231 
232         STGMEDIUM medium;
233         FORMATETC fmt = {
234             CF_HDROP,
235             NULL,
236             DVASPECT_CONTENT,
237             -1,
238             TYMED_HGLOBAL
239         };
240 
241         hr = pDataObj->GetData(&fmt, &medium);
242         if (FAILED(hr)) {
243             OutputDebugString(L"PyShellExt::GetArguments - failed to get CF_HDROP format");
244             return hr;
245         }
246         if (!medium.hGlobal) {
247             OutputDebugString(L"PyShellExt::GetArguments - CF_HDROP format had NULL hGlobal");
248             ReleaseStgMedium(&medium);
249             return E_FAIL;
250         }
251 
252         pdropfiles = (DROPFILES*)GlobalLock(medium.hGlobal);
253         if (!pdropfiles) {
254             OutputDebugString(L"PyShellExt::GetArguments - failed to lock CF_HDROP hGlobal");
255             ReleaseStgMedium(&medium);
256             return E_FAIL;
257         }
258 
259         if (pdropfiles->fWide) {
260             LPCWSTR files = (LPCWSTR)((char*)pdropfiles + pdropfiles->pFiles);
261             size_t len, count;
262             hr = FilenameListCchLengthW(files, 32767, &len, &count);
263             if (SUCCEEDED(hr)) {
264                 LPWSTR args = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * (len + 1));
265                 if (args) {
266                     hr = FilenameListCchCopyW(args, 32767, files, L" ");
267                     if (SUCCEEDED(hr)) {
268                         *pArguments = args;
269                     } else {
270                         CoTaskMemFree(args);
271                     }
272                 } else {
273                     hr = E_OUTOFMEMORY;
274                 }
275             }
276         } else {
277             LPCSTR files = (LPCSTR)((char*)pdropfiles + pdropfiles->pFiles);
278             size_t len, count;
279             hr = FilenameListCchLengthA(files, 32767, &len, &count);
280             if (SUCCEEDED(hr)) {
281                 LPSTR temp = (LPSTR)CoTaskMemAlloc(sizeof(CHAR) * (len + 1));
282                 if (temp) {
283                     hr = FilenameListCchCopyA(temp, 32767, files, " ");
284                     if (SUCCEEDED(hr)) {
285                         int wlen = MultiByteToWideChar(CP_ACP, 0, temp, (int)len, NULL, 0);
286                         if (wlen) {
287                             LPWSTR args = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * (wlen + 1));
288                             if (MultiByteToWideChar(CP_ACP, 0, temp, (int)len, args, wlen + 1)) {
289                                 *pArguments = args;
290                             } else {
291                                 OutputDebugString(L"PyShellExt::GetArguments - failed to convert multi-byte to wide-char path");
292                                 CoTaskMemFree(args);
293                                 hr = E_FAIL;
294                             }
295                         } else {
296                             OutputDebugString(L"PyShellExt::GetArguments - failed to get length of wide-char path");
297                             hr = E_FAIL;
298                         }
299                     }
300                     CoTaskMemFree(temp);
301                 } else {
302                     hr = E_OUTOFMEMORY;
303                 }
304             }
305         }
306 
307         GlobalUnlock(medium.hGlobal);
308         ReleaseStgMedium(&medium);
309 
310         return hr;
311     }
312 
NotifyDragWindow(HWND hwnd)313     HRESULT NotifyDragWindow(HWND hwnd) {
314         LRESULT res;
315 
316         if (!hwnd) {
317             return S_FALSE;
318         }
319 
320         res = SendMessage(hwnd, DDWM_UPDATEWINDOW, 0, NULL);
321 
322         if (res) {
323             OutputDebugString(L"PyShellExt::NotifyDragWindow - failed to post DDWM_UPDATEWINDOW");
324             return E_FAIL;
325         }
326 
327         return S_OK;
328     }
329 
330 public:
331     // IDropTarget implementation
332 
DragEnter(IDataObject * pDataObj,DWORD grfKeyState,POINTL pt,DWORD * pdwEffect)333     STDMETHODIMP DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
334         HWND hwnd;
335 
336         OutputDebugString(L"PyShellExt::DragEnter");
337 
338         pDataObj->AddRef();
339         data_obj = pDataObj;
340 
341         *pdwEffect = DROPEFFECT_MOVE;
342 
343         if (FAILED(UpdateDropDescription(data_obj))) {
344             OutputDebugString(L"PyShellExt::DragEnter - failed to update drop description");
345         }
346         if (FAILED(GetDragWindow(data_obj, &hwnd))) {
347             OutputDebugString(L"PyShellExt::DragEnter - failed to get drag window");
348         }
349         if (FAILED(NotifyDragWindow(hwnd))) {
350             OutputDebugString(L"PyShellExt::DragEnter - failed to notify drag window");
351         }
352 
353         return S_OK;
354     }
355 
DragLeave()356     STDMETHODIMP DragLeave() {
357         return S_OK;
358     }
359 
DragOver(DWORD grfKeyState,POINTL pt,DWORD * pdwEffect)360     STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
361         return S_OK;
362     }
363 
Drop(IDataObject * pDataObj,DWORD grfKeyState,POINTL pt,DWORD * pdwEffect)364     STDMETHODIMP Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
365         LPCWSTR args;
366 
367         OutputDebugString(L"PyShellExt::Drop");
368         *pdwEffect = DROPEFFECT_NONE;
369 
370         if (pDataObj != data_obj) {
371             OutputDebugString(L"PyShellExt::Drop - unexpected data object");
372             return E_FAIL;
373         }
374 
375         data_obj->Release();
376         data_obj = NULL;
377 
378         if (SUCCEEDED(GetArguments(pDataObj, &args))) {
379             OutputDebugString(args);
380             ShellExecute(NULL, NULL, target, args, target_dir, SW_NORMAL);
381 
382             CoTaskMemFree((LPVOID)args);
383         } else {
384             OutputDebugString(L"PyShellExt::Drop - failed to get launch arguments");
385         }
386 
387         return S_OK;
388     }
389 
390     // IPersistFile implementation
391 
GetCurFile(LPOLESTR * ppszFileName)392     STDMETHODIMP GetCurFile(LPOLESTR *ppszFileName) {
393         HRESULT hr;
394         size_t len;
395 
396         if (!ppszFileName) {
397             return E_POINTER;
398         }
399 
400         hr = StringCchLength(target, STRSAFE_MAX_CCH - 1, &len);
401         if (FAILED(hr)) {
402             return E_FAIL;
403         }
404 
405         *ppszFileName = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR) * (len + 1));
406         if (!*ppszFileName) {
407             return E_OUTOFMEMORY;
408         }
409 
410         hr = StringCchCopy(*ppszFileName, len + 1, target);
411         if (FAILED(hr)) {
412             CoTaskMemFree(*ppszFileName);
413             *ppszFileName = NULL;
414             return E_FAIL;
415         }
416 
417         return S_OK;
418     }
419 
IsDirty()420     STDMETHODIMP IsDirty() {
421         return S_FALSE;
422     }
423 
Load(LPCOLESTR pszFileName,DWORD dwMode)424     STDMETHODIMP Load(LPCOLESTR pszFileName, DWORD dwMode) {
425         HRESULT hr;
426         size_t len;
427 
428         OutputDebugString(L"PyShellExt::Load");
429         OutputDebugString(pszFileName);
430 
431         hr = StringCchLength(pszFileName, STRSAFE_MAX_CCH - 1, &len);
432         if (FAILED(hr)) {
433             OutputDebugString(L"PyShellExt::Load - failed to get string length");
434             return hr;
435         }
436 
437         if (target) {
438             CoTaskMemFree(target);
439         }
440         if (target_dir) {
441             CoTaskMemFree(target_dir);
442         }
443 
444         target = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR) * (len + 1));
445         if (!target) {
446             OutputDebugString(L"PyShellExt::Load - E_OUTOFMEMORY");
447             return E_OUTOFMEMORY;
448         }
449         target_dir = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR) * (len + 1));
450         if (!target_dir) {
451             OutputDebugString(L"PyShellExt::Load - E_OUTOFMEMORY");
452             return E_OUTOFMEMORY;
453         }
454 
455         hr = StringCchCopy(target, len + 1, pszFileName);
456         if (FAILED(hr)) {
457             OutputDebugString(L"PyShellExt::Load - failed to copy string");
458             return hr;
459         }
460 
461         hr = StringCchCopy(target_dir, len + 1, pszFileName);
462         if (FAILED(hr)) {
463             OutputDebugString(L"PyShellExt::Load - failed to copy string");
464             return hr;
465         }
466         if (!PathRemoveFileSpecW(target_dir)) {
467             OutputDebugStringW(L"PyShellExt::Load - failed to remove filespec from target");
468             return E_FAIL;
469         }
470 
471         OutputDebugString(target);
472         target_mode = dwMode;
473         OutputDebugString(L"PyShellExt::Load - S_OK");
474         return S_OK;
475     }
476 
Save(LPCOLESTR pszFileName,BOOL fRemember)477     STDMETHODIMP Save(LPCOLESTR pszFileName, BOOL fRemember) {
478         return E_NOTIMPL;
479     }
480 
SaveCompleted(LPCOLESTR pszFileName)481     STDMETHODIMP SaveCompleted(LPCOLESTR pszFileName) {
482         return E_NOTIMPL;
483     }
484 
GetClassID(CLSID * pClassID)485     STDMETHODIMP GetClassID(CLSID *pClassID) {
486         *pClassID = CLSID_PyShellExt;
487         return S_OK;
488     }
489 };
490 
491 CoCreatableClass(PyShellExt);
492 
DllGetClassObject(REFCLSID rclsid,REFIID riid,_COM_Outptr_ void ** ppv)493 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, _COM_Outptr_ void** ppv) {
494     return Module<InProc>::GetModule().GetClassObject(rclsid, riid, ppv);
495 }
496 
DllCanUnloadNow()497 STDAPI DllCanUnloadNow() {
498     return Module<InProc>::GetModule().Terminate() ? S_OK : S_FALSE;
499 }
500 
DllRegisterServer()501 STDAPI DllRegisterServer() {
502     LONG res;
503     SECURITY_ATTRIBUTES secattr = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
504     LPSECURITY_ATTRIBUTES psecattr = NULL;
505     HKEY key, ipsKey;
506     WCHAR modname[MAX_PATH];
507     DWORD modname_len;
508 
509     OutputDebugString(L"PyShellExt::DllRegisterServer");
510     if (!hModule) {
511         OutputDebugString(L"PyShellExt::DllRegisterServer - module handle was not set");
512         return SELFREG_E_CLASS;
513     }
514     modname_len = GetModuleFileName(hModule, modname, MAX_PATH);
515     if (modname_len == 0 ||
516         (modname_len == MAX_PATH && GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
517         OutputDebugString(L"PyShellExt::DllRegisterServer - failed to get module file name");
518         return SELFREG_E_CLASS;
519     }
520 
521     DWORD disp;
522     res = RegCreateKeyEx(HKEY_LOCAL_MACHINE, CLASS_SUBKEY, 0, NULL, 0,
523         KEY_ALL_ACCESS, psecattr, &key, &disp);
524     if (res == ERROR_ACCESS_DENIED) {
525         OutputDebugString(L"PyShellExt::DllRegisterServer - failed to write per-machine registration. Attempting per-user instead.");
526         res = RegCreateKeyEx(HKEY_CURRENT_USER, CLASS_SUBKEY, 0, NULL, 0,
527             KEY_ALL_ACCESS, psecattr, &key, &disp);
528     }
529     if (res != ERROR_SUCCESS) {
530         OutputDebugString(L"PyShellExt::DllRegisterServer - failed to create class key");
531         return SELFREG_E_CLASS;
532     }
533 
534     res = RegCreateKeyEx(key, L"InProcServer32", 0, NULL, 0,
535         KEY_ALL_ACCESS, psecattr, &ipsKey, NULL);
536     if (res != ERROR_SUCCESS) {
537         RegCloseKey(key);
538         OutputDebugString(L"PyShellExt::DllRegisterServer - failed to create InProcServer32 key");
539         return SELFREG_E_CLASS;
540     }
541 
542     res = RegSetValueEx(ipsKey, NULL, 0,
543         REG_SZ, (LPBYTE)modname, modname_len * sizeof(modname[0]));
544 
545     if (res != ERROR_SUCCESS) {
546         RegCloseKey(ipsKey);
547         RegCloseKey(key);
548         OutputDebugString(L"PyShellExt::DllRegisterServer - failed to set server path");
549         return SELFREG_E_CLASS;
550     }
551 
552     res = RegSetValueEx(ipsKey, L"ThreadingModel", 0,
553         REG_SZ, (LPBYTE)(L"Apartment"), sizeof(L"Apartment"));
554 
555     RegCloseKey(ipsKey);
556     RegCloseKey(key);
557     if (res != ERROR_SUCCESS) {
558         OutputDebugString(L"PyShellExt::DllRegisterServer - failed to set threading model");
559         return SELFREG_E_CLASS;
560     }
561 
562     SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
563 
564     OutputDebugString(L"PyShellExt::DllRegisterServer - S_OK");
565     return S_OK;
566 }
567 
DllUnregisterServer()568 STDAPI DllUnregisterServer() {
569     LONG res_lm, res_cu;
570 
571     res_lm = RegDeleteTree(HKEY_LOCAL_MACHINE, CLASS_SUBKEY);
572     if (res_lm != ERROR_SUCCESS && res_lm != ERROR_FILE_NOT_FOUND) {
573         OutputDebugString(L"PyShellExt::DllUnregisterServer - failed to delete per-machine registration");
574         return SELFREG_E_CLASS;
575     }
576 
577     res_cu = RegDeleteTree(HKEY_CURRENT_USER, CLASS_SUBKEY);
578     if (res_cu != ERROR_SUCCESS && res_cu != ERROR_FILE_NOT_FOUND) {
579         OutputDebugString(L"PyShellExt::DllUnregisterServer - failed to delete per-user registration");
580         return SELFREG_E_CLASS;
581     }
582 
583     if (res_lm == ERROR_FILE_NOT_FOUND && res_cu == ERROR_FILE_NOT_FOUND) {
584         OutputDebugString(L"PyShellExt::DllUnregisterServer - extension was not registered");
585         return SELFREG_E_CLASS;
586     }
587 
588     SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
589 
590     OutputDebugString(L"PyShellExt::DllUnregisterServer - S_OK");
591     return S_OK;
592 }
593 
DllMain(_In_opt_ HINSTANCE hinst,DWORD reason,_In_opt_ void *)594 STDAPI_(BOOL) DllMain(_In_opt_ HINSTANCE hinst, DWORD reason, _In_opt_ void*) {
595     if (reason == DLL_PROCESS_ATTACH) {
596         hModule = hinst;
597 
598         cfDropDescription = RegisterClipboardFormat(CFSTR_DROPDESCRIPTION);
599         if (!cfDropDescription) {
600             OutputDebugString(L"PyShellExt::DllMain - failed to get CFSTR_DROPDESCRIPTION format");
601         }
602         cfDragWindow = RegisterClipboardFormat(L"DragWindow");
603         if (!cfDragWindow) {
604             OutputDebugString(L"PyShellExt::DllMain - failed to get DragWindow format");
605         }
606 
607         DisableThreadLibraryCalls(hinst);
608     }
609     return TRUE;
610 }