Sign in to follow this  
Followers 0
tonedeaf

Creating/Managing multiple forms AutoIt - Part III

3 posts in this topic

This is Part III of the discussion on management of multiple forms in AutoIt

The first two parts of this topic can be found here:

Creating/Managing multiple forms in AutoIt - Part I

Creating/Managing multiple forms in AutoIt - Part II

I started to make an application to illustrate the design concepts. In Part III we're going to complete that application.

Following form issues are discussed.

> Converting a large AutoIt script into a project with multiple files.

> Creating form, control event routines.

Creating an AutoIt project:

If the UserManager application project was coded in VB, it would look like this:

Posted Image

If we were coding this in VB the project would be saved with 3 main files:

UserManager.vbp, frmResetPassword.frm, frmUserManager.frm

Similarily in AutoIt we'll split the single AutoIt UserManager script created in Part II into 3 parts:

UserManager.au3, frmUserManager.au3, frmResetPassword.au3

UserManager.au3 - the main project file. It contains the code common to the entire application which is:

1. The global variable declarations

2. #include <file> statements

3. The main WHILE event loop

4. Common functions used in the entire applicaton. Like: UserManager_ErrorHandler(), _GetCtrl() and other functions.

frmUserManager.au3 - this file would store all functions relating to the the FORM frmUserManager

frmResetPassword.au3 - this file would store all functions relating to the the FORM frmResetPassword

Project: UserManager

Files: UserManager.au3, frmUserManager.au3, frmResetPassword.au3

UserManager.au3

Global $msg; application wide message variable

; Global arrays for holding form controls
Global $afrmUserManager
Global $afrmResetPassword

; AutoIt Options
Opt("MustDeclareVars", 1); Variables must be pre-declared
Opt("TrayIconHide", 1); Default tray menu items (Script Paused/Exit) will not be shown.

; Include project files
#include <GUIConstants.au3>

#include "frmUserManager.au3"
#include "frmResetPassword.au3"

CreateForm_frmUserManager($afrmUserManager); frmUserManager is main window
CreateForm_frmResetPassword($afrmResetPassword, $afrmUserManager[0][1]); frmResetPassword is child window to frmUserManager

GUISetState(@SW_SHOW, $afrmUserManager[0][1])

While 1
    $msg = GuiGetMsg(1)
    
; Prevent unnessary calls to _GetCtrl() by excluding $GUI_EVENT_MOUSEMOVE
    If $msg[0] <> $GUI_EVENT_MOUSEMOVE Then
        Select
        Case $msg[1] = $afrmUserManager[0][1]
            frmUserManager_WndProc()
        Case $msg[1] = $afrmResetPassword[0][1]
            frmResetPassword_WndProc()
            
        EndSelect
    EndIf
WEnd

Func UserManager_WndProc()
    Select
        
    Case $msg[0] = $GUI_EVENT_CLOSE
        GUIDelete($afrmUserManager[0][1])
        GUIDelete($afrmResetPassword[0][1])
        Exit
    
    EndSelect
EndFunc

Func _GetCtrl($sCtrlName, $afrmArray)   
    For $i = 0 to UBound($afrmArray, 1) - 1
        If $afrmArray[$i][0] = $sCtrlName Then
            Return $afrmArray[$i][1]
        EndIf
    Next
    
; Error Handler
    If $i > UBound($afrmArray, 1) - 1 Then
        UserManager_ErrorHandler("Control reference invalid ", _
                                "Control Name: " & $sCtrlName  & @CRLF & _
                                "Form Name: " & $afrmArray[0][0], "Fatal")
        SetError(1)
    EndIf
EndFunc

; Application wide central message handler.
; Displays an error msg and exits the application if the error is fatal.
Func UserManager_ErrorHandler($sError, $sSolution, $sImpact)
    If $sImpact = "Non-Fatal" Then
        MsgBox(8256, "User Manager", $sError & @CRLF & @CRLF & $sSolution)
    ElseIf $sImpact = "Fatal" Then  
        MsgBox(8208, "User Manager", $sError & @CRLF & @CRLF & $sSolution)
        Exit
    EndIf
EndFunc

frmUserManager.au3

Func CreateForm_frmUserManager(ByRef $afrmNew)
    
    Dim $afrmNew[4][2]

    $afrmNew[0][0] = "$frmUserManager"
    $afrmNew[0][1] = GUICreate("User Accounts", 396, 274, (@DesktopWidth-396)/2, (@DesktopHeight-274)/2)
    GUICtrlCreateGroup("User Accounts:", 8, 8, 380, 73)
    $afrmNew[1][0] = "$icoUsers"
    $afrmNew[1][1] = GUICtrlCreateIcon("shell32.dll", 50, 22, 32)
    GUICtrlCreateLabel("Select a local user account and click Reset Password. " & _
                    "You must have the required priviledges to reset passwords.", 70, 33, 301, 31)
    GUICtrlCreateLabel("Users for this computer:", 8, 90, 115, 17)
    $afrmNew[2][0] = "$lvwUserAccounts"
    $afrmNew[2][1] = GUICtrlCreateListView("Name            |Full Name", 8, 110, 380, 121, _
                    BitOR($LVS_SINGLESEL, $LVS_SHOWSELALWAYS, $LVS_NOSORTHEADER), BitOR($LVS_EX_FULLROWSELECT, $WS_EX_CLIENTEDGE))
    $afrmNew[3][0] = "$cmdResetPwd"
    $afrmNew[3][1] = GUICtrlCreateButton("Reset &Password...", 273, 240, 115, 25)

EndFunc

Func frmUserManager_WndProc()
    
    Select
        
    Case $msg[0] = $GUI_EVENT_CLOSE
        UserManager_WndProc(); Send end application message if main window closed
    Case $msg[0] = _GetCtrl("$cmdResetPwd", $afrmUserManager)
    ; Show frmResetPassword
        GUISwitch($afrmUserManager[0][1])
        GUISetState(@SW_DISABLE)
        GUISwitch($afrmResetPassword[0][1])
        GUISetState(@SW_SHOW)
    
    EndSelect

EndFunc

frmResetPassword.au3

Func CreateForm_frmResetPassword(ByRef $afrmNew, $frmParent)
    Dim $afrmNew[6][2]

    $afrmNew[0][0] = "$frmResetPassword"
    $afrmNew[0][1] = GUICreate("Reset Password", 318, 107, (@DesktopWidth-100)/2, (@DesktopHeight-115)/2, _
                    $WS_CAPTION, -1, $frmParent)
    
    $afrmNew[1][0] = "$frmParent"
    $afrmNew[1][1] = $frmParent
    
    GUICtrlCreateLabel("New passsword:", 11, 13, 131, 13)
    $afrmNew[2][0] = "$txtNewPwd"
    $afrmNew[2][1] = GUICtrlCreateInput("", 143, 11, 165, 23, $ES_PASSWORD)
    GUICtrlCreateLabel("Confirm new passsword:", 11, 44, 131, 13)
    $afrmNew[3][0] = "$txtConfirmPwd"
    $afrmNew[3][1] = GUICtrlCreateInput("", 143, 41, 165, 23, $ES_PASSWORD)

    $afrmNew[4][0] = "$cmdOK"
    $afrmNew[4][1] = GUICtrlCreateButton("OK", 152, 75, 75, 23)
    $afrmNew[5][0] = "$cmdCancel"
    $afrmNew[5][1] = GUICtrlCreateButton("Cancel", 233, 75, 75, 23, $BS_DEFPUSHBUTTON)
EndFunc

Func frmResetPassword_WndProc()
    
    Select
        
    Case $msg[0] = $GUI_EVENT_CLOSE
        GUISwitch(_GetCtrl("$frmParent", $afrmResetPassword))
        GUISetState(@SW_ENABLE)
        GUISwitch($afrmResetPassword[0][1])
        GUISetState(@SW_HIDE)
    Case $msg[0] = _GetCtrl("$cmdCancel", $afrmResetPassword)
        $msg[0] = $GUI_EVENT_CLOSE
        frmResetPassword_WndProc()
    Case $msg[0] = _GetCtrl("$cmdOK", $afrmResetPassword)
        frmResetPassword_cmdOK_Click()
    
    EndSelect

EndFunc

The code from Part II is now spread across 3 AutoIt files. The files should be in the same directory and to run the code, only the first file (UserManager.au3) has to be run. The #include statements at the top automatically include the other two files in the project.

The main reason behind this organization is to keep the functions pertaining to a particular form into that form's .au3 file itself. The common functions, global variables and main While loop are all included in the UserManager.au3 which serves as the project's main file.

In order to better handle events, We have created three extra functions:

UserManager_WndProc()

Func UserManager_WndProc()
    Select
        
    Case $msg[0] = $GUI_EVENT_CLOSE
        GUIDelete($afrmUserManager[0][1])
        GUIDelete($afrmResetPassword[0][1])
        Exit
    
    EndSelect
