I know it is possible to call C# code from the PowerShell script by loading an assembly. But is there any way to pass and receive a value in between both C# code and PowerShell script.
Let's say I have a $path variable in my power script. I want to pass it to my c# code. And C# code will use the $path. After doing some stuff in the c# code it will return some value to the script. Is this possible? If it is, how can I do it? I must load a third party dll in my power shell and all one or two public methods on that dll to complete some task.
My PowerShell script code:
$scriptpath = $MyInvocation.MyCommand.Path;
$cureentDir = Split-Path $scriptpath;
$isSasDir = $cureentDir -match "mydir";
$requiredFile = "Core.dll";
$myPowersehllVal = "has value for c# code";
My C# code:
$Source = #"
using System.Net;
public class ExtendedWebClient : WebClient
{
String myPowersehllVal;
public int Timeout;
protected override WebRequest GetWebRequest(System.Uri address)
{
}
}
"#;
For getting PS values into C#
https://stackoverflow.com/a/22384009/3546415
In a more general sense, System.Management.Automation (Nuget Required) looks promising, in particular, Runspace and Pipeline.
Here are some good examples of usage:
https://msdn.microsoft.com/en-us/library/ee706576(v=vs.85).aspx
Something like this one seems similar to what you want.
Or maybe just use the PowerShell Class to execute PS commands from your C# module to set PS variable values.
Alternatively, without worrying about interop, you can kind of hack this by working through the file system and/or environmental variables. Following this paradigm, you could even use a memory mapped file and share variables with a broader set of applications. Powershell side would be something like this. For objects, serialization.
Related
I am planning to use IronPython along with C# for following use case.
I want to expose some functions which can invoked via python script.
Such script will be invoked by C# application.
For example there could be function prepared in C# ClearUserInput() that clears some data on WPF. Then I could expose more functions via some interface.
public interface IScriptContract
{
void ClearMainWindow();
void LoadProject(string projectName);
}
python script:
print "clearing window"
ClearMainWindow() --how to cooperating with C# code?
print "load proj"
ClearMainWindow("project.proj") --how to cooperating with C# code?
I want user can write some script that can invoke my .net function.
Is it possible to do that?
The standard way to do this is to create a 'host object' that is passed into the scripts that provides access to the host application (if you're familiar with web progamming, that's what the window instance is).
In IronPython you can use SetVariable on the ScriptScope object that is executing your code:
var contract = new ScriptContract();
var engine = Python.CreateEngine();
var mainScope = engine.CreateScope();
var scriptSource = engine.CreateScriptSourceFromFile("test.py", Encoding.Default, SourceCodeKind.File)
mainScope.SetVariable("host", contract);
scriptSource.Execute(scope);
From python it's then always available:
host.ClearMainWindow()
I'm trying to access .Net(C#) enums in IronPython, lets say we have
Test.dll
// Contains Several Enums
enum TestType{..}
enum TestQuality{..}
....
....
enum TestStatus{..}
//Similarly Multiple functions
public void StartTest(TestType testType, TestQuality testQuality){..}
....
....
public TestStatus GetTestStatus(){..}
and now if I try to call the above functions, I need to choose the appropriate enum parameters and so far what I did is this,
Iron Python [vs2012]
import clr
clr.AddReference('Test.dll')
from TestDll import *
test = Test()
# Initiate Enums
enumTestType = TestType
enumTestQuality = TestQuality
....
....
enumTestStatus = TestStatus
#Call Functions
test.StartTest(enumTestType.Basic, enumTestQuality.High)
....
....
# goes on
now the above IronPython code works fine, the only odd bit here is that I need to initiate all the enums(Intellisence doesnt work here) before I use them with the functions, this will become more difficult when there are more enums to use. whereas in C# environment(vs2012) we dont have to initiate but we can use them straight away when calling functions.
Is there a better way of dealing this in IronPython?
Please correct me if I'm wrong, thanks!
Assuming the enums are contained within your Test class you can either use them fully qualified
test.StartTest(Test.TestType.Basic, Test.TestQuality.High)
or by importing
from TestDll.Test import TestQuality, TestType
test.StartTest(TestType.Basic, TestQuality.High)
If the enums are in the same namespace as the Test class they should be usable without additional imports:
test.StartTest(TestType.Basic, TestQuality.High)
I had the same problem, but I fixed it in another way: using ScriptRuntime.LoadAssembly.
Prerequisites:
VS2013
C# app executable, plus the Test.dll assembly. IronPython is hosted by the C# app.
Test.dll: (note that all is within the TestDll namespace)
namespace TestDll
{
// Contains Several Enums
enum TestType{..}
enum TestQuality{..}
....
....
enum TestStatus{..}
//Similarly Multiple functions
public void StartTest(TestType testType, TestQuality testQuality){..}
....
....
public TestStatus GetTestStatus(){..}
}
I simply created the IronPython engine this way:
eng = Python.CreateEngine();
eng.Runtime.LoadAssembly(Assembly.GetAssembly(typeof(TestType))); // This allows "from TestDLL import *" in Python scripts
and then, execute the script with the usual
string pysrc = ...; // omitted, taken from the python script below
ScriptSource source = eng.CreateScriptSourceFromString(pysrc);
ScriptScope scope = eng.CreateScope();
source.Execute(scope);
This allowed me to write this Python code and execute it within the C# app: (note that I'm using the enum names directly)
from TestDll import *
test = Test()
#Call Functions
test.StartTest(TestType.Basic, TestQuality.High)
....
....
# goes on
I am experimenting with the Roslyn script engine. Using the following code, I set up my script engine.
var csharpEngine = new ScriptEngine();
csharpEngine.AddReference("System");
csharpEngine.AddReference(this.GetType().Assembly.Location);
scriptSession = csharpEngine.CreateSession();
Then I execute a script with the following line:
scriptSession.Execute(script);
The script contains a very simple reference to a static function on a class in my assembly.
private string script =
#"using System;
using RoslynWindow;
Hello.SayHello();";
In the output window the function merely prints to the console. So I have shown I can call into a public static member of my assembly without passing a "HostObjectModel" to the script engine. I want to prevent this from happening. I'd like to be able to register only specific members (functions, variables or properties) to be accessed by the script engine, and no others.
Any idea how to accomplish this?
Traverse the AST with Roslyn to check if the script tries to call anything you don't allow.
I'm just having a play with Roslyn but unsure on how to do the following.
To keep this simple, lets say I have a host program which has a method like so
public void DisplayMessage(string message)
{
MessageBox.Show(message);
}
Can I then have a script file called MyScript.csx and then somewhere in the script have something like
void Main()
{
Host.DisplayMessage("I am a script");
}
Then I have the host load the file and execute it.
If this sort of thing can't be done, is there a scripting system/engine based on c# that can do it?
These are the requirements
Host application can load script from a file.
Script file is written in c# and so can be written using VS2010 with syntax etc
Script file can access host public methods, properties etc
I wrote an Introduction to the Roslyn scripting API that covers most of what you're asking. The ScriptEngine type also has a ExecuteFile method that would be useful for what you're trying to do.
Disclaimer: I work for Microsoft on the Roslyn project.
Yes, you can do what you want using Roslyn.
First, create a public Host class that has a public DisplayMessage method (or use a existing class). Then create ScriptEngine, specifying the assembly that contains Host as a reference. After that, you can call ExecuteFile() on your file, with the Host object as another parameter:
var engine = new ScriptEngine(references: new[] { typeof(Host).Assembly });
engine.ExecuteFile("MyScript.csx", new Host());
The script files doesn't need any Main() method, and you call the method on the host object directly:
DisplayMessage("I am a script");
I'm looking to replicate the following in IronPython and searching has so far been fruitless and/or disappointing.
namespace Groceries
{
public class ChocolateMilk : Milk
{
// Other stuff here
}
}
The idea would be that the compiled Python DLL will be loaded into a C# program through System.Reflection.Assembly.Load and a GetType("Groceries.ChocolateMilk") on the loaded DLL would not return null.
The most recent answer I was able to find was in 2008 and said that it was impossible without using the Hosting API - http://lists.ironpython.com/pipermail/users-ironpython.com/2008-October/008684.html.
Any suggestions on how to accomplish this would be greatly appreciated. Any conclusions that this is currently impossible to do via IronPython will also be appreciated, but less so.
I'm a bit confused on what you're asking here. Are you trying to instantiate that C# code in your IronPython modules? Or do you have the equivalent classes written in IronPython and you want to instantiate them in your C# code?
Based on the link you posted, I suppose you're going for the latter and have IronPython classes that you want instantiated in your C# code. The answer is, you cannot directly instantiate them. When you compile IronPython code to an assembly, you cannot use the types defined there with your regular .NET code since there is not a one-to-one mapping between IronPython classes and .NET classes. You would have to host the assembly in your C# project and instantiate it that way.
Consider this module, Groceries.py compiled to Groceries.dll residing in the working directory:
class Milk(object):
def __repr__(self):
return 'Milk()'
class ChocolateMilk(Milk):
def __repr__(self):
return 'ChocolateMilk()'
To host the module in your C# code:
using System;
using IronPython.Hosting;
using System.IO;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var engine = Python.CreateEngine();
var groceriesPath = Path.GetFullPath(#"Groceries.dll");
var groceriesAsm = Assembly.LoadFile(groceriesPath);
engine.Runtime.LoadAssembly(groceriesAsm);
dynamic groceries = engine.ImportModule("Groceries");
dynamic milk = groceries.ChocolateMilk();
Console.WriteLine(milk.__repr__()); // "ChocolateMilk()"
}
}
Otherwise to go the other way and create an instance of your .NET type in your IronPython code (as your title suggests). You'd need to add the path to your assembly, reference it, then you could instantiate it as needed.
# add to path
import sys
sys.path.append(r'C:\path\to\assembly\dir')
# reference the assembly
import clr
clr.AddReferenceToFile(r'Groceries.dll')
from Groceries import *
chocolate = ChocolateMilk()
print(chocolate)