Convert a small PS script into a long line in a .BATch file
I have this PowerShell code that I got from the answer to this question; it show the location/dimensions of the cmd.exe window where the PS code run:
$WindowFunction,$RectangleStruct = Add-Type -MemberDefinition @'
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
'@ -Name "type$([guid]::NewGuid() -replace '-')" -PassThru
$MyWindowHandle = (Get-Process -Id (Get-WmiObject Win32_Process -Filter "ProcessId=$PID").ParentProcessId).MainWindowHandle
$WindowRect = New-Object -TypeName $RectangleStruct.FullName
$null = $WindowFunction::GetWindowRect($MyWindowHandle,[ref]$WindowRect)
Write-Host $WindowRect.Left $WindowRect.Top $WindowRect.Right $WindowRect.Bottom
When I run this code in a .ps1 script from the command line, it works correctly:
C:\Users\Antonio\Documents\test> powershell Set-ExecutionPolicy -ExecutionPolicy
Unrestricted -Scope Process; .\test.ps1
26 -7 943 738
I want to insert this code in a .BATch file in order to don't have a separate .ps1 file, so I must write the same code in a long line as parameters to powershell
command. However, in order to keep the readability, I want to use separate lines in the .bat file and terminate each one with the Batch continuation character ^
; this was my first attempt:
@echo off
PowerShell ^
$WindowFunction,$RectangleStruct = Add-Type -MemberDefinition '^
[DllImport("user32.dll", SetLastError = true)] ^
[return: MarshalAs(UnmanagedType.Bool)] ^
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); ^
[StructLayout(LayoutKind.Sequential)] ^
public struct RECT ^
{ ^
public int Left; ^
public int Top; ^
public int Right; ^
public int Bottom; ^
} ^
' -Name "type$([guid]::NewGuid() -replace '-')" -PassThru; ^
$MyWindowHandle = (Get-Process -Id (Get-WmiObject Win32_Process -Filter "ProcessId=$PID").ParentProcessId).MainWindowHandle; ^
$WindowRect = New-Object -TypeName $RectangleStruct.FullName; ^
$null = $WindowFunction::GetWindowRect($MyWindowHandle,[ref]$WindowRect); ^
Write-Host $WindowRect.Left $WindowRect.Top $WindowRect.Right $WindowRect.Bottom
%End PowerShell%
When I run this Batch file, several errors are reported:
C:\Users\Antonio\Documents\test> test.bat
Add-Type : c:\Users\Antonio\AppData\Local\Temp\yhd4ckqv.0.cs(8) : El nombre
'user32' no existe en el contexto actual
c:\Users\Antonio\AppData\Local\Temp\yhd4ckqv.0.cs(7) : {
c:\Users\Antonio\AppData\Local\Temp\yhd4ckqv.0.cs(8) : >>>
[DllImport(user32.dll, SetLastError = true)] [return:
MarshalAs(UnmanagedType.Bool)] public static extern bool GetWindowRect(IntPtr
hWnd, ref RECT lpRect); [StructLayout(LayoutKind.Sequential)] public struct
RECT { public int Left; public int Top; public int Right; public int Bottom; }
c:\Users\Antonio\AppData\Local\Temp\yhd4ckqv.0.cs(9) :
En línea: 1 Carácter: 36
+ $WindowFunction,$RectangleStruct = Add-Type -MemberDefinition '
[DllImport(user3 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~
+ CategoryInfo : InvalidData: (c:\Users\Antoni...contexto actual:
CompilerError) [Add-Type], Exception
+ FullyQualifiedErrorId : SOURCE_CODE_ERROR,Microsoft.PowerShell.Commands.
AddTypeCommand
Add-Type : No se puede agregar el tipo. Hubo errores de compilación.
En línea: 1 Carácter: 36
+ $WindowFunction,$RectangleStruct = Add-Type -MemberDefinition '
[DllImport(user3 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~
and a long et cetera....
I tried to pass the apostrophe to the line below, changed apostrophes by quotes and viceversa, eliminated additional spaces at beginning of each line and several other modifications, but I couldn't found the right way to write this code. I wrote several PS code segments before much larger than this one in the same way with no problems at all. Although I am an experienced programmer, I am novice to PowerShell and its multiple idiosyncrasies had always confused me...
What is the right way to write this PS code in a Batch file? I'll appreciate it if a simple explanation of the cause of the problem is also included...
Double quote literals must be escaped as \"
@echo off
PowerShell^
$WindowFunction,$RectangleStruct = Add-Type -MemberDefinition '^
[DllImport(\"user32.dll\", SetLastError = true)]^
[return: MarshalAs(UnmanagedType.Bool)]^
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);^
[StructLayout(LayoutKind.Sequential)]^
public struct RECT^
{^
public int Left;^
public int Top;^
public int Right;^
public int Bottom;^
}^
' -Name \"type$([guid]::NewGuid() -replace '-')\" -PassThru;^
$MyWindowHandle = (Get-Process -Id (^
Get-WmiObject Win32_Process -Filter \"ProcessId=$PID\"^
).ParentProcessId).MainWindowHandle;^
$WindowRect = New-Object -TypeName $RectangleStruct.FullName;^
$null = $WindowFunction::GetWindowRect($MyWindowHandle,[ref]$WindowRect);^
Write-Host $WindowRect.Left $WindowRect.Top $WindowRect.Right $WindowRect.Bottom
There was a topic on DosTips a while ago that addressed this. You can make a batch/Powershell hybrid with a simple header:
<# :
:: Header to create Batch/PowerShell hybrid
@echo off
setlocal
set "POWERSHELL_BAT_ARGS=%*"
if defined POWERSHELL_BAT_ARGS set "POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%"
endlocal & powershell -NoLogo -NoProfile -Command "$_ = $input; Invoke-Expression $( '$input = $_; $_ = \"\"; $args = @( &{ $args } %POWERSHELL_BAT_ARGS% );' + [String]::Join( [char]10, $( Get-Content \"%~f0\" ) ) )"
:: Any batch code that gets run after your PowerShell goes here
goto :EOF
#>
Just throw your Powershell code after the #>
and save the file as a regular .bat script. In your case:
<# :
:: Header to create Batch/PowerShell hybrid
@echo off
setlocal
set "POWERSHELL_BAT_ARGS=%*"
if defined POWERSHELL_BAT_ARGS set "POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%"
endlocal & powershell -NoLogo -NoProfile -Command "$_ = $input; Invoke-Expression $( '$input = $_; $_ = \"\"; $args = @( &{ $args } %POWERSHELL_BAT_ARGS% );' + [String]::Join( [char]10, $( Get-Content \"%~f0\" ) ) )"
goto :EOF
#>
$WindowFunction,$RectangleStruct = Add-Type -MemberDefinition @'
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
'@ -Name "type$([guid]::NewGuid() -replace '-')" -PassThru
$MyWindowHandle = (Get-Process -Id (Get-WmiObject Win32_Process -Filter "ProcessId=$PID").ParentProcessId).MainWindowHandle
$WindowRect = New-Object -TypeName $RectangleStruct.FullName
$null = $WindowFunction::GetWindowRect($MyWindowHandle,[ref]$WindowRect)
Write-Host $WindowRect.Left $WindowRect.Top $WindowRect.Right $WindowRect.Bottom