[Python-checkins] cpython (merge 3.5 -> default): Issue #27469: Adds a shell extension to the launcher so that drag and drop

steve.dower python-checkins at python.org
Sat Jul 23 11:02:23 EDT 2016


https://hg.python.org/cpython/rev/f56adb9800fa
changeset:   102431:f56adb9800fa
parent:      102429:a28abe83cf5c
parent:      102430:3005fc6cff8a
user:        Steve Dower <steve.dower at microsoft.com>
date:        Sat Jul 23 08:02:02 2016 -0700
summary:
  Issue #27469: Adds a shell extension to the launcher so that drag and drop works correctly.

files:
  Misc/NEWS                             |    6 +
  PC/pyshellext.cpp                     |  605 ++++++++++++++
  PC/pyshellext.def                     |    6 +
  PC/pyshellext.idl                     |   12 +
  PC/pyshellext.rc                      |   46 +
  PC/pyshellext_d.def                   |    6 +
  PCbuild/pcbuild.proj                  |    2 +
  PCbuild/pcbuild.sln                   |   26 +-
  PCbuild/pyproject.props               |    5 +-
  PCbuild/pyshellext.vcxproj            |   87 ++
  PCbuild/pyshellext.vcxproj.filters    |   40 +
  Tools/msi/launcher/launcher.wixproj   |   14 +
  Tools/msi/launcher/launcher.wxs       |    2 +-
  Tools/msi/launcher/launcher_files.wxs |   13 +
  Tools/msi/launcher/launcher_reg.wxs   |   10 +-
  15 files changed, 871 insertions(+), 9 deletions(-)


diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -244,6 +244,12 @@
 
 - Don't use largefile support for GNU/Hurd.
 
+Windows
+-------
+
+- Issue #27469: Adds a shell extension to the launcher so that drag and drop
+  works correctly.
+
 Tools/Demos
 -----------
 
