• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 909
  • Last Modified:

FileLoadException in .NET remoting

I have a .NET Remoting server that implements a custom interface, and a client application that talks to the server.  I am having trouble with backwards compatibility when I build a new version of my server -- that is, if I rebuild my server code and only change the version of the assemblies, my older client applications are unable to call certain methods on the interface while other methods work without any problems.  The methods that fail generate a FileLoadException.

After some testing it seems that the only methods that break are the interface methods that are overloaded and one of the overloaded function parameters is a custom class that I have defined in my assembly.  As near as I can tell, the overload method resolution algorithm is looking for an overload that takes a specific version of the custom class, and since the client and host are technically using different versions of the custom class assembly, it can't find the appropriate overload.

For what it's worth, methods that pass a custom class don't seem to have any problems as long as the method is not overloaded, and overloads don't have a problem as long as the parameters are all "stock" .NET types.

For instance, suppose I have defined the following:

   <Serializable()> _
   Public Class TestClass
   End Class

   Public Interface ITest
      Function TestMethod(ByVal abc As Date) As Integer
      Function TestMethod(ByVal abc As Single) As Integer
      Function TestMethod(ByVal abc As TestClass) As Integer
   End Interface

I build both my client and server referencing the above assembly, and they work just fine.  But if I change the version number on this assembly, and rebuild my server, my older client can still call the first two TestMethod(..) methods, but attempts to call the third generate a FileLoadException.

(Note: my assembly is signed with a strong name key file.)

I haven't been able to find any "official" documentation saying that this isn't allowed -- only one or two other unanswered posts on other sites -- so first I wanted to see if anyone knows for sure that this isn't allowed.  Second, now that I have some of this software in the field, is there any way that I can make future versions of my server backwards compatible with existing installations of my client?  That is, I have already deployed client applications at say version 1 that talk to my server at version 1, but now I need to add new functions to this interface and thus must release a version 2 of my server, but I need the previously distributed clients at version 1 to continue to work.

Thanks,
AJ

0
TomPro
Asked:
TomPro
  • 6
  • 4
1 Solution
 
_Katka_Commented:
Hi, what versions of .NET are you using ? Did you change your version of .NET in between your version 1.0 and 2.0 ?

regards,
Kate
0
 
_Katka_Commented:
In a meanwhile try to check this link if it resolves your problem. It deals with client version being stable while server is upgraded.

http://support.microsoft.com/kb/821626

regards,
Kate
0
 
TomProAuthor Commented:
We are using .NET 2.0 and this was not changed between our versions.

Regarding the KB link, I don't think that the problem description there accurately represents our scenario.  For one, the client only has a single copy of the .dll as it was initially installed -- this is version 1.  Our server has been rebuilt as version 2, and but when the older clients connect they only have version 1 of the .dll.  Since nothing has changed in the underlying code, I'd like the client & server just to ignore the version number and treat the two different versions of the custom "TestClass" class as the same -- like it does for the functions that aren't overloaded.

Thanks,
AJ
0
The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

 
_Katka_Commented:
Can you post here, the contents of the FileLoadException (also FusionLog), maybe there's a binding problem ? So
it may be that client is requesting a version 1.0 of TestClass, but it cannot be found as there is only 2.0 available.

<check the version redirection below>

regards,
Kate
<configuration>
   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <dependentAssembly>
            <assemblyIdentity name="myAssembly"
                              publicKeyToken="32ab4ba45e0a69a1"
                              culture="neutral" />
            <bindingRedirect oldVersion="1.0.0.0"
                             newVersion="2.0.0.0"/>
         </dependentAssembly>
      </assemblyBinding>
   </runtime>
</configuration>

Open in new window

0
 
_Katka_Commented:
To clarify, the code below redirects requests for version 1.0 to version 2.0, it's placed in the App.config (in on the client-side). A FileLoadException (or fusion-log) will hopefully shed some light on this problem.

regards,
Kate
0
 
TomProAuthor Commented:
The full details for the exception are shown below:

----------
A first chance exception of type 'System.IO.FileLoadException' occurred in mscorlib.dll
System.IO.FileLoadException: Could not load file or assembly 'TPRI.Core, Version=4.27.0.0, Culture=neutral, PublicKeyToken=66beb55932c93bb2' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
File name: 'TPRI.Core, Version=4.27.0.0, Culture=neutral, PublicKeyToken=66beb55932c93bb2'

