cherdeg Posted June 16, 2009 Share Posted June 16, 2009 (edited) Hi Professionals...This is designed to remove the local user's attribute "Password never expires" and is meant to be run after each login by a "Run"-Entry in Registry:expandcollapse popup; Hide the Tray-Icon ; ================================================================================================== #NoTrayIcon ; Define the include files ; ================================================================================================== #include <Array.au3> ; Variables ; ================================================================================================== Global Const $USER_NOCHANGEPASSWD = 0x40 ; Password change possible yes/no Global Const $USER_NOEXPIREPASSWD = 0x10000 ; Password expiration yes/no Global $h_NewBits = BitOR($USER_NOCHANGEPASSWD, $USER_NOEXPIREPASSWD) Global $s_ComputerName = @COMPUTERNAME ; main ;) ; ================================================================================================== $a_LocalUsers = _GetUserNames() For $i_i = 1 To UBound($a_LocalUsers) -1 _UserCtrlAttribs($s_ComputerName & "/" & $a_LocalUsers[$i_i], "Clear", $h_NewBits) Next ; Function _GetUserNames to find all local User-Accounts ; ============================================================================================== Func _GetUserNames() Local $o_CollectedUsers, $a_Tmp[1], $a_ArrayOfUsers[1] = ["user"] $o_CollectedUsers = ObjGet("WinNT://" & $s_ComputerName) $o_CollectedUsers.Filter = $a_ArrayOfUsers For $o_UserObjects In $o_CollectedUsers _ArrayAdd($a_Tmp, $o_UserObjects.Name) Next Return $a_Tmp EndFunc ;==>_GetUserNames ; Function _UserCtrlAttribs to set certain Attributes for local user Accounts ; ============================================================================================== Func _UserCtrlAttribs($s_UserName, $s_Mode, $i_AttributeBitsToChange) Local $o_UserObjects = ObjGet("WinNT://" & $s_UserName & ", User") $h_CurrentBits = $o_UserObjects.get("UserFlags") $i_Return = BitAND($h_CurrentBits, BitNOT($i_AttributeBitsToChange)) $o_UserObjects.Put("UserFlags", $i_Return) $o_UserObjects.SetInfo EndFunc ;==>_UserCtrlAttribsThe code is functional and feature-complete (for our purposes). But can anyone of you imagine any way to further speed things up? On my system the .exe takes about 3,3 seconds to remove the attribute for about 25 users. It would be perfect if that was done in about 1,5 to 2,0 seconds...any ideas?Best Regards,Chrisp.s.: Also "size matters". You know, I know and probably she knows too - how to further lower the executable's size? Edited June 16, 2009 by cherdeg Link to comment Share on other sites More sharing options...
Mat Posted June 16, 2009 Share Posted June 16, 2009 To cut the size, take out the include for array, as you only use 1 function, and write the function straight in. For $o_UserObjects In $o_CollectedUsers ReDim $aTmp[uBound ($aTmp) + 1] $aTmp[uBound ($aTmp) - 1] = $o_UserObjects.Name Next The other thing is to combine the 2 functions, so you only create half the objects. Func _UserCtrlAllAttribs ($s_Mode, $i_AttributeBitsToChange) Local $o_CollectedUsers, $a_Tmp[1], $a_ArrayOfUsers[1] = ["user"] Local $o_UserObjects = ObjGet("WinNT://" & $s_UserName & ", User") $o_CollectedUsers = ObjGet("WinNT://" & $s_ComputerName) $o_CollectedUsers.Filter = $a_ArrayOfUsers For $o_UserObjects In $o_CollectedUsers $h_CurrentBits = $o_UserObjects.get ("UserFlags") $i_Return = BitAND($h_CurrentBits, BitNOT($i_AttributeBitsToChange)) $o_UserObjects.Put("UserFlags", $i_Return) $o_UserObjects.SetInfo Next Return 1 EndFunc; ==> _UserCtrlAllAttribs I'm not sure about the code though... It's not been tested but its the idea that counts MDiesel AutoIt Project Listing Link to comment Share on other sites More sharing options...
cherdeg Posted June 16, 2009 Author Share Posted June 16, 2009 (edited) To cut the size, take out the include for array, as you only use 1 function, and write the function straight in.Okay, that's cool. It reduced the size from 292 Kbyte to, 284 Kbyte. That's way less than I awaited...but not your fault of course; great idea never the less! The other thing is to combine the 2 functions, so you only create half the objects.Yep, I thought about that idea too, but I can't get it to work properly; "Variable must be of type "Object"": $h_CurrentBits = $o_UserObject.get("UserFlags") $h_CurrentBits = $o_UserObject^ ERROR Any idea what would be wrong with the line before? I assume the line: $o_UserObject = ObjGet("WinNT://" & $o_UserObject.Name & ", User") ...doesn't get an object. So I also tried: $o_CurrentUserName = $o_UserObject.Name $o_UserObject = ObjGet("WinNT://" & $o_CurrentUserName & ", User") ...which also won't do it. Why? The only approach I can imagine is that items in a for...next loop are read-only (maybe even their child-objects?). But that's a shot into the blue... Here my current version (which is at least way shorter than before): ; Hide the Tray-Icon ; ================================================================================================== #NoTrayIcon ; Declaration of variables ; ================================================================================================== Global Const $USER_NOCHANGEPASSWD = 0x40 ; Password change possible yes/no Global Const $USER_NOEXPIREPASSWD = 0x10000 ; Password expiration yes/no Global $h_NewBits = BitOR($USER_NOCHANGEPASSWD, $USER_NOEXPIREPASSWD) Global $s_ComputerName = @COMPUTERNAME ; main() ; ================================================================================================== _DisablePasswordNeverExpires($h_NewBits) ; $s_ComputerName & "/" & $a_LocalUsers[$i_i] ; Function _DisablePasswordNeverExpires to ... now what would you think? ; ============================================================================================== Func _DisablePasswordNeverExpires ($i_AttributeBitsToChange) Local $o_CollectedUsers, $a_Tmp[1], $a_ArrayOfUsers[1] = ["user"] $o_CollectedUsers = ObjGet("WinNT://" & $s_ComputerName) $o_CollectedUsers.Filter = $a_ArrayOfUsers For $o_UserObject In $o_CollectedUsers $o_UserObject = ObjGet("WinNT://" & $o_UserObject.Name & ", User") $h_CurrentBits = $o_UserObject.get("UserFlags") $i_Return = BitAND($h_CurrentBits, BitNOT($i_AttributeBitsToChange)) $o_UserObject.Put("UserFlags", $i_Return) $o_UserObject.SetInfo Next EndFunc; ==> _DisablePasswordNeverExpires Edited June 16, 2009 by cherdeg Link to comment Share on other sites More sharing options...
cherdeg Posted June 16, 2009 Author Share Posted June 16, 2009 (edited) Okay - done! The result is even shorter than awaited - and to nothing has to be paid as much attention as I thought before; Microsoft seems to try making it foolproof (even more than I like). The now again working code is: ; Hide the Tray-Icon ; ================================================================================================== #NoTrayIcon ; Declaration of variables ; ================================================================================================== Global Const $USER_NOCHANGEPASSWD = 0x40 ; Password change possible yes/no Global Const $USER_NOEXPIREPASSWD = 0x10000 ; Password expiration yes/no Global $h_NewBits = BitOR($USER_NOCHANGEPASSWD, $USER_NOEXPIREPASSWD) Global $s_ComputerName = @COMPUTERNAME ; main() ; ================================================================================================== _DisablePasswordNeverExpires($h_NewBits) ; Function _DisablePasswordNeverExpires to find all local User-Accounts ; ============================================================================================== Func _DisablePasswordNeverExpires ($i_AttributeBitsToClear) Local $o_Collection, $a_FilterArray[1] = ["user"] $o_Collection = ObjGet("WinNT://" & $s_ComputerName) $o_Collection.Filter = $a_FilterArray For $o_UserObject In $o_Collection $h_AttributeSetCurrent = $o_UserObject.get("UserFlags") $i_AttributeSetNew = BitAND($h_AttributeSetCurrent, BitNOT($i_AttributeBitsToClear)) $o_UserObject.Put("UserFlags", $i_AttributeSetNew) $o_UserObject.SetInfo Next EndFunc; ==> _DisablePasswordNeverExpires My mistake: I thought one had to fish out the "full user object" after getting "only" all the account names. But you don't get "only" the account names, you already get the full object in the 1st place. So no second ObjGet is needed, just .Put the changes and .SetInfo - done. The resulting code now takes only 2.8 instead of 3.3 seconds to process the 25 users and the .exe-File is 284 Kbytes in size. Does anybody have something else to offer? Edited June 16, 2009 by cherdeg Link to comment Share on other sites More sharing options...
Richard Robertson Posted June 16, 2009 Share Posted June 16, 2009 The executable file won't get much smaller. The entire AutoIt interpreter is included, so there's nothing you can do about that. Even an empty script file will be almost that size. Link to comment Share on other sites More sharing options...
Inverted Posted June 16, 2009 Share Posted June 16, 2009 (edited) One way to lower the size is to override the icon with a nice small one. I just did a little test : UPXed with default icon "pack" : 284kb UPXed with custom 48x48 (3,68kb) icon : 263kb It's because AutoIt uses more than one icon. The difference is bigger for non-upxed cimpiled exes. Edited June 16, 2009 by Inverted Link to comment Share on other sites More sharing options...
Mat Posted June 16, 2009 Share Posted June 16, 2009 (edited) Local $aF[1] = ["user"], $oC = ObjGet("WinNT://" & @ComputerName) $oC.Filter = $aF For $oU In $oC $oU.Put("UserFlags", BitAND($oU.get("UserFlags"), -65601)) $oU.SetInfo Next I very much doubt you will see a performance difference though, not with that method anyhow. Why does it need to be so fast though? Surely you are executing a 1 off change?! MDiesel edit: even shorter code, and smaller variables. Edited June 16, 2009 by mdiesel AutoIt Project Listing Link to comment Share on other sites More sharing options...
cherdeg Posted June 16, 2009 Author Share Posted June 16, 2009 (edited) One way to lower the size is to override the icon with a nice small one.Great...could you please upload the one you used? Don't have an icon generator here...and, btw.: how "to override"?I very much doubt you will see a performance difference though, not with that method anyhow. Why does it need to be so fast though? Surely you are executing a 1 off change?!Me too But one has to try...!As I wrote in my OP, the .exe is meant to be started by a "Run"-entry in the registry of several machines. These machines are used for quite important driver builds and the developers leasing them are not willing to comply our security policies, which explicitly prohibit the use of the "lazy bum"-Attribute "Password never expires". If we manually click off the check, they will set it back the next day. The machines aren't domain members, so GPO is no option also; and local Security offers no option to do this afaik. But these guys are real-life developers; they have no clue of anything except their C++ compilers. So if a utility isn't located in the StartUp-folder, they won't find it. And if it isn't even noticeable (as in: "runs very fast") they won't start digging... Edited June 16, 2009 by cherdeg Link to comment Share on other sites More sharing options...
cherdeg Posted June 16, 2009 Author Share Posted June 16, 2009 (edited) The executable file won't get much smaller. The entire AutoIt interpreter is included, so there's nothing you can do about that. Even an empty script file will be almost that size.What about writing an AutoIt compiler?Just joking, keep cool Edited June 16, 2009 by cherdeg Link to comment Share on other sites More sharing options...
Richard Robertson Posted June 16, 2009 Share Posted June 16, 2009 Well, technically I already am writing one. Link to comment Share on other sites More sharing options...
cherdeg Posted June 16, 2009 Author Share Posted June 16, 2009 Well, technically I already am writing one.C'mon...you know what I mean... Link to comment Share on other sites More sharing options...
Inverted Posted June 16, 2009 Share Posted June 16, 2009 Hehe, I uploaded a couple of icon alternatives, select them in the compilation options to replace the default.icons.rar Link to comment Share on other sites More sharing options...
rover Posted June 17, 2009 Share Posted June 17, 2009 (edited) The code is functional and feature-complete (for our purposes). But can anyone of you imagine any way to further speed things up? On my system the .exe takes about 3,3 seconds to remove the attribute for about 25 users. It would be perfect if that was done in about 1,5 to 2,0 seconds...any ideas?@cherdeg if using the slow COM method you should add a COM error handler and use IsObj() to check returned objects NetUserEnum API is much faster. NetUserEnum retrieves usernames and flags of all accounts NetUserSetInfo sets flags I assume you have policy set to not allow blank passwords? example modified from this post by Ghost1987: MakeMeAdmin (In AutoIt) Edit: correction to second dllcall error handling, cleaned up variable declarations Global $g_eventerror = 0; to be checked to know if com error occurs. Must be reset after handling. Global $oMyError = ObjEvent("AutoIt.Error","ComErrorHandler") Func ComErrorHandler() Dim $oMyError, $fDebugFlag = True Local $sHexNumber = Hex($oMyError.number,8) Local $sDesc = StringStripWS($oMyError.windescription, 2) If $fDebugFlag Then ConsoleWrite("! AutoItCOM Test: We intercepted a COM Error !" & @CRLF & @CRLF & _ "err.description is: " & @TAB & $oMyError.description & @CRLF & _ "err.windescription:" & @TAB & $oMyError.windescription & @CRLF & _ "err.number is: " & @TAB & $sHexNumber & @CRLF & _ "err.scriptline is: " & @TAB & $oMyError.scriptline & @CRLF) EndIf $oMyError.clear $g_eventerror = 1; something to check for when this function returns Endfunc expandcollapse popup#NoTrayIcon #include <Array.au3> Opt('MustDeclareVars', 1) Local $aUsers1 = _NetUserSetPasswordChangeExpireFlags(@ComputerName) $aUsers1[0] &= " - " & @ComputerName _ArrayDisplay($aUsers1) Exit ;returns number of accounts with flags changed in returned array [0] element ;and names of user accounts changed in following elements Func _NetUserSetPasswordChangeExpireFlags($sServer = "", $fNoDisabledAccounts = True) ;Author: rover, based on _NetUserEnum() code from here: ;MakeMeAdmin (In AutoIt) - Author: Ghost1987 ;http://www.autoitscript.com/forum/index.php?showtopic=92252 Local Const $UF_ACCOUNTDISABLE = 0x2 Local Const $UF_PASSWD_CANT_CHANGE = 0x40 Local Const $UF_DONT_EXPIRE_PASSWD = 0x10000 Local Const $FILTER_NORMAL_ACCOUNT = 0x2 Local Const $UF_NORMAL_ACCOUNT = 0x200 Local Const $UF_SCRIPT = 0x1 Local Const $MAX_PREFERRED_LENGTH = -1 Local Const $NERR_Success = 0 Local $i_AttributeBitsToClear = BitOR($UF_PASSWD_CANT_CHANGE, $UF_DONT_EXPIRE_PASSWD) Local $iUnread = 0, $aRet, $iError = 0, $tUSER_INFO_1008, $pBuf, $tName, $tFlag, _ $iEntriesRead, $tTotalEntries, $tUserInfo1, $zUserInfo1, $aUserEnum[1] = [0] $aRet = DllCall("Netapi32.dll", "int", "NetUserEnum", "wstr", $sServer, "dword", 1, "dword", _ $FILTER_NORMAL_ACCOUNT, "ptr*", 0, "dword", $MAX_PREFERRED_LENGTH, "dword*", 0, "dword*", 0, "ptr", 0) $iError = @error If @error Or $aRet[0] <> $NERR_Success Then If UBound($aRet) = 9 Then $iError = $aRet[0] DllCall("Netapi32.dll", "int", "NetApiBufferFree", "ptr", $aRet[4]) EndIf Return SetError($iError, 1, $aUserEnum) EndIf $pBuf = $aRet[4] $iEntriesRead = $aRet[6] $tTotalEntries = $aRet[7] If $iEntriesRead <> $tTotalEntries Then $iUnread = $tTotalEntries - $iEntriesRead $tUserInfo1 = DllStructCreate("ptr;ptr;dword;dword;ptr;ptr;dword;ptr") $zUserInfo1 = DllStructGetSize($tUserInfo1) For $i = 1 To $iEntriesRead $tUserInfo1 = DllStructCreate("ptr;ptr;dword;dword;ptr;ptr;dword;ptr", $pBuf + ($i - 1) * $zUserInfo1) $tName = DllStructCreate("wchar[256]", DllStructGetData($tUserInfo1, 1)) $tFlag = DllStructGetData($tUserInfo1, 7) ;ConsoleWrite('! User: ' & DllStructGetData($tName,1) & ' - Flags: ' & $tFlag & @crlf) If BitAND($tFlag, $UF_ACCOUNTDISABLE) = 0 Or $fNoDisabledAccounts = False Then If BitAND($tFlag, $UF_PASSWD_CANT_CHANGE) = $UF_PASSWD_CANT_CHANGE Or _ BitAND($tFlag, $UF_DONT_EXPIRE_PASSWD) = $UF_DONT_EXPIRE_PASSWD Then $aUserEnum[0] += 1 ReDim $aUserEnum[$aUserEnum[0] + 1] $aUserEnum[$aUserEnum[0]] = DllStructGetData($tName, 1) $tUSER_INFO_1008 = DllStructCreate("dword usri1008flags") $tFlag = BitAND($tFlag, BitNOT($i_AttributeBitsToClear)) DllStructSetData($tUSER_INFO_1008, "usri1008flags", $tFlag) $aRet = DllCall("Netapi32.dll", "int", "NetUserSetInfo", "wstr", $sServer, _ "wstr", $aUserEnum[$aUserEnum[0]], "dword", 1008, _ "ptr", DllStructGetPtr($tUSER_INFO_1008), "dword*", 0) $iError = @error If @error Or $aRet[0] <> $NERR_Success Then If UBound($aRet) = 6 Then $iError = $aRet[0] DllCall("Netapi32.dll", "int", "NetApiBufferFree", "ptr", $pBuf) Return SetError($iError, 2, $aUserEnum) EndIf EndIf ElseIf $tFlag < BitOR($UF_NORMAL_ACCOUNT, $UF_SCRIPT) Then $aUserEnum[0] += 1 ReDim $aUserEnum[$aUserEnum[0] + 2] $aUserEnum[$aUserEnum[0] + 1] = "User account flag error: " & _ $tFlag & " -- User: " & DllStructGetData($tName, 1) EndIf Next If $iUnread <> 0 Then ReDim $aUserEnum[UBound($aUserEnum) + 1] $aUserEnum[UBound($aUserEnum) - 1] = "Accounts unread: " & $iUnread EndIf DllCall("Netapi32.dll", "int", "NetApiBufferFree", "ptr", $pBuf) Return SetError(0, 0, $aUserEnum) EndFunc ;==>_NetUserSetPasswordChangeExpireFlags Edit: correction to second dllcall error handling, cleaned up variable declarations Edited June 17, 2009 by rover I see fascists... Link to comment Share on other sites More sharing options...
cherdeg Posted June 17, 2009 Author Share Posted June 17, 2009 (edited) Hi Rover, nice to have another alternative: GREAT! But on my system it's not a bit faster that the "slow" COM version: 2.8 seconds. But of course you're generally right...if I had a slow computer with a lot of users it would probably play off its advantages. But concerning code clarity, complexity and manageability...to tweak your version one would need a lot more skills. Never the less I'll try to understand... Best Regards, Chris Edited June 17, 2009 by cherdeg Link to comment Share on other sites More sharing options...
rover Posted June 18, 2009 Share Posted June 18, 2009 Hi Rover,nice to have another alternative: GREAT! But on my system it's not a bit faster that the "slow" COM version: 2.8 seconds. But of course you're generally right...if I had a slow computer with a lot of users it would probably play off its advantages.But concerning code clarity, complexity and manageability...to tweak your version one would need a lot more skills. Never the less I'll try to understand... Best Regards,Chrisinterestingon one slow machine on my small 3 machine networkclearing target flags of 6 of 11 accounts, I get 3-6 seconds using COM, and about 1 second max using APIstick with the COM method and add the error handlingcheers I see fascists... Link to comment Share on other sites More sharing options...
cherdeg Posted June 18, 2009 Author Share Posted June 18, 2009 Rover, how do you measure? With any debugging / profiling tool? I just run the code from SciTe - maybe that's the problem? Link to comment Share on other sites More sharing options...
rover Posted June 18, 2009 Share Posted June 18, 2009 (edited) Rover, how do you measure? With any debugging / profiling tool? I just run the code from SciTe - maybe that's the problem? ConsoleWrites and TimerInit/TimerDiff Edit: It would help if I didn't post a quickly modified existing ConsoleWrite with @error/@extended reporting with a tacked on TimerDiff and promptly wreck error reporting by forgetting to buffer the returned error codes before calling TimerDiff #NoTrayIcon #include <Array.au3> Global $iTimer = TimerInit() Local $aUsers1 = _NetUserSetPasswordChangeExpireFlags(@ComputerName) Global $iErr = @error, $iExt = @extended ConsoleWrite("Time: " & Round(TimerDiff($iTimer)) & " - Error: " & $iErr & " - Extended: " & $iExt & @CRLF) $aUsers1[0] &= " - " & @ComputerName _ArrayDisplay($aUsers1) Exit I also use SysInternals DebugView with OutputDebugString API for compiled scripts #AutoIt3Wrapper_run_debug_mode=Y help file has a few debug functions as well haven't used them but there are debuggers on the forum as well Edited June 18, 2009 by rover I see fascists... Link to comment Share on other sites More sharing options...
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