someone

.Net 4.5 Progress Bar

11 posts in this topic

Hi all,

Wondering if doing something like this in Autoit is possible, and if anyone can help me figure out where to start.  The installation of .Net 4.5.1 allows for a 'piping' to a memory mapped segment in order to get the progress of the installation. 

http://msdn.microsoft.com/en-us/library/ff859983(v=vs.110).aspx

This way, you can use a parent installer program but have feedback about the installation progress.

They have a sample script in C++ http://code.msdn.microsoft.com/NET-Framework-45-Developer-e416a0ba/sourcecode?fileId=47345&pathId=663039622 , and also on the first link there are code sections.

You can download .Net 4.5.1 http://www.microsoft.com/en-us/download/details.aspx?id=40773 (web installer) if you want to try the commands out.

Thanks in advance, I'm really not sure where to start.


While ProcessExists('Andrews bad day.exe')
	BlockInput(1)
	SoundPlay('Music.wav')
	SoundSetWaveVolume('Louder')
WEnd

Share this post


Link to post
Share on other sites



First, look at the 4.0 version. It's much more simple.

The example make some confusion because it merge the code for our custom setup (aka chainerand the .NET setup (aka chainee). You don't need to bother with any method marked/commented as its for chainee only.

Then move to the 4.5 version, it adds some method for handling Abort (the 4.0 version do has this feature but it's not presented in the example), and add Mutex for assynchonus access to the MMIO. So do not care about the Mutex, an if you want only the progress, do not care about the event which is sent by chainer (m_hEventChainerSend)

Convert the C++ code to AutoIt should be easy, except the part with WaitForSingleObject/WaitForMultipleObjects. Its blocking function in AutoIt, and makes your custom GUI totally freeze. So better add a timeout parameter to those function, handle the timeout result and re-wait using loop. Remember to add a small sleep in the loop so AutoIt can do internal GUI message to make it not being frozen, 


99 little bugs in the code

99 little bugs!

Take one down, patch it around

117 little bugs in the code!

Share this post


Link to post
Share on other sites

Thanks binhnx, I'm sorry I am still so lost.  I'm looking at the 4.0 example but can't make much more sense of it.  I've attached what I believe is the right C++ code, and the link is here (http://code.msdn.microsoft.com/NET-Framework-4-Chainer-5dc250a1/sourcecode?fileId=45405&pathId=663039622)

Do I want to use GUIRegisterMsg with the piped MMIO name?  Is that how you are expected to worked with MMIO events?  I can't find any examples for this except the msdn one written in C++, was hoping to find something in vbs or something closer to Autoit.

#pragma once 
#include <windows.h> 
#include "IProgressObserver.h" 
 
namespace ChainerSample 
{ 
    // MMIO data structure for inter-process communication 
    struct MmioDataStructure 
    { 
        bool m_downloadFinished;        // Is download done yet? 
        bool m_installFinished;         // Is installer operation done yet? 
        bool m_downloadAbort;           // Set to cause downloader to abort 
        bool m_installAbort;            // Set to cause installer operation to abort 
        HRESULT m_hrDownloadFinished;   // HRESULT for download 
        HRESULT m_hrInstallFinished;    // HRESULT for installer operation 
        HRESULT m_hrInternalError;      // Internal error from MSI if applicable 
        WCHAR m_szCurrentItemStep[MAX_PATH];   // This gives the windows installer step being executed if an error occurs  
                                               // while processing an MSI, for example, "Rollback" 
        unsigned char m_downloadProgressSoFar; // Download progress 0 - 255 (0 to 100% done)  
        unsigned char m_installProgressSoFar;  // Install progress 0 - 255 (0 to 100% done) 
        WCHAR m_szEventName[MAX_PATH];         // Event that chainer creates and chainee opens to sync communications. 
    }; 
 
    // This is the class structure that one needs to follow to implement a chainer. 
    // You would derive your server class from MMIOChainer to chain the .NET 4 Redistributable. 
    // 
    // MmioChainerBase class manages the communication and synchronization data  
    // structures. It also implements common get accessors (for chainer) and set accessors (for chainee). 
    class MmioChainerBase 
    { 
        HANDLE m_section; 
        HANDLE m_event; 
        MmioDataStructure* m_pData;  
 
    protected: 
        // Constructor 
        MmioChainerBase(HANDLE section, HANDLE hevent) 
            : m_section(section) 
            , m_event(hevent)  
            , m_pData(MapView(section)) 
        { 
        } 
 
        // Destructor 
        virtual ~MmioChainerBase() 
        { 
            if (m_pData) 
            { 
                ::UnmapViewOfFile(m_pData); 
            } 
        } 
 
    public: 
        HANDLE GetEventHandle() const  
        {  
            return m_event; 
        } 
 
        HANDLE GetMmioHandle()  const 
        { 
            return m_section; 
        } 
 
        MmioDataStructure* GetData() 
        { 
            return m_pData; 
        } 
 
        //This is called by the chainer. 
        void Init(LPCWSTR eventName) 
        { 
            //Don't do anything if it is invalid. 
            if (NULL == m_pData) 
            { 
                return; 
            } 
 
            // Common items for download and install 
            wcscpy_s(m_pData->m_szEventName, MAX_PATH, eventName); 
 
            // Download specific data 
            m_pData->m_downloadFinished = false; 
            m_pData->m_downloadProgressSoFar = 0; 
            m_pData->m_hrDownloadFinished = E_PENDING; 
            m_pData->m_downloadAbort = false; 
 
            // Install specific data 
            m_pData->m_installFinished = false; 
            m_pData->m_installProgressSoFar = 0; 
            m_pData->m_hrInstallFinished = E_PENDING; 
            m_pData->m_installAbort = false; 
 
            m_pData->m_hrInternalError = S_OK; 
        } 
 
        // This is called by the chainer to force the chained setup to be canceled. 
        void Abort() 
        { 
            //Don't do anything if it is invalid. 
            if (NULL == m_pData) 
            { 
                return; 
            } 
 
            // Chainer told us to cancel. 
            m_pData->m_downloadAbort= true; 
            m_pData->m_installAbort = true; 
        } 
 
        // Called when chainer wants to know if chained setup has finished both download and installation. 
        bool IsDone() const  
        {  
            if (NULL == m_pData) 
            { 
                return true; 
            } 
 
            return m_pData->m_downloadFinished && m_pData->m_installFinished;  
        } 
 
        // Called by the chainer to get the overall progress, i.e., the combination of the download and installation. 
        unsigned char GetProgress() const  
        {  
            if (NULL == m_pData) 
            { 
                return 255; 
            } 
 
            return (m_pData->m_downloadProgressSoFar + m_pData->m_installProgressSoFar)/2; 
        } 
 
        // Get download progress. 
        unsigned char GetDownloadProgress() const 
        { 
            if (NULL == m_pData) 
            { 
                return 255; 
            } 
 
            return m_pData->m_downloadProgressSoFar; 
        } 
 
        // Get the installation progress. 
        unsigned char GetInstallProgress() const 
        { 
            if (NULL == m_pData) 
            { 
                return 255; 
            } 
 
            return m_pData->m_installProgressSoFar; 
        } 
 
        // Get the combined setup result, installation taking priority over download if both failed. 
        HRESULT GetResult() const 
        {  
            if (NULL == m_pData) 
            { 
                return S_FALSE; 
            } 
 
            if (m_pData->m_hrInstallFinished != S_OK) 
            { 
                return m_pData->m_hrInstallFinished; 
            } 
            else 
            { 
                return m_pData->m_hrDownloadFinished; 
            } 
        } 
 
        // Get the download result. 
        HRESULT GetDownloadResult() const 
        {  
            if (NULL == m_pData) 
            { 
                return S_FALSE; 
            } 
 
            return m_pData->m_hrDownloadFinished; 
        } 
 
        // Get the installation result. 
        HRESULT GetInstallResult() const 
        {  
            if (NULL == m_pData) 
            { 
                return S_FALSE; 
            } 
 
            return m_pData->m_hrInstallFinished; 
        } 
 
        // Get the internal result. 
        const HRESULT GetInternalResult() const 
        { 
            if (NULL == m_pData) 
            { 
                return S_FALSE; 
            } 
 
            return m_pData->m_hrInternalError; 
        } 
 
        // Get the current item step from chainee. 
        const CString GetCurrentItemStep() const 
        { 
            if (NULL == m_pData) 
            { 
                return L""; 
            } 
 
            return CString(m_pData->m_szCurrentItemStep); 
        } 
 
        // The chainee calls this to see if the chainer has asked that we cancel. 
        bool IsAborted() const  
        {  
            if (NULL == m_pData) 
            { 
                return false; 
            } 
 
            return m_pData->m_installAbort || m_pData->m_downloadAbort;  
        } 
 
    protected: 
        // Protected utility function to map the file into memory. 
        static MmioDataStructure* MapView(HANDLE section) 
        { 
            if (NULL == section) 
            { 
                return reinterpret_cast<MmioDataStructure*>(NULL); 
            } 
 
            return reinterpret_cast<MmioDataStructure*>(::MapViewOfFile(section, 
                FILE_MAP_WRITE, 
                0, 0, // offsets 
                sizeof(MmioDataStructure))); 
        } 
    }; 
 
    // This is the class that the consumer (chainer) should derive from. 
    class MmioChainer : protected MmioChainerBase 
    { 
    public: 
        // Constructor 
        MmioChainer (LPCWSTR sectionName, LPCWSTR eventName) 
            : MmioChainerBase(CreateSection(sectionName), CreateEvent(eventName)) 
        { 
            Init(eventName); 
        } 
 
        // Destructor 
        virtual ~MmioChainer () 
        { 
            ::CloseHandle(GetEventHandle()); 
            ::CloseHandle(GetMmioHandle()); 
        } 
 
    public: // The public methods:  Abort and Run. 
        using MmioChainerBase::Abort; 
        using MmioChainerBase::GetInstallResult; 
        using MmioChainerBase::GetInstallProgress; 
        using MmioChainerBase::GetDownloadResult; 
        using MmioChainerBase::GetDownloadProgress; 
        using MmioChainerBase::GetCurrentItemStep; 
 
        HRESULT GetInternalErrorCode() 
        { 
            return GetInternalResult(); 
        } 
 
        // Called by the chainer to start the chained setup. This blocks until setup is complete. 
        void Run(HANDLE process, IProgressObserver& observer) 
        { 
            HANDLE handles[2] = { process, GetEventHandle() }; 
 
            while(!IsDone()) 
            { 
                DWORD ret = ::WaitForMultipleObjects(2, handles, FALSE, 100); // INFINITE ?? 
                switch(ret) 
                { 
                case WAIT_OBJECT_0: 
                    { // Process handle closed.  Maybe it blew up, maybe it's just really fast.  Let's find out. 
                        if (IsDone() == false) // Not a good sign. 
                        { 
                            HRESULT hr = GetResult(); 
                            if (hr == E_PENDING) // Untouched 
                                observer.Finished(E_FAIL); 
                            else 
                                observer.Finished(hr); 
 
                            return; 
                        } 
                        break; 
                    } 
                case WAIT_OBJECT_0 + 1: 
                    observer.OnProgress(GetProgress()); 
                    break; 
                default: 
                    break; 
                }         
            } 
            observer.Finished(GetResult()); 
        } 
 
    private: 
        static HANDLE CreateSection(LPCWSTR sectionName) 
        { 
            return ::CreateFileMapping (INVALID_HANDLE_VALUE, 
                NULL, // Security attributes 
                PAGE_READWRITE, 
                0, // High-order DWORD of maximum size 
                sizeof(MmioDataStructure), // low-order DWORD of maximum size. 
                sectionName); 
        } 
        static HANDLE CreateEvent(LPCWSTR eventName) 
        { 
            return ::CreateEvent(NULL, FALSE, FALSE, eventName); 
        } 
    }; 
 
    // This class is used by the chainee. 
    class MmioChainee : protected MmioChainerBase 
    { 
    public: 
        MmioChainee(LPCWSTR sectionName) 
            : MmioChainerBase(OpenSection(sectionName), OpenEvent(GetEventName(sectionName))) 
        { 
        } 
 
        virtual ~MmioChainee() 
        { 
        } 
 
    private: 
        static HANDLE OpenSection(LPCWSTR sectionName) 
        { 
            return ::OpenFileMapping(FILE_MAP_WRITE, //Read/write access 
                FALSE,          // Do not inherit the name. 
                sectionName); 
        } 
 
        static HANDLE OpenEvent(LPCWSTR eventName) 
        {         
            return ::OpenEvent (EVENT_MODIFY_STATE | SYNCHRONIZE, 
                FALSE, 
                eventName); 
        } 
 
        static CString GetEventName(LPCWSTR sectionName) 
        { 
            CString cs = L""; 
 
            HANDLE handle = OpenSection(sectionName); 
            if (NULL == handle) 
            { 
                DWORD dw; 
                dw = GetLastError(); 
                printf("OpenFileMapping fails with last error: %08x",dw); 
            } 
            else 
            { 
                const MmioDataStructure* pData = MapView(handle); 
                if (pData) 
                { 
                    cs = pData->m_szEventName; 
                    ::UnmapViewOfFile(pData); 
                } 
                ::CloseHandle(handle); 
            } 
 
            return cs; 
        } 
    }; 
}

While ProcessExists('Andrews bad day.exe')
	BlockInput(1)
	SoundPlay('Music.wav')
	SoundSetWaveVolume('Louder')
WEnd

Share this post


Link to post
Share on other sites

No. You will not use GUIRegisterMsg

Recheck the example for .NET 4.0. 

The application is started with main method defined in ChainingdotNet4.cpp file.

It create a new Server object and then call Lauch() method on that newly created one.

The Server class constructor create a Mapped Memory Section and an Event.

In the Lauch() method, you can see all of things you need to do:

Launch the .Net setup, pass the mapped memory section name you created to that.

In that section, a member define the name of event you created. The .NET setup will notify that event whenever progress changed and set the corresponding progress value on the mapped memory you've created.

In your code, you listen to the event change by using WaitForSingle/Multiple Object(s) and then update the GUI according to the progress value (read from the mapped memory section)

That's all. I've code the whole script few days ago but forgot to post it. Yesterday, I (accidently) quit SciTe and all that code gone. Now I'm to lazy to rewrite it :(

Ya, then try writing yourself. I'll find some good drinks and rewrite it when my laziness gone ^^


99 little bugs in the code

99 little bugs!

Take one down, patch it around

117 little bugs in the code!

Share this post


Link to post
Share on other sites

Hey binhnx, can I send you a few good drinks!  I am unfortunately still not getting it.  :idiot:

I think I get what you are saying...here is how I understand it.

Run the .net setup with -pipe "myeventname"

use $obj = ObjCreate("myeventname.??")

then use ObjEvent($obj, ?? )

I did try to code it and tried a few variations but I'm definitely missing something.  Here is the broken code I have so far, not much obviously.  My next step is try to learn basic stuff about C, try to understand it that way.

;$pid = Run(@ScriptDir & "\dotNetFx40_Full_x86_x64.exe -pipe TheSectionName", "", @SW_SHOW)
Sleep(500)
$oNet = ObjCreate("TheSectionName.event")
If IsObj($oNet) Then
    MsgBox(0, "", "is obj")
Else
    MsgBox(0, "", "is NOT obj")
EndIf


Exit



$hGUI = GUICreate(".Net Progress Bar", 615, 250)
$Progress = GUICtrlCreateProgress(64, 32, 505, 65)
GUICtrlCreateLabel("Installing .Net ", 176, 112, 246, 39)
GUICtrlSetFont(-1, 20, 800, 0, "Trebuchet MS")
GUICtrlSetColor(-1, 0x000000)
$b_quit = GUICtrlCreateButton("Quit", 488, 184, 105, 57)
GUISetState(@SW_SHOW)

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            ExitLoop
        Case $b_quit
            ExitLoop
    EndSwitch
WEnd

While ProcessExists('Andrews bad day.exe')
	BlockInput(1)
	SoundPlay('Music.wav')
	SoundSetWaveVolume('Louder')
WEnd

Share this post


Link to post
Share on other sites

Shameless bump!


While ProcessExists('Andrews bad day.exe')
	BlockInput(1)
	SoundPlay('Music.wav')
	SoundSetWaveVolume('Louder')
WEnd

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

Sorry for reply late. I had trouble with my Internet Connection, its s..oo...oo....oo..... s...l...o....w, especially with some site so I could not do anything. 

This is the (hopefully working, I cannot test it, the installation fails after it check for calculate my free disk space (I have only a little disk space in C drive) :sweating: ) code, better late than never  :sweating:

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_AU3Check_Stop_OnWarning=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

If (Not IsDeclared('MAX_PATH')) Then Const $MAX_PATH = 255

Const $tagMMIODATA_V4_0 = 'BOOL m_downloadFinished;' & _    ;Is download complete?
  'BOOL m_installFinished;' & _       ;Is installation complete?
  'BOOL m_downloadAbort;' & _        ;Set to cause downloader to abort.
  'BOOL m_installAbort;' & _        ;Set to cause installer to abort.
  'HRESULT m_hrDownloadFinished;' & _      ;Resulting HRESULT for download.
  'HRESULT m_hrInstallFinished;' & _      ;Resulting HRESULT for installation.
  'HRESULT m_hrInternalError;' & _
  'WCHAR m_szCurrentItemStep[' & $MAX_PATH & '];' & _
  'BYTE m_downloadSoFar;' & _     ;Download progress 0-255 (0-100% done).
  'BYTE m_installSoFar;' & _      ;Installation progress 0-255 (0-100% done).
  'WCHAR m_szEventName[' & $MAX_PATH & '];'    ;Event that chainer creates and chainee opens to sync communications.

Const $tagMMIODATA_V4_5 = $tagMMIODATA_V4_0 & _
  'BYTE m_version;' & _         ;Version of the data structure, set by chainer:
  '' & _             ; 0x0: .NET Framework 4
  '' & _             ; 0x1: .NET Framework 4.5
  'DWORD m_messageCode;' & _        ;Current message sent by the chainee; 0 if no message is active.
  'DWORD m_messageResponse;' & _       ;Chainer's response to current message; 0 if not yet handled.
  'DWORD m_messageDataLength;' & _      ;Length of the m_messageData field, in bytes.
  'BYTE m_messageData[1];'        ;Variable length buffer, content depends on m_messageCode

Global $tagMMIODATA

#include <WinAPI.au3>
#include <WinAPIFiles.au3>

Global $__DotNetSetup_sSection = 0
Global $__DotNetSetup_hSection = 0
Global $__DotNetSetup_pSection = 0
Global $__DotNetSetup_hEventIn = 0
Global $__DotNetSetup_hEventOut = 0


; $iVersion: 0 = .NET 4.0; 1 = .NET 4.5
Func _DotNetSetup_Register($sSectionName = 'CreepyDotNetSetupSection', $sEventName = 'CreepyDotNetSetupEvent', $iVersion = 1)
; Fails if we've already registered.
If ($__DotNetSetup_hSection) Then Return SetError(1, 0, 0)

; Check valid parameters
If ($iVersion < 0 Or $iVersion > 1) Then $iVersion = 1
If (Not IsString($sSectionName)) Then $sSectionName = 'CreepyDotNetSetupSection'
; The event name and section name's length is restricted to MAX_PATH
If (StringLen($sSectionName) > $MAX_PATH) Then $sSectionName = StringLeft($sSectionName, $MAX_PATH)
If (Not IsString($sEventName)) Then $sEventName = 'CreepyDotNetSetupEvent'
; The event name and section name's length is restricted to MAX_PATH
If (StringLen($sEventName) > $MAX_PATH) Then $sEventName = StringLeft($sEventName, $MAX_PATH)

Switch $iVersion
  Case 0:
   $tagMMIODATA = $tagMMIODATA_V4_0
  Case 1
   $tagMMIODATA = $tagMMIODATA_V4_5
EndSwitch

; Size of the memory block we are going to allocate
Local $s_nSize = $iVersion = 0? DllStructGetSize(DllStructCreate($tagMMIODATA_V4_0)) : 65536

; Allocate mapped memory
; Create a memory-mapping section, set error code = 1000 (WinAPI error) and return if fails
$__DotNetSetup_hSection = _WinAPI_CreateFileMapping(0, $s_nSize, $sSectionName)
If (Not($__DotNetSetup_hSection)) Then Return SetError(1000, _WinAPI_GetLastError(), False)

; Map the created section into memory address. Set error code = 1000 (WinAPI error) and return if fails
$__DotNetSetup_pSection = _WinAPI_MapViewOfFile($__DotNetSetup_hSection)
If (Not($__DotNetSetup_pSection)) Then Return SetError(1000, _WinAPI_GetLastError(), False)

; Create an named event
$__DotNetSetup_hEventIn = _WinAPI_CreateEvent(0, False, False, $sEventName)
; Set error and return if fails
If (Not($__DotNetSetup_hEventIn)) Then Return SetError(1000, _WinAPI_GetLastError(), False)
; Additional event so we can terminate
$__DotNetSetup_hEventOut = _WinAPI_CreateEvent(0, False, False, $sEventName & '_send')
; This event is only used for terminating, then without it we can still start .Net setup and
; get progress normally, so we do not return error here, instead, flag it and
; then return an success value (true) but with a warning (set @error and @extended flag)
Local $iError[2] = [0, 0]
If (Not($__DotNetSetup_hEventOut)) Then
  $iError[0] = 1000
  $iError[1] = _WinAPI_GetLastError()
EndIf

; Now write the event name into the memory-mapped section
Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)
DllStructSetData($tMMIOData, 'm_szEventName', $sEventName)

; Save the section name so we can use it later.
; The event name is already written in the section so we don't need to save it
$__DotNetSetup_sSection = $sSectionName

; Since we allocate memory, we must finalize (delocate memory).
; If the .NET setup is successful, it will free the memory for us,
; if not, we must free ourselve or we cannot use that memory regions
; (memory leaks, not so scare since a restart can resolve it, but better safe than sorry).
OnAutoItExitRegister('__DotNetSetup_Finalize')
Return SetError($iError[0], $iError[1], True)
EndFunc

Func _DotNetSetup_Start($sAppPath, $sCommand, $fnProgressChanged, $fnFinished = '', $fnMsgReceived = '')
; Fails if we haven't registered yet
If Not($__DotNetSetup_hSection And $__DotNetSetup_pSection _
    And $__DotNetSetup_hEventIn) Then Return SetError(1, 0, 0)

Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)

; We need one handle to .NET setup process, and one handle to our event
; to catch two event: the process is interrupt by unexpected reason or by user,
; and the progress value changed
Local $tWaitHandles = DllStructCreate('HANDLE;HANDLE')

; Start the setup process and get information (handle to the setup thread and process)
Local $sNetSetupAppPath = $sAppPath & ' /q /norestart /pipe ' & $__DotNetSetup_sSection & ' ' & $sCommand
Local $tStartupInfo = DllStructCreate($tagSTARTUPINFO)
Local $tProcessInfo = DllStructCreate($tagPROCESS_INFORMATION)
If (Not _WinAPI_CreateProcess('', $sNetSetupAppPath, 0, 0, False, 0, 0, '', _
    DllStructGetPtr($tStartupInfo), DllStructGetPtr($tProcessInfo))) Then
  ; Cannot start the setup process.
  Return SetError(1000, _WinAPI_GetLastError(), False)
EndIf

Local $hThread = DllStructGetData($tProcessInfo, 'hThread')
Local $hProcess = DllStructGetData($tProcessInfo, 'hProcess')

If ($hProcess = 0) Then MsgBox(0, 'Error', 'Cannot start the setup process')
Return False

; Assign wait handles: to setup process,
DllStructSetData($tWaitHandles, 1, $hProcess)
; and to the event object
DllStructSetData($tWaitHandles, 2, $__DotNetSetup_hEventIn)


; All prepaired. Now the main part
While Not __DotNetSetup_IsDone()
  Local $dwRet = _WinAPI_WaitForMultipleObjects(2, DllStructGetPtr($tWaitHandles), False, 100)
  Switch $dwRet
   Case 0; WAIT_OBJ_0: the handle to the setup process is signaled
    ; - Comment from C example -
    ; Process handle closed. Maybe it blew up, maybe it's just really fast. Let's find out.
    ; ---
    Local $bSuccess = False
    If __DotNetSetup_IsDone() Then
     ; The setup process is successfully done
     $bSuccess = True
     ; The setup process automatically free memory for us so we don't need to do anymore
     OnAutoItExitUnRegister('__DotNetSetup_Finalize')
     ; Reset global variables
     __DotNetSetup_Cleanup(False)
    EndIf
    ; If not, leave the $bSuccess flag as false

    Call($fnFinished, $bSuccess, __DotNetSetup_GetResult(False), _WinAPI_GetExitCodeProcess($hProcess))
    ExitLoop
   Case 1; WAIT_OBJ_0+1: the handle to our event is signaled, means the progress value has changed.
    Call($fnProgressChanged, __DotNetSetup_GetProgress(False))
    If ($__DotNetSetup_hEventOut) Then
     Local $dwMessage = 0
     Local $dwBufferSize = 0
     Local $pBuffer = 0
     If (__DotNetSetup_GetMessage($dwMessage, $dwBufferSize, $pBuffer)) Then
      Local $dwResponse = Call($fnMsgReceived, $dwMessage, $pBuffer, $dwBufferSize)

      DllStructSetData($tMMIOData, 'm_messageCode', 0);MMIO_NO_MESSAGE
      DllStructSetData($tMMIOData, 'm_messageResponse', $dwResponse)
      _WinAPI_SetEvent($__DotNetSetup_hEventOut)
     EndIf
    EndIf
   Case Else; WAIT_TIMEOUT:
    ; Nothing to do. Just keep looping
  EndSwitch
WEnd

; .Net setup process finished (either done or terminated, but finished).
; Now we has nothing to do with those process information handles. Then close them and return
_WinAPI_CloseHandle($hThread)
_WinAPI_CloseHandle($hProcess)
Return True
EndFunc

Func _DotNetSetup_Abort()
; Fails if we haven't registered yet
If Not($__DotNetSetup_hSection And $__DotNetSetup_pSection) Then Return SetError(1, 0, 0)
; Fails if registered but cannot create out-event
If Not($__DotNetSetup_hEventOut) Then Return SetError(1, 1, 0)

Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)
DllStructSetData($tMMIOData, 'm_downloadAbort', True)
DllStructSetData($tMMIOData, 'm_installAbort', True)
Return _WinAPI_SetEvent($__DotNetSetup_hEventOut)
EndFunc


Func __DotNetSetup_GetMessage(ByRef $dwMessage, ByRef $dwBufferSize, ByRef $pBuffer)
If (__DotNetSetup_DecodeMessage($dwMessage, $dwBufferSize, $pBuffer)) Then $dwBufferSize = -1
Return ($dwMessage <> 0)
EndFunc

Func __DotNetSetup_DecodeMessage($dwMessage, $dwBufferSize, ByRef $pBuffer)
Switch $dwMessage
  Case 1 ;MMIO_CLOSE_APPS
   Local $tMmioCloseApplications = DllStructCreate('', $pBuffer)
   Local $nApps = DllStructGetData($tMmioCloseApplications, 'm_dwApplicationsSize')
   Local $nAppInfoSize = DllStructGetSize(DllStructCreate(''))
   Local $pAppInfo = $pBuffer + DllStructGetSize(DllStructCreate('DWORD'))

   Dim $pBuffer[$nApps][2]
   For $i = 0 To $nApps - 1
    Local $tMmioApplication = DllStructCreate('', $pAppInfo)
    $aAppInfo[$i][0] = DllStructGetData($tMmioApplication, 'm_szName')
    $aAppInfo[$i][1] = DllStructGetData($tMmioApplication, 'm_dwPid')
    $pAppInfo += $nAppInfoSize
   Next
   Return True
EndSwitch
Return False
EndFunc

Func __DotNetSetup_IsDone()
Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)
Return DllStructGetData($tMMIOData, 'm_downloadFinished') And _
    DllStructGetData($tMMIOData, 'm_installFinished')
EndFunc

Func __DotNetSetup_IsAborted()
Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)
Return DllStructGetData($tMMIOData, 'm_installAbort') Or _
    DllStructGetData($tMMIOData, 'm_downloadAbort')
EndFunc

Func __DotNetSetup_GetProgress($bTotal = False)
Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)
Local $nDownloadProgress = DllStructGetData($tMMIOData, 'm_downloadSoFar')
Local $nInstallProgress = DllStructGetData($tMMIOData, 'm_installSoFar')
If ($bTotal) Then
  Return ($nDownloadProgress + $nInstallProgress) / 2
Else
  Local $aRet[2] = [$nDownloadProgress, $nInstallProgress]
  Return $aRet
EndIf
EndFunc

Func __DotNetSetup_GetResult($bTotal = False)
Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)
Local $hDownloadRet = DllStructGetData($tMMIOData, 'm_hrDownloadFinished')
Local $hInstallRet = DllStructGetData($tMMIOData, 'm_hrInstallFinished')
Local $hInternalRet = DllStructGetData($tMMIOData, 'm_hrInternalError')
If ($bTotal) Then
  ; Get the combined setup result, installation taking priority over download if both failed.
  Return $hInstallRet <> $S_OK? $hInstallRet : $hDownloadRet
Else
  Local $aRet[3] = [$hDownloadRet, $hInstallRet, $hInternalRet]
  Return $aRet
EndIf
EndFunc

Func __DotNetSetup_GetCurrentItemStep()
Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)
Return DllStructGetData($tMMIOData, 'm_szCurrentItemStep')
EndFunc



Func __DotNetSetup_Cleanup($bFreeMemory = True)
If ($bFreeMemory) Then
  If ($__DotNetSetup_hEventIn) Then _WinAPI_CloseHandle($__DotNetSetup_hEventIn)
  If ($__DotNetSetup_hEventOut) Then _WinAPI_CloseHandle($__DotNetSetup_hEventOut)
  If ($__DotNetSetup_pSection) Then _WinAPI_UnmapViewOfFile($__DotNetSetup_pSection)
  If ($__DotNetSetup_hSection) Then _WinAPI_CloseHandle($__DotNetSetup_hSection)
EndIf
$__DotNetSetup_hEventIn = 0
$__DotNetSetup_hEventOut = 0
$__DotNetSetup_pSection = 0
$__DotNetSetup_hSection = 0
$__DotNetSetup_sSection = 0
EndFunc

Func __DotNetSetup_Finalize()
__DotNetSetup_Cleanup(True)
EndFunc

Then call like this:

Func _ProgressChange($iProgress)
    ConsoleWrite(@CRLF & $iProgress)
EndFunc

_DotNetSetup_Register('MyEvent', 'MySection')
_DotNetSetup_Start('D:\Setup.exe', '', _ProgressChange)
 

Note 1: I've not implemented the message part yet. Will do it when I have more free time. :sweating:

Note 2: This is the part which only handle the setup progress. If you choose to use the custom setup, every other works (which is not cover in your link) should be done manually before you start the setup progress. Check this link.

Edited by binhnx

99 little bugs in the code

99 little bugs!

Take one down, patch it around

117 little bugs in the code!

Share this post


Link to post
Share on other sites

Hey I just saw the response, haven't had time to test anything out but THANK YOU!  I really appreciate the time you've given to helping me.  I'll run it through, and post back in a day or two...hopefully without too many questions :idiot: .

This kind of programming is definitely over my head, but I really look forward to understanding how it works.


While ProcessExists('Andrews bad day.exe')
	BlockInput(1)
	SoundPlay('Music.wav')
	SoundSetWaveVolume('Louder')
WEnd

Share this post


Link to post
Share on other sites

Its not working for me but I'm still stepping through it, I got a few syntax errors so I'm making sure fixing those I didn't break anything else.  Just wanted to get back to you.  Pretty much all that I did to 'fix' the syntax errors were to put the global variables at the top, little things like that.  I'll keep trying.


