0X80010011

RPC_E_CANTCALLOUT_AGAIN (0X80010011) – DDE Channel Limit Hit

Server & Cloud Intermediate 👁 8 views 📅 May 28, 2026

This error means your app tried to make a second DDE call on the same channel before the first one finished. The fix is usually serializing calls or using a fresh conversation handle.

1. DDE Call Overlap – the culprit 9 times out of 10

This error pops up when your app fires a second DDE transaction on the same conversation handle while the first one hasn't returned yet. DDE is single-threaded per channel by design – Windows won't let you nest calls. I see this most often with Excel automation: a macro calls DdeClientTransaction, and before that call completes, another macro (or the same one) tries to send another request. Boom – 0x80010011.

Fix it now

  1. Find every place your code calls DdeClientTransaction or DdeAbandonTransaction.
  2. Wrap them in a mutex or critical section. Only one call per conversation handle at a time.
  3. If you're using MFC's CDdeConversation, override OnExecute / OnRequest and ensure they don't trigger nested DDE operations.

Here's a C++ snippet that serializes DDE calls using a Win32 critical section inside a wrapper function:

CRITICAL_SECTION g_ddeCS;

HDDEDATA SafeDdeClientTransaction(LPBYTE pData, DWORD cbData, HCONV hConv,
LPWSTR pszItem, UINT wFmt, UINT wType, DWORD dwTimeout, LPDWORD pdwResult)
{
EnterCriticalSection(&g_ddeCS);
HDDEDATA hData = DdeClientTransaction(pData, cbData, hConv,
pszItem, wFmt, wType, dwTimeout, pdwResult);
LeaveCriticalSection(&g_ddeCS);
return hData;
}

Don't forget to initialize the critical section with InitializeCriticalSection at startup and DeleteCriticalSection on shutdown. This alone fixes the error in 90% of cases.

2. Using the Same Conversation Handle Across Threads

DDE conversation handles are not thread-safe. If you pass the same HCONV to two threads and both call DdeClientTransaction simultaneously, you'll hit 0x80010011. Most devs don't realize this because COM and named pipes are thread-safe – DDE isn't.

Fix it

  • Either serialize all access to the handle (same critical section as above), or
  • Create separate conversation handles per thread. Call DdeConnect once per thread, store the handle in thread-local storage, and never share it.

I've seen teams waste days trying to lock the server application. It's not the server – it's your own thread safety. Check your codebase for any DdeConnect result being stored in a global variable used by multiple threads. That's the smoking gun.

3. The Server Application Is Blocking on a Callback

Less common, but I've hit this with custom DDE servers that call DdePostAdvise or DdeQueryNextServer from inside a transaction callback. If the server's DdeCallback function makes another DDE call (even indirectly through a message pump), it can deadlock the DDE subsystem. The client then gets 0x80010011 on the next attempt.

Fix it

  • Audit your server's DdeCallback. No DDE API calls should be made from inside the callback. Post a message to a worker thread instead.
  • If the server uses DdeInitialize with APPCMD_CLIENTONLY or APPCMD_FILTERINITS, double-check the flags – some combinations disable reentrancy.
  • For Excel as a DDE server: Excel itself can choke if a DDE request triggers a worksheet recalculation that tries to pull data from another DDE source. Break the circular dependency – use static values or a separate data channel.

I once spent three hours chasing this on a trading app that used DDE to pull real-time prices from Bloomberg into Excel. The fix was to have the Excel VBA macro queue the DDE request with Application.OnTime instead of calling it directly during a worksheet change event.

Quick-Reference Summary Table

CauseTypical SymptomFixEffort
Overlapping calls on same channelError appears during back-to-back DDE requests in a loopSerialize with mutex/critical section30 mins
Multi-threaded handle sharingError only in multi-threaded scenarios, not single-threadUse per-thread conversation handles1-2 hours
Server callback reentrancyError happens when server processes a request that triggers another DDE callQueue callbacks to separate thread; avoid DDE in callback2-4 hours

Was this solution helpful?