Skip to content

Commit 29613ca

Browse files
authored
Adds shell extension DLLs to MSI installer (#78)
1 parent 9c81127 commit 29613ca

File tree

5 files changed

+132
-33
lines changed

5 files changed

+132
-33
lines changed

_msbuild.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,31 @@ def launcher_exe(name, platform, windowed=False):
148148
)
149149

150150

151+
def pyshellext(ext='.exe', **props):
152+
link_opts = ItemDefinition(
153+
'Link',
154+
AdditionalDependencies=Prepend('RuntimeObject.lib;'),
155+
)
156+
if ext != '.exe':
157+
link_opts.options['ModuleDefinitionFile'] = '$(SourceRootDir)src\\pyshellext\\pyshellext.def'
158+
159+
160+
return CProject(f"pyshellext{ext.rpartition('.')[0]}",
161+
VersionInfo(
162+
FileDescription='Python shell extension',
163+
OriginalFilename=f'pyshellext{ext}',
164+
),
165+
ItemDefinition('ClCompile', LanguageStandard='stdcpp20'),
166+
link_opts,
167+
Manifest('default.manifest'),
168+
CSourceFile('shellext.cpp'),
169+
ResourceFile('pyshellext.rc'),
170+
source='src/pyshellext',
171+
StaticLibcppLinkage=True,
172+
**props,
173+
)
174+
175+
151176
PACKAGE = Package('python-manager',
152177
PyprojectTomlFile('pyproject.toml'),
153178
# MSIX manifest
@@ -206,23 +231,9 @@ def launcher_exe(name, platform, windowed=False):
206231
main_exe("python3"),
207232
mainw_exe("pythonw3"),
208233

209-
CProject("pyshellext",
210-
VersionInfo(
211-
FileDescription="Python shell extension",
212-
OriginalFilename="pyshellext.exe",
213-
),
214-
Property('StaticLibcppLinkage', 'true'),
215-
ItemDefinition('ClCompile', LanguageStandard='stdcpp20'),
216-
ItemDefinition('Link',
217-
AdditionalDependencies=Prepend("RuntimeObject.lib;"),
218-
SubSystem='WINDOWS',
219-
),
220-
Manifest('default.manifest'),
221-
CSourceFile('shellext.cpp'),
222-
ResourceFile('pyshellext.rc'),
223-
source='src/pyshellext',
224-
ConfigurationType='Application',
225-
),
234+
pyshellext(".exe", ConfigurationType="Application"),
235+
pyshellext("-64.dll", Platform="x64"),
236+
pyshellext("-arm64.dll", Platform="ARM64"),
226237
)
227238

228239

make-msix.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,19 @@
5353
"/mf", "appx"])
5454

5555
# Clean up non-shipping files from LAYOUT
56+
preserved = [
57+
*LAYOUT.glob("pyshellext*.dll"),
58+
]
59+
60+
for f in preserved:
61+
print("Preserving", f, "as", TEMP / f.name)
62+
copyfile(f, TEMP / f.name)
63+
5664
unlink(
5765
*LAYOUT.rglob("*.pdb"),
5866
*LAYOUT.rglob("*.pyc"),
5967
*LAYOUT.rglob("__pycache__"),
68+
*preserved,
6069
)
6170

6271
# Package into DIST
@@ -122,3 +131,9 @@ def patch_appx(source):
122131
with zipfile.ZipFile(DIST_MSIXUPLOAD, "w") as zf:
123132
zf.write(DIST_STORE_MSIX, arcname=DIST_STORE_MSIX.name)
124133
zf.write(DIST_APPXSYM, arcname=DIST_APPXSYM.name)
134+
135+
136+
for f in preserved:
137+
print("Restoring", f, "from", TEMP / f.name)
138+
copyfile(TEMP / f.name, f)
139+
unlink(TEMP / f.name)

src/pymanager/msi.wxs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@
2727
<ComponentRef Id="ProductComponent" />
2828
<ComponentRef Id="RuntimeComponent" />
2929
<ComponentRef Id="TemplatesComponent" />
30+
<ComponentRef Id="PyShellExt64Component" />
31+
<ComponentRef Id="PyShellExtARM64Component" />
3032
</Feature>
3133

3234
<util:BroadcastEnvironmentChange />
35+
<util:QueryNativeMachine />
3336

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

47-
<!--
48-
When installed via MSIX, this extension must be out of proc.
49-
Apparently, when installed via MSI, it must be in proc. Which means we'd
50-
need to compile DLLs for each platform just for the MSI, as well as the
51-
EXE that we use for regular installs.
52-
Right now, not worth it.
53-
54-
<File Source="pyshellext.exe">
55-
<Class Id="{C7E29CB0-9691-4DE8-B72B-6719DDC0B4A1}" Advertise="no" Context="LocalServer32" />
56-
</File>
57-
-->
58-
5950
<File Source="version.txt" />
6051
<Environment Id="PATH" Action="set" Name="PATH" Part="last" System="yes" Value="[INSTALLFOLDER]" />
6152

@@ -115,5 +106,21 @@
115106
<File Source="templates\launcherw-32.exe" Name="launcherw-32.exe" />
116107
</Component>
117108

