Reputation: 469
One may wonder - how to make a .bat
file with embedded c# code to compile and execute it 'on-the-fly'?
Is it possible to have both batch-instructions and c# code in one file?
Upvotes: 3
Views: 773
Reputation: 57272
There are few ways.
1) Similar to the previous answers ,but with automatic detection the latest version of .net framework:
// 2>nul||@goto :batch
/*
@echo off
setlocal
:: find csc.exe
set "csc="
for /r "%SystemRoot%\Microsoft.NET\Framework\" %%# in ("*csc.exe") do set "csc=%%#"
if not exist "%csc%" (
echo no .net framework installed
exit /b 10
)
if not exist "%~n0.exe" (
call %csc% /nologo /w:0 /out:"%~n0.exe" "%~dpsfnx0" || (
exit /b %errorlevel%
)
)
%~n0.exe %*
endlocal & exit /b %errorlevel%
*/
using System;
class QE {
static void Main(string[] args) {
Console.WriteLine("Echo from C#");
}
}
2) In windows 10 the .net framework is installed by default and there the msbuild allows inline tasks which gives a possibility for in-memory compilation (i.e. - no additional exe files) and emebedding in xml which means no redundant output. Note that command line arguments are saved in CMD_ARGS
environment variable:
<!-- :
@echo off
echo -^- FROM BATCH
set "CMD_ARGS=%*"
:::::: Starting C# code :::::::
:: searching for msbuild location
for /r "%SystemRoot%\Microsoft.NET\Framework\" %%# in ("*msbuild.exe") do set "msb=%%#"
if not defined msb (
echo no .net framework installed
exit /b 10
)
rem :::::::::: calling msbuid :::::::::
call %msb% /nologo /noconsolelogger "%~dpsfnx0"
rem ::::::::::::::::::::::::::::::::::::
exit /b %errorlevel%
-->
<Project ToolsVersion="$(MSBuildToolsVersion)" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="_">
<_/>
</Target>
<UsingTask
TaskName="_"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll" >
<Task>
<Reference Include="$(MSBuildToolsPath)\System.Windows.Forms.dll"/>
<Using Namespace="System" />
<Code Type="Method" Language="cs">
<![CDATA[
public override bool Execute(){
MyMethod();
return true;
}
void MyMethod(){
Console.WriteLine("Whoa");
String CMD_ARGS=Environment.GetEnvironmentVariable("CMD_ARGS");
System.Console.WriteLine("Echo from C# with msBuild -- "+"$(MSBuildToolsVersion)");
}
]]>
</Code>
</Task>
</UsingTask>
</Project>
3) With powershell and Add-Type:
<# : batch portion
@echo off & setlocal
set "CMD_ARGS=%~1"
powershell -noprofile "iex (${%~f0} | out-string)"
goto :EOF
: end batch / begin powershell #>
param($psArg1 = $env:psArg1)
$CS = @"
namespace PS {
public class CS
{
public static void csEcho(string arg)
{ System.Console.WriteLine("echo from C# " + arg); }
}
}
"@
Add-Type -TypeDefinition $CS -Language CSharp
[PS.CS]::csEcho($psArg1 + " and PowerShell")
4) One more note - if you're not using P/Invoke may more convenient will be to use JScript.net which require less code and has similar syntax and give you access to the .net stuff and has no redundant output:
@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;
Console.Write("Echo from .NET")
Upvotes: 0
Reputation: 469
Yes, it is possible.
Here is an example:
Example.bat
/* 2> nul
@echo off && cls && echo Loading... && echo.
set WinDirNet=%WinDir%\Microsoft.NET\Framework
if exist "%WinDirNet%\v2.0.50727\csc.exe" set csc="%WinDirNet%\v2.0.50727\csc.exe"
if exist "%WinDirNet%\v3.5\csc.exe" set csc="%WinDirNet%\v3.5\csc.exe"
if exist "%WinDirNet%\v4.0.30319\csc.exe" set csc="%WinDirNet%\v4.0.30319\csc.exe"
if "%csc%" == "" ( echo .NET Framework not found! && echo. && pause && exit )
%csc% /nologo /out:"%~dpnx0.exe" "%~dpnx0"
if not "%ERRORLEVEL%" == "0" ( echo. && pause && exit )
cls
"%~dpnx0.exe" %*
del "%~dpnx0.exe"
exit
*/
using System;
class Program
{
static void Main()
{
System.Console.WriteLine("Hello, World!\r\nI am at " + System.Environment.Version);
}
}
Explanation: this batch-file consist of two parts: firstly a batch-code and secondary a c# code. When executing, command shell will ignore c#-comments /*
and */
as error-lines and execute only batch-code. Due exit command at the end of batch-block, the execution never reach c# code.
Batch part of the file searches for csc.exe
(.NET compiler). After found, batch file passes itself into csc.exe
to compile c# code. Due to comments (/* and */) batch-part is ignored and only c# part will be compiled. After compilation generated .exe file is executed and deleted after execution.
Edit: 2> nul
redirecting standard error (descriptor 2) to null to suppress 'not-found' message.
Upvotes: 4
Reputation: 82337
You can comment out the batch code, but you can't avoid that the comment is handled by batch as an error.
The error itself can be suppressed, but at least the first line will be shown.
/* 2>NUL
@echo off
...
start the C# code
*/
...
C#-Code
As C# seems not to accept one of the :@
characters as the first character in a file, I can't see a possible way for a perfect solution.
Upvotes: 1