self-compiled .net hybrids

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
npocmaka_
Posts: 512
Joined: 24 Jun 2013 17:10
Location: Bulgaria
Contact:

self-compiled .net hybrids

#1 Post by npocmaka_ » 08 Jan 2014 09:03

just expanding idea that I saw here: http_://www.dostips.com/forum/viewtopic.php?f=3&t=4762 .
As the .net framework comes with three default compilers jsc.exe - for javascript , csc.exe - for c# and vbc.exe for visual basic .net I've tried to create a hybrid for each of them (with "hello world " examples only).

jscript.net is the best option according to me - does not require a main method or wrapping something in a class and also supports @if directive - http://msdn.microsoft.com/en-us/library ... xa(v=vs.90).aspx (as well as jscript) so the notorious jscript/bat technique can be used for clear output .It's not so powerful language as C# ,but for a script purposes I think it's better (beware that the WScript methods are not accessible here):

Code: Select all

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
@echo off
setlocal
set "frm=%SystemRoot%\Microsoft.NET\Framework\"

for /f "tokens=* delims=" %%v in ('dir /b /a:d  /o:-n "%SystemRoot%\Microsoft.NET\Framework\v*"') do (
   set netver=%%v
   goto :break_loop
)
:break_loop
set jsc=%frm%%netver%\jsc.exe
call %jsc% /nologo /out:"%~n0.exe" "%~dpsfnx0"
%~n0.exe
endlocal
exit /b 0
*/
print("Hello World!");



c# more code is required compared to the jscript.net and have unavidable "//>nul 2>nul||" print (may be could be remoted with some namespace definition??).Nothing new here:


Code: Select all

//>nul 2>nul||@goto :batch
/*
:batch
@echo off
setlocal

:: find csc.exe
set "frm=%SystemRoot%\Microsoft.NET\Framework\"
for /f "tokens=* delims=" %%v in ('dir /b /a:d  /o:-n "%SystemRoot%\Microsoft.NET\Framework\v*"') do (
   set netver=%%v
   goto :break_loop
)
:break_loop
set csc=%frm%%netver%\csc.exe
:: csc.exe found

call %csc% /nologo /out:"%~n0.exe" "%~dpsfnx0"
%~n0.exe
endlocal
exit /b 0
*/
public class Hello
{
   public static void Main()
   {
      System.Console.WriteLine("Hello, C# World!");
   }
}



Visual Basic - uses similar technique as this here http://stackoverflow.com/a/16622325/388389 .But cannot reduce the initial print of " '1>nul 2>&1 || " , because here is not possible to define a method outside of a class/module or whatever is called :

Code: Select all

'>nul 2>&1||@copy /Y %windir%\System32\doskey.exe '.exe >nul
'& @echo off
'& setlocal
'& set "frm=%SystemRoot%\Microsoft.NET\Framework\"
'& for /f "tokens=* delims=" %%v in ('dir /b /a:d  /o:n "%SystemRoot%\Microsoft.NET\Framework\v*"') do set netver=%%v
'& set vbc=%frm%%netver%\vbc.exe
'& call %vbc% /nologo /out:"%~n0.exe" "%~dpsfnx0"
'& %~n0.exe
'& endlocal
'& exit /b 0

Imports System

Public Module modmain
   ' Main is the application's entry point.
   Sub Main()
     ' Write text to the console.
     Console.WriteLine ("Hello World using Visual Basic!")
   End Sub
End Module
Last edited by npocmaka_ on 13 Jan 2014 11:59, edited 2 times in total.

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: self-compiled .net hybrids

#2 Post by foxidrive » 09 Jan 2014 02:48

Thanks for investigating this npokmaka

If a compiled binary is needed and useful then IMO it's absolutely reasonable to include a script that is used in a temporary file, to create the binary,
and copy the binary to %windir% for future use (with the permission of the user).

The batch file can branch if the binary doesn't exist:

Code: Select all

newcommand >nul 2>nul & if errorlevel 9009 goto :createfile

npocmaka_
Posts: 512
Joined: 24 Jun 2013 17:10
Location: Bulgaria
Contact:

Re: self-compiled .net hybrids

#3 Post by npocmaka_ » 09 Jan 2014 15:35

foxidrive wrote:Thanks for investigating this npokmaka

If a compiled binary is needed and useful then IMO it's absolutely reasonable to include a script that is used in a temporary file, to create the binary,
and copy the binary to %windir% for future use (with the permission of the user).

The batch file can branch if the binary doesn't exist:

Code: Select all

newcommand >nul 2>nul & if errorlevel 9009 goto :createfile


%windir% is a sacred directory and wouldn't put anything there.Rather in a directory included in the %path% ...

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: self-compiled .net hybrids

#4 Post by penpen » 09 Jan 2014 16:30

If you are forced to suppress this output "//>nul 2>&1 ||", or "'1>nul 2>&1 ||" from selfCompile.bat you could use:

Code: Select all

@cmd /Q /C "selfCompile.bat"

penpen

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: self-compiled .net hybrids

#5 Post by foxidrive » 09 Jan 2014 21:30

npocmaka_ wrote:
foxidrive wrote:Thanks for investigating this npokmaka

If a compiled binary is needed and useful then IMO it's absolutely reasonable to include a script that is used in a temporary file, to create the binary,
and copy the binary to %windir% for future use (with the permission of the user).

The batch file can branch if the binary doesn't exist:

Code: Select all

newcommand >nul 2>nul & if errorlevel 9009 goto :createfile


%windir% is a sacred directory and wouldn't put anything there.Rather in a directory included in the %path% ...


%windir% is better than system32 in my view. It's on the path in a default install too.

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: self-compiled .net hybrids

#6 Post by Liviu » 15 May 2014 23:52

An alternative approach is to use a powershell wrapper, which in turn can compile and run c#/vb/js .net code on the fly. There would be some overhead due to the wrapper, but the big overhead remains .net itself, anyway, with or without powershell in the picture. Advantage would be doing away with the temp files and explicit compiler calls. The "trick" requires powershell v2+, which comes with win7+ or can be downloaded separately for xp http://support.microsoft.com/kb/968929. Details below - please pardon the coding style, I am not exactly a powershell/.net person.

Raw idea came from my hybrid posted at http://www.dostips.com/forum/viewtopic.php?p=34426#p34426. Leaving out the technicalities, it amounts to a cmd+powershell hybrid template like the following. EDIT: Thanks to npocmaka_ for pointing the thread at http://www.dostips.com/forum/viewtopic.php?f=3&t=5526 (sorry, not a live link since I exhausted my allowance of URLs per post already) where cmd/ps hybrids were previously discussed, including the "<# :" comment/label trick (npocmaka_), and the "powershell -c -" line to run a script from the standard input in order to work around the requirement of a .ps1 extension for actual files (siberia-man, piped-in "type"). [end edit]

Code: Select all

<# :: --------------------------------------------------------------- .cmd ----
@echo off & setlocal

@rem check default location if powershell not in the 'path'
set "ps=" & for %%X in (powerShell.exe) do (
  set "ps=%%~$PATH:X")
if not defined ps (
  set "ps=%systemRoot%\system32\windowsPowerShell\v1.0\powerShell.exe")

@rem execute script via standard input since powershell requires .ps1 extension
@rem pass arguments via environment since invocation has no actual command line
set psArgs="%~f0" %*
<"%~f0" "%ps%" -ExecutionPolicy bypass -NoProfile -Command -

endlocal & goto :eof & rem ------------------------------------------ .ps1 --#>

# capture 'psArgs' environment variable into '$psArgs' powershell variable
param($psArgs = $env:psArgs)

# parse '$psArgs' into '$psArgs.0', '$psArgs.1' etc arguments, up to max 9
# n.b. %* arguments will have undergone the additional batch expansion
#      convertFrom-csv will have dropped enclosing double-quotes
# e.g. original call 'ps-passthru.cmd 1^^^&2 "3 4" "5 ""6"" 7"' ends up as
#      $psArgs.0 = '<%cd%>\ps-passthru.cmd'
#      $psArgs.1 = '1&2'
#      $psArgs.2 = '3 4'
#      $psArgs.3 = '5 "6" 7'
if($psArgs) {
  $psArgs = ConvertFrom-Csv $psArgs -delim ' ' -header 0,1,2,3,4,5,6,7,8,9 }

for($i = 0; $($psArgs.$i); $i++) {
  Write-Output("arg[" + $i + "] = '" + $($psArgs.$i) + "'") }

exit 0  # ---------------------------------------------------------------------

Combining that with the "Add-Type" capability in powershell v2+, it's possible to write a batch file, which calls itself as a powershell script, which in turn loads/compiles/runs embedded c#/vb/js .net code.

Code: Select all

<# :: --------------------------------------------------------------- .cmd ----
@echo off & setlocal & echo .cmd

set "ps=" & for %%X in (powerShell.exe) do (
  set "ps=%%~$PATH:X")
if not defined ps (
  set "ps=%systemRoot%\system32\windowsPowerShell\v1.0\powerShell.exe")

set "psArg1=.cmd ->"
<"%~f0" "%ps%" -ExecutionPolicy bypass -NoProfile -Command -

endlocal & goto :eof & rem ------------------------------------------ .ps1 --#>

param($psArg1 = $env:psArg1)

# -------------------------------------------------------------------- .cs ----
$CS = @"
namespace PS {
  public class CS
  {
    public static void csEcho(string arg)
    { System.Console.WriteLine(arg + " .cmd.ps.cs"); }
  }
}
"@

# -------------------------------------------------------------------- .vb ----
$VB = @"
Namespace PS
  Public Class VB
    Public Shared Sub vbEcho(ByVal arg as String)
      System.Console.WriteLine(arg & " .cmd.ps.vb")
    End Sub
  End Class
End Namespace
"@

# -------------------------------------------------------------------- .js ----
$JS = @"
import System;
package PS
{
  public class JS
  {
    public static function jsEcho(arg : String)
    { System.Console.WriteLine(arg + " .cmd.ps.js"); }
  }
}
"@

# ------------------------------------------------------------------- .ps1 ----

Add-Type -TypeDefinition $CS -Language CSharp
Add-Type -TypeDefinition $VB -Language VisualBasic
Add-Type -TypeDefinition $JS -Language JScript

Write-Output($psArg1 + " .cmd.ps")

[PS.CS]::csEcho($psArg1 + " .cmd.ps ->")
[PS.VB]::vbEcho($psArg1 + " .cmd.ps ->")
[PS.JS]::jsEcho($psArg1 + " .cmd.ps ->")

exit 0  # ---------------------------------------------------------------------
Output under win7x64.sp1 and xp.sp3:

Code: Select all

C:\tmp>cmd-ps.cmd
.cmd
.cmd -> .cmd.ps
.cmd -> .cmd.ps -> .cmd.ps.cs
.cmd -> .cmd.ps -> .cmd.ps.vb
.cmd -> .cmd.ps -> .cmd.ps.js

Liviu
Last edited by Liviu on 16 May 2014 15:31, edited 1 time in total.

npocmaka_
Posts: 512
Joined: 24 Jun 2013 17:10
Location: Bulgaria
Contact:

Re: self-compiled .net hybrids

#7 Post by npocmaka_ » 16 May 2014 01:07

Hi Liviu,
Pretty interesting approach (powershell is powerful ...) .I like the missed compiled exe.
The main impediment is that the powershell is not installed on Vista and XP by default (not sure if this is viable argument considering their decreasing share) ,but it's merely impossible to see a Windows machine without installed .NET .

(Here I posted a similar way for batch/ps1 hybridization - comment from [ 21 Apr 2014].)