109+
<!-- IMAGE_FILE_MACHINE_AMD64 = 0x8664 = 34404 -->
110+
<Component Id="PyShellExt64Component" Directory="INSTALLFOLDER" Guid="D1946F6C-FB98-4395-BE63-D714A221A590"
111+
Condition="WIX_NATIVE_MACHINE = 34404">
112+
<File Source="pyshellext-64.dll">
113+
<Class Id="{C7E29CB0-9691-4DE8-B72B-6719DDC0B4A1}" Advertise="no" Context="InprocServer32" />
114+
</File>
115+
</Component>
116+
117+
<!-- IMAGE_FILE_MACHINE_ARM64 = 0xAA64 = 43620 -->
118+
<Component Id="PyShellExtARM64Component" Directory="INSTALLFOLDER" Guid="D1946F6C-FB98-4395-BE63-D714A221A591"
119+
Condition="WIX_NATIVE_MACHINE = 43620">
120+
<File Source="pyshellext-arm64.dll">
121+
<Class Id="{C7E29CB0-9691-4DE8-B72B-6719DDC0B4A1}" Advertise="no" Context="InprocServer32" />
122+
</File>
123+
</Component>
124+
118125
</Package>
119126
</Wix>

src/pyshellext/pyshellext.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
EXPORTS
2+
DllGetClassObject PRIVATE
3+
DllCanUnloadNow PRIVATE

src/pyshellext/shellext.cpp

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ HRESULT ReadAllIdleInstalls(std::vector<IdleData> &idles, HKEY hive, LPCWSTR roo
167167
}
168168

169169
class DECLSPEC_UUID(CLSID_LAUNCH_COMMAND) LaunchCommand
170-
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand>
170+
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand, IObjectWithSite>
171171
{
172172
std::wstring title;
173173
std::wstring exe;
@@ -269,6 +269,26 @@ class DECLSPEC_UUID(CLSID_LAUNCH_COMMAND) LaunchCommand
269269
*ppEnum = NULL;
270270
return E_NOTIMPL;
271271
}
272+
273+
// IObjectWithSite
274+
private:
275+
ComPtr<IUnknown> _site;
276+
277+
public:
278+
IFACEMETHODIMP GetSite(REFIID riid, void **ppvSite)
279+
{
280+
if (_site) {
281+
return _site->QueryInterface(riid, ppvSite);
282+
}
283+
*ppvSite = NULL;
284+
return E_FAIL;
285+
}
286+
287+
IFACEMETHODIMP SetSite(IUnknown *pSite)
288+
{
289+
_site = pSite;
290+
return S_OK;
291+
}
272292
};
273293

274294

@@ -316,7 +336,7 @@ class DECLSPEC_UUID(CLSID_COMMAND_ENUMERATOR) CommandEnumerator
316336

317337

318338
class DECLSPEC_UUID(CLSID_IDLE_COMMAND) IdleCommand
319-
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand>
339+
: public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand, IObjectWithSite>
320340
{
321341
std::vector<IdleData> idles;
322342
std::wstring iconPath;
@@ -432,12 +452,33 @@ class DECLSPEC_UUID(CLSID_IDLE_COMMAND) IdleCommand
432452
).Detach();
433453
return S_OK;
434454
}
455+
456+
// IObjectWithSite
457+
private:
458+
ComPtr<IUnknown> _site;
459+
460+
public:
461+
IFACEMETHODIMP GetSite(REFIID riid, void **ppvSite)
462+
{
463+
if (_site) {
464+
return _site->QueryInterface(riid, ppvSite);
465+
}
466+
*ppvSite = NULL;
467+
return E_FAIL;
468+
}
469+
470+
IFACEMETHODIMP SetSite(IUnknown *pSite)
471+
{
472+
_site = pSite;
473+
return S_OK;
474+
}
435475
};
436476

437477

438478
CoCreatableClass(IdleCommand);
439479

440480
#ifdef PYSHELLEXT_TEST
481+
441482
IExplorerCommand *MakeLaunchCommand(std::wstring title, std::wstring exe, std::wstring idle)
442483
{
443484
IdleData data = { .title = title, .exe = exe, .idle = idle };
@@ -449,10 +490,31 @@ IExplorerCommand *MakeIdleCommand(HKEY hive, LPCWSTR root)
449490
{
450491
return Make<IdleCommand>(hive, root).Detach();
451492
}
452-
#endif
453493

494+
#elif defined(_WINDLL)
495+
496+
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, _COM_Outptr_ void** ppv)
497+
{
498+
return Module<InProc>::GetModule().GetClassObject(rclsid, riid, ppv);
499+
}
500+
501+
502+
STDAPI DllCanUnloadNow()
503+
{
504+
return Module<InProc>::GetModule().Terminate() ? S_OK : S_FALSE;
505+
}
506+
507+
STDAPI_(BOOL) DllMain(_In_opt_ HINSTANCE hinst, DWORD reason, _In_opt_ void*)
508+
{
509+
if (reason == DLL_PROCESS_ATTACH) {
510+
hModule = hinst;
511+
DisableThreadLibraryCalls(hinst);
512+
}
513+
return TRUE;
514+
}
515+
516+
#else
454517

455-
#ifndef PYSHELLEXT_TEST
456518
class OutOfProcModule : public Module<OutOfProc, OutOfProcModule>
457519
{ };
458520

@@ -475,4 +537,5 @@ int WINAPI wWinMain(
475537
CoUninitialize();
476538
return 0;
477539
}
540+
478541
#endif

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy