StringExpansion in c# called from Powershell - c#

I have a small c# class, that does some logging stuff and that is called from a powershell scripting framework using:
[System.Reflection.Assembly]::LoadFrom("$ExtensionsPath\LogWriter.dll")
$Log = New-Object LogWriter($LogFile, $addTimeStamp, $logLevel, $overWrite)
Writing into the log file goes like this
$Log.AddInfo("myText")
Works fine so far.
What I am thinking about for some time is, if I am able to use stringexpansion in the AddInfo() method of my LogWriter class?
Look at the example:
$ModulesPath = ‘C:\temp\modules’
$test = ‘This is a text and I want to expand $ModulesPath in my c# LogWriter class’
$Log.AddInfo($test)
The c# class shall now expand the $modulespath in $test as powershell does. I already know that in c# I have access to the powershell runspace from which the c# class was called using System.Management.Automation Namespace. But then I am lost how to really expand the variable.
The entry written into the logfile should look like this:
This is a text and I want to expand C:\temp\modules in my c# LogWriter class
Of course I know I can do this in my script using
$Log.AddInfo(($ExecutionContext.InvokeCommand.ExpandString($test)))
But this is nasty because it looks ugly and if I forget to add this statement no expansion is done.
So I thought of retrieving the current Runspace in my c# class and do the ExpandString-Command there to get the expanded variable but I fail.
This is beyond my knowledge.
Anyone here to tell my if this is possible? I already think of some other tasks where to use this so please do not start a flame war about if this makes sense or not.
Rgds
Jan

How can the value for $ModulesPath be known outside of your script?
If you want it to be expanded in C#, then you have to send it, may be as a second Parameter to AddInfo like:
$Log.AddInfo($test, $ModulesPath)
Now it's known and the replacement could be done by:
string sNew = sTest.Replace("$ModulesPath", sModulesPath);
where sTest and sModulesPath are the parameters.

not sure if this is what you're asking about, but please try using double quotes on the $test string:
$test = "This is a text and I want to expand $ModulesPath in my c# LogWriter class"

Related

Custom main entry point parameters from the command line in C#

I saw somewhere online someone showing what appeared to be valid C# code. It allowed for custom parameters in the main method to be passed as command-line arguments. It looked something like this:
static void Main(FileInfo input, int maxSize = 9, bool someflag = false)
{
// code
}
This could be used in the command line like this:
$ myApp hello_world.txt --maxSize 10 --someflag
Is this actually possible, or is someone pulling my leg?
Yes you can with DragonFruit, a (currently) alpha feature of the dotnet CLI.
Interpreting the string[] arguments into behaviors has been left as a task for the developer. Did the user ask for help? Did they pass invalid input? Can the input be converted to the types that you need if they're not string? These problems are not solved for you.
What if you could declare a strongly-typed Main method? This was the question that led to the creation of the experimental app model called "DragonFruit", which allows you to create an entry point with multiple parameters of various types and using default values [...]
Here's a tutorial:
https://github.com/dotnet/command-line-api/blob/main/docs/Your-first-app-with-System-CommandLine-DragonFruit.md

Passing a COM object from C# to Perl using PerlNET

I’m trying to pass a COM object from C# code to Perl.
At the moment I’m wrapping my Perl code with PerlNET (PDK 9.4; ActiveState) and I have defined a simple subroutine (+ required pod declaration) in Perl to pass objects from C# to the wrapped Perl module.
It seems that the objects I pass are not recognized correctly as COM objects.
An example:
In C# (.NET 4.0), the ScriptControl is used to load a simple class from a file written in VBScript.
var host = new ScriptControl();
host.Language = "VBScript";
var text = File.ReadAllText("TestScript.vbs");
host.AddCode(text);
dynamic obj = host.Run("GetTestClass");
What I get (obj) is of type System.__ComObject. When I pass it to my Perl/PerlNET assembly and try to call method Xyz() in Perl I get the following (runtime) exception:
Can't locate public method Xyz() for System.__ComObject
If, however, I do more or less the same thing in Perl, it works. (In the following case, passing only the contents of my .vbs file as parameter.)
I can even use the script control :
sub UseScriptControl {
my ($self, $text) = #_;
my $script = Win32::OLE->new('ScriptControl');
$script->{Language} = 'VBScript';
$script->AddCode($text);
my $obj = $script->Run('GetTestClass');
$obj->Xyz();
}
Now, calling Xyz() on obj works fine (using Win32::OLE).
In both cases I use:
use strict;
use Win32;
use Win32::OLE::Variant;
Another approach:
I can invoke methods by using InvokeMember of class System.Type if I specify exactly which overload I want to use and which types I’m passing:
use PerlNET qw(typeof);
typeof($obj)->InvokeMember("Xyz",
PerlNET::enum("System.Reflection.BindingFlags.InvokeMethod"),
PerlNET::null("System.Reflection.Binder"),
$obj,
"System.Object[]"->new());
Using this approach would mean rewriting the whole wrapped Perl module. And using this syntax..
Now I am wondering if I am losing both the advantages of the dynamic keyword in .NET 4.0 and the dynamic characteristics of Perl (with Win32::OLE) by using PerlNET with COM objects.
It seems like my preferred solution boils down to some way of mimicking the behaviour of the dynamic keyword in C#/.NET 4.0.
Or, better, finding some way of converting the passed COM object to something that will be recognized as compatible with Win32::OLE. Maybe extract some information of the __ComObject for it to be identified correctly as COM object.
I have to add that I posted to the PDK discussion site too (but didn’t get any response yet): http://community.activestate.com/node/18247
I also posted it to PerlMonks - as I'm not quite sure if this is more a Perl or C#/.NET question:
http://www.perlmonks.org/?node_id=1146244
I would greatly appreciate any help - or advise on where to look further.

