I have a "core" assembly that contains a series of PowerShell Cmdlets written in C#. I also have a WPF "gui" application that uses this "core" assembly. In order use and compile the "core", I obviously need PowerShell's types and therefore I added a reference to System.Management.Automation.dll
to my project. I did this by simply adding a reference to that DLL from my local machines GAC. Everything compiles and runs smoothly on my machine.
When I deploy & run my "gui" on a different machine, I get strange errors. For example:
System.MissingMethodException: Method not found: 'System.Management.Automation.PSDataCollection`1<System.Management.Automation.InformationRecord> System.Management.Automation.PSDataStreams.get_Information()'
I have narrowed the cause down to this: My "gui" implements a custom PowerShell host and loads the "core" and executes it's cmdlets. It is done like so:
Host = new MyCustomHost();
var rs = RunspaceFactory.CreateRunspace(Host);
PsInstance = PowerShell.Create();
PsInstance.Runspace = rs;
rs.Open();
PsInstance.Streams.Information.DataAdded += delegate (object s, DataAddedEventArgs e)
{
nvtLogger.WriteInformation(PsInstance.Streams.Information[e.Index].MessageData.ToString());
};
Apparently the property PsInstance.Streams.Information
is not found during runtime.
I found that the System.Management.Automation.dll
that I added as a reference to the project is not necessarily the one getting loaded. The framework first looks into the GAC, and if it finds a copy there, it uses that one instead of mine.
I understand that the cause for the issue is, that the deployment machine has an older version of PowerShell installed, which does not implement the property I am using - and throws the exception above.
What I do not understand, however, is: how can the framework load this assembly from the GAC, when it is not compatible with the one I have built against?
As far as I know, the framework uses AssemblyVersion, Name and PublicKeyToken to identify the assembly.
My local assembly (working):
Assembly Location: C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll
Assembly Fullname: System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
File ProductVersion: 10.0.14393.1532
File Size: 7,1MB
Server assembly (not working):
Assembly Location: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll
Assembly Fullname: System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
File ProductVersion: 6.3.9600.18728
File Size: 5,8MB
The two files are indisputably different. But according to AssemblyName they are identical. This way the framework will load one or the other.
How can it be that two apparently different files (even by just looking at the file's size) have the same AssemblyName?
I'd like to add that the target machine is a Windows Server 2012R2, but it is not the only one where I noticed this. I have also noticed it on a freshly installed Windows 7 and another Windows 2012 Server.
I ended up using the official Powershell reference asseblies packages on NuGet and lived happy ever after. Too keep it compatible as much as possible I now almost always use
Microsoft.PowerShell.3.ReferenceAssemblies
. No more issues since.