Creating a signalR client HubConnection with Powershell - c#

I am trying to connect to a SignalR hub using a powershell script. I am very new to powershell, so please excuse any rookie mistake.
I have set up a minimal not working example of what I have tried here :
Gist
Relevant code:
Load dlls
$dllFolder = -join((Get-Item -Path ".\" -Verbose).FullName, "\bin\Debug\")
[string[]] $dllPathsToLoad = #("\Newtonsoft.Json.dll", "\Microsoft.AspNet.SignalR.Client.dll")
$token = "insertyourtokenhere"
function LoadDllPaths($dlls)
{
foreach ($dll in $dlls)
{
$dllpath = $dllFolder + $dll
[System.Reflection.Assembly]::LoadFrom($dllpath)
}
}
[...]
LoadDllPaths($dllPathsToLoad)
Create HubConnection:
$server = "https://localhost/rest/"
[...]
$hub = New-Object Microsoft.AspNet.SignalR.Client.HubConnection($server)
Steps:
Create a new Visual Studio project
Add Newtonsoft.Json v10.0.2 Nuget package (latest)
Add Microsoft.AspNet.SignalR.Client v2.2.2 Nuget package (latest)
Add powershell script to the root of the project
With powershell (run as admin), type .\HubConnectionTestsScript.ps1
Result:
View on imgur
Error : System.Management.Automation.MethodInvocationException: Exception calling ".ctor" with "1" argument(s): "Could not load file or assembly 'Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified." ---> System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The system cannot find the file specified.
at Microsoft.AspNet.SignalR.Client.Connection..ctor(String url, String queryString)
at Microsoft.AspNet.SignalR.Client.HubConnection..ctor(String url, Boolean useDefaultUrl)
--- End of inner exception stack trace ---
at System.Management.Automation.DotNetAdapter.AuxiliaryConstructorInvoke(MethodInformation methodInformation, Object[] arguments, Object[] originalArguments)
at System.Management.Automation.DotNetAdapter.ConstructorInvokeDotNet(Type type, ConstructorInfo[] constructors, Object[] arguments)
at Microsoft.PowerShell.Commands.NewObjectCommand.CallConstructor(Type type, ConstructorInfo[] constructors, Object[] args)
This signalR source code object seems to be the problem, I just don't see what part of it can be throwing this error.
Question:
Why does the error mention Newtonsoft.Json v6.0.0 when signalR dependencies say >=6.0.4, and I have 10.0.2?
Am I doing anything wrong in my Powershell script which could be causing this?
Thank you very much! Any help is appreciated at this point

I managed to solve this issue with some help from a colleague. Sharing the solution here in case anyone ever struggles on the same problem.
It appears that one of SignalR dependencies tries to load an old version of Newtonsoft.Json. We can force it to redirect him to our own instance of Newtonsoft.Json
Inspired by this gist, here is the idea :
When you load your Json Assembly, store it in a variable
$newtonsoftAssembly = [System.Reflection.Assembly]::LoadFrom($dllFolder + "\Newtonsoft.Json.dll")
Afterwards, setup the redirect bindings. My best guess is that this intercepts any call to load an assembly, giving us the opportunity to return our own Json assembly instead of letting him fail to find the version he wants (6.0.0 in my case).
function RedirectJsonBindings()
{
$onAssemblyResolveEventHandler = [System.ResolveEventHandler] {
param($sender, $e)
# You can make this condition more or less version specific as suits your requirements
if ($e.Name.StartsWith("Newtonsoft.Json")) {
Write-Host "Newtonsoft assembly" $e.Name -ForegroundColor DarkGreen
return $newtonsoftAssembly
}
foreach($assembly in [System.AppDomain]::CurrentDomain.GetAssemblies()) {
if ($assembly.FullName -eq $e.Name) {
return $assembly
}
}
return $null
}
[System.AppDomain]::CurrentDomain.add_AssemblyResolve($onAssemblyResolveEventHandler)
}
And finally, at the end of your script, unbind
# Detach the event handler (not detaching can lead to stack overflow issues when closing PS)
[System.AppDomain]::CurrentDomain.remove_AssemblyResolve($onAssemblyResolveEventHandler)

Related

Is there an way to add reference(dll) in parent path in C#?

== compile command ==
csc -r:"../Newtonsoft.Json.dll" test.cs
== exec command ==
mono test.exe
== exec result : dependency error ==
System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=11.0.0.0, Culture=neutral,
PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies.
"Newtonsoft.Json.dll" this file is located in parent path. so I added a reference about dll and compile succeeded, but when I executed the exe file, it failed to get dll reference I added.
And when I put cs file and dll file together in the same directory, it worked very well, but that's not what I wanted.
Is there a solution to add a reference from dll file which is located in parent path using command line interface?
I used csc for compiler and mono for execution.
Thanks.
References are pathless. What that means is that wherever the assembly resides, all your program knows is that it has a reference to Newtonsoft.Json, Version=x.x.x.x, Culture=... and so on. You can do some things with the application configuration (application.config or myprogram.exe.config) to organize things into subfolders (using the probing setting) or specify a URL location for the file (using the codebase setting). You can set up the environment to change the search path and so on.
Or you can add some runtime code that allows you to override the default behavior and the call Assembly.LoadFrom to provide a full path to the file. You can either do it as part of your initialization or in a handler for the AppDomain.AssemblyResolve event - which is generally better method since it will only be called when the assembly is actually needed.
For example:
using System.IO;
using System.Reflection;
static class ParentPathResolver
{
public static void Init()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolve);
}
private static Assembly? Resolve(object? sender, ResolveEventArgs args)
{
var filename = new AssemblyName(args.Name).Name + ".dll";
var fullname = Path.GetFullPath(Path.Combine("..", filename));
if (File.Exists(fullname))
return Assembly.LoadFrom(fullname);
return null;
}
}
Of course you can add your own code to the Resolve method to search pretty much anywhere, just as long as you don't get caught in a resolution loop. I've used the AssemblyResolve event to do fun things like loading assemblies from compressed resources.