EndFunc

frmUserManager_WndProc()

Func frmUserManager_WndProc()
    
    Select
        
    Case $msg[0] = $GUI_EVENT_CLOSE
        UserManager_WndProc(); Send end application message if main window closed
    Case $msg[0] = _GetCtrl("$cmdResetPwd", $afrmUserManager)
    ; Show frmResetPassword
        GUISwitch($afrmUserManager[0][1])
        GUISetState(@SW_DISABLE)
        GUISwitch($afrmResetPassword[0][1])
        GUISetState(@SW_SHOW)
    
    EndSelect

EndFunc

frmResetPassword_WndProc()

Func frmResetPassword_WndProc()
    
    Select
        
    Case $msg[0] = $GUI_EVENT_CLOSE
        GUISwitch(_GetCtrl("$frmParent", $afrmResetPassword))
        GUISetState(@SW_ENABLE)
        GUISwitch($afrmResetPassword[0][1])
        GUISetState(@SW_HIDE)
    Case $msg[0] = _GetCtrl("$cmdCancel", $afrmResetPassword)
        $msg[0] = $GUI_EVENT_CLOSE
        frmResetPassword_WndProc()
    Case $msg[0] = _GetCtrl("$cmdOK", $afrmResetPassword)
        frmResetPassword_cmdOK_Click()
    
    EndSelect

EndFunc

Based on which form is causing the event, the master WHILE loop transfers the execution to that particular FORM's WindowProdcedure function.

The following diagram illustrates this procedure:

Posted Image

If $msg = $GUI_EVENT_CLOSE for the main form (frmUserManager), then it is handled by UserManager_WndProc() which cleans up the memory by deleting all forms and exiting the application.

The $GUI_EVENT_MOUSEMOVE event is excluded in the master WHILE loop to prevent triggering unnessay events when the mouse is moved. I always exclude this event unless I absolutely need to process mouse move actions. This reduces the CPU's application event processing.

Try out the code, it should work exactly as we had it at the end of Part II.

Creating form, control event routines:

Now we have the application nicely arranged, we can make it functional by adding the routines which actually do the work.

We'll add the necessary functions to each of the files:

File to be updated: frmUserManager.au3

frmUserManager_LoadIcons() - To set the icons on frmUserManager

Func frmUserManager_LoadIcons()
    If @compiled = 1 Then
        GUISetIcon(@ScriptFullPath, 0, $afrmUserManager[0][1])
        GUICtrlSetImage(_GetCtrl("$icoUsers", $afrmUserManager), @ScriptFullPath, 0)
        GUICtrlSetImage(_GetCtrl("$lvwUserAccounts", $afrmUserManager), @ScriptFullPath, 0)
    Else
        GUISetIcon("icons\0.ico", 0, $afrmUserManager[0][1])
        GUICtrlSetImage(_GetCtrl("$icoUsers", $afrmUserManager), "icons\0.ico", 0)
        GUICtrlSetImage(_GetCtrl("$lvwUserAccounts", $afrmUserManager), "icons\0.ico", 0)
    EndIf
EndFunc

frmUserManager_ChangeControlState() - The "Reset Password..." button should only be enabled if the user has selected a User Account in the ListView. This function will check if an entry is selected in the ListView control and enable/disable the "Reset Password..." button accordingly.

Func frmUserManager_ChangeControlState()
    
    Local $lvwUserAccounts, $cmdResetPwd
    
    $lvwUserAccounts = _GetCtrl("$lvwUserAccounts", $afrmUserManager)
    $cmdResetPwd = _GetCtrl("$cmdResetPwd", $afrmUserManager)
    
    If ControlListView($afrmUserManager[0][1], "", $lvwUserAccounts, "GetSelectedCount") > 0 Then
        GUICtrlSetState($cmdResetPwd, $GUI_ENABLE)
    Else
        GUICtrlSetState($cmdResetPwd, $GUI_DISABLE)
    EndIf
        
EndFunc

frmUserManager_ListUserAccounts() - This function populates the ListView control with the Local User Accounts. The code came straight out of Microsoft ScriptCenter Repository, converted to AutoIt.

Func frmUserManager_ListUserAccounts()

    Local $lvwUserAccounts
    Local $oNTService, $oItem
    
    $lvwUserAccounts = _GetCtrl("$lvwUserAccounts", $afrmUserManager)
    
    $oNTService = ObjGet("WinNT://" & @ComputerName)
    
    For $oItem in $oNTService
        If $oItem.class = "User" Then
            GUICtrlCreateListViewItem($oItem.Name & "|" & $oItem.FullName, $lvwUserAccounts)
        EndIf
    Next
    
    _GUICtrlListViewSetColumnWidth($lvwUserAccounts, 0, $LVSCW_AUTOSIZE)
    
