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.init_apartment() RuntimeError: Cannot change thread mode after it is set. #690

Closed
pbvinoth opened this issue Aug 27, 2020 · 13 comments

Comments

@pbvinoth
Copy link

Hi All,

We are trying to invoke winrt apis using remote rpyc calls in remote machine. When we tried to inistialize the
objects we faced error
Error added: <Error: _get_exception_class..Derived: Cannot change thread mode after it is set.
RuntimeError: Cannot change thread mode after it is set.

    remote_wf = self._dev.rpyc.modules.winrt
    slash.logger.info("winrt init done")
    u = remote_wf.Uri("https://github.com/")
    u2 = u.combine_uri("Microsoft/xlang/tree/master/src/tool/python")
    print(str(u2))

and observed the following error in call stack
========= Remote Traceback (1) =========
Traceback (most recent call last):
File "C:\Users\itl\pytm_win\pytm_win\lib\site-packages\rpyc\core\protocol.py", line 323, in _dispatch_request
res = self._HANDLERS[handler](self, *args)
File "C:\Users\itl\pytm_win\pytm_win\lib\site-packages\rpyc\core\protocol.py", line 585, in handle_call
return obj(args, **dict(kwargs))
File "C:\Users\itl\pytm_win\pytm_win\lib\site-packages\rpyc\core\service.py", line 160, in getmodule
return import(name, None, None, "
")
File "C:\Users\itl\pytm_win\pytm_win\lib\site-packages\winrt_init
.py", line 4, in
_winrt.init_apartment()
RuntimeError: Cannot change thread mode after it is set.>

Could someone help us to get it solved?

Regards,
Vinoth P B

@BenJKuhn BenJKuhn self-assigned this Sep 2, 2020
@BenJKuhn BenJKuhn added the bug label Sep 2, 2020
@AlexGuo1998
Copy link

Hi, same error here but when running scripts with a debugger attached.
Just run the demo code with PyCharm (pydevd) or VSCode debugger (debugpy):

import winrt.windows.foundation as wf

And it prints (in VSCode)

Traceback (most recent call last):
  File "C:\Program Files\Python37\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\Program Files\Python37\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "c:\Users\xxx\.vscode\extensions\ms-python.python-2020.9.114305\pythonFiles\lib\python\debugpy\__main__.py", line 45, in <module>
    cli.main()
  File "c:\Users\xxx\.vscode\extensions\ms-python.python-2020.9.114305\pythonFiles\lib\python\debugpy/..\debugpy\server\cli.py", line 430, in main
    run()
  File "c:\Users\xxx\.vscode\extensions\ms-python.python-2020.9.114305\pythonFiles\lib\python\debugpy/..\debugpy\server\cli.py", line 267, in run_file
    runpy.run_path(options.target, run_name=compat.force_str("__main__"))
  File "C:\Program Files\Python37\lib\runpy.py", line 263, in run_path
    pkg_name=pkg_name, script_name=fname)
  File "C:\Program Files\Python37\lib\runpy.py", line 96, in _run_module_code
    mod_name, mod_spec, pkg_name, script_name)
  File "C:\Program Files\Python37\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "c:\Users\xxx\PycharmProjects\test\winrt_demo.py", line 9, in <module>
    import winrt.windows.foundation as wf
  File "C:\Users\xxx\PycharmProjects\test\venv\lib\site-packages\winrt\__init__.py", line 4, in <module>
    _winrt.init_apartment()
RuntimeError: 无法在设置线程模式后对其加以更改。

My system language is Chinese, the last line translates to Cannot change thread mode after it is set.
Could you please investigate this?

@BenJKuhn
Copy link
Member

I may not get time to debug this right away, but I can give you a few quick tips that might help> WinRT users RoInitialize, which is basically just a wrapper around CoInitialize/ CoInitializeEx. If _winrt.init_apartment is failing due to having already been set, you should be able to debug by running the process under the VS or WinDbg debugger, and setting a breakpoint on three fucntions: combase!CoInitialize, combase!RoInitialize, and combase!CoInitializeEx. You might only actually need the last one. The error suggests that something else you're importing is already initializing COM, but defaulting to a different mode. Breaking on combase entry points should allow you to see when that's happening, and see which modes are being set.