calling Invoke-Expression with Parameters in Powershell

I've written a powershell module in c# that has a bunch of cmdlets like
Add-VM
The cmdlets reach out to an API and pull data back.
but for the sake of uniformity with the ssh CLI of the product, i've written a function called newtask that accepts 'addvm' as an argument and $args.
for example
newtask addvm -id 12345
I then invoke Add-VM and pass $args as a string like so
Invoke-Expression Add-VM $argstr
The problem is that Add-VM throws an error that it cannot find a positional parameter that accepts argument System.Object[]
A positional parameter cannot be found that accepts argument 'System.Object[]'
While I could easily alias 'addvm' to 'Add-VM', i'm trying to maintain uniformity with the ssh CLI so that new users can quickly start utilizing this module.
I figured that sending a string like '-id 12345' would suffice but it's not. Does the pscmdlet expect to receive something else?
Thanks in advance.
I know this is a little old now, but I was having a similar issue and a co-worker showed me that escaping $argstr prevents the object from getting converted to a string.
Invoke-Expression "Add-VM `$argstr"
That error is from Invoke-Expression not Add-VM and you just need quotes around the argument:
Invoke-Expression "Add-VM $argstr"
This has the drawback of forcing all objects into string format. This might be acceptable for simple types like ints and strings but if you want to pass through a more complex object it won't work. An alternative would be to splat the arguments with #args but I don't think you can do this through Invoke-Expression or Invoke-Command. You need to directly call the cmdlet:
function newtask {
params([string]$command)
switch ($command) {
"addvm" { Add-VM #args }
"deletevm" { Remove-VM #args }
}
}

Instantiating C# Classes in Lua

I am writing a scripting engine for my game using the LuaInterface library. I am getting an error when attempting to instantiate the class in Lua. The error is:
"./Scripts/sv_worldgen.lua:2: attempt to call global 'Campfire' (a string value)"
Where sv_worldgen.lua is (in entirety):
function GenerateChunk(worldChunk, chunkGridPosition)
tf = Campfire()
tf:SetPosition(chunkGridPosition)
end
Campfire is a class in C#, and appears to be exposed to lua as per the CLRPackage example and of course the LuaInterface Reference. I cannot seem to get around this error, and I have done due diligence of searching. The only other behavior of the script I can manage throws a similar error, but where it is "(a table value)". What am I doing wrong? Thank you in advance!
I tried explicitly doing Campfire._ctor(), but _ctor() is a string value.
This was resolved by using CLRPackage and using it to first load the assembly.
//Lua
JASG = CLRPackage("JASG", "JASG")
Then and only then can you link the classname to the actual C# class using (this must be done before trying to access it in Lua):
//Lua
Campfire=JASG.Campfire;
and then normal instantiation can occur by
//Lua
cf = Campfire()

Changing Variable Type on RunTime

Today i had a challenge with my College and i gaved up ,no idea how to achieve it .
Is there a way to declare a String ,as Constant and on Load Event maybe using Reflection to change String to non-Constant assign a value from XML ,than Change it to Constant again .
And all of the Code which does that (Constant to Non-Constant),should be Stored in a String ,and on Load before Type Change ,it should be Decrypted and Injected into the Application .
example:
private const String RegNumber = "";
//Change RegNumber to Writable String
//Change RegNumber value
//Than Change RegNumber back to const again
PS : Please sorry but i have no idea where to start ,and show here some code .
You can't declare it as const but you can declare it as static readonly:
private static readonly string Foo = ReadValueFromAssembly();
static string ReadValueFromAssembly()
{
// Perform your logic and return the string here
}
Would that do everything you need? It's not really clear what you mean about the "code which does that [...] should be decrypted and injected into the application" but you can make the above method do anything you need it to as normal.
As a side-note, it's generally a bad idea to do a lot of work in a type initializer like this.
EDIT: You can store code as a string, use CSharpCodeProvider to compile it at execution time, and then execute the compiled code. I have a sample of this in "Snippy" which I used for C# in Depth as a quick tool for compiling snippets.
It may not even exist at runtime, the compiler could have just replaced all usages of it with their literal values (in fact, it probably has, though I don't think it's required to by the standard).
So no, I don't see how this could be possible.
It is theoratically possible. See
How to programmatically compile code using C# compiler
http://support.microsoft.com/kb/304655
You can write the code in a string and compile using the API mentioned in above article.
I have not done that before but it should give you an idea on how to start.
Also see,
Can I change value of constant in C#?

Categories