Server stack trace:
   at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
   at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
   at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
   at System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
   at System.Reflection.Assembly.Load(String assemblyString)
   at System.UnitySerializationHolder.GetRealObject(StreamingContext context)
   at System.Runtime.Serialization.ObjectManager.ResolveObjectReference(ObjectHolder holder)
   at System.Runtime.Serialization.ObjectManager.DoFixups()
   at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   at System.Runtime.Remoting.Channels.CoreChannel.DeserializeBinaryRequestMessage(String objectUri, Stream inputStream, Boolean bStrictBinding, TypeFilterLevel securityLevel)
   at System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at TPRI.NtRtInfo.INtRtInfo.GetFileSystemInfos(String userName, String password, String domain, String shareName, String subpath, String filter, NtRtFileInfo[]& fileinfo)
   at TPRI.NtRtInfo.Client.GetFileSystemInfos(String userName, String password, String domain, String shareName, String subpath, String filter, NtRtFileInfo[]& fileinfo) in D:\tpriNtRt\std\Projects\Core\NtRtInfo\Client.vb:line 1682

WRN: Assembly binding logging is turned OFF.
To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.
Note: There is some performance penalty associated with assembly bind failure logging.
To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].
----------

I can't get the fusion logging to run even though I have set the registry value as directed.  To be clear, though, the client does not ever try to download updated files from the host system -- it only runs with the files that were originally installed.

I also tried to add the code to my app.config file (updating the name, publicKeyToken, oldVersion, and newVersion as appropriate), but it didn't seem to make a difference.  Even if it worked, though, I'm not sure that this would be an acceptable solution because it would still require me to make a change to all of the app.config files on each of the clients -- if I have to do that then I might as well just update the .exe and .dll's.

Thanks,
AJ



0
 
_Katka_Commented:
This is definitely a mismatch in version, an assembly with that type cannot be located. I'm not sure if this can be decently solved without intervention on the a client-side. It's better to have a redirection in the App.config as it is doesn't require you to rebuild the client (thus violating the distributed gold version). It can be redirected on machine level, but that still doesn't solve your problem. I'd try to use fuslogw.exe (fusion log) to determine why a binding is not working. Your options are:

A) Don't change assembly version, only file version

B) Change your client to point directly to a version "2.0"

C) Make a redirection in app.config (as I posted), but first determine what's wrong (I guess redirection is still missing something)

1) install Visual Studio SDK (if not already installed)
2) run fuslogvw.exe (located in "C:\Program Files (x86)\Microsoft SDKs\Windows\v6.0A\Bin\")
3) open settings (Settings...), and choose "Log bind failures to disk"
4) delete all messages (Delete All)
5) run your client
6) a binding failure should appear in the list, double click it
7) post-it here

regards,
Kate
0
 
TomProAuthor Commented:
Below is the output from the fuslogvw.exe.  In this case my server is v4.27.0.0 and my client is v4.28.2.0.

----------

*** Assembly Binder Log Entry  (4/13/2010 @ 4:14:53 PM) ***

The operation failed.
Bind result: hr = 0x80131040. No description available.

Assembly manager loaded from:  c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
Running under executable  D:\tpriNtRt\std\Projects\Texplore\bin\Debug\Texplore.exe
--- A detailed error log follows.

=== Pre-bind state information ===
LOG: User = AJJ8\ajj
LOG: DisplayName = TPRI.Core, Version=4.27.0.0, Culture=neutral, PublicKeyToken=66beb55932c93bb2
 (Fully-specified)
LOG: Appbase = file:///D:/tpriNtRt/std/Projects/Texplore/bin/Debug/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = Texplore.exe
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: D:\tpriNtRt\std\Projects\Texplore\bin\Debug\Texplore.exe.config
LOG: Using machine configuration file from c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
LOG: Post-policy reference: TPRI.Core, Version=4.27.0.0, Culture=neutral, PublicKeyToken=66beb55932c93bb2
LOG: GAC Lookup was unsuccessful.
LOG: Attempting download of new URL file:///D:/tpriNtRt/std/Projects/Texplore/bin/Debug/TPRI.Core.DLL.
LOG: Assembly download was successful. Attempting setup of file: D:\tpriNtRt\std\Projects\Texplore\bin\Debug\TPRI.Core.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: TPRI.Core, Version=4.28.2.0, Culture=neutral, PublicKeyToken=66beb55932c93bb2
WRN: Comparing the assembly name resulted in the mismatch: Minor Version
ERR: The assembly reference did not match the assembly definition found.
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.

----------

I've tried to compare cases where this works i.e. calling a function that isn't overloaded but still uses a custom class in my assembly, and they look to be similar.  In both cases I do see a successful fusion log entry like the following:

----------

*** Assembly Binder Log Entry  (4/13/2010 @ 4:23:55 PM) ***

The operation was successful.
Bind result: hr = 0x0. The operation completed successfully.

Assembly manager loaded from:  c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
Running under executable  D:\tpriNtRt\std\Projects\Texplore\bin\Debug\Texplore.exe
--- A detailed error log follows.

