diff options
author | Steve Dower <steve.dower@microsoft.com> | 2016-07-16 23:17:33 (GMT) |
---|---|---|
committer | Steve Dower <steve.dower@microsoft.com> | 2016-07-16 23:17:33 (GMT) |
commit | df450d1a18ba668874a2353a3870ba99c4848a75 (patch) | |
tree | 93d41ac47ec98cfe8d8fe1cc5488efe36aef2ed5 | |
parent | 5f804e387ebc70effb61dc124f52acef357471b3 (diff) | |
download | cpython-df450d1a18ba668874a2353a3870ba99c4848a75.zip cpython-df450d1a18ba668874a2353a3870ba99c4848a75.tar.gz cpython-df450d1a18ba668874a2353a3870ba99c4848a75.tar.bz2 |
Issue #27469: Adds a shell extension to the launcher so that drag and drop works correctly.
-rw-r--r-- | Misc/NEWS | 4 | ||||
-rw-r--r-- | PC/pyshellext.cpp | 605 | ||||
-rw-r--r-- | PC/pyshellext.def | 6 | ||||
-rw-r--r-- | PC/pyshellext.idl | 12 | ||||
-rw-r--r-- | PC/pyshellext.rc | 46 | ||||
-rw-r--r-- | PC/pyshellext_d.def | 6 | ||||
-rw-r--r-- | PCbuild/pcbuild.proj | 2 | ||||
-rw-r--r-- | PCbuild/pcbuild.sln | 26 | ||||
-rw-r--r-- | PCbuild/pyproject.props | 5 | ||||
-rw-r--r-- | PCbuild/pyshellext.vcxproj | 87 | ||||
-rw-r--r-- | PCbuild/pyshellext.vcxproj.filters | 40 | ||||
-rw-r--r-- | Tools/msi/launcher/launcher.wixproj | 14 | ||||
-rw-r--r-- | Tools/msi/launcher/launcher.wxs | 2 | ||||
-rw-r--r-- | Tools/msi/launcher/launcher_files.wxs | 13 | ||||
-rw-r--r-- | Tools/msi/launcher/launcher_reg.wxs | 10 |
15 files changed, 868 insertions, 10 deletions
@@ -134,8 +134,10 @@ Tools/Demos Windows ------- -- Issue #27309: Enabled proper Windows styles in python[w].exe manifest. +- Issue #27469: Adds a shell extension to the launcher so that drag and drop + works correctly. +- Issue #27309: Enabled proper Windows styles in python[w].exe manifest. What's New in Python 3.5.2? =========================== diff --git a/PC/pyshellext.cpp b/PC/pyshellext.cpp new file mode 100644 index 0000000..04fe61e --- /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 index 0000000..5424bd1 --- /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 index 0000000..c0a1838 --- /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 index 0000000..e5924a4 --- /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 index 0000000..7d2148b --- /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 index 9b865e8..c320434 100644 --- 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 index 766a870..42a4d2b 100644 --- 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 @@ EndProject 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 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libeay", "libeay.vcxproj", 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 @@ Global {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 index a3a9c2b..05473fd 100644 --- 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 index 0000000..0293935 --- /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 index 0000000..648e499 --- /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 index 67fb025..01a9dcb 100644 --- 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 index 80e838a..ebd875c 100644 --- 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 index 589dee5..5b79d76 100644 --- 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 index bb42255..981961a 100644 --- 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> |