Current configuration is:
Everything works ok until we've encountered problem with versions. When 2 versions of application (containing 2 versions of TLB) were installed on the same computer, both versions of TLB (for example, 2.0 and 2.1) are registered, and lower version stops to work. ProcessMonitor shows that, when trying to call web service of version 2.0 instance, it looks into registry for TLB's GUID, reads all sub-keys with version numbers, takes the last version (2.1) and starts reading its contents. After that, it reads DLL of version 2.1, and fails to make COM calls inside its own process - as far as I understand, due to problems in marshaling.
How can I make the Interop that would not require presence of TLB (at least, presence of registered TLB) to make in-process COM calls?
Thanks in advance.
Well, here's the way the problem was solved.
.NET seems to load TLB to build new apartment for each ASP.NET thread (everything happens when new thread first time tries to access some COM interface). It seems to use CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream functions for this purpose. I'm not able to prove that, but calling these functions causes the same effect: querying system registry and loading TLB of the highest compatible version (the same major version, highest minor version). CoGetInterfaceAndReleaseStream behavior isn't affected by manifest.
So, the real problem is that our project does not follow rules Microsoft recommends for version numbering: COM interfaces having the same major version should be compatible.
The only solution found was splicing: to get address of LoadTypeLib function (OleAut32.dll), and set JMP instruction in its beginning. JMP goes to our implementation, that checks name of TLB being loaded, and if it's one of known TLBs - redirects reading to local directory.