Input redirection is more simple but I have two worries (which I'll test later) does it support break :label as and is it possible to get command line arguments?

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: self-compiled .net hybrids

#8 Post by penpen » 16 May 2014 06:28

Just a side note: The initial vb.net/batch hybrid doesn't need to create the "'.exe" file:

Code: Select all

rem^ & @echo off
rem^ & setlocal
rem^ & set "frm=%SystemRoot%\Microsoft.NET\Framework\"
rem^ & for /f "tokens=* delims=" %%v in ('dir /b /a:d  /o:n "%SystemRoot%\Microsoft.NET\Framework\v*"') do set netver=%%v
rem^ & set vbc=%frm%%netver%\vbc.exe
rem^ & call %vbc% /nologo /out:"%~n0.exe" "%~dpsfnx0"
rem^ & %~n0.exe
rem^ & endlocal
rem^ & exit /b 0

Imports System

Public Module modmain
   ' Main is the application's entry point.
   Sub Main()
     ' Write text to the console.
     Console.WriteLine ("Hello World using Visual Basic!")
   End Sub
End Module
But it cannot avoid printing unwanted text ("rem &" in this case), too.

penpen

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: self-compiled .net hybrids

#9 Post by Liviu » 16 May 2014 15:20

@npocmaka_ thanks for the link. I missed that, and as good as google may be, it's still not easy to locate prior work when the relevant keyword looks like "<# :" ;-) I edited my other post to include a reference to the older thread.

npocmaka_ wrote:is it possible to get command line arguments?
Not directly, since scripts processed off the standard input stream don't allow for arguments. One way to work around it is by passing arguments in environment variables, like the examples above do. Another way would be to use piped input instead of redirected, and insert a few PS assignments like "echo $arg1='%1'" before the big 'type "%~f0"'.

Liviu

npocmaka_
Posts: 512
Joined: 24 Jun 2013 17:10
Location: Bulgaria
Contact:

Re: self-compiled .net hybrids

#10 Post by npocmaka_ » 17 Jun 2014 14:57

here's a jscript.net for color output (I know the FINDSTR functions , but anyway...)

Code: Select all


@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal

for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d  /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
   set "jsc=%%v"
)

if not exist "%~n0.exe" (
   "%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
)

%~n0.exe %*

endlocal & exit /b %errorlevel%

*/

import System;

var arguments:String[] = Environment.GetCommandLineArgs();

var newLine = false;
var output = "";
var foregroundColor = Console.ForegroundColor;
var backgroundColor = Console.BackgroundColor;
var evaluate = false;
var currentBackground=Console.BackgroundColor;
var currentForeground=Console.ForegroundColor;


//http://stackoverflow.com/a/24294348/388389
var jsEscapes = {
  'n': '\n',
  'r': '\r',
  't': '\t',
  'f': '\f',
  'v': '\v',
  'b': '\b'
};

function decodeJsEscape(_, hex0, hex1, octal, other) {
  var hex = hex0 || hex1;
  if (hex) { return String.fromCharCode(parseInt(hex, 16)); }
  if (octal) { return String.fromCharCode(parseInt(octal, 8)); }
  return jsEscapes[other] || other;
}

function decodeJsString(s) {
  return s.replace(
      // Matches an escape sequence with UTF-16 in group 1, single byte hex in group 2,
      // octal in group 3, and arbitrary other single-character escapes in group 4.
      /\\(?:u([0-9A-Fa-f]{4})|x([0-9A-Fa-f]{2})|([0-3][0-7]{0,2}|[4-7][0-7]?)|(.))/g,
      decodeJsEscape);
}


function printHelp( ) {
   print( arguments[0] + "  -s string [-f foreground] [-b background] [-n] [-e]" );
   print( " " );
   print( "   string          String to be printed" );
   print( "   foreground       Foreground color - a " );
   print( "               number between 0 and 15." );
   print( "   background       Background color - a " );
   print( "               number between 0 and 15." );
   print( "   -n               Indicates if a new line should" );
   print( "               be written at the end of the ");
   print( "               string(by default - no)." );
   print( "   -e               Evaluates special character " );
   print( "               sequences like \\n\\b\\r and etc ");
   print( "" );
   print( "Colors :" );
   for ( var c = 0 ; c < 16 ; c++ ) {
      
      Console.BackgroundColor = c;
      Console.Write( " " );
      Console.BackgroundColor=currentBackground;
      Console.Write( "-"+c );
      Console.WriteLine( "" );
   }
   Console.BackgroundColor=currentBackground;
   
   

}

