Sign in to follow this  
Followers 0

DISM console progress using StdoutRead

19 posts in this topic

Posted

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

Share this post


Link to post
Share on other sites



Posted

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)

Share this post


Link to post
Share on other sites

Posted (edited)

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

Share this post


Link to post
Share on other sites

Posted (edited)

@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

Share this post


Link to post
Share on other sites

Posted (edited)

@Mat

Reading percentage value, how do i accomplish that? Don't i have the exact same issue as above using that technique?

Edited by RvdH

Share this post


Link to post
Share on other sites

Posted (edited)

Read the start of my reply. I gave you the method I know of :x

Edit: Just to clarify, the reason I said to use the percentage is because I thought it might be easier to read a smaller block and end up with a more accurate number.

Edited by Mat

Share this post


Link to post
Share on other sites

Posted

RvdH, unless someone else comes along, I think solving this problem at hand is going to be more work than it is worth.

Share this post


Link to post
Share on other sites

Posted

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 !

Share this post


Link to post
Share on other sites

Posted

I did tell you. ReadConsoleOutput it allows you to copy a block of characters from one buffer to another. For an example look here

Share this post


Link to post
Share on other sites

Posted (edited)

@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

Share this post


Link to post
Share on other sites

Posted

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

Share this post


Link to post
Share on other sites

Posted

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 )

Share this post


Link to post
Share on other sites

Posted

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()

Share this post


Link to post
Share on other sites

Posted (edited)

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

Share this post


Link to post
Share on other sites

Posted

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.

Share this post


Link to post
Share on other sites

Posted (edited)

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

Share this post


Link to post
Share on other sites

Posted

Thank you for your script and Console UDF, it works perfectly with Windows PE 5 and DISM ;)

Share this post


Link to post
Share on other sites

Posted (edited)

ProgressExample.exe unavailable. Give me it please. Thanks :3

Edited by SmokieBlahBlah

Share this post


Link to post
Share on other sites

Posted

ProgressExample.exe unavailable. Give me it please. Thanks :3

!!!??? I need please document example code before give ProgressExample.exe. Do not delay or file deleted in few minutes!

Share this post


Link to post
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
Sign in to follow this  
Followers 0