10-20-2009 07:22 AM
Hello,
I spent half a day on this one... so I'm beat up and fed up...
I'm having an issue, I want to create a .NET object using a library I've created.
The assembly(library) has codependencies on several other assemblies. ( all are in the same folder as the sequence file ).
And down the line, when I want to perform the contructor call I get the message
Unable to find assembly X error and I get the stacktrace.
Now... I had this thing before when I was using the same .NET lib in a bare VI (bare-meaning not iside a lvproj file), then I read that I need a LabVIEW project in order to properly search for the libraries.
I've done as the help states and all was fixed.
So I've created a workspace file, and also I've set the search directory on the directory when I have the assemblies.But it still crashes.
( I'm probably missing something ... 😞 )
The only very dirty workaround I've managed to do, is to upload the .NET assemblies that TestStand can't find to the <TeststandInstallationDir>\bin.
As I figured out that CLR will probably have the SeqEdit.exe set in as one of the possible search locations. And it worked.
Please help me out as this isn't the kind of solution I'm going to be able to deploy for a customer.
I also cannot add my .NET assemblies to the GAC as they're not strongly signed, and I'm not their developer.
Thanks for any help,
Maciej
Solved! Go to Solution.
10-21-2009 09:45 AM - edited 10-21-2009 09:47 AM
Hello Macie,
TestStand uses the Assembly.LoadFrom() method when calling Assemblies from the .NET Adapter. Using this method of calling .NET Assemblies, TestStand will automatically load dependencies from the same directory as the top-level assembly specified in the Module tab of the .NET Adapter Step Settings pane. I've put a simple example together and attached it to this post to demonstrate this behavior.
I'm not sure why your assemblies are encountering this behavior and unfortunately it sounds like you don't have the source code for them. You may want to look at the MSDN Help Topic: Locating the Assembly through Codebases or Probing to get an understanding of how the CLR probes for assemblies and see if using a Configuration file would give you the ability to store your assemblies in a directory other than the application directory (the codebase option may be the only one that will possibly work).
Hope this helps!
10-21-2009 12:03 PM
Macie -
Could you please provide the call stack displayed in the Error dialog? A simple copy and paste to this thread would suffice. This might help me to provide you with a workaround for this problem.
Thanks and I look forward to your response!
10-21-2009 01:46 PM
Manooch_H, thanks for your interest and prompt reply, I will be looking through your first reply tommorow, as my business day is over.
Just to give you some more information, this is the dependencypath :
A.dll
B.dll
C.exe <--- this is the assembly where TS says it can't find the lib, even though all the dll's are in the same dir.
(4 more dll's, that C.exe depends on)
The requested stacktrace.
( I've renamed the assembly names,namespaces, and hashed the version in the stacktrace )
-------------------------------------------------------------------------------
The instance of the .NET class could not be retrieved.
Unable to find assembly 'C, Version=X.X.X.X, Culture=neutral, PublicKeyToken=null'.
Source: mscorlib at System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name)
at System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable)
at System.Runtime.Serialization.Formatters.Binary.ObjectMap.Create(String name, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
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.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
at Company.C.Tool.ToolDocument.Open(Stream stream, String defaultToolName)
at Company.C.ToolSpecification.ToolSpecificationGenerator.LoadToolDocument()
at Company.C.ToolSpecification.ToolSpecificationGenerator.Generate()
at Company.B.Tasks.CreateToolSpecification(String toolPath)
at Company.A.ToolSpecification..ctor(String toolFilePath) in D:\Projects\TestingLibraries\A\ToolSpecification.cs:line 60
10-22-2009 07:57 AM
Hi,
I've done some reading on the Assembly.LoadFrom().
Although the mehanism of that function isn't clear to me in 100%... based on what I managed to find out, everything should work the way it is set up now,
but it doesn't.
I still haven't figured this one out.
Regards,
Maciej
10-22-2009 09:39 AM - last edited on 10-22-2009 12:23 PM by Support
Macie,
Thanks for providing the error call stack. This helped us understand the problem such that we could investigate it further and provide a solution for you. Here's the reason you're receiving this error.
TestStand's .NET Adapter uses the Assembly.LoadFrom() method to load the specified .NET assembly into memory. The LoadFrom() method provides the useful feature of searching for any dependencies of the specified assembly within the same directory. This allows TestStand to load assemblies and their dependencies from locations other than the Global Assembly Cache (GAC) or the Application Directory (<TestStand>\Bin). When TestStand calls the LoadFrom() method on the specified assembly and it's dependencies, the assembly and dependencies are loaded into memory in the LoadFrom Context.
When you attempt to deserialize types in your code, the CLR will look to the Load Context for the assembly that contains the type that is deserialized. Because the Load Context is a different context than the LoadFrom Context, the CLR does not recognize that the assembly is already loaded into memory and it tries to load it using the Assembly.Load() method. The Load() method only searches for assemblies in the GAC and the Application Directory. Thus, calling the Load() method on the assembly that contains the type that is deserialized will fail if your assemblies are not located in the GAC or Application Directory and you will receive the error that you're seeing.
There are a couple of solutions to this behavior:
1. You can create an Application Configuration file that uses the Codebase element to point to the location of the necessary assemblies on disk and place the config file (SeqEdit.exe.config) in the Application Directory (<TestStand>\Bin). This would require that you provide a <codeBase> element with the absolute path of the assembly for every assembly that contains types that are deserialized. This approach has a couple of downsides:
The SeqEdit.exe.config file would look similar to the image below:
2. You can create your own .NET assembly to provide an AssemblyResolve event handler that returns the assembly already loaded in the LoadFrom Context when deserialize is called. This assembly would have to be called at the beginning of your sequence for each of your assemblies that contain types that are deserialized. This is the approach that I would recommend.
I have attached a zip file below that contains an example of the above mentioned problem and proposed solutions. This zip file contains four files:
Please let me know if you have any questions regarding this example. Hope this helps!
10-22-2009 11:29 AM
Macie -
I've updated the ResolveAssembly.dll source code to make it threadsafe. Please use the example attached to this post.
10-22-2009 12:50 PM
Works like a charm:)
Thank you isn't enough. 😄
But Thank you very much for digging into this, and not only giving a reply but even giving the 🙂 code & dll to fix this.
Best Salutaions,
Maciej