Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

winrt/winsdk error #2785

Open
markox92 opened this issue Jan 31, 2025 · 13 comments
Open

winrt/winsdk error #2785

markox92 opened this issue Jan 31, 2025 · 13 comments

Comments

@markox92
Copy link

I have searched for solutions but cannot find, I have made some apps to get Windows Notification count, and when I run it with python from console all is working perfectly fine, but when I build .exe there is always same error Error: [WinError -2147023728] Element not found

I have using import

from winrt.windows.ui.notifications.management import UserNotificationListener, UserNotificationListenerAccessStatus
from winrt.windows.ui.notifications import NotificationKinds

Also, i have tried with winsdk
from winsdk.windows.ui.notifications import management

And I have absolutely same error from both

Any idea why this does not work when I build exe file, but works fine with python from console? There is no error when building the app.exe, but when I run it, I see this as the output.

I have tried also import as packages inside build_options this modules but also does not help.

I have Python 3.12.8 and latest cx_Freeze also I have tried with dev.
I have no more ideas where the problem could be, any help is welcome?

@marcelotduarte
Copy link
Owner

I don't know this package, but maybe this discussion can help you.

@markox92
Copy link
Author

Oh, this is bad, I have exactly the same problem when I try to subscribe to add_notification_changed and got this error but only with exe file, Python working fine. So cx_Freeze won't work with this module?

@marcelotduarte
Copy link
Owner

Many packages work out of the box, others require us to make hooks if some dll is not being copied, something like that. We would have to analyze this case, with a good example of how to reproduce the error.

@markox92
Copy link
Author

markox92 commented Feb 1, 2025

Here is the script, and you can run it from the console. It will get real-time Windows notifications, however, if you try to make an .exe file it will run but you should only get the initial count and than you will also see an error, I think it comes from this line: listener.add_notification_changed(on_notification_changed)

import asyncio
import logging
from enum import IntFlag
from winsdk.windows.ui.notifications import management
from PyQt6.QtCore import QThread

class NotificationKinds(IntFlag):
    toast = 1
    tile = 2
    badge = 4
    proto = 8

def get_all_kinds():
    return (
        NotificationKinds.toast |
        NotificationKinds.tile |
        NotificationKinds.badge |
        NotificationKinds.proto
    )

class WindowsNotificationEventListener(QThread):
    def __init__(self):
        super().__init__()
        self.previous_count = 0
        self.total_notifications = 0
        self.loop = asyncio.new_event_loop()
 
        
    async def update_count(self, sender):
        try:
            notifications = await sender.get_notifications_async(get_all_kinds())
            count = len(notifications)
            return count
        except Exception as e:
            print(f"Error updating count: {e}")
            return None

    async def watch_notifications(self):
        try:
            listener = management.UserNotificationListener.current
            access_result = await listener.request_access_async()
            if access_result == management.UserNotificationListenerAccessStatus.ALLOWED:
                # Initial count with all kinds
                initial_count = await self.update_count(listener)
                if initial_count is not None:
                    self.previous_count = initial_count
                    self.total_notifications += initial_count
                    print(f"Initial notifications: {initial_count}")

                def on_notification_changed(sender, args):
                    # nonlocal self
                    try:
                        future = asyncio.run_coroutine_threadsafe(self.update_count(sender), self.loop)
                        current_count = future.result()
                        if current_count is not None:
                            if current_count > self.previous_count:
                                added = current_count - self.previous_count
                                self.total_notifications += added
                            elif current_count < self.previous_count:
                                if current_count == 0:
                                    self.total_notifications = 0
                            self.previous_count = current_count
                            print(f"Total notifications: {self.total_notifications}")
                    except Exception as e:
                        print(f"Error fetching Windows Notification count: {e}")

                # Retry for subscribing to the NotificationChanged event
                max_retries = 3
                for attempt in range(max_retries):
                    try:
                        listener.add_notification_changed(on_notification_changed)
                        print("Subscribed to Windows Notification Changed event.")
                        break
                    except Exception as e:
                        print(f"Failed to subscribe to Windows Notification Changed event (attempt {attempt + 1}): {e}")
                        if attempt < max_retries - 1:
                            await asyncio.sleep(2 ** attempt)  # Exponential backoff
                        else:
                            return

                await self.loop.create_future()

            else:
                print("Access denied to notifications")
        except Exception as e:
            logging.error(f"Error: {e}")

    def run(self):
        asyncio.set_event_loop(self.loop)
        try:
            # Schedule the watch_notifications coroutine
            task = self.loop.create_task(self.watch_notifications())
            # Run the event loop indefinitely
            self.loop.run_forever()
        except asyncio.CancelledError:
            print("Event loop has been cancelled")
        finally:
            task.cancel()
            try:
                self.loop.run_until_complete(task)
            except asyncio.CancelledError:
                pass
            finally:
                self.loop.close()
                
