Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 28 additions & 17 deletions _msbuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,31 @@ def launcher_exe(name, platform, windowed=False):
)


def pyshellext(ext='.exe', **props):
link_opts = ItemDefinition(
'Link',
AdditionalDependencies=Prepend('RuntimeObject.lib;'),
)
if ext != '.exe':
link_opts.options['ModuleDefinitionFile'] = '$(SourceRootDir)src\\pyshellext\\pyshellext.def'


return CProject(f"pyshellext{ext.rpartition('.')[0]}",
VersionInfo(
FileDescription='Python shell extension',
OriginalFilename=f'pyshellext{ext}',
),
ItemDefinition('ClCompile', LanguageStandard='stdcpp20'),
link_opts,
Manifest('default.manifest'),
CSourceFile('shellext.cpp'),
ResourceFile('pyshellext.rc'),
source='src/pyshellext',
StaticLibcppLinkage=True,
**props,
)


PACKAGE = Package('python-manager',
PyprojectTomlFile('pyproject.toml'),
# MSIX manifest
Expand Down Expand Up @@ -206,23 +231,9 @@ def launcher_exe(name, platform, windowed=False):
main_exe("python3"),
mainw_exe("pythonw3"),

CProject("pyshellext",
VersionInfo(
FileDescription="Python shell extension",
OriginalFilename="pyshellext.exe",
),
Property('StaticLibcppLinkage', 'true'),
ItemDefinition('ClCompile', LanguageStandard='stdcpp20'),
ItemDefinition('Link',
AdditionalDependencies=Prepend("RuntimeObject.lib;"),
SubSystem='WINDOWS',
),
Manifest('default.manifest'),
CSourceFile('shellext.cpp'),
ResourceFile('pyshellext.rc'),
source='src/pyshellext',
ConfigurationType='Application',
),
pyshellext(".exe", ConfigurationType="Application"),
pyshellext("-64.dll", Platform="x64"),
pyshellext("-arm64.dll", Platform="ARM64"),
)


Expand Down
15 changes: 15 additions & 0 deletions make-msix.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,19 @@
"/mf", "appx"])

# Clean up non-shipping files from LAYOUT
preserved = [
*LAYOUT.glob("pyshellext*.dll"),
]

for f in preserved:
print("Preserving", f, "as", TEMP / f.name)
copyfile(f, TEMP / f.name)

unlink(
*LAYOUT.rglob("*.pdb"),
*LAYOUT.rglob("*.pyc"),
*LAYOUT.rglob("__pycache__"),
*preserved,
)

# Package into DIST
Expand Down Expand Up @@ -122,3 +131,9 @@ def patch_appx(source):
with zipfile.ZipFile(DIST_MSIXUPLOAD, "w") as zf:
zf.write(DIST_STORE_MSIX, arcname=DIST_STORE_MSIX.name)
zf.write(DIST_APPXSYM, arcname=DIST_APPXSYM.name)


for f in preserved:
print("Restoring", f, "from", TEMP / f.name)
copyfile(TEMP / f.name, f)
unlink(TEMP / f.name)
31 changes: 19 additions & 12 deletions src/pymanager/msi.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@
<ComponentRef Id="ProductComponent" />
<ComponentRef Id="RuntimeComponent" />
<ComponentRef Id="TemplatesComponent" />
<ComponentRef Id="PyShellExt64Component" />
<ComponentRef Id="PyShellExtARM64Component" />
</Feature>

<util:BroadcastEnvironmentChange />
<util:QueryNativeMachine />

<Component Id="ProductComponent" Directory="INSTALLFOLDER" Guid="8BEC1259-B220-499B-9656-DC59B7F5BE24">
<File KeyPath="yes" Source="py-manager.exe" Name="python.exe" Id="python.exe" />
Expand All @@ -44,18 +47,6 @@
<File Source="vcruntime140.dll" />
<File Source="vcruntime140_1.dll" />

<!--
When installed via MSIX, this extension must be out of proc.
Apparently, when installed via MSI, it must be in proc. Which means we'd
need to compile DLLs for each platform just for the MSI, as well as the
EXE that we use for regular installs.
Right now, not worth it.

<File Source="pyshellext.exe">
<Class Id="{C7E29CB0-9691-4DE8-B72B-6719DDC0B4A1}" Advertise="no" Context="LocalServer32" />
</File>
-->

<File Source="version.txt" />
<Environment Id="PATH" Action="set" Name="PATH" Part="last" System="yes" Value="[INSTALLFOLDER]" />

Expand Down Expand Up @@ -115,5 +106,21 @@
<File Source="templates\launcherw-32.exe" Name="launcherw-32.exe" />
</Component>

<!-- IMAGE_FILE_MACHINE_AMD64 = 0x8664 = 34404 -->
<Component Id="PyShellExt64Component" Directory="INSTALLFOLDER" Guid="D1946F6C-FB98-4395-BE63-D714A221A590"
Condition="WIX_NATIVE_MACHINE = 34404">
<File Source="pyshellext-64.dll">
<Class Id="{C7E29CB0-9691-4DE8-B72B-6719DDC0B4A1}" Advertise="no" Context="InprocServer32" />
</File>
</Component>

<!-- IMAGE_FILE_MACHINE_ARM64 = 0xAA64 = 43620 -->
<Component Id="PyShellExtARM64Component" Directory="INSTALLFOLDER" Guid="D1946F6C-FB98-4395-BE63-D714A221A591"
Condition="WIX_NATIVE_MACHINE = 43620">
<File Source="pyshellext-arm64.dll">
<Class Id="{C7E29CB0-9691-4DE8-B72B-6719DDC0B4A1}" Advertise="no" Context="InprocServer32" />
</File>
</Component>

</Package>
</Wix>
3 changes: 3 additions & 0 deletions src/pyshellext/pyshellext.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
71 changes: 67 additions & 4 deletions src/pyshellext/shellext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ HRESULT ReadAllIdleInstalls(std::vector<IdleData> &idles, HKEY hive, LPCWSTR roo
}

class DECLSPEC_UUID(CLSID_LAUNCH_COMMAND) LaunchCommand
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand>
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand, IObjectWithSite>
{
std::wstring title;
std::wstring exe;
Expand Down Expand Up @@ -269,6 +269,26 @@ class DECLSPEC_UUID(CLSID_LAUNCH_COMMAND) LaunchCommand
*ppEnum = NULL;
return E_NOTIMPL;
}

// IObjectWithSite
private:
ComPtr<IUnknown> _site;

public:
IFACEMETHODIMP GetSite(REFIID riid, void **ppvSite)
{
if (_site) {
return _site->QueryInterface(riid, ppvSite);
}
*ppvSite = NULL;
return E_FAIL;
}

IFACEMETHODIMP SetSite(IUnknown *pSite)
{
_site = pSite;
return S_OK;
}
};


Expand Down Expand Up @@ -316,7 +336,7 @@ class DECLSPEC_UUID(CLSID_COMMAND_ENUMERATOR) CommandEnumerator


class DECLSPEC_UUID(CLSID_IDLE_COMMAND) IdleCommand
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand>
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand, IObjectWithSite>
{
std::vector<IdleData> idles;
std::wstring iconPath;
Expand Down Expand Up @@ -432,12 +452,33 @@ class DECLSPEC_UUID(CLSID_IDLE_COMMAND) IdleCommand
).Detach();
return S_OK;
}

// IObjectWithSite
private:
ComPtr<IUnknown> _site;

public:
IFACEMETHODIMP GetSite(REFIID riid, void **ppvSite)
{
if (_site) {
return _site->QueryInterface(riid, ppvSite);
}
*ppvSite = NULL;
return E_FAIL;
}

IFACEMETHODIMP SetSite(IUnknown *pSite)
{
_site = pSite;
return S_OK;
}
};


CoCreatableClass(IdleCommand);

#ifdef PYSHELLEXT_TEST

IExplorerCommand *MakeLaunchCommand(std::wstring title, std::wstring exe, std::wstring idle)
{
IdleData data = { .title = title, .exe = exe, .idle = idle };
Expand All @@ -449,10 +490,31 @@ IExplorerCommand *MakeIdleCommand(HKEY hive, LPCWSTR root)
{
return Make<IdleCommand>(hive, root).Detach();
}
#endif

#elif defined(_WINDLL)

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_(BOOL) DllMain(_In_opt_ HINSTANCE hinst, DWORD reason, _In_opt_ void*)
{
if (reason == DLL_PROCESS_ATTACH) {
hModule = hinst;
DisableThreadLibraryCalls(hinst);
}
return TRUE;
}

#else

#ifndef PYSHELLEXT_TEST
class OutOfProcModule : public Module<OutOfProc, OutOfProcModule>
{ };

Expand All @@ -475,4 +537,5 @@ int WINAPI wWinMain(
CoUninitialize();
return 0;
}

#endif