=== Pre-bind state information ===
LOG: User = AJJ8\ajj
LOG: DisplayName = TPRI.Core
 (Partial)
LOG: Appbase = file:///D:/tpriNtRt/std/Projects/Texplore/bin/Debug/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = Texplore.exe
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: D:\tpriNtRt\std\Projects\Texplore\bin\Debug\Texplore.exe.config
LOG: Using machine configuration file from c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///D:/tpriNtRt/std/Projects/Texplore/bin/Debug/TPRI.Core.DLL.
LOG: Assembly download was successful. Attempting setup of file: D:\tpriNtRt\std\Projects\Texplore\bin\Debug\TPRI.Core.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: TPRI.Core, Version=4.28.2.0, Culture=neutral, PublicKeyToken=66beb55932c93bb2
LOG: A partially-specified assembly bind succeeded from the application directory. Need to re-apply policy.
LOG: Using application configuration file: D:\tpriNtRt\std\Projects\Texplore\bin\Debug\Texplore.exe.config
LOG: Using machine configuration file from c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config.
LOG: Post-policy reference: TPRI.Core, Version=4.28.2.0, Culture=neutral, PublicKeyToken=66beb55932c93bb2
LOG: Binding succeeds. Returns assembly from D:\tpriNtRt\std\Projects\Texplore\bin\Debug\TPRI.Core.dll.
LOG: Assembly is loaded in default load context.

----------

Here it looks like it is searching for the assembly with only a partial name (w/out version number), and is successful.  But again, I see this entry in both cases -- it's only when calling the function that is overloaded that the exception is generated in my application.

Thanks,
AJ

0
 
_Katka_Commented:
As far as I can tell, it seems that problematic is TPRI.Core.dll. I'd guess that you share this assembly both by a server and client, while you versioned it for one, but the other one still references the old assembly version. Try to redirect also the version of the TPRI.Core.dll in the App.config from 4.27.0.0 to 4.28.2.0. In the future, I'd suggest to:

1) version both server and client simultaneously in a case they share the code that has changed (only version breaker), thus choose option A (from previous post) as it is a good practice, a short post from Suzanne Cook on when to change assembly version, and why it's usually better to just increase only the file version and let the assembly versioning only for those cases when shipping incompatible (API has changed, or backward compatibility was broken) client-server versions: http://blogs.msdn.com/suzcook/archive/2003/05/29/57148.aspx - assembly version should denote the incompatibility, while file version should be the real visual version - it's getting usually mixed together, and both versions are increased at the same time, which is ok, when you're distributing independent products (both server and client at the same time) but this doesn't seem to be the case

2) you can solve it by redirection, this can lead to a web of redirections which can easily get out of hands, but you have explicit control over client-server compatibility, also a check on the client would be good (if not already implemented), in my experience it can usually be handled pretty well, but we're still using option 3) thou

Option 1 in more detail:

So it should go like this (ideally) but sometimes a redirection is needed anyway (human error disaster) - assembly_name file-version (assembly-version):

Client 1.0 (1.0) - Server 1.0 (1.0) - Shared assembly 1.0 (1.0) - ok no problem here
Client 1.1 (1.0) - Server 1.0 (1.0) - Shared assembly 1.0 (1.0) - only client has changed slightly (GUI for example), APIs are the same so no assembly version changed
Client 1.2 (1.1) - Server 1.1 (1.1) - Shared assembly 1.1 (1.1) - API has changed (interfaces are in the shared assembly, client + server uses the shared assembly thus a change in all of them), the old client won't work with a new server, and that's desired, because the API is incompatible anyway (it should be checked by a client instead of just terminating on the FileLoadException as in unhandled)
Client 1.3 (1.1) - Server 1.1 (1.1) - Shared assembly 1.2 (1.1) - both client and shared assembly has changed, but only minor changes (no version breakers) - old client will still work with server because the assembly version holds still
Client 1.3 (1.1) - Server 1.2 (1.1) - Shared assembly 1.2 (1.1) - server has changed, but again only cosmetic changes, thus only increase file-version, so the old client will work happily with a new file-version server

As you can notice in this approach only compatible version will work together. The client with assembly version 1.1 won't and shouldn't work with 1.0 (because the API has changed), thus it won't, while file-versions are increasing happily.

So my recommendation is to use option 2) now to fix the things without a hassle, and try to consolidate your assembly versioning as per option 1) in the future, to avoid these complications. If you still choose the road of option 2) redirection should solve it.

Note: I hope, I didn't make some fatal typo in the versioning.

Let me know, if the redirection of the TPRI.Core.dll did it.

regards,
Kate
0
 
TomProAuthor Commented:
It sounds like I just can't do what I was trying to do.  Katka explained my mistake -- changing assembly version instead of the file version.
0

Featured Post

Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

  • 6
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now