EndFunc

The above functions should be called when the frmUserManager form is created. To achieve this we create a function frmUserManager_Initialize() similiar to VB's Form_Load() sub.

Func frmUserManager_Initialize()
    
    frmUserManager_LoadIcons()
    frmUserManager_ListUserAccounts()
    frmUserManager_ChangeControlState()
    
EndFunc

This function will be be called in the UserManager.au3 after the form is displayed.

**Don't add the line below to frmUserManager.au3**

GUISetState(@SW_SHOW, $afrmUserManager[0][1])
frmUserManager_Initialize()

We also update the frmUserManager_WndProc() function:

frmUserManager_WndProc()

Func frmUserManager_WndProc()
    
    frmUserManager_ChangeControlState()
    
    Select
        
    Case $msg[0] = $GUI_EVENT_CLOSE
        UserManager_WndProc(); Send end application message if main window closed
    Case $msg[0] = _GetCtrl("$cmdResetPwd", $afrmUserManager)
    ; Initialize frmResetPassword
        frmResetPassword_Initialize()
    ; Show frmResetPassword
        GUISwitch($afrmUserManager[0][1])
        GUISetState(@SW_DISABLE)
        GUISwitch($afrmResetPassword[0][1])
        GUISetState(@SW_SHOW)
    
    EndSelect

EndFunc

The frmUserManager_ChangeControlState() function is called before any events for this form are processed. So that we can determine if a user action (if he selected an entry in ListView) should enable the "Reset Password..." button or not.

When the event = $cmdResetPwd, frmResetPassword form is displayed.

The function frmResetPassword_Initialize() is discussed below and is called before the form is displayed.

We now move to the frmResetPassword.au3 file and add the necessary functions.

File to be updated: frmResetPassword.au3

frmResetPassword_Initialize() - Initializes the controls on the frmResetPassword form.

Func frmResetPassword_Initialize()
    Local $txtNewPwd, $txtConfirmPwd
    
    $txtNewPwd = _GetCtrl("$txtNewPwd", $afrmResetPassword)
    $txtConfirmPwd = _GetCtrl("$txtConfirmPwd", $afrmResetPassword)
    
    GUICtrlSetData($txtNewPwd, "")
    GUICtrlSetData($txtConfirmPwd, "")
    GUICtrlSetState($txtNewPwd, $GUI_FOCUS)
EndFunc

frmResetPassword_cmdOK_Click() - Routine checks whether the New Password and the Confirm Password boxes match. If the passwords match, the selected User Account is determined by getting the ListView selected item and the function UserManager_ResetPwd() is called with the User Account name and the new password.

Func frmResetPassword_cmdOK_Click()
    Local $lvwUserAccounts
    Local $txtNewPwd, $txtConfirmPwd
    Local $iSelected, $sUserAccount
    
    $lvwUserAccounts = _GetCtrl("$lvwUserAccounts", $afrmUserManager)
    
    $txtNewPwd = _GetCtrl("$txtNewPwd", $afrmResetPassword)
    $txtConfirmPwd = _GetCtrl("$txtConfirmPwd", $afrmResetPassword)
    
    If GUICtrlRead($txtNewPwd) <> GUICtrlRead($txtConfirmPwd) Then
        UserManager_ErrorHandler("The passwords you type do not match.", _
                                "Type the password for this account again in both boxes.", "Non-Fatal")
        GUICtrlSetState($txtNewPwd, $GUI_FOCUS)
        Return
    Else
        $iSelected = ControlListView($afrmUserManager[0][1], "", $lvwUserAccounts, "GetSelected")
        $sUserAccount = ControlListView($afrmUserManager[0][1], "", $lvwUserAccounts, "GetText", $iSelected, 0)
        
    ; Hide frmResetPassword
        $msg[0] = $GUI_EVENT_CLOSE
        frmResetPassword_WndProc()
        
    ; Reset User password
        UserManager_ResetPwd($sUserAccount, GUICtrlRead($txtNewPwd))
    EndIf
EndFunc

Updated frmResetPassword_WndProc function:

frmResetPassword_WndProc()