function errorChecker( e:Error ) {
      if ( e.message == "Input string was not in a correct format." ) {
         print( "the color parameters should be numbers between 0 and 15" );
         Environment.Exit( 1 );
      } else if (e.message == "Index was outside the bounds of the array.") {
         print( "invalid arguments" );
         Environment.Exit( 2 );
      } else {
         print ( "Error Message: " + e.message );
         print ( "Error Code: " + ( e.number & 0xFFFF ) );
         print ( "Error Name: " + e.name );
         Environment.Exit( 666 );
      }
}

function numberChecker( i:Int32 ){
   if( i > 15 || i < 0 ) {
      print("the color parameters should be numbers between 0 and 15");
      Environment.Exit(1);
   }
}


if ( arguments.length == 1 || arguments[1].toLowerCase() == "-help" || arguments[1].toLowerCase() == "-help"   ) {
   printHelp();
   Environment.Exit(0);
}

for (var arg = 1; arg <= arguments.length-1; arg++ ) {
   if ( arguments[arg].toLowerCase() == "-n" ) {
      newLine=true;
   }
   
   if ( arguments[arg].toLowerCase() == "-e" ) {
      evaluate=true;
   }
   
   if ( arguments[arg].toLowerCase() == "-s" ) {
      output=arguments[arg+1];
   }
   
   
   if ( arguments[arg].toLowerCase() == "-b" ) {
      
      try {
         backgroundColor=Int32.Parse( arguments[arg+1] );
      } catch(e) {
         errorChecker(e);
      }
   }
   
   if ( arguments[arg].toLowerCase() == "-f" ) {
      try {
         foregroundColor=Int32.Parse(arguments[arg+1]);
      } catch(e) {
         errorChecker(e);
      }
   }
}

Console.BackgroundColor = backgroundColor ;
Console.ForegroundColor = foregroundColor ;

if ( evaluate ) {
   output=decodeJsString(output);
}

if ( newLine ) {
   Console.WriteLine(output);   
} else {
   Console.Write(output);
   
}

Console.BackgroundColor = currentBackground;
Console.ForegroundColor = currentForeground;

Last edited by npocmaka_ on 19 Jun 2014 03:21, edited 4 times in total.

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: self-compiled .net hybrids

#11 Post by foxidrive » 18 Jun 2014 02:57

That looks useful npocmaka.

Is there a way to print a double quote?

npocmaka_
Posts: 512
Joined: 24 Jun 2013 17:10
Location: Bulgaria
Contact:

Re: self-compiled .net hybrids

#12 Post by npocmaka_ » 18 Jun 2014 05:44

the double quotes are not a problem:

Image
But the backslash is :!: and should be doubled
With a little effort I could include the javascript special sequences and capability to print unicode /extended asci characters (didn't thought about this :) )

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: self-compiled .net hybrids

#13 Post by foxidrive » 18 Jun 2014 06:09

I see, thanks.

npocmaka_
Posts: 512
Joined: 24 Jun 2013 17:10
Location: Bulgaria
Contact:

Re: self-compiled .net hybrids

#14 Post by npocmaka_ » 18 Jun 2014 15:14

I've updated the colored output program - now it has additional switch -e that will evaluate special character sequneces in jscript and will allow output of characters by their unicode numbers:

Image

npocmaka_
Posts: 512
Joined: 24 Jun 2013 17:10
Location: Bulgaria
Contact:

Re: self-compiled .net hybrids

#15 Post by npocmaka_ » 24 Jun 2014 15:10

Password hidder:

Code: Select all

@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal

for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d  /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
   set "jsc=%%v"
)

if not exist "%~n0.exe" (
   "%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
)

for /f "tokens=* delims=" %%p in ('"%~n0.exe"') do (
   set "pass=%%p"
)

echo your password is %pass%

endlocal & exit /b %errorlevel%

*/



import System;



var pwd = "";
var key;

Console.Error.Write("Enter password: ");

        do {
           key = Console.ReadKey(true);
           if ( (key.KeyChar.ToString().charCodeAt(0)) >= 20 && (key.KeyChar.ToString().charCodeAt(0) <= 126) ) {
           pwd=pwd+(key.KeyChar.ToString());
              Console.Error.Write("*");
           }   

        } while (key.Key != ConsoleKey.Enter);
      Console.Error.WriteLine();
        Console.WriteLine(pwd);

Post Reply