How can I dynamically reference an assembly that looks for another assembly?

Apologies for the dodgy question - happy to rephrase if someone has a better suggestion.
I'm trying to create an object by dynamically invoking an assembly belonging to another application.
The following PowerShell code is working nicely for me:
[Reflection.Assembly]::LoadFrom("C:\Program Files\Vendor\Product\ProductAPI.dll")
$bobject = new-object ProductAPI.BasicObject
$bobject.AddName("Some Name")
I'm struggling to do the same thing in C#. Based on other posts on StackOverflow I currently have this:
System.Reflection.Assembly myDllAssembly =
System.Reflection.Assembly.LoadFile("C:\\Program Files\\Vendor\\Product\\ProductAPI.dll");
System.Type BasicObjectType = myDllAssembly.GetType("ProductAPI.BasicObject");
var basicObjectInstance = Activator.CreateInstance(BasicObjectType);
The final line results in a TargetInvocationException.
{"Could not load file or assembly 'AnotherObject, Version=1.2.345.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."
It appears that the BasicObject constructor is trying to invoke AnotherObject (from AnotherObject.dll in the same folder) but can't find it.
Any tips on how to get around this?
If it can't find a dependent assembly in the usual places, you'll need to manually specify how to find them.
The two easiest ways I'm aware of for doing this:
manually load the dependent assemblies in advance with
Assembly.Load.
handle the AssemblyResolve event for the domain which is loading the
assembly with additional assembly dependencies.
Both essentially require you to know the dependencies for the assembly you're trying to load in advance but I don't think that's such a big ask.
If you go with the first option, it would also be worthwhile looking into the difference between a full Load and a reflection-only Load.
If you would rather go with 2 (which I'd recommend), you can try something like this which has the added benefit of working with nested dependency chains (eg MyLib.dll references LocalStorage.dll references Raven.Client.dll references NewtonSoft.Json.dll) and will additionally give you information about what dependencies it can't find:
AppDomain.CurrentDomain.AssemblyResolve += (sender,args) => {
// Change this to wherever the additional dependencies are located
var dllPath = #"C:\Program Files\Vendor\Product\lib";
var assemblyPath = Path.Combine(dllPath,args.Name.Split(',').First() + ".dll");
if(!File.Exists(assemblyPath))
throw new ReflectionTypeLoadException(new[] {args.GetType()},
new[] {new FileNotFoundException(assemblyPath) });
return Assembly.LoadFrom(assemblyPath);
};

FileNotFoundException when trying to load Autofac as an embedded assembly

Previous versions of Autofac worked, but since they switched to making it a Portable Class Library it won't load.
I tried applying the fix listed here (KB2468871) but it told me that it was not needed.
The error goes away when I move the Autofac.dll file into the same location as the executable. When it loads it from the external DLL it loads fine.
Why won't it work as an embedded DLL?
Here is the exception:
System.IO.FileNotFoundException: Could not load file or assembly 'System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes' or one of its dependencies. The system cannot find the file specified.
Stack trace:
at Autofac.Core.Registration.ComponentRegistration..ctor(Guid id, IInstanceActivator activator, IComponentLifetime lifetime, InstanceSharing sharing, InstanceOwnership ownership, IEnumerable`1 services, IDictionary`2 metadata)
at Autofac.Core.Container..ctor()
at Autofac.ContainerBuilder.Build(ContainerBuildOptions options)
at MyApp.Configuration.Bootstrapper.Run(String[] args) in c:\Dev\MyApp\App\Configuration\Bootstrapper.cs:line 25
at MyApp.Configuration.EntryPoint.Main(String[] args) in c:\Dev\MyApp\App\Configuration\EntryPoint.cs:line 22
If it helps, here is the part of the .csproj file that embeds the DLLs into the executable:
<Target Name="AfterResolveReferences">
<ItemGroup>
<EmbeddedResource Include="#(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
<LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
... and here is the EntryPoint class:
internal static class EntryPoint
{
[STAThread]
private static void Main(params string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => loadEmbeddedAssembly(e.Name);
Bootstrapper.Run(args); // must call separate class when using embedded assemblies
}
private static Assembly loadEmbeddedAssembly(string name)
{
var container = Assembly.GetExecutingAssembly();
var path = new AssemblyName(name).Name + ".dll";
using (var stream = container.GetManifestResourceStream(path))
{
if (stream == null)
{
return null;
}
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
return Assembly.Load(bytes);
}
}
}
I could easily repro your problem by using Assembly.Load(byte[]) on a PCL library that targeted Silverlight and used a System.Core type, this problem isn't specific to Autofac. It is a rather evil way to load an assembly, about as bad as Assembly.LoadFile(). Not having a loading context is a recipe for DLL Hell.
The CLR has an unenviable job to do with these PCL library references, it needs to magically map the retargetable assembly references to a real one. In other words, the 2.0.5.0 reference needs to be mapped to the correct one for the runtime version, 4.0.0.0 in your case. It can only do this if it has a good idea what the actual runtime version is, not having a loading context makes that difficult. Clearly it doesn't attempt to do so, it fires the AssemblyResolve event again for the 2.0.5.0 reference.
Which is the solution, remarkable simple in hind-sight. Just intercept the resolve request for the retargetable assembly references and use Assembly.Load() to let the CLR sort it out from the loading context of your AppDomain. Alter your AssemblyResolve event handler like this:
private static Assembly loadEmbeddedAssembly(string name)
{
if (name.EndsWith("Retargetable=Yes")) {
return Assembly.Load(new AssemblyName(name));
}
// Rest of your code
//...
}
This worked well in my test app, I trust it will solve your problem with Autofac as well.
Maybe the problem is how you try to load your EmbeddedAssembly.
I have very small knowledge about how you can do it
but i know the following example from code project works fine for me so maybe it will help you :-)
Load DLL From Embedded Resource
(If i misunderstand your question please tell me and i will delete my Answer)

ASP.net exception "FileNotFoundException" after idle when assembly file is present in the bin folder

The file is present, correctly named and not corrupted. If I move it out and back in from the "Bin", it works again, for about 5 minutes, then the error bellow comes back. Any operation that refreshes the file is fine, publishing it the anew, renaming or moving makes the site work again, for a moment.
{"Message":"Could not load file or assembly \u0027Ouranos,
Version=1.0.0.0, Culture=fr-CA, PublicKeyToken=null\u0027 or one of
its dependencies. Le fichier spécifié est introuvable.","StackTrace":"
at Services.Asynchrone(String DimensionX, String DimensionY, String
Action, String Culture, String Utilisateur, String Interface, String
Source, String Champ, String Valeur, String Classement, String
Direction, StriFileNotFoundExceptionng Page, String
Itérations)","ExceptionType":"System.IO."}
Fusion did give me an error code (0x80070002), which pointed me to get Process Monitor. Which lead me to the temporary assembly folder. Now I may be wrong about this. Comparing the cache files from an healthy website and the sick one, I noticed something odd.
Healthy website as all the DLL from the BIN in cache.
The sick website is missing two DLL in the cache that are present in the BIN.
Now, I know that ASP.net tends to say that the main library is missing when it's in fact one of the referenced library that is missing. In this current situation I don't know what I could do to fix that problem. The two DLL are not set in the cache, thus when it tries to load the main DLL it fails locating the two others from the cache and throws a file not found on the main DLL.
The two culprits are:
PresentationCore.dll
WindowsBase.dll
To troubleshot this kinf of errors, you can use Fusion log, instructions about how to enable it and how to use it can be found here: How to enable assembly bind failure logging (Fusion) in .NET.
It would seem that the following code actually fixes the problem. It checks for all the required assemblies for the assembly and loads the missing. I had such a code before and it did not work, because without the !(Assemblée is System.Reflection.Emit.AssemblyBuilder) && (Assemblée.GetType().FullName != "System.Reflection.Emit.InternalAssemblyBuilder") was not present and has the code causing an exception in .net 4.0 and over. It's not elegant, but it does the job.
public static void Chargeur()
{
var Assemblées_Chargées = (from Assembly Assemblée in AppDomain.CurrentDomain.GetAssemblies() where !(Assemblée is System.Reflection.Emit.AssemblyBuilder) && (Assemblée.GetType().FullName != "System.Reflection.Emit.InternalAssemblyBuilder") && (!Assemblée.GlobalAssemblyCache) && (Assemblée.CodeBase != Assembly.GetExecutingAssembly().CodeBase) select Assemblée).ToList();
var Chemins_Chargés = Assemblées_Chargées.Select(Assemblée => Assemblée.Location).ToArray();
var Chemins_Référencés = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");
var Assemblées_NonChargées = Chemins_Référencés.Where(Références => !Chemins_Chargés.Contains(Références, StringComparer.InvariantCultureIgnoreCase)).ToList();
Assemblées_NonChargées.ForEach(path => Assemblées_Chargées.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));
}

Creating instance of class accessor

I am currently writing a unit test framework which shall in the end run standard unit tests written in Visual Studio. The Framework is currently not working correctly with accessors. Consider the following test method:
[TestMethod()]
public void TestMethod()
{
ExampleMethods_Accessor target = null;
target = new ExampleMethods_Accessor();
target.SomeMethod();
}
In this example, the accessor has been generated by Visual Studio. The unit test works perfectly fine when run using the Unit Testing environment of Visual Studio. However, I would like to invoke the TestMethod() from within my Framework. At the line "target = new ExampleMethods_Accessor()", the following exception is thrown:
The type initializer for "Proband.ExampleMethods_Accessor" threw an excepition.
Inner exception:
Could not load file or assembly: Proband, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null...
Has anyone an idea of how the Microsoft Unit Testing Framework invokes unit tests? I was thinking it might be due to the missing TestContext object. This is "null" in my case. When starting the unit test in Visual Studio, the TestContext object contains a lot of information. Could it be, that I need to initialize it properly? How would it need to be initialized?
Thanks for all help,
Christian
EDIT:
I kept experimenting with the way accessors are working. I used ILSpy to see what code is being generated into the Proband_Accessor.dll. It turns out that the instruction causing the exception is:
SomeClass_Accessor.m_privateType = new PrivateType("Probant, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Probant.SomeClass");
I modified my unit test code to be like this (just for test):
[TestMethod()]
[DeploymentItem("Proband.dll")]
public void SomeMethodTest()
{
ExampleMethods_Accessor target = null;
ExampleMethods c = null;
try
{
Assembly.Load("Proband, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); // this works fine
PrivateType tx = new PrivateType(typeof(ExampleMethods)); // this works fine as well (also without loading the assembly)
PrivateType t = new PrivateType("Proband, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "Proband.ExampleMethods"); // this causes the exception
c = new ExampleMethods(); // this works fine
target = new ExampleMethods_Accessor(); // this causes the exception as well
}
catch (Exception ex)
{
Console.WriteLine();
}
int actual;
actual = target.SomeMethod();
}
I do absolutely not understand, why "new PrivateType("Proband, Version...." does not work. Has anyone an idea?
I have managed to create a workaround for the issue.
To my AppDomain, I am adding an AssemblyResolveEventHandler:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
This event handler contains the following code:
private Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
if(args.Name == "Proband, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
{
// resolving correct assembly of type under test
return typeof(ExampleMethods).Assembly;
}
else
{
return null;
}
}
Now the line of code "target = new ExampleMethods_Accessor();" works fine and returns the correct accessor object.
I still do not understand, why the Assembly cannot be resolved automatically.
Even if it is very unlikely that anyone will have the same problem: I hope this answer helps someone :)
I am not doing anything nearly as complex, but I had:
web application project using .NET 3.5
Configuration project using .NET 3.5
Test project using .NET 3.5
I was getting the same BadImageFormat exception when trying to run a unit test using an accessor.
I found the following link:
http://connect.microsoft.com/VisualStudio/feedback/details/677203/even-after-installing-vs2010-sp1-unit-tests-targeting-3-5-framework-fail-if-they-are-using-private-accessor#details
The second work-around solved my problem. I changed the test project to target .NET 4.0 and it worked.
I just had this exact problem, and it was because I removed the DeploymentItem attribute from the test method. Once I added it again, I no longer got the error on the build machine.
[TestMethod]
[DeploymentItem("FedImportServer.dll")] // ** This is necessary for the build machine. **
public void SourceFileStillExistsAfterProcessingFails()
Note: I never got the error when running it locally.
This is the error:
Test method FedImportTests.FedImportServiceHostTest.FileNoLongerExistsAfterSucessfulProcessing threw exception:
System.TypeInitializationException: The type initializer for 'FedImportServer.Processing.FileProcessor_Accessor' threw an exception. ---> System.IO.FileNotFoundException: Could not load file or assembly 'FedImportServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

Categories