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

Bug: co_await apartment_context crashs with ''CoInitialize has not been called.'' #1415

Closed
heroboy opened this issue May 7, 2024 · 3 comments

Comments

@heroboy
Copy link

heroboy commented May 7, 2024

Version

C++/WinRT v2.0.220110.5

Summary

No response

Reproducible example

winrt::fire_and_forget foo()
{
	co_await winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(L"c:\\");
}

winrt::fire_and_forget bar()
{
	winrt::apartment_context ctx;
	co_await winrt::resume_background();
	co_await ctx;
}


int main() {

	winrt::init_apartment(winrt::apartment_type::single_threaded);
	//foo();
	bar();

	MSG msg;
	while (GetMessage(&msg, 0, 0, 0))
	{
		DispatchMessage(&msg);
	}
	return 0;
}

Expected behavior

no crash.

Actual behavior

crash at co_await ctx; with 'CoInitialize has not been called.'

Additional comments

Uncomment the foo(), await any async windows runtime function, and later co_await ctx will no longer crash.
Or call winrt::init_apartment() after winrt::resume_background()

@dmachaj
Copy link
Contributor

dmachaj commented May 7, 2024

This example program does not initialize the MTA in the process. The background tasks therefore run outside the MTA and the "you didn't initialize COM" error is raised.

The call to foo works around this issue because the implementation of StorageFolder::GetFolderFromPathAsync has a side effect of initializing the MTA in the process.

I think the appropriate fix would be to have your code initialize the MTA before it tries to use it. One way that can be done is by calling CoIncrementMTAUsage. Simply call that before the call to bar and the crash is avoided.

@heroboy
Copy link
Author

heroboy commented May 8, 2024

Thank you.
I had never think apartment is a property of process.
I tested:

std::thread{ []() {
	winrt::init_apartment();
	::Sleep(10000);
} }.detach();

std::thread{ []() {
	auto type = winrt::impl::get_apartment_type();
	printf("%d\n",type.first);
} }.join();

I think once a thread is initialized as MTA. All thread not initialized will be considered as MTA.

@heroboy heroboy closed this as completed May 8, 2024
@heroboy
Copy link
Author

heroboy commented May 8, 2024

But I find that initialized with single threaded will also make the ContextCallback success.

void test()
{
	winrt::com_ptr<IContextCallback> ctx;
	CoGetObjectContext(IID_IContextCallback, ctx.put_void());
	assert(ctx);

	TrySubmitThreadpoolCallback([](PTP_CALLBACK_INSTANCE pci, PVOID p) {
		
		auto x = winrt::impl::get_apartment_type();
		winrt::com_ptr<IContextCallback> ctx;
		*ctx.put_void() = p;
		winrt::init_apartment(winrt::apartment_type::single_threaded);
		ComCallData data;
		HRESULT hr = ctx->ContextCallback([](ComCallData*) {
			return S_OK;
		}, &data, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr);
		printf("[%d]hr=%d\n", GetCurrentThreadId(), hr);

	}, ctx.detach(), 0);
}

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