While ProcessExists('Andrews bad day.exe')
	BlockInput(1)
	SoundPlay('Music.wav')
	SoundSetWaveVolume('Louder')
WEnd

Share this post


Link to post
Share on other sites

I redid the code so I could lean better to help figure out what is going wrong.  There were a few errors (the structs had incompatible autoit data types, etc).  Here is the code I have now, there are 2 problems I have I can't figure out...  Right now I am trying only .Net 4

Line 100: _WinAPI_WaitForMultipleObjects isn't continuing (it always reaches the timeout, -1 makes it hang indefinitely

Line 117: DllStructGetData($tMMIOData, 'm_installProgressSoFar') is giving me @error = 2. 

I put tons of message boxes and a few tooltips in the code to help me.  This is the link to the C++ code I am trying to replicate.

#include <WinAPI.au3>
#include <WinAPIFiles.au3>
#include <WinAPIProc.au3>
#include <Array.au3>

Global $__DotNetSetup_sSection = 0
Global $__DotNetSetup_hSection = 0
Global $__DotNetSetup_pSection = 0
Global $__DotNetSetup_hEventIn = 0
Global $__DotNetSetup_hEventOut = 0
Global $__DotNetSetup_sSection = "dotnet4sectionname"
Global $__DotNetSetup_sEvent = "dotnet4eventname"

