{"id":349,"date":"2014-09-04T13:41:20","date_gmt":"2014-09-04T11:41:20","guid":{"rendered":"http:\/\/www.tapper-ware.net\/blog\/?p=349"},"modified":"2014-09-04T14:21:02","modified_gmt":"2014-09-04T12:21:02","slug":"using-c-in-batch-files","status":"publish","type":"post","link":"https:\/\/www.tapper-ware.net\/blog\/using-c-in-batch-files\/","title":{"rendered":"Using C# in Batch files"},"content":{"rendered":"<h3>Batch Files<\/h3>\n<p>People usually smile when I say that some parts of our network like the filtering of system events are held together by batch files. It just seems so arcane, but there are some big benefits:<\/p>\n<p>You don&#8217;t have to compile anything, you always know where the source code is and you can simply copy them between machines without having to set up anything. And since it&#8217;s more or less a legacy technology, Microsoft isn&#8217;t really changing a lot anymore, so there&#8217;s little chance of an upgrade breaking a batch file.<\/p>\n<p>The only problem is: cmd.exe shell syntax is a horrible, horrible mess and even the most basic string functions can take ages to implement&#8230; plus, the code you&#8217;ll write look like gibberish to anybody else no matter what you do. Plus there&#8217;s the horrible, horrible string escaping behavior and the very strange behavior of variables.<\/p>\n<p>&nbsp;<\/p>\n<h3>PowerShell<\/h3>\n<p>So, Microsoft started developing a replacement: PowerShell.exe . And functionality-wise it&#8217;s wonderful&#8230; it can be run interactively, it doesn&#8217;t need compilation, it has useful variables, it can access the system&#8217;s .NET libraries&#8230; it all sounds wonderful&#8230; until you try to run the darn thing. Let&#8217;s just say: The syntax is frighteningly bad, never mind the documentation plus the fact that for some bizarre reason you&#8217;re allowed to run batch files or EXE files, but you need to set an additional policy before you&#8217;re allowed to run PowerShell scripts!<\/p>\n<p>&nbsp;<\/p>\n<h3>The C# Compiler<\/h3>\n<p>But enough ranting. Thankfully there&#8217;s an alternative that&#8217;s preinstalled on all modern Windows System: The C# compiler. Yes, it&#8217;s there, even if you don&#8217;t have VisualStudio installed. Just enter<\/p>\n<p>dir &#8220;%WINDIR%\\Microsoft.NET\\Framework\\v4*&#8221;<\/p>\n<p>on the commandline and you&#8217;ll see the directory of all installed .NET 4 frameworks, each containing CSC.EXE, which is the C# compiler.<\/p>\n<p>Now, you could just use that, but that means a whole lot of temp files since you can&#8217;t pipe to CSC.EXE and you can&#8217;t run the code immediately. However there&#8217;s another way to access it: Through .NET itself via System.CodeDom.Compiler.CodeDomProvider .<\/p>\n<p>&nbsp;<\/p>\n<h3>Using PowerShell to access the C# Compiler<\/h3>\n<p>Thankfully, there&#8217;s one thing that PowerShell gets right: Giving you access to .NET . It&#8217;s not a pleasant experience, but it is possible. And there&#8217;s another thing PowerShell gets right: it allows piping anything to it. So we can use a little PowerShell script that invokes CodeDomProvider.CreateProvider to compile our code on the fly and run it immediately.<\/p>\n<p>It&#8217;s really pretty simple:<\/p>\n<p><a title=\"GIST\" href=\"https:\/\/gist.github.com\/hansschmucker\/f4031a37241d3d95e433\">GIST<\/a><\/p>\n<pre>$opt = New-Object System.CodeDom.Compiler.CompilerParameters;\r\n$opt.GenerateInMemory = $true;\r\n$cr = [System.CodeDom.Compiler.CodeDomProvider]::CreateProvider\r\n   (\"CSharp\").CompileAssemblyFromSource($opt,\r\n   \"public class App { public static void Main() { \"+ $input+\" } }\");\r\nif($cr.CompiledAssembly){\r\n\u00a0\u00a0 \u00a0$obj = $cr.CompiledAssembly.CreateInstance(\"App\");\r\n\u00a0\u00a0 \u00a0$obj.GetType().GetMethod(\"Main\").Invoke($obj, $null);\r\n}else{\r\n\u00a0\u00a0 \u00a0$cr.errors;\r\n}<\/pre>\n<p>It&#8217;s really very straight forward. Take STDIN, wrap it in a Main function, compile it, run it, report error if there was one during compilation.Through the magic of horrible cmd.exe paramter escaping, this looks a bit differently when passed directly to PowerShell.exe (3 quotes), but you should still be able to recognize it. Just put it in any old batch file (I&#8217;m using c#.cmd which I also added to my system&#8217;s PATH variable so that I don&#8217;t have to enter the whole path each time), but be sure to put it in a single line, because even escaping the linebreak with &#8220;^&#8221; won&#8217;t work for arguments of PowerShell.exe :<\/p>\n<p><a title=\"GIST\" href=\"https:\/\/gist.github.com\/hansschmucker\/820d3bdeca34c665b77a\">GIST<\/a><\/p>\n<pre>@PowerShell -Command \" $opt = New-Object System.CodeDom.Compiler.\r\n CompilerParameters; $opt.GenerateInMemory = $true; $cr = [System.\r\n CodeDom.Compiler.CodeDomProvider]::CreateProvider(\"\"\"CSharp\"\"\").\r\n CompileAssemblyFromSource($opt,\"\"\"public class App { public\r\n static void Main() { \"\"\"+ $input+\"\"\" } }\"\"\"); if(\r\n $cr.CompiledAssembly) {$obj = $cr.CompiledAssembly.\r\n CreateInstance(\"\"\"App\"\"\"); $obj.GetType().GetMethod(\"\"\"Main\"\"\").\r\n Invoke($obj, $null);}else{ $cr.errors; } \"<\/pre>\n<p>Horrible, I know. But it works.<\/p>\n<p>&nbsp;<\/p>\n<h3>Including C# inline in batch files<\/h3>\n<p>Now, if you want to actually include any C# in your batch file, it&#8217;s surprisingly straight-forward since the cmd.exe ECHO command actually has very straight forward escaping rules. Well, except for | and &amp; , which you best avoid by using .Equals() . But new lines just need to be escaped with a &#8220;^&#8221; at the end of the line and a space before the final pipe character. OK, that sounds way worse than it actually is:<\/p>\n<pre>@echo ^\r\nvar a=\"Hello\";^\r\nvar b=\"World\";^\r\nvar foo=(a+\" \"+b).ToUpper();^\r\nSystem.Console.WriteLine(foo);^\r\nif(System.IO.File.Exists(\"C#.cmd\")){^\r\n\u00a0\u00a0 \u00a0System.Console.WriteLine(\"Hey, you named it C#.cmd too :)\");^\r\n}^\r\n\u00a0|c#<\/pre>\n<p>That&#8217;s what a typical call would look like. Again, note the &#8220;^&#8221; at the end of each line and the space before &#8220;|c#&#8221;. Remember this and you will be fine. Of course, you can also put the CSharp code in a separate file and use @TYPE to pipe it directly to C#.CMD, so it won&#8217;t need any escaping.<\/p>\n<p>&nbsp;<\/p>\n<h3>Issues<\/h3>\n<p>Well, there&#8217;s obviously the issue of escaping your code if you use ECHO to include it inline, but I really don&#8217;t think there&#8217;s any way to avoid it.<\/p>\n<p>There are some issues which are mostly due to the C# code running inside the PowerShell process, rather than the CMD.EXE process. Most importantly: You cannot set environment variables without setting them user- or system-wide. You can set the environment variables of the PowerShell process, but these won&#8217;t be visible to the parent CMD.EXE process either. Your only way out is to use STDOUT and STDERR and FOR \/F to move it to a variable. If that doesn&#8217;t work (which may be the case if you want to include the code inline, because escaping inside a CMD.EXE FOR call is incredibly difficult), you&#8217;ll need to transport the information using the filesystem.<\/p>\n<p>And since we&#8217;re piping the code to PowerShell, STDIN will obviously not be available&#8230; so no ReadLine().<\/p>\n<p>&nbsp;<\/p>\n<h3>TODO<\/h3>\n<p>Well, obviously support for commandline arguments would be nice at some point, but I haven&#8217;t needed it so far.<\/p>\n<p>It would also be nice if the PowerShell could add the class\/main wrapper only if there is no method given in the source code. For now I&#8217;m simply using two different batch files, c#.cmd and c#full.cmd<\/p>\n<p>&nbsp;<\/p>\n<p>Hopefully this will make your life a bit easier \ud83d\ude42<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Batch Files People usually smile when I say that some parts of our network like the filtering of system events are held together by batch files. It just seems so arcane, but there are some big benefits: You don&#8217;t have to compile anything, you always know where the source code is and you can simply &hellip; <a href=\"https:\/\/www.tapper-ware.net\/blog\/using-c-in-batch-files\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Using C# in Batch files<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[29,28,31,30,32],"_links":{"self":[{"href":"https:\/\/www.tapper-ware.net\/blog\/wp-json\/wp\/v2\/posts\/349"}],"collection":[{"href":"https:\/\/www.tapper-ware.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tapper-ware.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tapper-ware.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tapper-ware.net\/blog\/wp-json\/wp\/v2\/comments?post=349"}],"version-history":[{"count":16,"href":"https:\/\/www.tapper-ware.net\/blog\/wp-json\/wp\/v2\/posts\/349\/revisions"}],"predecessor-version":[{"id":367,"href":"https:\/\/www.tapper-ware.net\/blog\/wp-json\/wp\/v2\/posts\/349\/revisions\/367"}],"wp:attachment":[{"href":"https:\/\/www.tapper-ware.net\/blog\/wp-json\/wp\/v2\/media?parent=349"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tapper-ware.net\/blog\/wp-json\/wp\/v2\/categories?post=349"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tapper-ware.net\/blog\/wp-json\/wp\/v2\/tags?post=349"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}