Call class file method from NuGet package using Powershell - c#

I'm trying to create a nuget package from my class libary. So far I managed to create and install the package in a test project, but I also want to create a package manager command. My NuGet package is created with the NuGet Package Explorer.
NuGet Package structure
NuGet files content
init.ps1
param($installPath, $toolsPath, $package)
Import-Module (Join-Path $toolsPath MyNuGetCommands.psm1)
MyNuGetCommands.psm1
function Hello($name, $city)
{
Write-Host (‘Hello ‘ + $name + ‘. See you soon in ‘ + $city + ‘.’)
$lib = [Reflection.Assembly]::LoadFile("\\lib\EF.XML.dll")
$obj = new-object Parser
$result = $obj.UpdateXml()
}
Export-ModuleMember Hello
Register-TabExpansion ‘Hello’ #{
‘name’ = { "MSFTees", "MVPs", "My friends" };
‘city’ = { "Redmond", "Seattle", "Bellevue", "Duvall" };
}
I found the Hello function on the net and it worked in my project so I thougt lets add some lines found here to call my C# method. When I call the Hello function in my test project I get this errors:
Error 1
Exception calling "LoadFile" with "1" argument(s): "Cannot find the network path. (Exception from HRESULT: 0x80070035)" At
\svr\redirectedfolders\Stage\my documents\visual studio
2015\Projects\Test\packages\XML.EF.1.0.0\tools\MyNuGetCommands.psm1:5
char:43
+ $lib = [Reflection.Assembly]::LoadFile <<<< ("\lib\EF.XML.dll")
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
Error 2
New-Object : Cannot find type [Parser]: make sure the assembly
containing this type is loaded. At
\svr\redirectedfolders\Stage\my documents\visual studio
2015\Projects\Test\packages\XML.EF.1.0.0\tools\MyNuGetCommands.psm1:6
char:22
+ $obj = new-object <<<< Parser
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
Error 3
You cannot call a method on a null-valued expression. At
\svr\redirectedfolders\Stage\my documents\visual studio
2015\Projects\Test\packages\XML.EF.1.0.0\tools\MyNuGetCommands.psm1:7
char:29
+ $result = $obj.UpdateXml <<<< ()
+ CategoryInfo : InvalidOperation: (UpdateXml:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
So I think in the above errors obj is null but how can I fix this (I am not sure if LoadFile path is correct)? I did run set-executionpolicy RemoteSigned in Powershell.
Parser.cs file structure in EF.XML.dll
public class Parser
{
public void UpdateXml()
{
//code
}
}
This is the code to I use to call the method from a .cs file (which works but I want to call this from my Powershell Module:
var parser = new EF.XML.Parser();
parser.UpdateXml();

Related

Powershell and .NET5.0 fails loading the assembly

Background
Using visual studio 2019 v16.10.3 I have created this (standard MS example source) class library and successfully compiled into MyMathLib.DLL
using System;
namespace MyMathLib
{
public class Methods
{
public Methods()
{
}
public static int Sum(int a, int b)
{
return a + b;
}
public int Product(int a, int b)
{
return a * b;
}
}
}
My MyMathLib.csproj file looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>
And then I want to try executing this code using powershell using the following script:
[string]$assemblyPath='S:\Sources\ResearchHans\DotNetFromPowerShell\MyMathLib\MyMathLib\bin\Debug\net5.0\MyMathLib.dll'
Add-Type -Path $assemblyPath
When I run this I get an error, and ask what's going on by querying the error for LoaderExceptions:
PS C:\WINDOWS\system32> S:\Sources\ResearchHans\DotNetFromPowerShell\TestDirectCSharpScript2.ps1
Add-Type : Kan een of meer van de gevraagde typen niet laden. Haal de LoaderExceptions-eigenschap op voor meer informatie.
At S:\Sources\ResearchHans\DotNetFromPowerShell\TestDirectCSharpScript2.ps1:2 char:1
+ Add-Type -Path $assemblyPath
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Add-Type], ReflectionTypeLoadException
+ FullyQualifiedErrorId : System.Reflection.ReflectionTypeLoadException,Microsoft.PowerShell.Commands.AddTypeCommand
PS C:\WINDOWS\system32> $Error[0].Exception.LoaderExceptions
Kan bestand of assembly System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a of een van de afhankelijkhed
en hiervan niet laden. Het systeem kan het opgegeven bestand niet vinden.
PS C:\WINDOWS\system32>
(Sorry for dutch windows messages: something like "Cannot load one or more of the types" and the loader exception Cannot load file or assembly "System.Runtime" etc...
I have seen many related issues on SO, but they are usually quite old and do not refer to .NET5.0
This works
When I directly compile the example source like below, it works just fine.
$code = #"
using System;
namespace MyNameSpace
{
public class Responder
{
public static void StaticRespond()
{
Console.WriteLine("Static Response");
}
public void Respond()
{
Console.WriteLine("Instance Respond");
}
}
}
"#
# Check the type has not been previously added within the session, otherwise an exception is raised
if (-not ([System.Management.Automation.PSTypeName]'MyNameSpace.Responder').Type)
{
Add-Type -TypeDefinition $code -Language CSharp;
}
[MyNameSpace.Responder]::StaticRespond();
$instance = New-Object MyNameSpace.Responder;
$instance.Respond();
Why?
The whole Idea to have a proof of concept that I can utilize my .NET5 libraries using e.g. powershell scripts. I actually have to load a much more complicated assembly, but this oversimplified example seems to be my primary issue at the moment.
The question
What am I missing? - How do I get this to work?
TIA for bearing with me.
You didn't state what version of Powershell that you're using. However, I can reproduce the error by the following.
In VS 2019, create a Class Library (C# Windows Library).
Rename the class from "Class1.cs" to "Methods.cs"
Methods.cs
using System;
namespace MyMathLib
{
public class Methods
{
public int Sum(int a, int b)
{
return a + b;
}
public int Product(int a, int b)
{
return a * b;
}
}
}
Compile.
Open Windows Powershell
Type: (Get-Host).version
Then type the following:
PS C:\Users\Test> [string]$assemblyPath="C:\Temp\MyMathLib.dll"
PS C:\Users\Test> Add-Type -Path $assemblyPath
which results in the following error:
Add-Type : Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
At line:1 char:1
+ Add-Type -Path $assemblyPath
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Add-Type], ReflectionTypeLoadException
+ FullyQualifiedErrorId : System.Reflection.ReflectionTypeLoadException,Microsoft.PowerShell.Commands.AddTypeCommand
This occurs because .NETCore 5 isn't supported by Windows Powershell 5.1 - only on versions 6 and above. For Powershell 5.1 or below, it's necessary to use .NETFramework instead.
See Resolving PowerShell module assembly dependency conflicts which states the following:
PowerShell and .NET
... In general, PowerShell 5.1 and below run on .NET Framework, while PowerShell 6 and above run on .NET Core. These two
implementations of .NET load and handle assemblies differently.
See Installing PowerShell on Windows for how to get the latest version of Powershell.
In Powershell 7.1.3, one can call a method from MyMathLib.dll by doing the following:
PS C:\Users\Test> [string]$assemblyPath="C:\Temp\MyMathLib.dll"
PS C:\Users\Test> Add-Type -Path $assemblyPath
PS C:\Users\Test> $m1 = New-Object -TypeName MyMathLib.Methods
PS C:\Users\Test> $m1.Sum(2,3)
Note: Since the class/methods aren't static, it's necessary to create an instance by using $m1 = New-Object -TypeName MyMathLib.Methods
However, if it's static (as shown below):
MyMathLib.cs
using System;
namespace MyMathLib
{
public static class Methods
{
public static int Sum(int a, int b)
{
return a + b;
}
public static int Product(int a, int b)
{
return a * b;
}
}
}
Then one can do the following in Powershell:
PS C:\Users\Test> [string]$assemblyPath="C:\Temp\MyMathLib.dll"
PS C:\Users\Test> Add-Type -Path $assemblyPath
PS C:\Users\Test> [MyMathLib.Methods]::Sum(3,2)

How to pass parameter as class not as string to New-Object?

I can't seem to pass $outputStream as Stream to StreamWritter constructor. It gets passed as string and then it invokes wrong constructor that expects string not stream.
$tcpClient = New-Object System.Net.Sockets.TCPClient
$connect = $tcpClient.BeginConnect("localhost", 8080, $null, $null)
$success = $connect.AsyncWaitHandle.WaitOne($testTimeoutMs, $true)
[System.Net.Sockets.NetworkStream]$outputStream = $tcpClient.GetStream()
$writer = New-Object System.IO.StreamWriter -ArgumentList $outputStream, [System.Text.Encoding].ASCII
I want to invnoke this (https://msdn.microsoft.com/en-us/library/3aadshsx(v=vs.110).aspx) constructor but it invokes one with string parameters instead of Stream.
This results in this error:
New-Object : Exception calling ".ctor" with "2" argument(s): "The process cannot access the file 'C:\Users\Maciej\System.Net.Sockets.NetworkStream' because it is being used by anothe
r process."
At line:1 char:12
+ $writer = New-Object System.IO.StreamWriter -ArgumentList $outputStr ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
How to force to pass this parameter as Stream not as string?
Your problem is trying to access the [Encoding] class's static member with the wrong syntax:
[Text.Encoding].ASCII
In PowerShell, you access type static members with the following syntax:
[Text.Encoding]::ASCII
This object has the type [System.Text.ASCIIEncoding] according to .GetType() which inherits from [System.Text.Encoding] and should resolve the constructor errors you're receiving:
$tcpClient = New-Object -TypeName 'System.Net.Sockets.TCPClient'
$connect = $tcpClient.BeginConnect('localhost', 8080, $Null, $Null)
$success = $connect.AsyncWaitHandle.WaitOne($testTimeoutMs, $True)
$outputStream = $tcpClient.GetStream()
$writer = New-Object -TypeName 'System.IO.StreamWriter' -ArgumentList #($outputStream, [System.Text.Encoding]::ASCII)
As a side-note, in v5+, you can natively call the constructor method:
$streamWriter = [IO.StreamWriter]::new($outputStream, [Text.Encoding]::ASCII)

PowerShell - Cannot call web service method passing and returning object

I'm trying to call a web service method from my PowerShell script but it's failing. Here is the C# code:
public MyTable InsertRow(MyTable data)
{
var dataContext = new MyEntities();
dataContext.MyTables.AddObject(data);
dataContext.SaveChanges();
return data;
}
This is how I call it in PowerShell:
$uri = "http://localhost/MyWcfService/MyService.svc?wsdl"
$svc = New-WebServiceProxy -Uri $uri -UseDefaultCredential
$t = $svc.GetType().Namespace
$attributes = New-Object($t + ".MyTable")
$attributes.Name = "Project X"
$attributes.Comment = "This is a test"
$resp = $svc.InsertRow($attributes)
$resp
And this is the error that I get:
Cannot find an overload for "InsertRow" and the argument count: "1". At line:1 char:1
+ $resp = $svc.InsertRow($attributes)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
What am I doing wrong?
Update:
This is the built-in .NET type when I call $attributes.GetType():
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True MyTable Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1WcfService_MyService_s...
I gave up on trying to call my RESTful Web service in PowerShell. I used this tutorial to create entity data in a data service and then used this sample code to consume the service using C#. This I have not yet tried, but I should be able to use the C# code in PowerShell script (this being my project's requirement).

Error while accessing custom dll from PowerShell 1.0

Following is my powershell script,
function hello()
{
$dllpath = "C:\\Documents and Settings\\raj\\pstest\\testlib.dll";
[Reflection.Assembly]::LoadFrom($dllpath) | out-null;
$obj = New-Object testlib.TestClass;
$obj.print();
}
hello
Following is the TestClass in testlib which i am trying t access in powershell
using System;
namespace testlib
{
class TestClass
{
public TestClass()
{
}
public void print()
{
Console.WriteLine("Hi");
}
}
}
But i am getting error like below,
New-Object : Cannot find type [testlib.TestClass]: make sure the assembly conta
ining this type is loaded.
At C:\Documents and Settings\raj\pstest\script1.ps1:5 char:19
+ $obj = New-Object <<<< testlib.TestClass;
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentExcepti
on
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewOb
jectCommand
You cannot call a method on a null-valued expression.
At C:\Documents and Settings\raj\pstest\script1.ps1:6 char:12
+ $obj.print <<<< ();
+ CategoryInfo : InvalidOperation: (print:String) [], RuntimeExce
ption
+ FullyQualifiedErrorId : InvokeMethodOnNull
I have tried using add-type cmddlet but it is also giving the same response.
I guess the dll is getting loaded properly into the powershell but I am not able to instantiate the object of TestClass. Please tell me what I am doing wrong.
If i remove out-null following is the output im getting,
GAC Version Location
--- ------- --------
False v2.0.50727 C:\Documents and Settings\553566\pstest\testlib.dll
New-Object : Cannot find type [testlib.TestClass]: make sure the assembly conta
ining this type is loaded.
At C:\Documents and Settings\raj\pstest\script1.ps1:5 char:19
+ $obj = New-Object <<<< testlib.TestClass;
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentExcepti
on
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewOb
jectCommand
You cannot call a method on a null-valued expression.
At C:\Documents and Settings\raj\pstest\script1.ps1:6 char:12
+ $obj.print <<<< ();
+ CategoryInfo : InvalidOperation: (print:String) [], RuntimeExce
ption
+ FullyQualifiedErrorId : InvokeMethodOnNull
Try this:
[code]$dll = $($env:userprofile + '\pstest\testdll.dll')
And then try to invoke some static method from your library. For example:
$dll = $($env:userprofile + '\pstest\test.dll')
[void][Reflection.Assembly]::LoadFile($dll)
[MyNamespace.MyClass]::Print()
OOPS my bad .........
The TestClass should be a public one :(
After that modification its working

could not able to copy the proxy file using vs2008 powershell

Hi I have written new functionality in the existing webservice.
I am copying the proxy file when rebuilding and copying to the specific location
i am using powershell but its not working .i get the following error.
**The term 'wsdl.exe' is not recognized as the name of a cmdlet, function, script
file, or operable program. Check the spelling of the name, or if a path was in
cluded, verify that the path is correct and try again.
At C:\[path edited for security]\RebuildProxy.ps1:30 char:9
+ wsdl.exe <<<< /fields "/l:CS" "/n:$namespace" "/out:$outCSFile" "/urlkey:Tes
tEndpoint" "$wsdlUrl";
+ CategoryInfo : ObjectNotFound: (wsdl.exe:String) [], CommandNot
FoundException
+ FullyQualifiedErrorId : CommandNotFoundException**
After rebuild i get the message the file has been modified outside the source editor[ the generated proxy file already there in the location]
could you please help me on this
posted below the powershell code
param (
[string]$webServiceProjFile = $(throw "webServiceProjFile paramter is required." ),
[string]$serviceFile = $(throw "serviceFile parameter is required."),
[string]$outCSFile = $(throw "outCSFile paramter is required." )
)
if (! [IO.File]::Exists($webServiceProjFile))
{
throw "$webServiceProjFile note found.";
}
if (! [IO.File]::Exists($outCSFile))
{
throw "$outCSFile note found.";
}
# read the project file into an XML document.
$projectFileXml = [xml] (Get-Content $webServiceProjFile );
# access the configured IIS URL
$serviceWsdlUrl = [string]::Concat($projectFileXml.Project.ProjectExtensions.VisualStudio.FlavorProperties.WebProjectProperties.IISUrl.Trim(), '/', $serviceFile);
$namespace = "";
# Read the namespace for the proxy from the proxy C# file
Get-Content $outCSFile | ForEach-Object { if ($_ -match "^\s*namespace\s+([A-Za-z._]+)\s+{\s*$") { $namespace = $matches[1] }};
$wsdlUrl = [string]::Concat("$serviceWsdlUrl", '?wsdl');
# Regenerate the proxy using WSDL.exe
wsdl.exe /fields "/l:CS" "/n:$namespace" "/out:$outCSFile" "/urlkey:TestEndpoint" "$wsdlUrl";
# Update the generated C# file so the proxy class interits from WSE2 base class.
(Get-Content $outCSFile) |
ForEach-Object { $_ -replace "\s+\:\s+System\.Web\.Services\.Protocols\.SoapHttpClientProtocol", " : Microsoft.Web.Services2.WebServicesClientProtocol" } |
Set-Content $outCSFile ;
$projectDirectory = [IO.Path]::GetDirectoryName($outCSFile);
$appConfigFilePath = [IO.Path]::Combine($projectDirectory, "App.config");
(Get-Content $appConfigFilePath) |
ForEach-Object { $_ -replace '<add\s+key="TestEndpoint"\s+value="[^"]*"\s+/>', "<add key=""TestEndpoint"" value=""$serviceWsdlUrl"" />" } |
Set-Content $appConfigFilePath ;
WSDL.EXE is not in the path. On my computer it comes with the visual studio.
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\x64\wsdl.exe

Categories