1

Trying to run a bit of C# code from a batch file.

I do NOT want to create an intermediate temporary file, compile it and then run it.

Not the self-compiling method, will not do for my purposeenter link description here

It should be possible to run that C# program on a single line, calling it with powershell -command Add-Type -TypeDefinition @" "@ etc..

First here is the simple C# program to run

Add-Type -TypeDefinition @"
using System;
 
public class LogicCheck
{
    public static void Check(bool condition)
    {
        if (condition)
        {
            Console.WriteLine("Result is True");
        }
            else
        {
            Console.WriteLine("Result is False");
        }
    }
}
"@


[LogicCheck]::Check($false)

You can executing it by saving it to a file and running from console

powershell -command "Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass"
powershell -file "csharp script.ps1"

Here are some of my early attempts

powershell -command Add-Type -TypeDefinition @" using System; public class LogicCheck { public static void Check(bool condition) { if (condition) { Console.WriteLine("Result is True"); } else { Console.WriteLine("Result is False"); } } } "@ [LogicCheck]::Check($false)
powershell -command Add-Type -TypeDefinition @" using System; public class LogicCheck { public static void Check(bool condition) { if (condition) { Console.WriteLine(\"Result is True\"); } else { Console.WriteLine(\"Result is False\"); } } } "@ [LogicCheck]::Check($false)
powershell -command Add-Type -TypeDefinition @' using System; public class LogicCheck { public static void Check(bool condition) { if (condition) { Console.WriteLine("Result is True"); } else { Console.WriteLine("Result is False"); } } } '@ [LogicCheck]::Check($false)

Then after some readings, I discovered here string need to open and close on their own lines

So I tried a streamlined version without using here strings. After all, C# is semi colon terminated and shouldn't care at all about the presence of CrLFs

So I tried a few more permutations without the here string construct

powershell -command Add-Type -TypeDefinition using System; public class LogicCheck { public static void Check(bool condition) { if (condition) { Console.WriteLine("Result is True"); } else { Console.WriteLine("Result is False"); } } } [LogicCheck]::Check($false)
powershell -command Add-Type -TypeDefinition " using System; public class LogicCheck { public static void Check(bool condition) { if (condition) { Console.WriteLine("Result is True"); } else { Console.WriteLine("Result is False"); } } } " [LogicCheck]::Check($false)
powershell -command " Add-Type -TypeDefinition using System; public class LogicCheck { public static void Check(bool condition) { if (condition) { Console.WriteLine(\"Result is True\"); } else { Console.WriteLine(\"Result is False\"); } } } [LogicCheck]::Check($false)"
powershell -command " Add-Type -TypeDefinition using System; public class LogicCheck { public static void Check(bool condition) { if (condition) { Console.WriteLine(^"Result is True^"); } else { Console.WriteLine(^"Result is False^"); } } } [LogicCheck]::Check($false)"

These do not work either

So I figured, maybe I can use the here string after all, if I insert %LF% characters in the proper place ?

powershell -command Add-Type -TypeDefinition @"%LF% using System; public class LogicCheck { public static void Check(bool condition) { if (condition) { Console.WriteLine("Result is True"); } else { Console.WriteLine("Result is False"); } } } %LF%"@ 
powershell -command Add-Type -TypeDefinition @"%LF% using System; public class LogicCheck { public static void Check(bool condition) { if (condition) { Console.WriteLine(\"Result is True\"); } else { Console.WriteLine(\"Result is False\"); } } } %LF%"@ 
powershell -command Add-Type -TypeDefinition @'%LF% using System; public class LogicCheck { public static void Check(bool condition) { if (condition) { Console.WriteLine(\"Result is True\"); } else { Console.WriteLine(\"Result is False\"); } } } %LF%'@ 
powershell -command " Add-Type -TypeDefinition @"%LF% using System; public class LogicCheck { public static void Check(bool condition) { if (condition) { Console.WriteLine("Result is True"); } else { Console.WriteLine("Result is False"); } } } %LF%"@ 

This too, did not work

Here are all the permutations I have tried so far

code https://pastebin.com/KqVAQ1T9 console output https://pastebin.com/6kTHJgEN code https://pastebin.com/CTbS0hfK console output https://pastebin.com/QbMG4E00