diff --git a/PC/pyshellext.cpp b/PC/pyshellext.cpp
new file mode 100644
--- /dev/null
+++ b/PC/pyshellext.cpp
@@ -0,0 +1,605 @@
+// Support back to Vista
+#define _WIN32_WINNT _WIN32_WINNT_VISTA
+#include <sdkddkver.h>
+
+// Use WRL to define a classic COM class
+#define __WRL_CLASSIC_COM__
+#include <wrl.h>
+
+#include <windows.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+#include <olectl.h>
+#include <strsafe.h>
+
+#include "pyshellext_h.h"
+
+#define DDWM_UPDATEWINDOW (WM_USER+3)
+
+static HINSTANCE hModule;
+static CLIPFORMAT cfDropDescription;
+static CLIPFORMAT cfDragWindow;
+
+static const LPCWSTR CLASS_SUBKEY = L"Software\\Classes\\CLSID\\{BEA218D2-6950-497B-9434-61683EC065FE}";
+static const LPCWSTR DRAG_MESSAGE = L"Open with %1";
+
+using namespace Microsoft::WRL;
+
+HRESULT FilenameListCchLengthA(LPCSTR pszSource, size_t cchMax, size_t *pcchLength, size_t *pcchCount) {
+    HRESULT hr = S_OK;
+    size_t count = 0;
+    size_t length = 0;
+
+    while (pszSource && pszSource[0]) {
+        size_t oneLength;
+        hr = StringCchLengthA(pszSource, cchMax - length, &oneLength);
+        if (FAILED(hr)) {
+            return hr;
+        }
+        count += 1;
+        length += oneLength + (strchr(pszSource, ' ') ? 3 : 1);
+        pszSource = &pszSource[oneLength + 1];
+    }
+
+    *pcchCount = count;
+    *pcchLength = length;
+    return hr;
+}
+
+HRESULT FilenameListCchLengthW(LPCWSTR pszSource, size_t cchMax, size_t *pcchLength, size_t *pcchCount) {
+    HRESULT hr = S_OK;
+    size_t count = 0;
+    size_t length = 0;
+
+    while (pszSource && pszSource[0]) {
+        size_t oneLength;
+        hr = StringCchLengthW(pszSource, cchMax - length, &oneLength);
+        if (FAILED(hr)) {
+            return hr;
+        }
+        count += 1;
+        length += oneLength + (wcschr(pszSource, ' ') ? 3 : 1);
+        pszSource = &pszSource[oneLength + 1];
+    }
+
+    *pcchCount = count;
+    *pcchLength = length;
+    return hr;
+}
+
+HRESULT FilenameListCchCopyA(STRSAFE_LPSTR pszDest, size_t cchDest, LPCSTR pszSource, LPCSTR pszSeparator) {
+    HRESULT hr = S_OK;
+    size_t count = 0;
+    size_t length = 0;
+
+    while (pszSource[0]) {
+        STRSAFE_LPSTR newDest;
+
+        hr = StringCchCopyExA(pszDest, cchDest, pszSource, &newDest, &cchDest, 0);
+        if (FAILED(hr)) {
+            return hr;
+        }
+        pszSource += (newDest - pszDest) + 1;
+        pszDest = PathQuoteSpacesA(pszDest) ? newDest + 2 : newDest;
+
+        if (pszSource[0]) {
+            hr = StringCchCopyExA(pszDest, cchDest, pszSeparator, &newDest, &cchDest, 0);
+            if (FAILED(hr)) {
+                return hr;
+            }
+            pszDest = newDest;
+        }
+    }
+
+    return hr;
+}
+
+HRESULT FilenameListCchCopyW(STRSAFE_LPWSTR pszDest, size_t cchDest, LPCWSTR pszSource, LPCWSTR pszSeparator) {
+    HRESULT hr = S_OK;
+    size_t count = 0;
+    size_t length = 0;
+
+    while (pszSource[0]) {
+        STRSAFE_LPWSTR newDest;
+
+        hr = StringCchCopyExW(pszDest, cchDest, pszSource, &newDest, &cchDest, 0);
+        if (FAILED(hr)) {
+            return hr;
+        }
+        pszSource += (newDest - pszDest) + 1;
+        pszDest = PathQuoteSpacesW(pszDest) ? newDest + 2 : newDest;
+
+        if (pszSource[0]) {
+            hr = StringCchCopyExW(pszDest, cchDest, pszSeparator, &newDest, &cchDest, 0);
+            if (FAILED(hr)) {
+                return hr;
+            }
+            pszDest = newDest;
+        }
+    }
+
+    return hr;
+}
+
+
+class PyShellExt : public RuntimeClass<
+    RuntimeClassFlags<ClassicCom>,
+    IDropTarget,
+    IPersistFile
+>
+{
+    LPOLESTR target, target_dir;
+    DWORD target_mode;
+
+    IDataObject *data_obj;
+
+public:
+    PyShellExt() : target(NULL), target_dir(NULL), target_mode(0), data_obj(NULL) {
+        OutputDebugString(L"PyShellExt::PyShellExt");
+    }
+
+    ~PyShellExt() {
+        if (target) {
+            CoTaskMemFree(target);
+        }
+        if (target_dir) {
+            CoTaskMemFree(target_dir);
+        }
+        if (data_obj) {
+            data_obj->Release();
+        }
+    }
+
+private:
+    HRESULT UpdateDropDescription(IDataObject *pDataObj) {
+        STGMEDIUM medium;
+        FORMATETC fmt = {
+            cfDropDescription,
+            NULL,
+            DVASPECT_CONTENT,
+            -1,
+            TYMED_HGLOBAL
+        };
+
+        auto hr = pDataObj->GetData(&fmt, &medium);
+        if (FAILED(hr)) {
+            OutputDebugString(L"PyShellExt::UpdateDropDescription - failed to get DROPDESCRIPTION format");
+            return hr;
+        }
+        if (!medium.hGlobal) {
+            OutputDebugString(L"PyShellExt::UpdateDropDescription - DROPDESCRIPTION format had NULL hGlobal");
+            ReleaseStgMedium(&medium);
+            return E_FAIL;
+        }
+        auto dd = (DROPDESCRIPTION*)GlobalLock(medium.hGlobal);
+        StringCchCopy(dd->szMessage, sizeof(dd->szMessage) / sizeof(dd->szMessage[0]), DRAG_MESSAGE);
+        StringCchCopy(dd->szInsert, sizeof(dd->szInsert) / sizeof(dd->szInsert[0]), PathFindFileNameW(target));
+        dd->type = DROPIMAGE_MOVE;
+
+        GlobalUnlock(medium.hGlobal);
+        ReleaseStgMedium(&medium);
+
+        return S_OK;
+    }
+
+    HRESULT GetDragWindow(IDataObject *pDataObj, HWND *phWnd) {
+        HRESULT hr;
+        HWND *pMem;
+        STGMEDIUM medium;
+        FORMATETC fmt = {
+            cfDragWindow,
+            NULL,
+            DVASPECT_CONTENT,
+            -1,
+            TYMED_HGLOBAL
+        };
+
+        hr = pDataObj->GetData(&fmt, &medium);
+        if (FAILED(hr)) {
+            OutputDebugString(L"PyShellExt::GetDragWindow - failed to get DragWindow format");
+            return hr;
+        }
+        if (!medium.hGlobal) {
+            OutputDebugString(L"PyShellExt::GetDragWindow - DragWindow format had NULL hGlobal");
+            ReleaseStgMedium(&medium);
+            return E_FAIL;
+        }
+
+        pMem = (HWND*)GlobalLock(medium.hGlobal);
+        if (!pMem) {
+            OutputDebugString(L"PyShellExt::GetDragWindow - failed to lock DragWindow hGlobal");
+            ReleaseStgMedium(&medium);
+            return E_FAIL;
+        }
+
+        *phWnd = *pMem;
+
+        GlobalUnlock(medium.hGlobal);
+        ReleaseStgMedium(&medium);
+
+        return S_OK;
+    }
+
+    HRESULT GetArguments(IDataObject *pDataObj, LPCWSTR *pArguments) {
+        HRESULT hr;
+        DROPFILES *pdropfiles;
+
+        STGMEDIUM medium;
+        FORMATETC fmt = {
+            CF_HDROP,
+            NULL,
+            DVASPECT_CONTENT,
+            -1,
+            TYMED_HGLOBAL
+        };
+
+        hr = pDataObj->GetData(&fmt, &medium);
+        if (FAILED(hr)) {
+            OutputDebugString(L"PyShellExt::GetArguments - failed to get CF_HDROP format");
+            return hr;
+        }
+        if (!medium.hGlobal) {
+            OutputDebugString(L"PyShellExt::GetArguments - CF_HDROP format had NULL hGlobal");
+            ReleaseStgMedium(&medium);
+            return E_FAIL;
+        }
+
+        pdropfiles = (DROPFILES*)GlobalLock(medium.hGlobal);
+        if (!pdropfiles) {
+            OutputDebugString(L"PyShellExt::GetArguments - failed to lock CF_HDROP hGlobal");
+            ReleaseStgMedium(&medium);
+            return E_FAIL;
+        }
+
+        if (pdropfiles->fWide) {
+            LPCWSTR files = (LPCWSTR)((char*)pdropfiles + pdropfiles->pFiles);
+            size_t len, count;
+            hr = FilenameListCchLengthW(files, 32767, &len, &count);
+            if (SUCCEEDED(hr)) {
+                LPWSTR args = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * (len + 1));
+                if (args) {
+                    hr = FilenameListCchCopyW(args, 32767, files, L" ");
+                    if (SUCCEEDED(hr)) {
+                        *pArguments = args;
+                    } else {
+                        CoTaskMemFree(args);
+                    }
+                } else {
+                    hr = E_OUTOFMEMORY;
+                }
+            }
+        } else {
+            LPCSTR files = (LPCSTR)((char*)pdropfiles + pdropfiles->pFiles);
+            size_t len, count;
+            hr = FilenameListCchLengthA(files, 32767, &len, &count);
+            if (SUCCEEDED(hr)) {
+                LPSTR temp = (LPSTR)CoTaskMemAlloc(sizeof(CHAR) * (len + 1));
+                if (temp) {
+                    hr = FilenameListCchCopyA(temp, 32767, files, " ");
+                    if (SUCCEEDED(hr)) {
+                        int wlen = MultiByteToWideChar(CP_ACP, 0, temp, (int)len, NULL, 0);
+                        if (wlen) {
+                            LPWSTR args = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * (wlen + 1));
+                            if (MultiByteToWideChar(CP_ACP, 0, temp, (int)len, args, wlen + 1)) {
+                                *pArguments = args;
+                            } else {
+                                OutputDebugString(L"PyShellExt::GetArguments - failed to convert multi-byte to wide-char path");
+                                CoTaskMemFree(args);
+                                hr = E_FAIL;
+                            }
+                        } else {
+                            OutputDebugString(L"PyShellExt::GetArguments - failed to get length of wide-char path");
+                            hr = E_FAIL;
+                        }
+                    }
+                    CoTaskMemFree(temp);
+                } else {
+                    hr = E_OUTOFMEMORY;
+                }
+            }
+        }
+
+        GlobalUnlock(medium.hGlobal);
+        ReleaseStgMedium(&medium);
+
+        return hr;
+    }
+
+    HRESULT NotifyDragWindow(HWND hwnd) {
+        LRESULT res;
+
+        if (!hwnd) {
+            return S_FALSE;
+        }
+
+        res = SendMessage(hwnd, DDWM_UPDATEWINDOW, 0, NULL);
+
+        if (res) {
+            OutputDebugString(L"PyShellExt::NotifyDragWindow - failed to post DDWM_UPDATEWINDOW");
+            return E_FAIL;
+        }
+
+        return S_OK;
+    }
+
+public:
+    // IDropTarget implementation
+
+    STDMETHODIMP DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+        HWND hwnd;
+
+        OutputDebugString(L"PyShellExt::DragEnter");
+
+        pDataObj->AddRef();
+        data_obj = pDataObj;
+
+        *pdwEffect = DROPEFFECT_MOVE;
+
+        if (FAILED(UpdateDropDescription(data_obj))) {
+            OutputDebugString(L"PyShellExt::DragEnter - failed to update drop description");
+        }
+        if (FAILED(GetDragWindow(data_obj, &hwnd))) {
+            OutputDebugString(L"PyShellExt::DragEnter - failed to get drag window");
+        }
+        if (FAILED(NotifyDragWindow(hwnd))) {
+            OutputDebugString(L"PyShellExt::DragEnter - failed to notify drag window");
+        }
+
+        return S_OK;
+    }
+
+    STDMETHODIMP DragLeave() {
+        return S_OK;
+    }
+
+    STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+        return S_OK;
+    }
+
+    STDMETHODIMP Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+        LPCWSTR args;
+
+        OutputDebugString(L"PyShellExt::Drop");
+        *pdwEffect = DROPEFFECT_NONE;
+
+        if (pDataObj != data_obj) {
+            OutputDebugString(L"PyShellExt::Drop - unexpected data object");
+            return E_FAIL;
+        }
+
+        data_obj->Release();
+        data_obj = NULL;
+
+        if (SUCCEEDED(GetArguments(pDataObj, &args))) {
+            OutputDebugString(args);
+            ShellExecute(NULL, NULL, target, args, target_dir, SW_NORMAL);
+
+            CoTaskMemFree((LPVOID)args);
+        } else {
+            OutputDebugString(L"PyShellExt::Drop - failed to get launch arguments");
+        }
+
+        return S_OK;
+    }
+
+    // IPersistFile implementation
+
+    STDMETHODIMP GetCurFile(LPOLESTR *ppszFileName) {
+        HRESULT hr;
+        size_t len;
+
+        if (!ppszFileName) {
+            return E_POINTER;
+        }
+
+        hr = StringCchLength(target, STRSAFE_MAX_CCH - 1, &len);
+        if (FAILED(hr)) {
+            return E_FAIL;
+        }
+
+        *ppszFileName = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR) * (len + 1));
+        if (!*ppszFileName) {
+            return E_OUTOFMEMORY;
+        }
+
+        hr = StringCchCopy(*ppszFileName, len + 1, target);
+        if (FAILED(hr)) {
+            CoTaskMemFree(*ppszFileName);
+            *ppszFileName = NULL;
+            return E_FAIL;
+        }
+
+        return S_OK;
+    }
+
+    STDMETHODIMP IsDirty() {
+        return S_FALSE;
+    }
+
+    STDMETHODIMP Load(LPCOLESTR pszFileName, DWORD dwMode) {
+        HRESULT hr;
+        size_t len;
+
+        OutputDebugString(L"PyShellExt::Load");
+        OutputDebugString(pszFileName);
+
+        hr = StringCchLength(pszFileName, STRSAFE_MAX_CCH - 1, &len);
+        if (FAILED(hr)) {
+            OutputDebugString(L"PyShellExt::Load - failed to get string length");
+            return hr;
+        }
+
+        if (target) {
+            CoTaskMemFree(target);
+        }
+        if (target_dir) {
+            CoTaskMemFree(target_dir);
+        }
+
+        target = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR) * (len + 1));
+        if (!target) {
+            OutputDebugString(L"PyShellExt::Load - E_OUTOFMEMORY");
+            return E_OUTOFMEMORY;
+        }
+        target_dir = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR) * (len + 1));
+        if (!target_dir) {
+            OutputDebugString(L"PyShellExt::Load - E_OUTOFMEMORY");
+            return E_OUTOFMEMORY;
+        }
+
+        hr = StringCchCopy(target, len + 1, pszFileName);
+        if (FAILED(hr)) {
+            OutputDebugString(L"PyShellExt::Load - failed to copy string");
+            return hr;
+        }
+
+        hr = StringCchCopy(target_dir, len + 1, pszFileName);
+        if (FAILED(hr)) {
+            OutputDebugString(L"PyShellExt::Load - failed to copy string");
+            return hr;
+        }
+        if (!PathRemoveFileSpecW(target_dir)) {
+            OutputDebugStringW(L"PyShellExt::Load - failed to remove filespec from target");
+            return E_FAIL;
+        }
+
+        OutputDebugString(target);
+        target_mode = dwMode;
+        OutputDebugString(L"PyShellExt::Load - S_OK");
+        return S_OK;
+    }
+
+    STDMETHODIMP Save(LPCOLESTR pszFileName, BOOL fRemember) {
+        return E_NOTIMPL;
+    }
+
+    STDMETHODIMP SaveCompleted(LPCOLESTR pszFileName) {
+        return E_NOTIMPL;
+    }
+
+    STDMETHODIMP GetClassID(CLSID *pClassID) {
+        *pClassID = CLSID_PyShellExt;
+        return S_OK;
+    }
+};
+
+CoCreatableClass(PyShellExt);
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, _COM_Outptr_ void** ppv) {
+    return Module<InProc>::GetModule().GetClassObject(rclsid, riid, ppv);
+}
+
+STDAPI DllCanUnloadNow() {
+    return Module<InProc>::GetModule().Terminate() ? S_OK : S_FALSE;
+}
+
+STDAPI DllRegisterServer() {
+    LONG res;
+    SECURITY_ATTRIBUTES secattr = { sizeof(SECURITY_ATTRIBUTES), NULL, FALSE };
+    LPSECURITY_ATTRIBUTES psecattr = NULL;
+    HKEY key, ipsKey;
+    WCHAR modname[MAX_PATH];
+    DWORD modname_len;
+
+    OutputDebugString(L"PyShellExt::DllRegisterServer");
+    if (!hModule) {
+        OutputDebugString(L"PyShellExt::DllRegisterServer - module handle was not set");
+        return SELFREG_E_CLASS;
+    }
+    modname_len = GetModuleFileName(hModule, modname, MAX_PATH);
+    if (modname_len == 0 ||
+        (modname_len == MAX_PATH && GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
+        OutputDebugString(L"PyShellExt::DllRegisterServer - failed to get module file name");
+        return SELFREG_E_CLASS;
+    }
+
+    DWORD disp;
+    res = RegCreateKeyEx(HKEY_LOCAL_MACHINE, CLASS_SUBKEY, 0, NULL, 0,
+        KEY_ALL_ACCESS, psecattr, &key, &disp);
+    if (res == ERROR_ACCESS_DENIED) {
+        OutputDebugString(L"PyShellExt::DllRegisterServer - failed to write per-machine registration. Attempting per-user instead.");
+        res = RegCreateKeyEx(HKEY_CURRENT_USER, CLASS_SUBKEY, 0, NULL, 0,
+            KEY_ALL_ACCESS, psecattr, &key, &disp);
+    }
+    if (res != ERROR_SUCCESS) {
+        OutputDebugString(L"PyShellExt::DllRegisterServer - failed to create class key");
+        return SELFREG_E_CLASS;
+    }
+
+    res = RegCreateKeyEx(key, L"InProcServer32", 0, NULL, 0,
+        KEY_ALL_ACCESS, psecattr, &ipsKey, NULL);
+    if (res != ERROR_SUCCESS) {
+        RegCloseKey(key);
+        OutputDebugString(L"PyShellExt::DllRegisterServer - failed to create InProcServer32 key");
+        return SELFREG_E_CLASS;
+    }
+
+    res = RegSetValueEx(ipsKey, NULL, 0,
+        REG_SZ, (LPBYTE)modname, modname_len * sizeof(modname[0]));
+
+    if (res != ERROR_SUCCESS) {
+        RegCloseKey(ipsKey);
+        RegCloseKey(key);
+        OutputDebugString(L"PyShellExt::DllRegisterServer - failed to set server path");
+        return SELFREG_E_CLASS;
+    }
+
+    res = RegSetValueEx(ipsKey, L"ThreadingModel", 0,
+        REG_SZ, (LPBYTE)(L"Apartment"), sizeof(L"Apartment"));
+
+    RegCloseKey(ipsKey);
+    RegCloseKey(key);
+    if (res != ERROR_SUCCESS) {
+        OutputDebugString(L"PyShellExt::DllRegisterServer - failed to set threading model");
+        return SELFREG_E_CLASS;
+    }
+
+    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
+
+    OutputDebugString(L"PyShellExt::DllRegisterServer - S_OK");
+    return S_OK;
+}
+
+STDAPI DllUnregisterServer() {
+    LONG res_lm, res_cu;
+
+    res_lm = RegDeleteTree(HKEY_LOCAL_MACHINE, CLASS_SUBKEY);
+    if (res_lm != ERROR_SUCCESS && res_lm != ERROR_FILE_NOT_FOUND) {
+        OutputDebugString(L"PyShellExt::DllUnregisterServer - failed to delete per-machine registration");
+        return SELFREG_E_CLASS;
+    }
+
+    res_cu = RegDeleteTree(HKEY_CURRENT_USER, CLASS_SUBKEY);
+    if (res_cu != ERROR_SUCCESS && res_cu != ERROR_FILE_NOT_FOUND) {
+        OutputDebugString(L"PyShellExt::DllUnregisterServer - failed to delete per-user registration");
+        return SELFREG_E_CLASS;
+    }
+
+    if (res_lm == ERROR_FILE_NOT_FOUND && res_cu == ERROR_FILE_NOT_FOUND) {
+        OutputDebugString(L"PyShellExt::DllUnregisterServer - extension was not registered");
+        return SELFREG_E_CLASS;
+    }
+
+    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
+
+    OutputDebugString(L"PyShellExt::DllUnregisterServer - S_OK");
+    return S_OK;
+}
+
+STDAPI_(BOOL) DllMain(_In_opt_ HINSTANCE hinst, DWORD reason, _In_opt_ void*) {
+    if (reason == DLL_PROCESS_ATTACH) {
+        hModule = hinst;
+
+        cfDropDescription = RegisterClipboardFormat(CFSTR_DROPDESCRIPTION);
+        if (!cfDropDescription) {
+            OutputDebugString(L"PyShellExt::DllMain - failed to get CFSTR_DROPDESCRIPTION format");
+        }
+        cfDragWindow = RegisterClipboardFormat(L"DragWindow");
+        if (!cfDragWindow) {
+            OutputDebugString(L"PyShellExt::DllMain - failed to get DragWindow format");
+        }
+
+        DisableThreadLibraryCalls(hinst);
+    }
+    return TRUE;
+}
\ No newline at end of file
diff --git a/PC/pyshellext.def b/PC/pyshellext.def
new file mode 100644
--- /dev/null
+++ b/PC/pyshellext.def
@@ -0,0 +1,6 @@
+LIBRARY	"pyshellext"
+EXPORTS
+  DllRegisterServer PRIVATE
+  DllUnregisterServer PRIVATE
+  DllGetClassObject PRIVATE
+  DllCanUnloadNow PRIVATE
diff --git a/PC/pyshellext.idl b/PC/pyshellext.idl
new file mode 100644
--- /dev/null
+++ b/PC/pyshellext.idl
@@ -0,0 +1,12 @@
+import "ocidl.idl";
+
+[uuid(44039A76-3BDD-41C1-A31B-71C00202CE81), version(1.0)]
+library PyShellExtLib
+{
+    [uuid(BEA218D2-6950-497B-9434-61683EC065FE), version(1.0)]
+    coclass PyShellExt
+    {
+        [default] interface IDropTarget;
+        interface IPersistFile;
+    }
+};
\ No newline at end of file
diff --git a/PC/pyshellext.rc b/PC/pyshellext.rc
new file mode 100644
--- /dev/null
+++ b/PC/pyshellext.rc
@@ -0,0 +1,46 @@
+#include <windows.h>
+
+#include "python_ver_rc.h"
+
+// Include the manifest file that indicates we support all
+// current versions of Windows.
+#include <winuser.h>
+1 RT_MANIFEST "python.manifest"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION PYVERSION64
+ PRODUCTVERSION PYVERSION64
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "000004b0"
+        BEGIN
+            VALUE "CompanyName", PYTHON_COMPANY "\0"
+            VALUE "FileDescription", "Python\0"
+            VALUE "FileVersion", PYTHON_VERSION
+            VALUE "InternalName", "Python Launcher Shell Extension\0"
+            VALUE "LegalCopyright", PYTHON_COPYRIGHT "\0"
+            VALUE "OriginalFilename", "pyshellext" PYTHON_DEBUG_EXT ".dll\0"
+            VALUE "ProductName", "Python\0"
+            VALUE "ProductVersion", PYTHON_VERSION
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x0, 1200
+    END
+END
\ No newline at end of file
diff --git a/PC/pyshellext_d.def b/PC/pyshellext_d.def
new file mode 100644
--- /dev/null
+++ b/PC/pyshellext_d.def
@@ -0,0 +1,6 @@
+LIBRARY	"pyshellext_d"
+EXPORTS
+  DllRegisterServer PRIVATE
+  DllUnregisterServer PRIVATE
+  DllGetClassObject PRIVATE
+  DllCanUnloadNow PRIVATE
diff --git a/PCbuild/pcbuild.proj b/PCbuild/pcbuild.proj
--- a/PCbuild/pcbuild.proj
+++ b/PCbuild/pcbuild.proj
@@ -46,6 +46,8 @@
     <Projects Include="python3dll.vcxproj" />
     <!-- py[w].exe -->
     <Projects Include="pylauncher.vcxproj;pywlauncher.vcxproj" />
+    <!-- pyshellext.dll -->
+    <Projects Include="pyshellext.vcxproj" />
     <!-- _freeze_importlib -->
     <Projects Include="_freeze_importlib.vcxproj" />
     <!-- Extension modules -->
diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln
--- a/PCbuild/pcbuild.sln
+++ b/PCbuild/pcbuild.sln
@@ -1,6 +1,6 @@
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 14
-VisualStudioVersion = 14.0.22823.1
+VisualStudioVersion = 14.0.25123.0
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{553EC33E-9816-4996-A660-5D6186A0B0B3}"
 	ProjectSection(SolutionItems) = preProject
@@ -63,8 +63,14 @@
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_testbuffer", "_testbuffer.vcxproj", "{A2697BD3-28C1-4AEC-9106-8B748639FD16}"
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pylauncher", "pylauncher.vcxproj", "{7B2727B5-5A3F-40EE-A866-43A13CD31446}"
+	ProjectSection(ProjectDependencies) = postProject
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782} = {0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}
+	EndProjectSection
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pywlauncher", "pywlauncher.vcxproj", "{1D4B18D3-7C12-4ECB-9179-8531FF876CE6}"
+	ProjectSection(ProjectDependencies) = postProject
+		{7B2727B5-5A3F-40EE-A866-43A13CD31446} = {7B2727B5-5A3F-40EE-A866-43A13CD31446}
+	EndProjectSection
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_freeze_importlib", "_freeze_importlib.vcxproj", "{19C0C13F-47CA-4432-AFF3-799A296A4DDC}"
 EndProject