PyWinRT should be tolerant of running in different modes though. This is something that I should be able to address (and probably add an API to check mode, since it will matter for some apps). PyWinRT defaults to MTA. It's likely the case that some other module that you're loading defaulted to STA and loaded first. I'll leave the issue open to address this in a future update. In the meantime I hope that helps you diagnose your issue.

@rye761
Copy link

rye761 commented Dec 6, 2020

I have run into this issue as well. In this case, I was able to get pythoncom to load using MTA by specifying sys.coinit_flags = 0 but then that breaks the Qt GUI. Not sure if that's because Qt is then trying to load STA and getting the same error WinRT is getting when it tries to load MTA. Error is Qt warning: QWindowsContext: OleInitialize() failed: "COM error 0xffffffff80010106 RPC_E_CHANGED_MODE (Unknown error 0x080010106)" Would probably be best if WinRT also respected the sys.coinit_flags var.

@BenJKuhn
Copy link
Member

I like the suggestion. We're not going to have time to look at this soon, so I'm going to mark this as a good first issue. The code in question is currently using C++ /WinRT's init_apartment helper. See:
https://github.com/microsoft/xlang/blob/master/src/tool/python/strings/module_methods.cpp

The work would be to change to check the python property, and call CoInitializeEx. It should also probably handle RPC_E_CHANGED_MODE gracefully and avoid calling CoUninitialize in that case as well.

@WT2
Copy link

WT2 commented Apr 20, 2021

Hello folks, did you find a solution for this one?
Thanks in advance :)

@rye761
Copy link

rye761 commented Apr 25, 2021

Hello folks, did you find a solution for this one?
Thanks in advance :)

We found that simply using the API on a separate thread worked for the minimal use it is being used for in Anki.

@dlech
Copy link
Contributor

dlech commented Jun 24, 2021

Ideally, importing modules should not have side effects. Having winrt call _winrt.init_apartment() on init violates this principal.

So, I think the options here are:

  1. Require users to manually call _winrt.init_apartment() if needed, e.g. not needed when using a GUI toolkit (breaking change).
  2. Add an environment variable check (or similar mechanism) to allow opting out of the automatic init. Or perhaps use the environment variable to select STA instead of the default MTA.
  3. Recommend that users call _winrt.deinit_apartment() before using a GUI toolkit or other COM interop code that requires a single apartment thread.

@zerkerX
Copy link

zerkerX commented Jan 10, 2022

This defect also caused us a bit of headache in our Sphinx (autodoc) documentation generation. We have a series of programs and libraries on our program; while none of the runtime combinations are problematic, the combined imports generating our documentation is. Since this dependency is only needed inside a function body, we ended up putting try/except around the import to work around this.

@dlech
Copy link
Contributor

dlech commented Jan 10, 2022

I actually found out recently that the explicit call to init_apartment() is not needed at all. (I'm guessing cppwinrt calls CoIncrementMTAUsage implicitly on the first creation of a winrt object.) So I removed the call in my fork.

For the Sphinx autodoc issue though, autodoc_mock_imports is probably a better solution.

@github-actions
Copy link

github-actions bot commented Mar 6, 2023

This issue is stale because it has been open 10 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@kennykerr
Copy link
Contributor

Please see this repo for Python support: https://github.com/pywinrt/pywinrt

@jamiemaclennan
Copy link

It's amazing that this can go unfixed. The code on the WinRT pypi page (https://pypi.org/project/winrt/) won't run in VSCode. Going to the file that calls init_apartment has a big WARNING: Please don't edit

How is any dev to know what they're supposed to do here?

@dlech
Copy link
Contributor

dlech commented Mar 10, 2023

Use https://pypi.org/project/winsdk/ instead of https://pypi.org/project/winrt/.

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

No branches or pull requests

9 participants