【问题标题】:C# source code embedded into .bat file嵌入到 .bat 文件中的 C# 源代码
【发布时间】:2016-06-29 07:26:54
【问题描述】:

有人可能想知道 - 如何使用嵌入式 c# 代码制作 .bat 文件以“即时”编译和执行它?
是否可以在一个文件中同时包含批处理指令和 c# 代码?

【问题讨论】:

    标签: c# .net batch-file compilation csc


    【解决方案1】:

    是的,这是可能的。
    这是一个例子:

    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);
        }
    }
    

    说明:这个批处理文件由两部分组成:第一部分是批处理代码,第二部分是c#代码。执行时,命令 shell 将忽略 c#-cmets /**/ 作为错误行并仅执行批处理代码。由于批处理块末尾的退出命令,执行永远不会到达 c# 代码。
    文件的批处理部分搜索csc.exe(.NET 编译器)。找到后,批处理文件将自己传递到csc.exe 以编译c#代码。由于 cmets (/* 和 */),批处理部分被忽略,只有 c# 部分将被编译。编译后生成的.exe文件被执行,执行后删除。

    编辑2> nul 将标准错误(描述符 2)重定向到 null 以抑制“未找到”消息。

    【讨论】:

    • 无论何时启动批处理文件,您都会得到'/*' is not recognized as an internal or external command, operable program or batch file.,不过:p
    • @Nyerguds 我认为,cls 应该会解决这个问题:p
    • @Exerion 哦,我从不怀疑这一点。只是没有真正的开始顺序对两者都完全有效。您只是选择了两个弊端中的较小者,因为 .bat 不关心单个非法命令,而 cs 文件不会编译;)
    • 哦,我找到了。使用/* 2> nul 作为第一行将抑制“未找到”消息:stackoverflow.com/q/11060166/395685 遗憾的是,由于这是在 @echo 关闭之前,它不会抑制显示的命令,所以它仍然需要 cls .
    • 啊,我添加了一个小附录。仍然需要 cls,因为该命令位于 @echo off 之前,这意味着它会出现在屏幕上。而且你也不能在它前面加上@,因为这会再次使它成为C#无效。但至少你不会让用户担心错误消息会在他们的控制台上闪烁一秒钟,我猜。
    【解决方案2】:

    您可以注释掉批处理代码,但您无法避免注释被批处理作为错误处理。

    可以抑制错误本身,但至少会显示第一行。

    /* 2>NUL
    @echo off
    ...
    start the C# code
    */
    ...
    C#-Code
    

    由于 C# 似乎不接受 :@ 字符之一作为文件中的第一个字符,因此我看不到完美解决方案的可能方法。

    【讨论】:

      【解决方案3】:

      有几种方法。

      1) 与之前的答案类似,但自动检测最新版本的 .net 框架:

      // 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) 在 Windows 10 中,默认安装了 .net 框架,并且 msbuild 允许内联任务,这提供了内存编译(即 - 没有额外的 exe 文件)和嵌入的可能性xml 这意味着没有多余的输出。注意命令行参数保存在CMD_ARGS环境变量中:

      <!-- :
          @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) 使用 powershell 和 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) 还有一点需要注意 - 如果您不使用 P/Invoke,可能更方便的是使用 JScript.net,它需要更少的代码并且具有相似的语法,并且可以让您访问 .净的东西,没有多余的输出:

      @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")
      

      【讨论】:

        猜你喜欢
        • 2012-05-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-31
        • 2023-03-30
        相关资源
        最近更新 更多