win32more-appsdk 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,160 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/#use-with-ide
110
+ .pdm.toml
111
+
112
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
+ __pypackages__/
114
+
115
+ # Celery stuff
116
+ celerybeat-schedule
117
+ celerybeat.pid
118
+
119
+ # SageMath parsed files
120
+ *.sage.py
121
+
122
+ # Environments
123
+ .env
124
+ .venv
125
+ env/
126
+ venv/
127
+ ENV/
128
+ env.bak/
129
+ venv.bak/
130
+
131
+ # Spyder project settings
132
+ .spyderproject
133
+ .spyproject
134
+
135
+ # Rope project settings
136
+ .ropeproject
137
+
138
+ # mkdocs documentation
139
+ /site
140
+
141
+ # mypy
142
+ .mypy_cache/
143
+ .dmypy.json
144
+ dmypy.json
145
+
146
+ # Pyre type checker
147
+ .pyre/
148
+
149
+ # pytype static type analyzer
150
+ .pytype/
151
+
152
+ # Cython debug symbols
153
+ cython_debug/
154
+
155
+ # PyCharm
156
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
159
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
+ #.idea/
@@ -0,0 +1,4 @@
1
+ Metadata-Version: 2.4
2
+ Name: win32more-appsdk
3
+ Version: 0.7.0
4
+ Requires-Dist: win32more-core==0.7.*
@@ -0,0 +1,11 @@
1
+ [project]
2
+ name = "win32more-appsdk"
3
+ version = "0.7.0"
4
+ dependencies = ["win32more-core==0.7.*"]
5
+
6
+ [build-system]
7
+ requires = ["hatchling >= 1.26"]
8
+ build-backend = "hatchling.build"
9
+
10
+ [tool.hatch.build.targets.wheel]
11
+ packages = ["src/win32more"]
@@ -0,0 +1,3 @@
1
+ import win32more.appsdk
2
+
3
+ win32more.appsdk.initialize()
@@ -0,0 +1,10 @@
1
+ # ruff: noqa: F401
2
+
3
+ # versioninfo.py will be installed by win32more-Microsoft.WindowsAppSDK
4
+ from .versioninfo import ( # noqa
5
+ WINDOWSAPPSDK_RELEASE_MAJORMINOR,
6
+ WINDOWSAPPSDK_RELEASE_VERSION_SHORTTAG_W,
7
+ WINDOWSAPPSDK_RUNTIME_VERSION_UINT64,
8
+ )
9
+
10
+ from ._runtime import initialize, is_self_contained, get_loaded_appsdk_info, RuntimeNotFoundError
@@ -0,0 +1,114 @@
1
+ from ctypes import pointer
2
+
3
+ from win32more import FAILED, Byte, Char, String, UInt32, WinError
4
+ from win32more._win32 import ARCH
5
+ from win32more.Windows.Win32.Foundation import (
6
+ APPMODEL_ERROR_NO_PACKAGE,
7
+ ERROR_INSUFFICIENT_BUFFER,
8
+ ERROR_SUCCESS,
9
+ )
10
+ from win32more.Windows.Win32.Storage.Packaging.Appx import (
11
+ PACKAGE_FILTER_DYNAMIC,
12
+ PACKAGE_FILTER_STATIC,
13
+ PACKAGE_INFO,
14
+ PACKAGE_VERSION,
15
+ GetCurrentPackageFullName,
16
+ )
17
+ from win32more.Windows.Win32.Storage.Packaging.Appx import GetCurrentPackageInfo as _GetCurrentPackageInfo
18
+ from win32more.Windows.Win32.Storage.Packaging.Appx import GetPackagePathByFullName as _GetPackagePathByFullName
19
+ from win32more.Windows.Win32.Storage.Packaging.Appx import GetPackagesByPackageFamily as _GetPackagesByPackageFamily
20
+
21
+
22
+ def IsPackagedProcess() -> bool:
23
+ return GetCurrentPackageFullName(UInt32(), None) != APPMODEL_ERROR_NO_PACKAGE
24
+
25
+
26
+ # https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/package-identity-overview
27
+ def GetFrameworkPackageFamilyName(major_minor_version: int, version_tag: PACKAGE_VERSION) -> str:
28
+ major_version = major_minor_version >> 16
29
+ minor_version = major_minor_version & 0xFFFF
30
+ version_tag_delimiter = "" if version_tag == "" else "-"
31
+ name = f"Microsoft.WindowsAppRuntime.{major_version}.{minor_version}{version_tag_delimiter}{version_tag}"
32
+ publisher_id = "8wekyb3d8bbwe"
33
+ return f"{name}_{publisher_id}"
34
+
35
+
36
+ def GetFrameworkPackageDirectory(
37
+ major_minor_version: int, version_tag: str, min_version: PACKAGE_VERSION
38
+ ) -> str | None:
39
+ family_name = GetFrameworkPackageFamilyName(major_minor_version, version_tag)
40
+ full_name = ResolvePackageDependency(family_name, min_version, ARCH)
41
+ if full_name is None:
42
+ return None
43
+ return GetPackagePathByFullName(full_name)
44
+
45
+
46
+ def ResolvePackageDependency(family_name: str, min_version: PACKAGE_VERSION, arch: str) -> str | None:
47
+ bestfit = None
48
+ latest_version = None
49
+
50
+ for full_name in GetPackagesByPackageFamily(family_name):
51
+ name, version, architecture, resource_id, publisher_id = full_name.split("_")
52
+ major, minor, build, revision = [int(x) for x in version.split(".")]
53
+ if architecture.upper() != arch:
54
+ continue
55
+ if (major, minor, build, revision) < (
56
+ min_version.Major,
57
+ min_version.Minor,
58
+ min_version.Build,
59
+ min_version.Revision,
60
+ ):
61
+ continue
62
+ if latest_version is None or latest_version < (major, minor, build, revision):
63
+ bestfit = full_name
64
+ latest_version = (major, minor, build, revision)
65
+
66
+ return bestfit
67
+
68
+
69
+ def GetPackagesByPackageFamily(family_name: str) -> list[str]:
70
+ count = UInt32()
71
+ buffer_length = UInt32()
72
+ r = _GetPackagesByPackageFamily(family_name, count, None, buffer_length, None)
73
+ if r == ERROR_SUCCESS:
74
+ return [] # not found
75
+ elif r != ERROR_INSUFFICIENT_BUFFER:
76
+ raise WinError(r)
77
+
78
+ full_names = (String * count.value)()
79
+ buffer = (Char * buffer_length.value)()
80
+ r = _GetPackagesByPackageFamily(family_name, count, full_names, buffer_length, buffer)
81
+ if r != ERROR_SUCCESS:
82
+ raise WinError(r)
83
+
84
+ return full_names[: count.value]
85
+
86
+
87
+ def GetPackagePathByFullName(full_name: str) -> str:
88
+ path_length = UInt32()
89
+ r = _GetPackagePathByFullName(full_name, path_length, None)
90
+ if r != ERROR_INSUFFICIENT_BUFFER:
91
+ raise WinError(r)
92
+
93
+ path = (Char * path_length.value)()
94
+ r = _GetPackagePathByFullName(full_name, path_length, path)
95
+ if r != ERROR_SUCCESS:
96
+ raise WinError(r)
97
+
98
+ return path.value
99
+
100
+
101
+ def GetCurrentPackageInfo(flags=PACKAGE_FILTER_DYNAMIC | PACKAGE_FILTER_STATIC) -> list[PACKAGE_INFO]:
102
+ size = UInt32()
103
+ r = _GetCurrentPackageInfo(flags, size, None, None)
104
+ if r != ERROR_INSUFFICIENT_BUFFER:
105
+ raise WinError(r)
106
+
107
+ buf = bytearray(size.value)
108
+ pinfo = pointer(PACKAGE_INFO.from_buffer(buf))
109
+ count = UInt32()
110
+ r = _GetCurrentPackageInfo(flags, size, Byte.from_buffer(buf), count)
111
+ if FAILED(r):
112
+ raise WinError(r)
113
+
114
+ return pinfo[0 : count.value]
@@ -0,0 +1,79 @@
1
+ from __future__ import annotations
2
+
3
+ from win32more import FAILED, ComError, windll
4
+ from win32more.Windows.Win32.Foundation import APPMODEL_ERROR_NO_PACKAGE, STATEREPOSITORY_E_DEPENDENCY_NOT_RESOLVED
5
+ from win32more.Windows.Win32.Storage.Packaging.Appx import PACKAGE_INFO, PACKAGE_VERSION
6
+
7
+ from . import (
8
+ WINDOWSAPPSDK_RELEASE_MAJORMINOR,
9
+ WINDOWSAPPSDK_RELEASE_VERSION_SHORTTAG_W,
10
+ WINDOWSAPPSDK_RUNTIME_VERSION_UINT64,
11
+ )
12
+ from ._packaging import GetCurrentPackageInfo, GetFrameworkPackageFamilyName
13
+ from .mddbootstrap import MddBootstrapInitialize, _OnNoMatch_ShowUI
14
+
15
+
16
+ class RuntimeNotFoundError(OSError):
17
+ def __init__(self) -> None:
18
+ major = WINDOWSAPPSDK_RELEASE_MAJORMINOR >> 16
19
+ minor = WINDOWSAPPSDK_RELEASE_MAJORMINOR & 0xFFFF
20
+ min_version = PACKAGE_VERSION(Version=WINDOWSAPPSDK_RUNTIME_VERSION_UINT64)
21
+ msg = f"This application requires the Windows App Runtime Version {major}.{minor} (MSIX package version >= {min_version.Major}.{min_version.Minor}.{min_version.Build}.{min_version.Revision})"
22
+ super().__init__(STATEREPOSITORY_E_DEPENDENCY_NOT_RESOLVED, msg)
23
+
24
+ def show_dialog(self) -> None:
25
+ _OnNoMatch_ShowUI(
26
+ WINDOWSAPPSDK_RELEASE_MAJORMINOR,
27
+ WINDOWSAPPSDK_RELEASE_VERSION_SHORTTAG_W,
28
+ PACKAGE_VERSION(Version=WINDOWSAPPSDK_RUNTIME_VERSION_UINT64),
29
+ )
30
+
31
+
32
+ def initialize() -> None:
33
+ if _appsdk_runtime_dll_exists():
34
+ # case 1: This is packaged application and appsdk is statically loaded via AppxManifest.xml settings.
35
+ # case 2: Application is self-contained package.
36
+ return
37
+
38
+ hr = MddBootstrapInitialize(
39
+ WINDOWSAPPSDK_RELEASE_MAJORMINOR,
40
+ WINDOWSAPPSDK_RELEASE_VERSION_SHORTTAG_W,
41
+ PACKAGE_VERSION(Version=WINDOWSAPPSDK_RUNTIME_VERSION_UINT64),
42
+ )
43
+
44
+ if FAILED(hr):
45
+ if hr == STATEREPOSITORY_E_DEPENDENCY_NOT_RESOLVED:
46
+ raise RuntimeNotFoundError()
47
+ raise ComError(hr)
48
+
49
+
50
+ def get_loaded_appsdk_info() -> PACKAGE_INFO:
51
+ family_name = GetFrameworkPackageFamilyName(
52
+ WINDOWSAPPSDK_RELEASE_MAJORMINOR, WINDOWSAPPSDK_RELEASE_VERSION_SHORTTAG_W
53
+ )
54
+ for info in GetCurrentPackageInfo():
55
+ if info.packageFamilyName == family_name:
56
+ return info
57
+ raise LookupError(f"{family_name} is not loaded")
58
+
59
+
60
+ # WindowsAppRuntime_IsSelfContained()
61
+ def is_self_contained() -> bool:
62
+ try:
63
+ get_loaded_appsdk_info()
64
+ except LookupError:
65
+ return True
66
+ except OSError as e:
67
+ if e.winerror == APPMODEL_ERROR_NO_PACKAGE:
68
+ # Process is not packaged process and appsdk is not loaded dynamically.
69
+ return True
70
+ raise
71
+ return False
72
+
73
+
74
+ def _appsdk_runtime_dll_exists() -> bool:
75
+ try:
76
+ windll["Microsoft.WindowsAppRuntime.dll"]
77
+ except AttributeError:
78
+ return False
79
+ return True
@@ -0,0 +1,145 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import sys
5
+ import webbrowser
6
+ from pathlib import Path
7
+
8
+ import win32more
9
+ from win32more import FAILED, Int32, String, UInt32, Void
10
+ from win32more._win32 import ARCH, winfunctype
11
+ from win32more.Windows.Win32.Foundation import (
12
+ HRESULT,
13
+ S_OK,
14
+ STATEREPOSITORY_E_DEPENDENCY_NOT_RESOLVED,
15
+ )
16
+ from win32more.Windows.Win32.Storage.Packaging.Appx import (
17
+ PACKAGE_DEPENDENCY_RANK_DEFAULT,
18
+ PACKAGE_VERSION,
19
+ PACKAGEDEPENDENCY_CONTEXT,
20
+ AddPackageDependency,
21
+ AddPackageDependencyOptions_None,
22
+ CreatePackageDependencyOptions_None,
23
+ PackageDependencyLifetimeKind_Process,
24
+ PackageDependencyProcessorArchitectures_Arm64,
25
+ PackageDependencyProcessorArchitectures_X64,
26
+ PackageDependencyProcessorArchitectures_X86,
27
+ TryCreatePackageDependency,
28
+ )
29
+ from win32more.Windows.Win32.UI.WindowsAndMessaging import IDYES, MB_ICONERROR, MB_YESNO, MessageBox
30
+
31
+ from . import WINDOWSAPPSDK_RUNTIME_VERSION_UINT64
32
+ from ._packaging import GetFrameworkPackageFamilyName, IsPackagedProcess
33
+
34
+ if ARCH == "ARM64":
35
+ PackageDependencyProcessorArchitectures_Current = PackageDependencyProcessorArchitectures_Arm64
36
+ os.add_dll_directory(os.path.dirname(win32more.__file__) + "\\dll\\arm64")
37
+ elif ARCH == "X64":
38
+ PackageDependencyProcessorArchitectures_Current = PackageDependencyProcessorArchitectures_X64
39
+ os.add_dll_directory(os.path.dirname(win32more.__file__) + "\\dll\\x64")
40
+ elif ARCH == "X86":
41
+ PackageDependencyProcessorArchitectures_Current = PackageDependencyProcessorArchitectures_X86
42
+ os.add_dll_directory(os.path.dirname(win32more.__file__) + "\\dll\\x86")
43
+ else:
44
+ assert False
45
+
46
+ MddBootstrapInitializeOptions = Int32
47
+ MddBootstrapInitializeOptions_None = 0
48
+ MddBootstrapInitializeOptions_OnError_DebugBreak = 1
49
+ MddBootstrapInitializeOptions_OnError_DebugBreak_IfDebuggerAttached = 2
50
+ MddBootstrapInitializeOptions_OnError_FailFast = 4
51
+ MddBootstrapInitializeOptions_OnNoMatch_ShowUI = 8
52
+ MddBootstrapInitializeOptions_OnPackageIdentity_NOOP = 16
53
+
54
+
55
+ @winfunctype("Microsoft.WindowsAppRuntime.Bootstrap.dll", entry_point="MddBootstrapInitialize")
56
+ def _MddBootstrapInitialize(majorMinorVersion: UInt32, versionTag: String, minVersion: PACKAGE_VERSION) -> HRESULT: ...
57
+
58
+
59
+ @winfunctype("Microsoft.WindowsAppRuntime.Bootstrap.dll", entry_point="MddBootstrapInitialize2")
60
+ def _MddBootstrapInitialize2(
61
+ majorMinorVersion: UInt32, versionTag: String, minVersion: PACKAGE_VERSION, options: MddBootstrapInitializeOptions
62
+ ) -> HRESULT: ...
63
+
64
+
65
+ @winfunctype("Microsoft.WindowsAppRuntime.Bootstrap.dll", entry_point="MddBootstrapShutdown")
66
+ def _MddBootstrapShutdown() -> Void: ...
67
+
68
+
69
+ def MddBootstrapInitialize(major_minor_version: int, version_tag: str, min_version: PACKAGE_VERSION) -> int:
70
+ return MddBootstrapInitialize2(major_minor_version, version_tag, min_version, MddBootstrapInitializeOptions_None)
71
+
72
+
73
+ def MddBootstrapInitialize2(
74
+ major_minor_version: int, version_tag: str, min_version: PACKAGE_VERSION, options: int
75
+ ) -> int:
76
+ if IsPackagedProcess():
77
+ # FIXME: Mddbootstrap API doesn't support packaged process.
78
+ # Since 1.6.5, WindowsAppSDK seems to work with Win11's package dependency API.
79
+ # I'm not sure if it is offically supported. It might have a problem.
80
+ VERSION_1_6_5 = 0x1770019109300000
81
+ if not _IsWin11() or WINDOWSAPPSDK_RUNTIME_VERSION_UINT64 < VERSION_1_6_5:
82
+ raise RuntimeError("Packaged process is not supported before Windows 11")
83
+ hr = _Initialize_Win11(GetFrameworkPackageFamilyName(major_minor_version, version_tag), min_version)
84
+ if FAILED(hr):
85
+ if hr == STATEREPOSITORY_E_DEPENDENCY_NOT_RESOLVED:
86
+ if options & MddBootstrapInitializeOptions_OnNoMatch_ShowUI:
87
+ _OnNoMatch_ShowUI(major_minor_version, version_tag, min_version)
88
+ return hr
89
+ return S_OK
90
+ else:
91
+ return _MddBootstrapInitialize2(major_minor_version, version_tag, min_version, options)
92
+
93
+
94
+ def MddBootstrapShutdown() -> None:
95
+ if IsPackagedProcess() and not _IsWin11():
96
+ raise RuntimeError("Packaged process is not supported before Windows 11")
97
+ elif _IsWin11():
98
+ pass
99
+ else:
100
+ _MddBootstrapShutdown()
101
+
102
+
103
+ def _IsWin11() -> bool:
104
+ return sys.getwindowsversion() >= (10, 0, 22000)
105
+
106
+
107
+ def _Initialize_Win11(family_name: str, min_version: PACKAGE_VERSION) -> int:
108
+ package_dependency_id = String()
109
+ hr = TryCreatePackageDependency(
110
+ None,
111
+ family_name,
112
+ min_version,
113
+ PackageDependencyProcessorArchitectures_Current,
114
+ PackageDependencyLifetimeKind_Process,
115
+ None,
116
+ CreatePackageDependencyOptions_None,
117
+ package_dependency_id,
118
+ )
119
+ if FAILED(hr):
120
+ return hr
121
+
122
+ package_dependency_context = PACKAGEDEPENDENCY_CONTEXT()
123
+ hr = AddPackageDependency(
124
+ package_dependency_id,
125
+ PACKAGE_DEPENDENCY_RANK_DEFAULT,
126
+ AddPackageDependencyOptions_None,
127
+ package_dependency_context,
128
+ None,
129
+ )
130
+ if FAILED(hr):
131
+ return hr
132
+
133
+ return S_OK
134
+
135
+
136
+ def _OnNoMatch_ShowUI(major_minor_version: int, version_tag: str, min_version: PACKAGE_VERSION) -> None:
137
+ caption = f"{Path(sys.executable).name} - This application could not be started"
138
+ text = f"""This application requires the Windows App Runtime
139
+ Version {major_minor_version >> 16}.{major_minor_version & 0xFFFF}
140
+ (MSIX package version >= {min_version.Major}.{min_version.Minor}.{min_version.Build}.{min_version.Revision})
141
+
142
+ Do you want to install a compatible Windows App Runtime now?"""
143
+ r = MessageBox(0, text, caption, MB_YESNO | MB_ICONERROR)
144
+ if r == IDYES:
145
+ webbrowser.open("https://docs.microsoft.com/windows/apps/windows-app-sdk/downloads")
@@ -0,0 +1,601 @@
1
+ import importlib
2
+ import sys
3
+ import weakref
4
+ import xml.etree.ElementTree as ET
5
+ from functools import partial
6
+ from pathlib import Path
7
+ from tempfile import NamedTemporaryFile
8
+
9
+ from win32more import FAILED, ComClass, WinError, asyncui
10
+ from win32more._winrt import ISelf, hstr
11
+ from win32more.Microsoft.UI.Xaml import Application, FrameworkElement, IApplicationOverrides
12
+ from win32more.Microsoft.UI.Xaml.Markup import IComponentConnector, IXamlMetadataProvider, IXamlType, XamlReader
13
+ from win32more.Microsoft.UI.Xaml.XamlTypeInfo import XamlControlsXamlMetaDataProvider
14
+ from win32more.Microsoft.Windows.ApplicationModel.Resources import ResourceManager
15
+ from win32more.Windows.Foundation import Uri
16
+ from win32more.Windows.UI.Xaml.Interop import TypeName
17
+ from win32more.Windows.Win32.System.Com import COINIT_APARTMENTTHREADED, CoInitializeEx, CoUninitialize
18
+ from win32more.Windows.Win32.UI.HiDpi import DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, SetProcessDpiAwarenessContext
19
+ from win32more.Windows.Win32.UI.WindowsAndMessaging import SetTimer
20
+
21
+ XMLNS_XAML = "http://schemas.microsoft.com/winfx/2006/xaml"
22
+ XMLNS_XAML_PRESENTATION = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
23
+
24
+ # FIXME: register_namespace() is global.
25
+ # This is required to prevent "ns0:" prefix for default namespace.
26
+ ET.register_namespace("", XMLNS_XAML_PRESENTATION)
27
+
28
+
29
+ class XamlApplication(ComClass, Application, IApplicationOverrides, IXamlMetadataProvider):
30
+ def __init__(self):
31
+ XamlApplication.__current = self
32
+ self._provider = None
33
+ super().__init__(own=True)
34
+ self.InitializeComponent()
35
+ self.ResourceManagerRequested += self.OnResourceManagerRequested
36
+
37
+ def OnResourceManagerRequested(self, sender, e):
38
+ resources_pri = Path(sys.executable).with_name("resources.pri")
39
+ if not resources_pri.exists():
40
+ resources_pri = Path(__file__).parent / "resources.pri"
41
+ e.CustomResourceManager = ResourceManager(str(resources_pri))
42
+
43
+ def InitializeComponent(self):
44
+ xaml_path = Path(__file__).with_name("app.xaml").as_posix()
45
+ resource_locator = Uri(f"ms-appx:///{xaml_path}")
46
+ Application.LoadComponent(self, resource_locator)
47
+
48
+ def OnLaunched(self, args):
49
+ # You should override this in your derived class
50
+ ...
51
+
52
+ def GetXamlType(self, type):
53
+ return self.AppProvider().GetXamlType(type)
54
+
55
+ # TODO: Is it needed to provide information for primitive or winui type?
56
+ def GetXamlTypeByFullName(self, fullName):
57
+ return self.AppProvider().GetXamlTypeByFullName(fullName)
58
+
59
+ def GetXmlnsDefinitions(self):
60
+ return self.AppProvider().GetXmlnsDefinitions()
61
+
62
+ def AppProvider(self):
63
+ if self._provider is None:
64
+ self._provider = XamlControlsXamlMetaDataProvider()
65
+ return self._provider
66
+
67
+ __current = None
68
+
69
+ @classmethod
70
+ def Start(cls, init):
71
+ r = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
72
+ if not r:
73
+ raise WinError()
74
+
75
+ hr = CoInitializeEx(None, COINIT_APARTMENTTHREADED)
76
+ if FAILED(hr):
77
+ raise WinError(hr)
78
+
79
+ with asyncui.HandCrankRunner() as runner:
80
+ SetTimer(0, 0, 100, lambda *_: runner.update())
81
+ Application.Start(lambda params: init())
82
+
83
+ # FIXME: force Release() to avoid exit with error code.
84
+ if XamlApplication.__current is not None:
85
+ XamlApplication.__current.Release()
86
+
87
+ CoUninitialize()
88
+
89
+
90
+ class XamlClass(ComClass, IComponentConnector):
91
+ def __init__(self, *args, own=True, **kwargs):
92
+ super().__init__(*args, own=own, **kwargs)
93
+ self.__component_connector = None
94
+
95
+ def Connect(self, connectionId, target):
96
+ self.__component_connector.Connect(connectionId, target)
97
+
98
+ def GetBindingConnector(self, connectionId, target):
99
+ return self.__component_connector.GetBindingConnector(connectionId, target)
100
+
101
+ def LoadComponentFromFile(self, xaml_path, encoding: str | None = None):
102
+ self.LoadComponentFromString(Path(xaml_path).read_text(encoding=encoding), xaml_path)
103
+
104
+ def LoadComponentFromString(self, xaml_str, xaml_path=None):
105
+ self.__component_connector = XamlComponentConnector()
106
+ self.__component_connector.Load(self, xaml_str, xaml_path)
107
+
108
+
109
+ class XamlComponentConnector:
110
+ def __init__(self):
111
+ self._connectors = {}
112
+
113
+ def Connect(self, connectionId, target):
114
+ for connect in self._connectors[connectionId]:
115
+ connect(target)
116
+
117
+ def GetBindingConnector(self, connectionId, target):
118
+ return None
119
+
120
+ def Load(self, component, xaml_str, xaml_path):
121
+ xaml_preprocessed = self._preprocess(component, xaml_str, xaml_path)
122
+ with NamedTemporaryFile(delete=False) as f:
123
+ f.write(xaml_preprocessed.encode("utf-8"))
124
+ f.close()
125
+ tmp_xaml_path = Path(f.name).as_posix()
126
+ resource_locator = Uri(f"ms-appx:///{tmp_xaml_path}")
127
+ Application.LoadComponent(component, resource_locator)
128
+ Path(f.name).unlink()
129
+
130
+ def _preprocess(self, component, xaml_str, xaml_path):
131
+ root = ET.fromstring(xaml_str)
132
+ for i, e in enumerate(root.iter()):
133
+ self._connectors[i] = []
134
+ for k, v in list(e.attrib.items()):
135
+ if k == f"{{{XMLNS_XAML}}}Name":
136
+ self._connectors[i].append(partial(self._connect_name, component, v))
137
+ elif k in _known_events:
138
+ if e == root:
139
+ self._connectors[i].append(partial(self._connect_event_root, component, k, v))
140
+ else:
141
+ self._connectors[i].append(partial(self._connect_event, component, k, v))
142
+ del e.attrib[k]
143
+ elif k in _path_names and xaml_path is not None:
144
+ # FIXME: Workaround for relative path.
145
+ src_path = Path(xaml_path).parent / v
146
+ if src_path.exists():
147
+ e.attrib[k] = src_path.absolute().as_posix()
148
+ if self._connectors[i]:
149
+ e.attrib[f"{{{XMLNS_XAML}}}ConnectionId"] = str(i)
150
+ return ET.tostring(root, encoding="unicode")
151
+
152
+ def _connect_name(self, component, bind_name, target):
153
+ setattr(component, bind_name, as_runtime_class(target))
154
+
155
+ def _connect_event(self, component, event_name, method_name, target):
156
+ event_setter = getattr(as_runtime_class(target), event_name)
157
+ event_setter += getattr(component, method_name)
158
+
159
+ def _connect_event_root(self, component, event_name, method_name, target):
160
+ event_setter = getattr(component, event_name)
161
+ event_setter += getattr(component, method_name)
162
+
163
+
164
+ # Load xaml and connect element and event handler to view object.
165
+ #
166
+ # <Button x:Name="Button1" Click="Button1_Click" />
167
+ #
168
+ # to be
169
+ #
170
+ # view.Button1 = Button()
171
+ # view.Button1.Click += view.Button1_Click
172
+ class XamlLoader:
173
+ @classmethod
174
+ def Load(cls, view: object, xaml_str: str) -> object:
175
+ return cls().execute(view, xaml_str)
176
+
177
+ def __init__(self):
178
+ self._connectors = {}
179
+
180
+ def execute(self, view, xaml_str):
181
+ xaml_preprocessed = self._preprocess(view, xaml_str)
182
+ uiroot = as_runtime_class(XamlReader.Load(xaml_preprocessed))
183
+ self._connect(uiroot)
184
+ return uiroot
185
+
186
+ def _preprocess(self, view, xaml_str):
187
+ root = ET.fromstring(xaml_str)
188
+ for i, e in enumerate(root.iter()):
189
+ if e is root:
190
+ name = ""
191
+ elif f"{{{XMLNS_XAML}}}Name" in e.attrib:
192
+ name = e.attrib[f"{{{XMLNS_XAML}}}Name"]
193
+ else:
194
+ name = f"__dummy{i}"
195
+ self._connectors[name] = []
196
+ for k, v in list(e.attrib.items()):
197
+ if k == f"{{{XMLNS_XAML}}}Name" and e is not root:
198
+ self._connectors[name].append(partial(self._connect_name, view, name))
199
+ elif k in _known_events:
200
+ self._connectors[name].append(partial(self._connect_event, view, k, v))
201
+ del e.attrib[k]
202
+ if self._connectors[name] and f"{{{XMLNS_XAML}}}Name" not in e.attrib and e is not root:
203
+ e.attrib[f"{{{XMLNS_XAML}}}Name"] = name
204
+ return ET.tostring(root, encoding="unicode")
205
+
206
+ def _connect(self, uiroot):
207
+ try:
208
+ framework_element = uiroot.as_(FrameworkElement)
209
+ except OSError:
210
+ framework_element = uiroot.Content.as_(FrameworkElement)
211
+
212
+ for name, connectors in self._connectors.items():
213
+ for connect in connectors:
214
+ if name == "":
215
+ target = uiroot
216
+ else:
217
+ target = framework_element.FindName(name)
218
+ connect(target)
219
+
220
+ def _connect_name(self, view, bind_name, target):
221
+ setattr(view, bind_name, as_runtime_class(target))
222
+
223
+ def _connect_event(self, view, event_name, method_name, target):
224
+ event_setter = getattr(as_runtime_class(target), event_name)
225
+ event_setter += getattr(view, method_name)
226
+
227
+
228
+ class XamlType(ComClass, IXamlType):
229
+ def __init__(
230
+ self,
231
+ full_name,
232
+ type_kind,
233
+ *,
234
+ base_type=None,
235
+ boxed_type=None,
236
+ content_property=None,
237
+ is_array=False,
238
+ is_bindable=False,
239
+ is_collection=False,
240
+ is_dictionary=False,
241
+ is_markup_extension=False,
242
+ item_type=None,
243
+ key_type=None,
244
+ activate_instance=None,
245
+ add_to_map=None,
246
+ add_to_vector=None,
247
+ create_from_string=None,
248
+ ):
249
+ super().__init__(own=True)
250
+ self._full_name = full_name
251
+ self._type_kind = type_kind
252
+ self._base_type = base_type
253
+ self._boxed_type = boxed_type
254
+ self._content_property = content_property
255
+ self._is_array = is_array
256
+ self._is_bindable = is_bindable
257
+ self._is_collection = is_collection
258
+ self._is_constructible = bool(activate_instance)
259
+ self._is_dictionary = is_dictionary
260
+ self._is_markup_extension = is_markup_extension
261
+ self._item_type = item_type
262
+ self._key_type = key_type
263
+ self._activate_instance = activate_instance
264
+ self._add_to_map = add_to_map
265
+ self._add_to_vector = add_to_vector
266
+ self._create_from_string = create_from_string
267
+
268
+ def get_BaseType(self):
269
+ return self._base_type
270
+
271
+ def get_BoxedType(self):
272
+ return self._boxed_type
273
+
274
+ def get_ContentProperty(self):
275
+ return self._content_property
276
+
277
+ def get_FullName(self):
278
+ return self._full_name
279
+
280
+ def get_IsArray(self):
281
+ return self._is_array
282
+
283
+ def get_IsBindable(self):
284
+ return self._is_bindable
285
+
286
+ def get_IsCollection(self):
287
+ return self._is_collection
288
+
289
+ def get_IsConstructible(self):
290
+ return self._is_constructible
291
+
292
+ def get_IsDictionary(self):
293
+ return self._is_dictionary
294
+
295
+ def get_IsMarkupExtension(self):
296
+ return self._is_markup_extension
297
+
298
+ def get_ItemType(self):
299
+ return self._item_type
300
+
301
+ def get_KeyType(self):
302
+ return self._key_type
303
+
304
+ def get_UnderlyingType(self):
305
+ return TypeName(hstr(self._full_name), self._type_kind)
306
+
307
+ def ActivateInstance(self):
308
+ if self._activate_instance is None:
309
+ raise NotImplementedError()
310
+ return self._activate_instance()
311
+
312
+ def AddToMap(self, instance, key, value):
313
+ if self._add_to_map is None:
314
+ raise NotImplementedError()
315
+ self._add_to_map(instance, key, value)
316
+
317
+ def AddToVector(self, instance, value):
318
+ if self._add_to_vector is None:
319
+ raise NotImplementedError()
320
+ self._add_to_vector(instance, value)
321
+
322
+ def CreateFromString(self, value):
323
+ if self._boxed_type is not None:
324
+ return self._boxed_type.CreateFromString(value)
325
+ if self._create_from_string is not None:
326
+ return self._create_from_string(value)
327
+ # TODO:
328
+ # return fromStringConverter()
329
+ raise NotImplementedError()
330
+
331
+ def GetMember(self, name):
332
+ # TODO:
333
+ raise NotImplementedError()
334
+
335
+ def RunInitializer(self):
336
+ pass
337
+
338
+
339
+ def xaml_typename(name, kind):
340
+ hs = hstr(name)
341
+ tn = TypeName(hs, kind)
342
+ weakref.finalize(tn, hs.clear)
343
+ return tn
344
+
345
+
346
+ def as_runtime_class(uielement):
347
+ try:
348
+ return uielement.as_(ISelf).GetSelf()
349
+ except OSError:
350
+ return uielement.as_(_get_runtime_class(uielement))
351
+
352
+
353
+ def _get_runtime_class(uielement):
354
+ namespace, name = _get_runtime_class_name(uielement).rsplit(".", 1)
355
+ module = importlib.import_module(f"win32more.{namespace}")
356
+ return getattr(module, name)
357
+
358
+
359
+ def _get_runtime_class_name(uielement):
360
+ hs = hstr(own=True)
361
+ hr = uielement.GetRuntimeClassName(hs)
362
+ if FAILED(hr):
363
+ raise WinError(hr)
364
+ return str(hs)
365
+
366
+
367
+ _known_events = {
368
+ "AccessKeyDisplayDismissed",
369
+ "AccessKeyDisplayRequested",
370
+ "AccessKeyInvoked",
371
+ "ActionButtonClick",
372
+ "Activated",
373
+ "ActualPlacementChanged",
374
+ "ActualThemeChanged",
375
+ "AddPages",
376
+ "AddScrollVelocityRequested",
377
+ "AddTabButtonClick",
378
+ "AnchorRequested",
379
+ "AnimatedVisualInvalidated",
380
+ "ArrangeInvalidated",
381
+ "BackRequested",
382
+ "BeforeTextChanging",
383
+ "BindingFailed",
384
+ "BringIntoViewRequested",
385
+ "BringingIntoView",
386
+ "CalendarViewDayItemChanging",
387
+ "CanExecuteChanged",
388
+ "CanExecuteRequested",
389
+ "CanScrollChanged",
390
+ "CandidateWindowBoundsChanged",
391
+ "Changed",
392
+ "CharacterReceived",
393
+ "Checked",
394
+ "ChoosingGroupHeaderContainer",
395
+ "ChoosingItemContainer",
396
+ "CleanUpVirtualizedItemEvent",
397
+ "Click",
398
+ "CloseButtonClick",
399
+ "CloseRequested",
400
+ "Closed",
401
+ "Closing",
402
+ "Collapsed",
403
+ "CollectionChanged",
404
+ "ColorChanged",
405
+ "Completed",
406
+ "CompositionScaleChanged",
407
+ "Confirmed",
408
+ "ContainerContentChanging",
409
+ "ContextCanceled",
410
+ "ContextMenuOpening",
411
+ "ContextRequested",
412
+ "CopyingToClipboard",
413
+ "CoreProcessFailed",
414
+ "CoreWebView2Initialized",
415
+ "CurrentChanged",
416
+ "CurrentChanging",
417
+ "CurrentStateChanged",
418
+ "CurrentStateChanging",
419
+ "CuttingToClipboard",
420
+ "DataContextChanged",
421
+ "DateChanged",
422
+ "DatePicked",
423
+ "DetailLabelRequested",
424
+ "DirectManipulationCompleted",
425
+ "DirectManipulationStarted",
426
+ "DisplayModeChanged",
427
+ "DoubleTapped",
428
+ "DownloadProgress",
429
+ "DragCompleted",
430
+ "DragDelta",
431
+ "DragEnter",
432
+ "DragItemsCompleted",
433
+ "DragItemsStarting",
434
+ "DragLeave",
435
+ "DragOver",
436
+ "DragStarted",
437
+ "DragStarting",
438
+ "Drop",
439
+ "DropCompleted",
440
+ "DropDownClosed",
441
+ "DropDownOpened",
442
+ "DynamicOverflowItemsChanging",
443
+ "EffectiveViewportChanged",
444
+ "ElementClearing",
445
+ "ElementIndexChanged",
446
+ "ElementPrepared",
447
+ "ErrorsChanged",
448
+ "ExecuteRequested",
449
+ "Expanding",
450
+ "ExtentChanged",
451
+ "ExternalTornOutTabsDropped",
452
+ "ExternalTornOutTabsDropping",
453
+ "FocusDisengaged",
454
+ "FocusEngaged",
455
+ "GetPreviewPage",
456
+ "GettingFocus",
457
+ "GotFocus",
458
+ "Holding",
459
+ "HorizontalSnapPointsChanged",
460
+ "ImageFailed",
461
+ "ImageOpened",
462
+ "Indeterminate",
463
+ "Invoked",
464
+ "IsCheckedChanged",
465
+ "IsDisplayModeEnabledChanged",
466
+ "IsEnabledChanged",
467
+ "IsScrollingWithMouseChanged",
468
+ "IsTextTrimmedChanged",
469
+ "ItemClick",
470
+ "ItemClicked",
471
+ "ItemInvoked",
472
+ "ItemsChanged",
473
+ "ItemsInfoRequested",
474
+ "ItemsPicked",
475
+ "ItemsUnlocked",
476
+ "KeyDown",
477
+ "KeyUp",
478
+ "LayoutUpdated",
479
+ "LoadCompleted",
480
+ "Loaded",
481
+ "Loading",
482
+ "LosingFocus",
483
+ "LostFocus",
484
+ "ManipulationCompleted",
485
+ "ManipulationDelta",
486
+ "ManipulationInertiaStarting",
487
+ "ManipulationStarted",
488
+ "ManipulationStarting",
489
+ "MapElementClick",
490
+ "MapServiceErrorOccurred",
491
+ "MeasureInvalidated",
492
+ "ModeChanged",
493
+ "Navigated",
494
+ "Navigating",
495
+ "NavigationCompleted",
496
+ "NavigationFailed",
497
+ "NavigationStarting",
498
+ "NavigationStopped",
499
+ "NoFocusCandidateFound",
500
+ "OpenFailed",
501
+ "Opened",
502
+ "Opening",
503
+ "Paginate",
504
+ "PanRequested",
505
+ "PaneClosed",
506
+ "PaneClosing",
507
+ "PaneOpened",
508
+ "PaneOpening",
509
+ "PaneToggleRequested",
510
+ "PasswordChanged",
511
+ "PasswordChanging",
512
+ "Paste",
513
+ "PivotItemLoaded",
514
+ "PivotItemLoading",
515
+ "PivotItemUnloaded",
516
+ "PivotItemUnloading",
517
+ "PointerCanceled",
518
+ "PointerCaptureLost",
519
+ "PointerEntered",
520
+ "PointerExited",
521
+ "PointerMoved",
522
+ "PointerPressed",
523
+ "PointerReleased",
524
+ "PointerWheelChanged",
525
+ "PreviewKeyDown",
526
+ "PreviewKeyUp",
527
+ "PrimaryButtonClick",
528
+ "ProcessKeyboardAccelerators",
529
+ "PropertyChanged",
530
+ "QuerySubmitted",
531
+ "RefreshRequested",
532
+ "RefreshStateChanged",
533
+ "Rendered",
534
+ "Rendering",
535
+ "ResourceManagerRequested",
536
+ "RightTapped",
537
+ "Scroll",
538
+ "ScrollAnimationStarting",
539
+ "ScrollByRequested",
540
+ "ScrollCompleted",
541
+ "ScrollToRequested",
542
+ "Scrolling",
543
+ "SecondaryButtonClick",
544
+ "SectionHeaderClick",
545
+ "SectionsInViewChanged",
546
+ "SelectedDateChanged",
547
+ "SelectedDatesChanged",
548
+ "SelectedIndexChanged",
549
+ "SelectedTimeChanged",
550
+ "SelectionChanged",
551
+ "SelectionChanging",
552
+ "SizeChanged",
553
+ "StateChanged",
554
+ "SuggestionChosen",
555
+ "SurfaceContentsLost",
556
+ "TabCloseRequested",
557
+ "TabDragCompleted",
558
+ "TabDragStarting",
559
+ "TabDroppedOutside",
560
+ "TabItemsChanged",
561
+ "TabStripDragOver",
562
+ "TabStripDrop",
563
+ "TabTearOutRequested",
564
+ "TabTearOutWindowRequested",
565
+ "TakeFocusRequested",
566
+ "Tapped",
567
+ "TextChanged",
568
+ "TextChanging",
569
+ "TextCompositionChanged",
570
+ "TextCompositionEnded",
571
+ "TextCompositionStarted",
572
+ "TextSubmitted",
573
+ "ThumbnailRequested",
574
+ "Tick",
575
+ "TimeChanged",
576
+ "TimePicked",
577
+ "Toggled",
578
+ "TransitionCompleted",
579
+ "Unchecked",
580
+ "UnhandledException",
581
+ "Unloaded",
582
+ "ValueChanged",
583
+ "VectorChanged",
584
+ "VerticalSnapPointsChanged",
585
+ "ViewChangeCompleted",
586
+ "ViewChangeStarted",
587
+ "ViewChanged",
588
+ "ViewChanging",
589
+ "VisibilityChanged",
590
+ "WebMessageReceived",
591
+ "XamlResourceReferenceFailed",
592
+ "XamlShutdownCompletedOnThread",
593
+ "ZoomAnimationStarting",
594
+ "ZoomCompleted",
595
+ }
596
+
597
+
598
+ _path_names = {
599
+ "Source",
600
+ "ProfilePicture",
601
+ }
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Application
3
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
5
+ <Application.Resources>
6
+ <ResourceDictionary>
7
+ <ResourceDictionary.MergedDictionaries>
8
+ <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
9
+ </ResourceDictionary.MergedDictionaries>
10
+ </ResourceDictionary>
11
+ </Application.Resources>
12
+ </Application>