Func frmResetPassword_WndProc()
    
    Select
        
    Case $msg[0] = $GUI_EVENT_CLOSE
        GUISwitch(_GetCtrl("$frmParent", $afrmResetPassword))
        GUISetState(@SW_ENABLE)
        GUISwitch($afrmResetPassword[0][1])
        GUISetState(@SW_HIDE)
    Case $msg[0] = _GetCtrl("$cmdCancel", $afrmResetPassword)
        $msg[0] = $GUI_EVENT_CLOSE
        frmResetPassword_WndProc()
    Case $msg[0] = _GetCtrl("$cmdOK", $afrmResetPassword)
        frmResetPassword_cmdOK_Click()
    
    EndSelect
EndFunc
The only addition is the frmResetPassword_cmdOK_Click() routine when the user clicks the OK button.

And we finally update the UserManager.au3 file, which is the main file for the project. All the forms are created here and we also have the most important function here which resets the password for a local user account.

Here are the functions:

File to be updated: UserManager.au3

UserManager_ResetPwd()

Func UserManager_ResetPwd($sUserAccount, $sPassword)
    Local $oUserAccount
    
    $oUserAccount = ObjGet("WinNT://" & @ComputerName & "/" & $sUserAccount & ", user")
    If IsObj($oUserAccount) Then
        $oUserAccount.SetPassword($sPassword)
        If Not @error Then
            $oUserAccount.SetInfo   
        EndIf
    Else
        SetError(1)
        Return
    EndIf
EndFunc

The above function requires a COM Error Handler to be installed. The following lines do this:

Global $oCOMError; COM Error object

; Install the COM Error Handler
$oCOMError = ObjEvent("AutoIt.Error","UserManager_COMErrorHandler")

Func UserManager_COMErrorHandler()
    UserManager_ErrorHandler("Unable to set user password.", _
                            "Ensure that your account has the required priviledges to reset password for this user.", _
                            "Non-Fatal")
    SetError(1)
EndFunc

Updated include files and the form creation:

; Include project files
#include <GUIConstants.au3>
#Include <GuiListView.au3>

#include "frmUserManager.au3"
#include "frmResetPassword.au3"

CreateForm_frmUserManager($afrmUserManager); frmUserManager is main window
CreateForm_frmResetPassword($afrmResetPassword, $afrmUserManager[0][1]); frmResetPassword is child window to frmUserManager

GUISetState(@SW_SHOW, $afrmUserManager[0][1])
frmUserManager_Initialize()
We include the <GuiListView.au3> to use the _GUICtrlListViewSetColumnWidth() function in frmUserManager.au3. We also call the function frmUserManager_Initialize after the frmUserManager gets displayed.

If you've followed discussion till this point, the three updated .au3 files comprise a fully functional application. To launch the application simply run the main project file - UserManager.au3 and you have a GUI application which can reset local user account passwords.

These design guidelines are actually what evolved over the course of time since I've been programming with AutoIt. AutoIt is a great platform for coding even regular windows applications which are free from any external dependencies. It gives all the advantages of the C++ Windows API programming with easily written code. I hope input from members will furthur standardize the best approach for large project development in AutoIt.

The completed application with the necessay files is attached. You need the beta version (for GuiListView routines and ActiveX) to compile or run this application.

UserManager.zip

Share this post


Link to post
Share on other sites



This is Part III of the discussion on management of multiple forms in AutoIt

The first two parts of this topic can be found here:

Creating/Managing multiple forms in AutoIt - Part I

Creating/Managing multiple forms in AutoIt - Part II

The completed application with the necessay files is attached. You need the beta version (for GuiListView routines and ActiveX) to compile or run this application.

UserManager.zip

Hi,

I like the direction you have gone with this. I have captured all 3 posts in PDF files for future review. I also hope the folks developing Koda are reading this thread too.

I'm running Win 2Kp sp 4. When I run either UserManager.exe or UserManager.au3, they present the list of local accounts, but when I try to change the password of one of them, it fails with the message to check my permissions. The login I used for this is in the Admin Group. I can open Computer Management and change anything. Ideas?


[font="Verdana"]Thanks for the response.Gene[/font]Yes, I know the punctuation is not right...

Share this post


Link to post
Share on other sites

Hm interesting after reading your code i had never thought about seperating out the different forms before from the main program. It is a very efficient way of doing.

I do have a program I started work on some time ago though that actually embeds the forms into the main gui. instead of having different forms completely.

I would not mind releasing the code for others. But I'm not sure if I'll have time before i have to go. At any rate thanks for these articles I have archived them myself for latter use.

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