You're staring at 0x8001000B in Event Viewer or a stack trace, and the app just won't talk to its COM server. Annoying, but it's usually one of three things and each one has a clean fix.
The fix
Before you reinstall anything, run this. It tells you whether the failure is memory-related (transient) or structural (the client is genuinely trying to send something the marshaller can't serialize).
Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='Microsoft-Windows-DistributedCOM'} -MaxEvents 50 |
Where-Object { $_.Message -match '0x8001000B' } |
Select-Object TimeCreated, Message | Format-List
If you see the same failing method every time — same interface, same arguments — skip step 1 and go to step 2. If the failures are scattered across different calls and correlate with load, do step 1.
- Relieve memory pressure on the client. The marshaller allocates a flat buffer for every parameter before the RPC call goes out. A 500 MB
SAFEARRAYparameter will try to allocate 500 MB in one contiguous chunk. On a 32-bit client that's already using 1.2 GB of its 2 GB user-mode space, that allocation fails, and you get0x8001000Binstead of a cleanE_OUTOFMEMORY. Real-world trigger: a VB6 or .NET x86 app that loads a big CSV, holds it in memory, then passes it to an out-of-process COM server. Fix: rebuild the client as x64, or stream the data in chunks. - Check the proxy/stub for size assumptions. If you wrote the IDL, run
midl /env win32and inspect the generated_proxy.c. Look forNdrOleAllocatecalls with a hard-coded size, or a[size_is]parameter paired with alonglength field. A signed length that becomes negative after a cast will make the marshaller try to allocate a ridiculous buffer. The fix is to declare length parameters asunsigned longorULONGin the IDL and rebuild both proxy and stub. - Register the correct proxy/stub. If the server was built by someone else and you're consuming it, a mismatched proxy DLL (32-bit client reaching a 64-bit server through a stale proxy) will fail marshaling on parameter types the client thinks it knows. Run
regsvr32 /uthenregsvr32on the proxy DLL in the matching bitness. Check withGet-ItemProperty HKLM:\SOFTWARE\Classes\Interface\{your-iid}\ProxyStubClsid32.
Why this works
What's actually happening is that the COM client, before it ever touches the network, has to flatten every [in] parameter into a contiguous byte stream that the RPC runtime can ship. That flattening is the marshaling step, and it's done by the proxy, which calls into rpcrt4.dll and ultimately NdrClientMarshal. If any of those allocations fail, or any of the type descriptors don't match what the stub on the other side expects, the runtime bails with RPC_E_CLIENT_CANTMARSHAL_DATA. The reason step 1 fixes it is that the failing allocation in NdrOleAllocate is contiguous — fragmenting your heap with a bunch of smaller blocks won't help, you need the whole chunk to fit. The reason step 2 fixes it is that the type descriptor in the generated proxy is what the runtime trusts; if it says "allocate len bytes" and len is negative, len as a size_t becomes enormous and the allocation throws. The reason step 3 fixes it is subtle: 32-bit and 64-bit proxies have different wire formats for pointer-sized types, so a mismatch produces a descriptor that doesn't match the stub's expectations, and marshaling fails before the call even leaves the process.
Less common variations
- Custom marshaler returning wrong size. If your type uses
IMarshalor a custom[wire_marshal], andGetMarshalSizeMaxreturns a value smaller than whatMarshalInterfaceactually writes, the buffer overruns and the runtime reports0x8001000Binstead of a buffer-overrun error. Audit the two methods together. CoInitializeSecuritycalled after the first marshaled call. Marshaling happens before the security context is fully set up in some cross-apartment scenarios. Move yourCoInitializeSecuritycall to right afterCoInitializeEx, before any interface pointers cross apartments.- VARIANT holding a byref array to a 64-bit server from a 32-bit client. The
VT_ARRAY | VT_BYREFcombination is a known marshaling trap. Convert to a plainVT_ARRAYwith a copy, or useSafeArrayCopybefore the call. - Heap corruption from an earlier bug. If you've already got a heap that's been scribbled on, the marshaller's allocation can fail for reasons that have nothing to do with RPC. Run the client under Application Verifier with the Basics and Heaps checks on. If it trips, you've got a different bug wearing this error's clothes.
Prevention
Keep marshaled parameters small. A megabyte here and there is fine; half a gigabyte across an apartment boundary is asking for this error on 32-bit. Prefer streaming interfaces (IStream, IEnumXXX) over SAFEARRAY when the payload is big or unbounded. Build the client and server with the same compiler and the same MIDL, in the same bitness, and ship the proxy/stub as a pair — never mix. Add a unit test that calls the interface with the largest payload you expect in production; if it survives on a fresh process but fails on a process that's been running for a day, you've got a fragmentation problem and you need to fix the allocation pattern, not just bump the RAM.
The short version: this error means the client can't pack the parameters. Fix the packing (memory, types, bitness), and it goes away.