@@ -84,6 +90,8 @@
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ssleay", "ssleay.vcxproj", "{10615B24-73BF-4EFA-93AA-236916321317}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pyshellext", "pyshellext.vcxproj", "{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -684,6 +692,22 @@
 		{10615B24-73BF-4EFA-93AA-236916321317}.Release|Win32.Build.0 = Release|Win32
 		{10615B24-73BF-4EFA-93AA-236916321317}.Release|x64.ActiveCfg = Release|x64
 		{10615B24-73BF-4EFA-93AA-236916321317}.Release|x64.Build.0 = Release|x64
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Debug|Win32.ActiveCfg = Debug|Win32
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Debug|Win32.Build.0 = Debug|Win32
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Debug|x64.ActiveCfg = Debug|x64
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Debug|x64.Build.0 = Debug|x64
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.PGInstrument|Win32.Build.0 = PGInstrument|Win32
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.PGInstrument|x64.ActiveCfg = PGInstrument|x64
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.PGInstrument|x64.Build.0 = PGInstrument|x64
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.PGUpdate|Win32.Build.0 = PGUpdate|Win32
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.PGUpdate|x64.ActiveCfg = PGUpdate|x64
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.PGUpdate|x64.Build.0 = PGUpdate|x64
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|Win32.ActiveCfg = Release|Win32
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|Win32.Build.0 = Release|Win32
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|x64.ActiveCfg = Release|x64
+		{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}.Release|x64.Build.0 = Release|x64
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/PCbuild/pyproject.props b/PCbuild/pyproject.props
--- a/PCbuild/pyproject.props
+++ b/PCbuild/pyproject.props
@@ -83,8 +83,9 @@
       <SuppressStartupBanner>true</SuppressStartupBanner>
       <TargetEnvironment>Win32</TargetEnvironment>
       <TargetEnvironment Condition="'$(Platform)' == 'x64'">X64</TargetEnvironment>
-      <TypeLibraryName>$(OutDir)%(Filename).tlb</TypeLibraryName>
-      <HeaderFileName>$(IntDir)%(Filename)_h.h</HeaderFileName>
+      <OutputDirectory>$(IntDir)</OutputDirectory>
+      <InterfaceIdentifierFileName>$(MSBuildProjectName)_i.c</InterfaceIdentifierFileName>
+      <ProxyFileName>$(MSBuildProjectName)_p.c</ProxyFileName>
     </Midl>
   </ItemDefinitionGroup>
 
diff --git a/PCbuild/pyshellext.vcxproj b/PCbuild/pyshellext.vcxproj
new file mode 100644
--- /dev/null
+++ b/PCbuild/pyshellext.vcxproj
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="PGInstrument|Win32">
+      <Configuration>PGInstrument</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="PGInstrument|x64">
+      <Configuration>PGInstrument</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="PGUpdate|Win32">
+      <Configuration>PGUpdate</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="PGUpdate|x64">
+      <Configuration>PGUpdate</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{0F6EE4A4-C75F-4578-B4B3-2D64F4B9B782}</ProjectGuid>
+    <RootNamespace>pyshellext</RootNamespace>
+    <TargetName>pyshellext</TargetName>
+    <SupportPGO>false</SupportPGO>
+  </PropertyGroup>
+  <Import Project="python.props" />
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <PropertyGroup>
+    <MakeVersionInfoBeforeTarget>ClCompile</MakeVersionInfoBeforeTarget>
+  </PropertyGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="pyproject.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>version.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <SubSystem>Console</SubSystem>
+      <ModuleDefinitionFile>..\PC\pyshellext$(PyDebugExt).def</ModuleDefinitionFile>
+    </Link>
+    <Midl>
+      <CompileInterface>true</CompileInterface>
+    </Midl>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\PC\pyshellext.cpp" />
+    <Midl Include="..\PC\pyshellext.idl" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\PC\pyshellext.def" />
+    <None Include="..\PC\pyshellext_d.def" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="..\PC\pyshellext.rc" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/PCbuild/pyshellext.vcxproj.filters b/PCbuild/pyshellext.vcxproj.filters
new file mode 100644
--- /dev/null
+++ b/PCbuild/pyshellext.vcxproj.filters
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\PC\pyshellext.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <Midl Include="..\PC\pyshellext.idl">
+      <Filter>Source Files</Filter>
+    </Midl>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="..\PC\pyshellext.rc">
+      <Filter>Resource Files</Filter>
+    </ResourceCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\PC\pyshellext.def">
+      <Filter>Source Files</Filter>
+    </None>
+    <None Include="..\PC\pyshellext_d.def">
+      <Filter>Source Files</Filter>
+    </None>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/Tools/msi/launcher/launcher.wixproj b/Tools/msi/launcher/launcher.wixproj
--- a/Tools/msi/launcher/launcher.wixproj
+++ b/Tools/msi/launcher/launcher.wixproj
@@ -7,6 +7,7 @@
         <OutputType>Package</OutputType>
         <DefineConstants>UpgradeCode=1B68A0EC-4DD3-5134-840E-73854B0863F1;$(DefineConstants)</DefineConstants>
         <IgnoreCommonWxlTemplates>true</IgnoreCommonWxlTemplates>
+        <SuppressICEs>ICE80</SuppressICEs>
     </PropertyGroup>
     <Import Project="..\msi.props" />
     <ItemGroup>
@@ -18,5 +19,18 @@
         <EmbeddedResource Include="*.wxl" />
     </ItemGroup>
     
+    <Target Name="_EnsurePyEx86" Condition="!Exists('$(BuildPath32)py.exe')" BeforeTargets="PrepareForBuild">
+        <MSBuild Projects="$(PySourcePath)PCBuild\pylauncher.vcxproj" Properties="Platform=Win32" />
+    </Target>
+    <Target Name="_EnsurePywEx86" Condition="!Exists('$(BuildPath32)pyw.exe')" BeforeTargets="PrepareForBuild">
+        <MSBuild Projects="$(PySourcePath)PCBuild\pywlauncher.vcxproj" Properties="Platform=Win32" />
+    </Target>
+    <Target Name="_EnsurePyShellExt86" Condition="!Exists('$(BuildPath32)pyshellext.dll')" BeforeTargets="PrepareForBuild">
+        <MSBuild Projects="$(PySourcePath)PCBuild\pyshellext.vcxproj" Properties="Platform=Win32" />
+    </Target>
+    <Target Name="_EnsurePyShellExt64" Condition="!Exists('$(BuildPath64)pyshellext.dll')" BeforeTargets="PrepareForBuild">
+        <MSBuild Projects="$(PySourcePath)PCBuild\pyshellext.vcxproj" Properties="Platform=x64" />
+    </Target>
+    
     <Import Project="..\msi.targets" />
 </Project>
\ No newline at end of file
diff --git a/Tools/msi/launcher/launcher.wxs b/Tools/msi/launcher/launcher.wxs
--- a/Tools/msi/launcher/launcher.wxs
+++ b/Tools/msi/launcher/launcher.wxs
@@ -28,7 +28,7 @@
 
             <RemoveExistingProducts After="InstallValidate">UPGRADE or REMOVE_350_LAUNCHER</RemoveExistingProducts>
         </InstallExecuteSequence>
-        
+
         <!-- Python 3.5.0 shipped with a different UpgradeCode -->
         <Upgrade Id="A71530B9-E89D-53DB-9C2D-C6D7551876D8">
             <UpgradeVersion Minimum="0.0.0.0" Property="REMOVE_350_LAUNCHER" />
diff --git a/Tools/msi/launcher/launcher_files.wxs b/Tools/msi/launcher/launcher_files.wxs
--- a/Tools/msi/launcher/launcher_files.wxs
+++ b/Tools/msi/launcher/launcher_files.wxs
@@ -20,6 +20,19 @@
                 <Condition>ALLUSERS=1</Condition>
                 <RegistryValue KeyPath="yes" Root="HKMU" Key="Software\Python\PyLauncher" Name="InstallDir" Value="[LauncherInstallDirectory]" Type="string" />
             </Component>
+
+            <Component Id="pyshellext_amd64.dll" Directory="LauncherInstallDirectory" Guid="{E7411EFD-F1DD-40EB-B0C7-4BA02BF3E75F}" Win64="yes">
+                <Condition>VersionNT64</Condition>
+                <File Id="pyshellext_amd64.dll" Name="pyshellext.amd64.dll" Source="!(bindpath.Build64)\pyshellext.dll">
+                    <Class Id="{BEA218D2-6950-497B-9434-61683EC065FE}" Advertise="no" Context="InprocServer32" ThreadingModel="apartment" />
+                </File>
+            </Component>
+            <Component Id="pyshellext_win32.dll" Directory="LauncherInstallDirectory" Guid="{C5936696-9A5A-45A0-A830-D172C3329282}">
+                <Condition>NOT VersionNT64</Condition>
+                <File Id="pyshellext_win32.dll" Name="pyshellext.win32.dll" Source="!(bindpath.Build32)\pyshellext.dll">
+                    <Class Id="{BEA218D2-6950-497B-9434-61683EC065FE}" Advertise="no" Context="InprocServer32" ThreadingModel="apartment" />
+                </File>
+            </Component>
         </ComponentGroup>
     </Fragment>
 </Wix>
diff --git a/Tools/msi/launcher/launcher_reg.wxs b/Tools/msi/launcher/launcher_reg.wxs
--- a/Tools/msi/launcher/launcher_reg.wxs
+++ b/Tools/msi/launcher/launcher_reg.wxs
@@ -10,14 +10,14 @@
                         <Verb Id="open" TargetFile="py.exe" Argument=""%L" %*" />
                     </Extension>
                 </ProgId>
-                <RegistryValue Root="HKCR" Key="Python.File\shellex\DropHandler" Value="{60254CA5-953B-11CF-8C96-00AA00B8708C}" Type="string" />
+                <RegistryValue Root="HKCR" Key="Python.File\shellex\DropHandler" Value="{BEA218D2-6950-497B-9434-61683EC065FE}" Type="string" />
                 
                 <ProgId Id="Python.NoConFile" Description="!(loc.PythonNoConFileDescription)" Advertise="no" Icon="py.exe" IconIndex="1">
                     <Extension Id="pyw" ContentType="text/plain">
                         <Verb Id="open" TargetFile="pyw.exe" Argument=""%L" %*" />
                     </Extension>
                 </ProgId>
-                <RegistryValue Root="HKCR" Key="Python.NoConFile\shellex\DropHandler" Value="{60254CA5-953B-11CF-8C96-00AA00B8708C}" Type="string" />
+                <RegistryValue Root="HKCR" Key="Python.NoConFile\shellex\DropHandler" Value="{BEA218D2-6950-497B-9434-61683EC065FE}" Type="string" />
                 
                 <ProgId Id="Python.CompiledFile" Description="!(loc.PythonCompiledFileDescription)" Advertise="no" Icon="py.exe" IconIndex="2">
                     <Extension Id="pyc">
@@ -25,21 +25,21 @@
                     </Extension>
                     <Extension Id="pyo" />
                 </ProgId>
-                <RegistryValue Root="HKCR" Key="Python.CompiledFile\shellex\DropHandler" Value="{60254CA5-953B-11CF-8C96-00AA00B8708C}" Type="string" />
+                <RegistryValue Root="HKCR" Key="Python.CompiledFile\shellex\DropHandler" Value="{BEA218D2-6950-497B-9434-61683EC065FE}" Type="string" />
 
                 <ProgId Id="Python.ArchiveFile" Description="!(loc.PythonArchiveFileDescription)" Advertise="no" Icon="py.exe" IconIndex="1">
                     <Extension Id="pyz" ContentType="application/x-zip-compressed">
                         <Verb Id="open" TargetFile="py.exe" Argument=""%L" %*" />
                     </Extension>
                 </ProgId>
-                <RegistryValue Root="HKCR" Key="Python.ArchiveFile\shellex\DropHandler" Value="{60254CA5-953B-11CF-8C96-00AA00B8708C}" Type="string" />
+                <RegistryValue Root="HKCR" Key="Python.ArchiveFile\shellex\DropHandler" Value="{BEA218D2-6950-497B-9434-61683EC065FE}" Type="string" />
                 
                 <ProgId Id="Python.NoConArchiveFile" Description="!(loc.PythonNoConArchiveFileDescription)" Advertise="no" Icon="py.exe" IconIndex="1">
                     <Extension Id="pyzw" ContentType="application/x-zip-compressed">
                         <Verb Id="open" TargetFile="pyw.exe" Argument=""%L" %*" />
                     </Extension>
                 </ProgId>
-                <RegistryValue Root="HKCR" Key="Python.NoConArchiveFile\shellex\DropHandler" Value="{60254CA5-953B-11CF-8C96-00AA00B8708C}" Type="string" />
+                <RegistryValue Root="HKCR" Key="Python.NoConArchiveFile\shellex\DropHandler" Value="{BEA218D2-6950-497B-9434-61683EC065FE}" Type="string" />
             </Component>
         </ComponentGroup>
     </Fragment>

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list