Const $MAX_PATH = 255
Const $tagMMIODATA_V4_0 = 'BOOL m_downloadFinished;' & _    ;Is download complete?
  'BOOL m_installFinished;' & _       ;Is installation complete?
  'BOOL m_downloadAbort;' & _        ;Set to cause downloader to abort.
  'BOOL m_installAbort;' & _        ;Set to cause installer to abort.
  'LONG m_hrDownloadFinished;' & _      ;Resulting HRESULT for download.
  'LONG m_hrInstallFinished;' & _      ;Resulting HRESULT for installation.
  'LONG m_hrInternalError;' & _
  'WCHAR m_szCurrentItemStep[' & $MAX_PATH & '];' & _
  'char m_downloadSoFar;' & _     ;Download progress 0-255 (0-100% done).
  'char m_installSoFar;' & _      ;Installation progress 0-255 (0-100% done).
  'WCHAR m_szEventName[' & $MAX_PATH & '];'    ;Event that chainer creates and chainee opens to sync communications.

Global $tagMMIODATA = $tagMMIODATA_V4_0

$reg = _DotNetSetup_Register($__DotNetSetup_sEvent, $__DotNetSetup_sSection)
MsgBox(0, "reg", "return=" & $reg & ", " & @error & ", " & @extended)
_start()

Func _start()
    If Not($__DotNetSetup_hSection And $__DotNetSetup_pSection _
        And $__DotNetSetup_hEventIn) Then Return SetError(1, 0, 0)

    Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)
    If @error Then
        MsgBox(0, "$tMMIOData error", "error: " & @error)
        Exit
    Else
        MsgBox(0, "no $tMMIOData error", DllStructGetSize($tMMIOData))
    EndIf
    Local $tWaitHandles = DllStructCreate('HANDLE;HANDLE')
    If @error Then
        MsgBox(0, "$tWaitHandles error", "error: " & @error)
        Exit
    Else
        MsgBox(0, "no $tWaitHandles error", DllStructGetSize($tWaitHandles))
    EndIf

    ;Start the setup process and get information (handle to the setup thread and process)
    $sAppPath = @ScriptDir & '\dotNetFx40_Full_x86_x64.exe'
    Local $sNetSetupAppPath = $sAppPath & ' /pipe ' & $__DotNetSetup_sSection ;-pipe? or /pipe
    Local $tStartupInfo = DllStructCreate($tagSTARTUPINFO)
    If @error Then
        MsgBox(0, "$tStartupInfo error", "error: " & @error)
        Exit
    Else
        MsgBox(0, "no $tStartupInfo error", DllStructGetSize($tStartupInfo))
    EndIf
    Local $tProcessInfo = DllStructCreate($tagPROCESS_INFORMATION)
    If @error Then
        MsgBox(0, "$tProcessInfo error", "error: " & @error)
        Exit
    Else
        MsgBox(0, "no $tProcessInfo error", DllStructGetSize($tProcessInfo))
    EndIf
    If (Not _WinAPI_CreateProcess('', $sNetSetupAppPath, 0, 0, False, 0, 0, '', DllStructGetPtr($tStartupInfo), DllStructGetPtr($tProcessInfo))) Then
        MsgBox(0, "", "can't start")
        ; Cannot start the setup process.
        Return SetError(1000, _WinAPI_GetLastError(), False)
    EndIf

    ;;;;up to here
    Local $hThread = DllStructGetData($tProcessInfo, 'hThread')
    Local $hProcess = DllStructGetData($tProcessInfo, 'hProcess')
    MsgBox(0, "$hThread", $hThread)
    MsgBox(0, "$hProcess", $hProcess)

    ; Assign wait handles: to setup process,
    DllStructSetData($tWaitHandles, 1, $hProcess)
    If @error Then MsgBox(0, "$tWaitHandles error", "$tWaitHandles $hProcess struct setdata error")
    ; and to the event object
    DllStructSetData($tWaitHandles, 2, $__DotNetSetup_hEventIn)
    If @error Then MsgBox(0, "$tWaitHandles error", "$tWaitHandles $__DotNetSetup_hEventIn struct setdata error")
    MsgBox(0, DllStructGetPtr($tWaitHandles), "about to go into while")

;~  MsgBox(0, "", $__DotNetSetup_hEventIn)
;~  _WinAPI_WaitForSingleObject($hProcess, -1)
;~  MsgBox(0, "", "wait over")

    While Not __DotNetSetup_IsDone()
        ;MsgBox(0, "should be 0 until done", __DotNetSetup_IsDone())
        Sleep(5000)
        Local $dwRet = _WinAPI_WaitForMultipleObjects(2, DllStructGetPtr($tWaitHandles), False, 100)
        If $dwRet = -1 Then MsgBox(0, "WinAPI",  _WinAPI_GetLastError())
        ToolTip($dwRet)
        Switch $dwRet
            Case 0 ;closed
                MsgBox(0, "", "dotnet closed")
                ExitLoop
        EndSwitch
        __DotNetSetup_GetProgress()
    WEnd

EndFunc


