What does error E_ILLEGAL_DELEGATE_ASSIGNMENT mean?

Raymond

A customer reported that they program was crashing because it got the error E_ILLEGAL_DELEGATE_ASSIGNMENT. The description of this error is “A delegate was assigned when not allowed,” but what does that mean?

The most common source of this error is attempting to set a completion callback for a Windows Runtime IAsyncXxx asynchronous operation when a callback has already been set. There can be only one completion callback for an asynchronous operation, and you get this error if you try to set a second one.

You typically do not set completion callbacks explicitly. They are usually set for you as part of your programming framework. In the case of C++/WinRT, the most common place it it happens is when you co_await an asynchronous operation. (Less common sources are functions like when_all or when_any.)

The good news is that the stack trace from the crash dump usually points to the code that attempted to register the second callback.

SomeClass::SomeClass()
{
    m_start = StartAsync();
}

IAsyncAction SomeClass::DoThing1Async()
{
    co_await m_start;
    Thing1();
}

IAsyncAction SomeClass::DoThing2Async()
{
    co_await m_start; // ← crash here
    Thing2();
}

The idea here is that the object initializes itself at construction, but does so asynchronously. When the Do­Thing methods are called, they first wait for the initialization to complete, and then proceed with the actual operation.

The problem is that we are co_awaiting the m_start object more than once. Each coroutine’s call to co_await will try to set its own continuation as the completion handler so it can resume execution when the operation completes. The first one to perform the co_await succeeds. The rest fail with E_ILLEGAL_DELEGATE_ASSIGNMENT.

The rule that we walk away with is “Each IAsyncXxx can be awaited only once.”

Okay, so what do we do if we need to await something more than once, like here, where we want all of the operations to wait for the initialization to complete before proceeding with work?

You’ll have to find something that can be awaited more than once.

We’ll look into possible solutions next time.

4 comments

Comments are closed. Login to edit/delete your existing comments

  • Neil Rashbrook

    In JavaScript you can of course await the same Promise multiple times, and it’s really handy to be able to create something asynchronously, so to speak, and then have all the users await the result of the creation. (Less vaguely, the arrangement I’m thinking of is that there’s an “asynchronous” lookup function which first of all tries to retrieve a cached Promise, and if one doesn’t exist it creates one and asynchronously initialises the object, putting the resulting Promise in the cache, returning it for the caller to wait for. The lookup function can’t itself wait for the initialisation of course.)

    • Neil Rashbrook

      Ugh, I mean that it creates and asynchronously initialises an object, but for some reason I don’t seem to be able to edit my comment…

  • 紅樓鍮

    cppcoro::shared_task (github.com/lewissbaker/cppcoro#shared_taskt) looks good to me, though the library doesn’t seem to aim to be production-grade.

  • Ian Yates

    Reminds me of the ValueTask vs Task differences in .net – one you await once only and the other you can await as much as you like (as another commenter said, like the well known JS Promise)