tonedeaf Posted December 10, 2005 Posted December 10, 2005 This is Part III of the discussion on management of multiple forms in AutoItThe first two parts of this topic can be found here:Creating/Managing multiple forms in AutoIt - Part ICreating/Managing multiple forms in AutoIt - Part III 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:If we were coding this in VB the project would be saved with 3 main files: UserManager.vbp, frmResetPassword.frm, frmUserManager.frmSimilarily in AutoIt we'll split the single AutoIt UserManager script created in Part II into 3 parts:UserManager.au3, frmUserManager.au3, frmResetPassword.au3UserManager.au3 - the main project file. It contains the code common to the entire application which is:1. The global variable declarations2. #include <file> statements3. The main WHILE event loop4. 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 frmUserManagerfrmResetPassword.au3 - this file would store all functions relating to the the FORM frmResetPasswordProject: UserManagerFiles: UserManager.au3, frmUserManager.au3, frmResetPassword.au3UserManager.au3expandcollapse popupGlobal $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 EndFuncfrmUserManager.au3Func 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 EndFuncfrmResetPassword.au3expandcollapse popupFunc 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 EndFuncThe 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 EndFuncfrmUserManager_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 EndFuncfrmResetPassword_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 EndFuncBased 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: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.au3frmUserManager_LoadIcons() - To set the icons on frmUserManagerFunc 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 EndFuncfrmUserManager_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 EndFuncfrmUserManager_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) EndFuncThe 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() EndFuncThis 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 EndFuncThe 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.au3frmResetPassword_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) EndFuncfrmResetPassword_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 EndFuncUpdated 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 EndFuncThe 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.au3UserManager_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 EndFuncThe 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) EndFuncUpdated 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
Gene Posted December 11, 2005 Posted December 11, 2005 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...
webmedic Posted December 11, 2005 Posted December 11, 2005 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.
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now