Sounds like you want Thrift.
Could you please elaborate on Windows part of your statement? What particularly are you referring to?
Other than the performance overhead (that I initially chalked up to 'oh, COM is just slow I guess'), the way I finally noticed was when a pointer I passed across the COM boundary wasn't valid (because it was to memory in another process). Whoops! Everything was working perfectly up until that point.
(FWIW, I am pretty sure the invalid pointer thing only happened because I was passing a raw address around - VB6 doesn't have pointer types.)
Plus, since COM provides ways to do source-level and binary compatibility, you can leverage that for your RPC.
I won't call it awesome, but it's quite robust and gets used in many places on Windows.
At a more basic level, you can trivially implement RPC using windows messages, though the security model improvements in 7 and 8 have made this a bit more complicated. There is excellent, straightforward infrastructure for establishing message loops and sending messages - really easy to get right - and it doesn't require your application to have UI. Pretty much any thread can receive and process messages if it wants to.
You accidentally summarized my experience with development on Windows in the nineties :-)
It's funny that what was designed as a feature of the ecosystem (tooling that abstracts complexity), completely turned me away from Windows development and into Linux development. Development on linux was, at the time, much cruder and closer to the metal. The positive note was that everyone was on the same boat, so documentation of low level routines, as well as community support, were flawless.
(not that I can, to this day, understand documentation created by kernel hackers, such as the documentation of nftables, tc or the ifb module, but I digress)
it’s not an OS primitive (yet: kdbus), but why does it need to?
OpenRPC and CORBA are out of fashion. Google protocol buffers help, but don't come with a standard RPC mechanism. HTTP with JSON is easy to do but has high overhead. Then there's the handling of failure, the curse of callback-oriented systems. ("He said he'd call back. Why didn't he call back? Doesn't he like me any more? Should I dump him?")
The lack of a good, standardized interprocess call mechanism results in interprocess call logic appearing at the application level. At that level, it's intertwined with business logic and often botched.