if __name__ == "__main__":
    listener = WindowsNotificationEventListener()
    listener.start()
    listener.wait()

@marcelotduarte
Copy link
Owner

marcelotduarte commented Feb 4, 2025

I tested the sample with Python 3.10 and 3.12, and it showed the error message three times (three attempts) when I ran python test.py, and it broke.
Also, it does the same as executable.

You can try using --packages and --includes-msvcr, for example:
cxfreeze --script test.py --packages=winsdk --includes-msvcr

@markox92
Copy link
Author

markox92 commented Feb 4, 2025

With python (3.12.8) on Win11 24H2 (but also works on win10, tested on VM now)

Image

.exe

Image

@markox92
Copy link
Author

markox92 commented Feb 4, 2025

I've been playing with this for several hours now and I can't figure out why it's not working. I've included the module in the cx_Freeze setup script, but it seems like the problem is somewhere else. Maybe we're missing some DLL file or something that I can't seem to spot. Generally, I use the winsdk for a lot of things, and only this part isn't working with cx_Freeze, looks like only add_notification_changed does not work here. If we figure out why probably we will fix both WinSDK and WinRT at the same time.

@marcelotduarte
Copy link
Owner

I only tested it on a VM with Windows 11, with py 3.10, and also with py 3.12. In both, using python test.py I saw the same message that you saw in the case of the exe. In other words, it didn't work for me at all.
To test, I installed the winsdk and pyqt6 packages. Do I need any other packages?
Actually, do I really need pyqt6? Could you simplify the test without using pyqt6?

@markox92
Copy link
Author

markox92 commented Feb 4, 2025

I only tested it on a VM with Windows 11, with py 3.10, and also with py 3.12. In both, using python test.py I saw the same message that you saw in the case of the exe. In other words, it didn't work for me at all. To test, I installed the winsdk and pyqt6 packages. Do I need any other packages? Actually, do I really need pyqt6? Could you simplify the test without using pyqt6?

Sorry, I just copied my test code. Here is a simpler version without PyQt6, you only need to install pip install winsdk

import asyncio
from winsdk.windows.ui.notifications import management

class WindowsNotificationEventListener:
    def __init__(self):
        self.count = 0
        self.loop = asyncio.new_event_loop()

    async def get_notification_count(self, listener):
        notifications = await listener.get_notifications_async(15) # 15 to get all notifications
        return len(notifications)

    def handle_notification_change(self, sender, args):
        async def update_count():
            new_count = await self.get_notification_count(sender)
            self.count = new_count
            print(f"Notification count: {self.count}")

        asyncio.run_coroutine_threadsafe(update_count(), self.loop)

    async def watch_notifications(self):
        listener = management.UserNotificationListener.current
        if await listener.request_access_async() != management.UserNotificationListenerAccessStatus.ALLOWED:
            print("Access denied to notifications API")
            return

        self.count = await self.get_notification_count(listener)
        print(f"Initial notification count: {self.count}")

        listener.add_notification_changed(self.handle_notification_change)

        await asyncio.Future()

    def run(self):
        asyncio.set_event_loop(self.loop)
        self.loop.run_until_complete(self.watch_notifications())
        self.loop.run_forever()

if __name__ == "__main__":
    listener = WindowsNotificationEventListener()
    listener.run()

result should be like

Image

@marcelotduarte
Copy link
Owner

python test.py

Initial notification count: 1
Traceback (most recent call last):
  File "D:\testes\2785\test.py", line 41, in <module>
    listener.run()
  File "D:\testes\2785\test.py", line 36, in run
    self.loop.run_until_complete(self.watch_notifications())
  File "C:\Users\Marcelo\AppData\Roaming\uv\python\cpython-3.12.8-windows-x86_64-none\Lib\asyncio\base_events.py", line 686, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "D:\testes\2785\test.py", line 30, in watch_notifications
    listener.add_notification_changed(self.handle_notification_change)
OSError: [WinError -2147023728] Elemento não encontrado

@markox92
Copy link
Author

markox92 commented Feb 5, 2025

Hmm, this is strange. I have Python installed from the Microsoft Store, so maybe that's why I'm not getting any errors like this, and everything is working fine?

@marcelotduarte
Copy link
Owner

I had tested with python from python.org and now with uv python. Windows Store python has some restrictions[1] and generally does not allow modifications to the executable, which needs to be done when you freeze. I'll try something later.

[1] https://docs.python.org/3.12/using/windows.html#known-issues

@markox92
Copy link
Author

markox92 commented Feb 5, 2025

OK, I removed Python from the Store, installed Python 3.12.9 from python.org, and encountered the same error as you. I uninstalled it again and installed it from the Store, and now everything is working fine :(

btw only winsdk module is installed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants