Jump to content

DISM console progress using StdoutRead


RvdH
 Share

Recommended Posts

Hello,

It has been a while last time i was here, nice to see everything is still up and running like it was in the time i joined here.

Anyone can help me out, i like to read DISM StdoutRead and display a custom progress bar using AutoIt

I have found some partial scripts here, but none seems to work with DISM

Posted Image

Posted Image

In the code below the idea was to read out the increasing '=' signs as shown in the pics above

#RequireAdmin
Opt("MustDeclareVars", 0)

; Script Start - Add your code below here

; Set the max number of = signs to expect
$maxTicks = 52 ;26 = signs both left and right from the percentage indicator

; Run the child process, connecting to its STDOUT pipe
$process = Run(@COMSPEC & ' /k dism /mount-wim /wimfile:' & @ScriptDir & '/boot.wim /index:1 /mountDir:C:\Mount', @ScriptDir, @SW_HIDE, 2)


; Show the progress bar
ProgressOn("= Count", "Counting...")

; Read from the child's STDOUT
While 1
    $readTicks = StdoutRead($process)

   ; if StdoutRead sets @error to -1 we're at EOF, so exit
    If @error = -1 Then ExitLoop
    StringReplace($readTicks, "=", "0")
   ; StringReplace keeps a count of chars it replaces in @extended
    $totalTicks += @extended
   ; Adjust the progress bar
    ProgressSet(($totalTicks / $maxTicks) * 100)
WEnd

; Hide the progress bar
ProgressOff()

MsgBox(0, "Debug", "Done.")
; Finished

Anyone got an idea how to make this work?

RvdH

Link to comment
Share on other sites

Hey,

I am getting a undeclared variable error from this line: $totalTicks += @extended. I added the $totalTicks = 0 at the beginning of the script but then I realized that it doesn't make sense to add the new number of "=" to the old number of "=". So I simply changed that to:

StringReplace($readTicks, "=", "0")
   ; StringReplace keeps a count of chars it replaces in @extended
   ; Adjust the progress bar
    ProgressSet((@extended / $maxTicks) * 100)

In the end, I wrote a "unit test" for your script since I can't test DISM like you can. And it shows that the code works:

; Script Start - Add your code below here

; Set the max number of = signs to expect
$maxTicks = 52 ;26 = signs both left and right from the percentage indicator

; Run the child process, connecting to its STDOUT pipe
$process = 0;Run(@COMSPEC & ' /k dism /mount-wim /wimfile:' & @ScriptDir & '/boot.wim /index:1 /mountDir:C:\Mount', @ScriptDir, @SW_HIDE, 2)

; Show the progress bar
ProgressOn("= Count", "Counting...")

; Read from the child's STDOUT
While 1
    $readTicks = _StdoutRead($process)

   ; if StdoutRead sets @error to -1 we're at EOF, so exit
    If @error = -1 Then ExitLoop
    StringReplace($readTicks, "=", "0")
   ; StringReplace keeps a count of chars it replaces in @extended
   ; Adjust the progress bar
    ProgressSet((@extended / $maxTicks) * 100)
WEnd

; Hide the progress bar
ProgressOff()

MsgBox(0, "Debug", "Done.")
; Finished

Func _StdoutRead($ignoreMe)
    Static $i = 0
    Switch $i
        Case 0
            Return "===="
        Case 1
            Return "==========="
        Case 2
            Return "=================="
        Case 3
            Return "============================="
        Case 4
            Return "===================================================="
        Case Else
            Return SetError(1)
    EndSwitch
    $i += 1
EndFunc

If this still doesn't fix your problem, then I don't think AutoIt is reading the output of the progress from console. ($readTicks is always an empty line)

Link to comment
Share on other sites

I think Manadar is right in that you can't read that progress bar all that easily... I think you need to use ReadConsoleOutput to read a specific block, and unfortunately it's one of the functions I haven't implemented you in my console udf.

Edit: Can't you just read the percentage value?

Edited by Mat
Link to comment
Share on other sites

@Manadar

Thx for the prompt reply

Looks like you are right, progress indicator just hangs. So it seems like you said AutoIt is not reading the dynamic changing values in a line... damn, another way to accomplish that?

Edited by RvdH
Link to comment
Share on other sites

Hi,

I have the same problem, except I try to do it in .NET. I tried to send an email to the developers of gimagex and imagex/dism (Microsoft), but they didn't answered... There is a solution for sure, because gimagex have a progress bar... but what is the solution !?

Thanks !

Link to comment
Share on other sites

@Manadar

Thx, guess you are right

@Mat,

To be honest, your suggestion for me is way out of my (AutoIt) programming skills. With other words i have no clue of where to start from.

Maybe you could add it to your "TODO" list for your console udf? :x

Edited by RvdH
Link to comment
Share on other sites

Mmm, any known alternative to using DISM to test on. (if DISM is not using some special MS-trick here of course.)

"Straight_and_Crooked_Thinking" : A "classic guide to ferreting out untruths, half-truths, and other distortions of facts in political and social discussions."
"The Secrets of Quantum Physics" : New and excellent 2 part documentary on Quantum Physics by Jim Al-Khalili. (Dec 2014)

"Believing what you know ain't so" ...

Knock Knock ...
 

Link to comment
Share on other sites

I want to get the full list, the only problem is that it uses extensive structures... Such as needing an array of a particular structure. You should be ok making a workaround using just one CHAR_INFO struct and calling the function 4 times to get the percentage, or write it in C in which case it gets easier (but other bit's will get harder :x )

Link to comment
Share on other sites

Mmm, any known alternative to using DISM to test on. (if DISM is not using some special MS-trick here of course.)

Yes. Before you look at the code, this is NOT a good example, and I would never want it used for anything other than testing on. The correct would be making a second buffer and using WriteConsoleOutput, making the cursor jump everywhere is not the way to do this.

#include <Console.au3>

_Console_Alloc()

; The width of the dism progress is 60 chars, 2 chars are brackets so 58 possible = signs.
; The percentage starts at 27 chars, and is right aligned in 6 chars (Always to 1dp).

Local $tScreenBufferInfo = _Console_GetScreenBufferInfo()
Local $iWidth = DllStructGetData($tScreenBufferInfo, "SizeX")
Local $iY = DllStructGetData($tScreenBufferInfo, "CursorPositionY")

_Console_FillOutputCharacter(-1, " ", $iWidth, 0, $iY)
_Console_FillOutputCharacter(-1, "[", 1, 0, $iY)
_Console_FillOutputCharacter(-1, "]", 1, 59, $iY)

_Console_SetCursorPosition(-1, 1, $iY)

Local $iPercent
For $i = 1 To 58
    $sPercent = StringFormat("%.1f%%", Round(($i / 58) * 100, 1))
    _Console_SetCursorPosition(-1, 33 - StringLen($sPercent), $iY)
    _Console_Write($sPercent)

    If $i < 33 - StringLen($sPercent) Or $i >= 33 Then
        _Console_SetCursorPosition(-1, $i, $iY)
        _Console_Write("=")
    EndIf

    _Console_SetCursorPosition(-1, 0, $iY + 1)

    Sleep(100)
Next

Sleep(2000)

_Console_Free()
Link to comment
Share on other sites

Ok, a few updates. You can use the ReadConsoleOutputCharacter function rather than the ReadConsoleOutput one. I don't see any reason why I can't implement it soon :x it looks like it's easy enough.

Also, another better example (in C++ as I haven't implemented the function in AutoIt yet):

#include "stdafx.h"
#include <windows.h> 

// The width of the dism progress is 60 chars, 2 chars are brackets so 58 possible = signs.
// The percentage starts at char 27, and is right aligned in 6 chars (Always to 1dp).

int _tmain(int argc, _TCHAR* argv[])
{
    char sBar[60];
    FillMemory(sBar, 60, ' ');

    float nPercent;
    char sPercent[6];
    HANDLE hConsole;
    COORD dwTarget;
    DWORD dwWritten;
    CONSOLE_CURSOR_INFO tCCI;

    hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 

    dwTarget.X = 0;
    dwTarget.Y = 0;

    GetConsoleCursorInfo(hConsole, &tCCI);
    tCCI.bVisible = false;
    SetConsoleCursorInfo(hConsole, &tCCI);

    // Add knowns:
    sBar[0] = '[';
    sBar[59] = ']';
    sBar[32] = '%';

    for (int i = 1; i <= 58; i++)
    {
        nPercent = (float)i / 58 * 100;
        sprintf(sPercent, "%5.1f", nPercent);

        if (i < 30 || i > 32)
            sBar[i] = '=';

        for (int n = 0; n <= 4; n++)
        {
            if (sPercent[n] == ' ') continue;

            sBar[27 + n] = sPercent[n];
        }

        WriteConsoleOutputCharacterA(hConsole, &sBar[0], 60, dwTarget, &dwWritten);
        Sleep(100);
    }

    dwTarget.X = 0;
    dwTarget.Y = 1;
    SetConsoleCursorPosition(hConsole, dwTarget);


    tCCI.bVisible = true;
    SetConsoleCursorInfo(hConsole, &tCCI);

    getchar();

    return 0;
}

ProgressExample.exe

Edit: And finally the solution. It runs slightly behind, and eats a lot of cpu - but it works :P It needs the ProgressExample.exe I wrote from above.

#include <Console.au3>

$__gvKernel32 = DllOpen("kernel32.dll")

Local $iPid = Run("ProgressExample.exe", @ScriptDir)
ProcessWait($iPid)

_Console_Attach($iPid)

; The width of the dism progress is 60 chars, 2 chars are brackets so 58 possible = signs.
; The percentage starts at 27 chars, and is right aligned in 6 chars (Always to 1dp).

Local $hStdOut = _Console_GetStdHandle($STD_OUTPUT_HANDLE)
Local $tScreenBufferInfo = _Console_GetScreenBufferInfo($hStdOut)
Local $iY = DllStructGetData($tScreenBufferInfo, "CursorPositionY")

ProgressOn("ProgressExample.exe", "...")
While ProcessExists($iPid)
    $s = _Console_ReadConsoleOutputCharacter($hStdOut, 5, 27, $iY)
    ProgressSet(Execute(StringReplace($s, "=", "")))
WEnd
ProgressOff()

_Console_Free()
Exit

Func _Console_ReadConsoleOutputCharacter($hConsoleOutput, $nNumberOfCharsToRead, $iX, $iY, $fUnicode = Default, $hDll = -1)
    Local $tCoord, $tBuffer, $aResult

    If $fUnicode = Default Then $fUnicode = $__gfUnicode
    If $hDll = -1 Then $hDll = $__gvKernel32
    If $hConsoleOutput = -1 Then $hConsoleOutput = _Console_GetStdHandle($STD_OUTPUT_HANDLE, $hDll)

    $tCoord = BitShift($iY, -16) + $iX

    If $fUnicode Then
        $tBuffer = DllStructCreate("wchar[" & ($nNumberOfCharsToRead + 1) & "]")

        $aResult = DllCall($hDll, "bool", "ReadConsoleOutputCharacterW", _
                "handle", $hConsoleOutput, _
                "ptr", DllStructGetPtr($tBuffer), _
                "dword", $nNumberOfCharsToRead, _
                "dword", $tCoord, _
                "dword*", 0)
    Else
        $tBuffer = DllStructCreate("char[" & ($nNumberOfCharsToRead + 1) & "]")

        $aResult = DllCall($hDll, "bool", "ReadConsoleOutputCharacterA", _
                "handle", $hConsoleOutput, _
                "ptr", DllStructGetPtr($tBuffer), _
                "dword", $nNumberOfCharsToRead, _
                "dword", $tCoord, _
                "dword*", 0)
    EndIf
    If @error Or (Not $aResult[0]) Then Return SetError(@error, @extended, "")

    Return SetExtended($aResult[4], DllStructGetData($tBuffer, 1))
EndFunc   ;==>_Console_ReadConsoleOutputCharacter
Edited by Mat
Link to comment
Share on other sites

Wow, Mat that's fantastic

Just tried to port it to the actual DISM console window, Local $iY = DllStructGetData($tScreenBufferInfo, "CursorPositionY") seems off, it doesn't display the progress. Once changed to '5' it functions correctly.

Link to comment
Share on other sites

Ah right ok, I was assuming the cursor would be on the same line as the progress, but that doesn't have to be the case with WriteConsoleOutputCharacter.

Thanks for reminding me as well, I've added those two functions to the library now. Up to 85 :x With the latest version:

#include <Console.au3>

$__gvKernel32 = DllOpen("kernel32.dll")

Local $iPid = Run("ProgressExample.exe", @ScriptDir)
ProcessWait($iPid)

_Console_Attach($iPid)

; The width of the dism progress is 60 chars, 2 chars are brackets so 58 possible = signs.
; The percentage starts at 27 chars, and is right aligned in 6 chars (Always to 1dp).

ProgressOn("ProgressExample.exe", "...")
While ProcessExists($iPid)
    $s = _Console_ReadOutputCharacter(-1, 5, 27, 5)
    ProgressSet(Execute(StringReplace($s, "=", "")))
WEnd
ProgressOff()

_Console_Free()
Exit
Edited by Mat
Link to comment
Share on other sites

  • 2 years later...
  • 3 months later...
  • 1 year later...

 

Hello,

I tried to modify this script for deploying a WIM image via DISM.

The WIM deployment is working, but not the progress bar.

Any idea to get this working?

Thanks

PS: console.au3 Ver 0.0.0.27

#include <Console.au3>

_DeployWim()

Func _DeployWim()
    ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ; Apply Wim Image with GUI Bar
    ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    ;Variables
        $ApplicationSource = @ScriptDir
        $WIMPACKAGE_NAME = "Win10-Prof-64bit-EN.wim"
        $IMAGEX_PATH = $ApplicationSource & "\Imagex32\Dism.exe" ; <Path to imagex.exe>
        $PACKAGE_NAME = $ApplicationSource & "\WimFiles\" & $WIMPACKAGE_NAME ; <Path to your WIM package>
        $DRIVE_DEST = "w:\" ; <Drive Destination (i.e.: C:)>
        $SPLASH_TITLE = "Applying image ..." ; example "My Company Imaging"

        $__gvKernel32 = DllOpen("kernel32.dll")

        Local $iPid = Run($IMAGEX_PATH & " /Apply-Image /ImageFile:" & $PACKAGE_NAME & " /index:1 /ApplyDir:" & $DRIVE_DEST, "", @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)

        ProcessWait($iPid)

        _Console_Attach($iPid)

        ; The width of the dism progress is 60 chars, 2 chars are brackets so 58 possible = signs.
        ; The percentage starts at 27 chars, and is right aligned in 6 chars (Always to 1dp).

        ProgressOn("Dism.exe", "...")
            While ProcessExists($iPid)
                $s = _Console_ReadOutputCharacter(-1, 5, 27, 5)
                ProgressSet(Execute(StringReplace($s, "=", "")))
            WEnd
        ProgressOff()

        _Console_Free()
    Exit
EndFunc


Func _Console_ReadConsoleOutputCharacter($hConsoleOutput, $nNumberOfCharsToRead, $iX, $iY, $fUnicode = Default, $hDll = -1)
    Local $tCoord, $tBuffer, $aResult

    If $fUnicode = Default Then $fUnicode = $__gfUnicode
    If $hDll = -1 Then $hDll = $__gvKernel32
    If $hConsoleOutput = -1 Then $hConsoleOutput = _Console_GetStdHandle($STD_OUTPUT_HANDLE, $hDll)

    $tCoord = BitShift($iY, -16) + $iX

    If $fUnicode Then
        $tBuffer = DllStructCreate("wchar[" & ($nNumberOfCharsToRead + 1) & "]")

        $aResult = DllCall($hDll, "bool", "ReadConsoleOutputCharacterW", _
                "handle", $hConsoleOutput, _
                "ptr", DllStructGetPtr($tBuffer), _
                "dword", $nNumberOfCharsToRead, _
                "dword", $tCoord, _
                "dword*", 0)
    Else
        $tBuffer = DllStructCreate("char[" & ($nNumberOfCharsToRead + 1) & "]")

        $aResult = DllCall($hDll, "bool", "ReadConsoleOutputCharacterA", _
                "handle", $hConsoleOutput, _
                "ptr", DllStructGetPtr($tBuffer), _
                "dword", $nNumberOfCharsToRead, _
                "dword", $tCoord, _
                "dword*", 0)
    EndIf
    If @error Or (Not $aResult[0]) Then Return SetError(@error, @extended, "")

    Return SetExtended($aResult[4], DllStructGetData($tBuffer, 1))
EndFunc   ;==>_Console_ReadConsoleOutputCharacter

 

 

Edited by tarvr
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...