Shodan
  • 1,065
  • 2
  • 13
  • 35
  • 1
    Just to be clear, you're asking for a batch wrapper for a powershell wrapper for C#? Because that's totally possible; I just want to confirm that that's what you're looking for. – SomethingDark Jun 19 '23 at 20:06
  • Well, that's probably it, if you look at the first 60 or so lines of https://pastebin.com/KqVAQ1T9 you will see what I'm trying to do . Put powershell script into variables and then run them as single line commands. When this did not work for the C# program, I started building a whole single line command. This also did not work. – Shodan Jun 19 '23 at 20:30
  • As mentionned in the question, I specifically do not want self-compiling batch files or writing the C# program to an intermediate file. Though those options are clever and they work. I want either single line C# compile and run, or C# script in a n enviroment variable and then run with powershell -command %my_csharp_program% – Shodan Jun 19 '23 at 20:33
  • Please take a look [here](https://stackoverflow.com/q/76384913). The answer given there should provide you with the information you need. – Compo Jun 19 '23 at 20:54
  • Thank you @Compo, I am investigating mklement0 's answer, it sounds like exactly what I was trying to do, I will test this syntax and report – Shodan Jun 20 '23 at 00:33
  • @Compo The method you have suggested works. I was so close. So how I understand it is, no here-string and one extra terminator. I will be writing down the full and proper solution – Shodan Jun 20 '23 at 01:39

1 Answers1

3

You can have a batch/powershell hybrid wrapper for a powershell/C# hybrid by taking advantage of how powershell does block comments. If you want more batch code, that will go between the <# and the #>. If you want more powershell code, that goes at the bottom of the script. Nothing gets compiled and no temp files are written.

<# :
@echo off
setlocal
set "POWERSHELL_BAT_ARGS=%*"
if defined POWERSHELL_BAT_ARGS set "POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%"
endlocal & powershell -NoLogo -NoProfile -Command "$_ = $input; Invoke-Expression $( '$input = $_; $_ = \"\"; $args = @( &{ $args } %POWERSHELL_BAT_ARGS% );' + [String]::Join( [char]10, $( Get-Content \"%~f0\" ) ) )"
goto :EOF
#>

Add-Type -TypeDefinition @"
using System;
 
public class LogicCheck
{
    public static void Check(bool condition)
    {
        if (condition)
        {
            Console.WriteLine("Result is True");
        }
            else
        {
            Console.WriteLine("Result is False");
        }
    }
}
"@


[LogicCheck]::Check($false)
SomethingDark
  • 13,229
  • 5
  • 50
  • 55
  • 1
    Word of the day (or at least the answer): **polyglot** – Ben Voigt Jun 19 '23 at 20:51
  • Could you explain how <# : and #> work in a batch file ? I cannot find a reference on how to part the meaning of, at least, <# : – Shodan Jun 19 '23 at 22:43
  • Also I confirm that this does work, but this is what I meant by "self-compiled" which is probably the wrong term. This method has too much impact on the whole structure of the batch file. The issue I'm having with this form is that I would have many different C# programs and I wish to call them from the same batch file. I would call these functions with call :csharpfunction1 call :csharpfunction2 and have all the code and execution living inside those labels. – Shodan Jun 19 '23 at 22:44
  • I do upvote, this is cleaner than the other form of this which uses //>nul 2>nul||@goto :batch . But it's not the answer I am hoping for. – Shodan Jun 19 '23 at 22:46
  • @Shodan - You should be able to put as many C# programs in here as you want in the `Add-Type` section. Traditionally when you need C# in a Windows shell script, you access it via powershell since that has .NET capabilities and then just use batch as a wrapper so that you can double-click the script. – SomethingDark Jun 19 '23 at 22:58
  • I don't have a great understanding of C# in this context. How would the launcher script choose which C# program to launch ? Also would there be a way to play the C# code chunks elsewhere than in the end ? – Shodan Jun 20 '23 at 00:25
  • I have tried my hand at crafting a powershell command that does this, but it fails. Maybe you can see what I'm trying to do here powershell -NoLogo -NoProfile -Command "$_ = $input; Invoke-Expression $( '$input = $_; $_ = \"\"; $args = @( &{ $args } %_powershell_run_sharp_arguments% );' + [String]::Join( [char]10, ($(Get-Content \"%~f0\" | Select-String -Pattern '%~1', '%~2' -Context 0,1000 | ForEach-Object { if ($_.Context.PostContext) { $_.Context.PostContext } })[0..^1] ) )" – Shodan Jun 20 '23 at 00:25
  • To restrict the join function to concatenate only the lines between two batch file labels as specified by a function call. I have created a demo script, but it fails https://pastebin.com/EAGh78ab – Shodan Jun 20 '23 at 00:27
  • Specifically, something about this part, is malformed $(Get-Content \"%~f0\" | Select-String -Pattern '%~1', '%~2' -Context 0,1000 | ForEach-Object { if ($_.Context.PostContext) { $_.Context.PostContext } })[0..^1] – Shodan Jun 20 '23 at 00:30
  • Here is the full console output https://pastebin.com/pFHDrTfX – Shodan Jun 20 '23 at 00:31
  • Presumably, you'd just have additional `Add-Type` commands and use the `[Class]::Function()` syntax to call whatever you wanted. Honestly, I've never seen anyone try to do precisely what you're doing, which _VERY_ strongly suggests that you're going about it the completely wrong way. – SomethingDark Jun 20 '23 at 01:17
  • Wrong way is my middle name, thanks to @compo I have found the "canon" solution to my original question, but I like your method, it would be cleaner as to not require onerous escaping as with the "proper" method. But still I have not figured out how to modify it to use the 'Select-String -Pattern' with start and end batch :labels – Shodan Jun 20 '23 at 01:37