2

I have a CMD batch file which takes a long time to process and I wish to alert the user that some input is required by flashing the CMD window.

This example works great in powershell but if I try to compile it from within CMD by

powershell -c "Add-Type -TypeDefinition @\";"^
 "using System;"^
..... and so on

it fails at the very first line. Similar problem discussed here but looks like there was never a solution given.

So, does anyone have ideas as to how I can make this work and get my CMD window to flash?

edit: @mklement0's answer got me in the right direction. THANK YOU.

jeffp
  • 25
  • 5
  • @Compo's comment is now gone, but to recap it, with a tl;dr qualification: `;` after `@\"` shouldn't be there, but even without it your code wouldn't work, because PowerShell's here-strings require a _newline_ after the opening and before the closing delimiter, which you cannot provide from `cmd.exe`, because line continuation with `^` does _not_ insert newlines. Quoting _each line_ with`"..."` is an option, as long as each such line _has whitespace before the opening `"`_. My answer forgoes this for a simpler representation, but it does require selective `^`-escaping of `cmd.exe` metachars. – mklement0 Jun 01 '23 at 21:55

1 Answers1

3

Passing multiline PowerShell commands from cmd.exe is nontrivial and requires lots of careful escaping.

Here's a minimal example with Add-Type: Using C# code, it defines class [Foo] with a static Bar() method and then calls it from PowerShell:

powershell -noprofile -c Add-Type -TypeDefinition ' ^
  public class Foo { ^
    public static string Bar() { return \"hi!\"; } ^
  } ^
  '; ^
  [Foo]::Bar()
  • An overview of the escaping rules is in this answer; a few things worth noting here:

    • Here-strings (@"<newline>...<newline>"@ and @'<newline>...<neline>'@) cannot be used, because they have strict whitespace requirements, notably requiring a newline both immediately after the opening @" / @' as well as before the closing @" / @', whereas cmd.exe's line-continuation with ^ does not result in newlines. Therefore, your approach won't work (even with the extraneous ; after the opening @" removed).

    • Use '...' (verbatim (single-quoted) strings), where possible, which simplifies escaping.
      Note that PowerShell's regular string literals, not just here-strings, are also capable of spanning multiple lines, which the solution above takes advantage of.

    • When you do need to use ", escape them as \" - if a " occurs alone or there is an uneven number of them on a given interior line, escape it / the last one as \^" (sic).

      • Note that anything outside \"...\" is seen as unquoted by cmd.exe, so that metacharacters such as & and | require individual ^-escaping there.
  • You should be able to apply these rules to your FlashWindowEx P/Invoke declaration.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Thank you this solved my other question ! Would it be wise to escape all " as \^" and not have to count them to check if there's an even or odd number of doublequotes ? – Shodan Jun 20 '23 at 01:33
  • @Shodan, if you use this approach, you'll need to individually `^`-escape all other `cmd.exe` metacharacters that occur between `\^"...\^"` too. E.g., instead of `return \"hi & bye!\"`, you'd have to use `return \^"hi ^& bye!\^"` – mklement0 Jun 20 '23 at 02:45