throws an InvalidCastException HResult=-2147467262.
You are getting a much more detailed exception message, it will tell you that the cast failed due to the E_NOINTERFACE error return. Which is normally pretty hard to diagnose, except in this case, there really is no interface. The declarations you used in the client are a gross mismatch with the ones used in the server:
the ComRibbonApplicationMenuServerClass declaration in the client doesn't implement any interface. Since you arbitrarily omitted [ClassInterface(ClassInterfaceType.None)], .NET will auto-generate one. It has an random [Guid] that will never match the server's, thus generating the E_NOINTERFACE error.
you arbitrarily gave the client side interface declaration the [InterfaceType(ComInterfaceType.InterfaceIsDual)] attribute. The server omits it and thus uses the default which is ComInterfaceType.InterfaceIsIDispatch. Such an interface can only be called late-bound, the client has to use IDispatch. Which means that if you fix the problem in the first bullet, it will still fail because the server doesn't actually implement the interface. In C#, you'd have to use the dynamic keyword to use such a server.
Clearly it is absolutely essential that the client uses the exact same declarations as the server, normally ensured by using Tlbexp.exe
on the server to generate a type library. It is somewhat guessable why you got into this pickle, the IDE will refuse to let you add a reference to the type library, it can see that the server was implemented in .NET and tell you to add a normal .NET reference instead. Which is rather good advice, it doesn't make sense to use COM at all when the normal .NET way already works in a much superior way.
You can fool the machine by using a decompiler on the interop library that Tlbexp.exe generates and copy/paste them into the client, thus making sure you have an exact match. Or by using late-binding with the dynamic keyword in the client app, required anyway because the server uses ComInterfaceType.InterfaceIsIDispatch. Also much less painful since you don't have to repeatedly do the copy/paste step when you alter the server.
But you do need to keep in mind that you are not actually using COM when you do this, the CLR itself is smart enough to see that there's .NET code talking to .NET code and will skip creating the RCW and CCW. So if you are doing this for testing, do keep in mind that you are not actually testing the server the way it is going to be used in a real client app. You might as well test it by actually adding a .NET reference.