Func __DotNetSetup_GetProgress($bTotal = False)
    Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)
    Local $nDownloadProgress = DllStructGetData($tMMIOData, 'm_downloadProgressSoFar')
    Local $nInstallProgress = DllStructGetData($tMMIOData, 'm_installProgressSoFar')
    MsgBox(0, "$nInstallProgress", $nInstallProgress & " @error = " & @error) ;getting error 2 - Element out of range or unknown.
    If ($bTotal) Then
      Return ($nDownloadProgress + $nInstallProgress) / 2
    Else
      Local $aRet[2] = [$nDownloadProgress, $nInstallProgress]
      Return $aRet
    EndIf
EndFunc


Func __DotNetSetup_IsDone()
    Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)
;~  Return DllStructGetData($tMMIOData, 'm_downloadFinished') And DllStructGetData($tMMIOData, 'm_installFinished')
    Return DllStructGetData($tMMIOData, 'm_installFinished') ;just returning (0/1) for installfinished
EndFunc

Func _DotNetSetup_Register($sSectionName, $sEventName)
    ; Fails if we've already registered.
    If ($__DotNetSetup_hSection) Then Return SetError(1, 0, 0)

; Size of the memory block we are going to allocate
    Local $s_nSize = DllStructGetSize(DllStructCreate($tagMMIODATA))
    MsgBox(0, "$s_nSize", $s_nSize)
    ; Allocate mapped memory
    ; Create a memory-mapping section, set error code = 1000 (WinAPI error) and return if fails
    $__DotNetSetup_hSection = _WinAPI_CreateFileMapping(0, $s_nSize, $sSectionName)
    If (Not($__DotNetSetup_hSection)) Then Return SetError(1000, _WinAPI_GetLastError(), False)

    ; Map the created section into memory address. Set error code = 1000 (WinAPI error) and return if fails
    $__DotNetSetup_pSection = _WinAPI_MapViewOfFile($__DotNetSetup_hSection)
    If (Not($__DotNetSetup_pSection)) Then Return SetError(1000, _WinAPI_GetLastError(), False)

    ; Create an named event
    $__DotNetSetup_hEventIn = _WinAPI_CreateEvent(0, False, False, $sEventName)
    ; Set error and return if fails
    If (Not($__DotNetSetup_hEventIn)) Then Return SetError(1000, _WinAPI_GetLastError(), False)
    ; Additional event so we can terminate
    ;$__DotNetSetup_hEventOut = _WinAPI_CreateEvent(0, False, False, $sEventName & '_send')
    ; This event is only used for terminating, then without it we can still start .Net setup and
    ; get progress normally, so we do not return error here, instead, flag it and
    ; then return an success value (true) but with a warning (set @error and @extended flag)
    Local $iError[2] = [0, 0]
    If (Not($__DotNetSetup_hEventOut)) Then
      $iError[0] = 1000
      $iError[1] = _WinAPI_GetLastError()
    EndIf

    ; Now write the event name into the memory-mapped section
    Local $tMMIOData = DllStructCreate($tagMMIODATA, $__DotNetSetup_pSection)
    $struct = DllStructSetData($tMMIOData, 'm_szEventName', $sEventName)
    If @error Then
        MsgBox(0, "$struct error", "error: " & @error)
        Exit
    Else
        MsgBox(0, "no $struct error", DllStructGetSize($struct))
    EndIf
    ; Save the section name so we can use it later.
    ; The event name is already written in the section so we don't need to save it
    $__DotNetSetup_sSection = $sSectionName

    ; Since we allocate memory, we must finalize (delocate memory).
    ; If the .NET setup is successful, it will free the memory for us,
    ; if not, we must free ourselve or we cannot use that memory regions
    ; (memory leaks, not so scare since a restart can resolve it, but better safe than sorry).
    OnAutoItExitRegister('__DotNetSetup_Finalize')
    Return SetError($iError[0], $iError[1], True)
EndFunc

Func __DotNetSetup_Cleanup($bFreeMemory = True)
    If ($bFreeMemory) Then
        If ($__DotNetSetup_hEventIn) Then _WinAPI_CloseHandle($__DotNetSetup_hEventIn)
        If ($__DotNetSetup_hEventOut) Then _WinAPI_CloseHandle($__DotNetSetup_hEventOut)
        If ($__DotNetSetup_pSection) Then _WinAPI_UnmapViewOfFile($__DotNetSetup_pSection)
        If ($__DotNetSetup_hSection) Then _WinAPI_CloseHandle($__DotNetSetup_hSection)
    EndIf
    $__DotNetSetup_hEventIn = 0
    $__DotNetSetup_hEventOut = 0
    $__DotNetSetup_pSection = 0
    $__DotNetSetup_hSection = 0
    $__DotNetSetup_sSection = 0
EndFunc

Func __DotNetSetup_Finalize()
__DotNetSetup_Cleanup(True)
EndFunc

While ProcessExists('Andrews bad day.exe')
	BlockInput(1)
	SoundPlay('Music.wav')
	SoundSetWaveVolume('Louder')
WEnd

Share this post


Link to post
Share on other sites

I didn't double-check the struct. My bad.

unsigned char should be BYTE. You can also use CHAR but BYTE is better (when you get value, char type give you a letter while byte give you a number)

The 2nd problem cause by my mistaken combine 2 version so the struct signature is the old .NET 4 but I get the value by using the variable name in the .NET 4.5 example. 

I'm not sure about the 1st problem. Maybe the process creation fails. So I add a debug (msgbox) statement to check.

Edited my script. 


99 little bugs in the code

99 little bugs!

Take one down, patch it around

117 little bugs in the code!

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