Jump to content

Ascend4nt

Active Members
  • Posts

    1,368
  • Joined

  • Last visited

  • Days Won

    5

Ascend4nt last won the day on December 3 2014

Ascend4nt had the most liked content!

1 Follower

Profile Information

  • Location
    NJ

Recent Profile Visitors

2,350 profile views

Ascend4nt's Achievements

  1. Never one to be satisfied with edge cases, I took the initative to properly fool-proof this beast against processes with 16K+ handles. Yup, now the truncated bits of handles are properly restored using simple wraparound detection. Enjoy! Update 2014-10-01: Fixed: _NTObjGetHandlesUD() was failing to return an error code if $FilterBy was 0, resulting in empty arras Improved: _NTObjGetHandlesUD() now tracks & calculates internally what handle values are beyond 65,536 Added: NTKernelObjectSpam (simple test of creating tons of Kernel Objects)
  2. step887, much appreciated. It looks to be working as it should. I'd recommend deleting or obfuscating the S-1-5-21-xxx SIDs, as I'm not sure if that info can be used for malicious purposes
  3. Nice article describing how one can use alternate methods to find DLL export addresses. I'm curious why you posted this here? You could have easily create a blog I've done this export lookup in Assembly threads before, as you probably know. One source of inspiration I used for doing that was this article, which uses assembly and hash lookups. (I don't use hashes myself) Also, for anyone interested in seeing whats in the TEB and PEB in more detail, try the GUI's out in the Processes and Threads UDF in my signature.
  4. Update: I was thankfully wrong about the 64K limit on system object handles. There's actually a really high limit - 16,777,216 - and its not actually O/S-wide, but Process-specific (which gives quite a lot of legroom). The unfortunate problem that creates is that 16+million is more than what would fit in a 16-bit variable. The NtQuerySystemInformation API call which is used to gather information on handles returns an array of SYSTEM_HANDLE_INFORMATION structures, and those structures limit handle values to 16-bits. This amount is okay when a Process's handles are never in excess of 16-bits, but for values above this (65,536+), the upper bits are lost, and the handle value then indexes other handles, which can create a mess of confusion. Fortunately, this shouldn't be a problem with most processes, as anything more than a few thousand handles for one process is highly unlikely (and probably a sign of a buggy program). Another important thing to note is that each handle # is process-specific. So even if there are 7,000 handles for 100 processes, the API call will still return the correct results, as each handle # is unique only to the process to which it belongs. This also means that handle # 4 in one process is something completely different in another process. Also important to note: handle values (or handle-table indexes) are each offset by 4, so approximately 64K/4, or 16K handles can be consumed by a process before it surpasses 16-bits. The example below shows this. Use NTKernelObjectsInspect to compare handle values to see where the 'wraparound' happens just after 0xFFFC. For information on system handles, check out Mark Russinovich's excellent blogs, specifically "Pushing the Limits of Windows: Handles" #include <WinAPI.au3> #include <Array.au3> ; Handle values (indexes into handle tables) are offset 4 bytes from each other, so aroune 16K handles ; would be the max representable in 16-bits. (There are already handles at this point, so this will ; indeed cause 17+ bits to be used) Local $aHandles = _HandleGenerate(65536 / 4) ;MsgBox(0, "Handles Generated", "Generated handles = " & UBound($aHandles)) _ArrayDisplay($aHandles, "Handles Generated") Func _HandleGenerate($iMax) ; Limit of 4999999 is much less than the real max (16 million+) If $iMax < 0 Or $iMax > 4999999 Then Return SetError(1, 0, 0) Local $hEvent, $aHandles[$iMax] $hEvent = _WinAPI_CreateEvent(0, True, True, "_EVENTOBJ_") If @error Or $hEvent = 0 Then Return SetError(-1, @error, 0) ConsoleWrite("Event Handle for '_EVENTOBJ' = " & $hEvent& @CRLF) $aHandles[0] = $hEvent For $i = 1 To $iMax - 1 ; DUPLICATE_SAME_ACCESS (2) $aHandles[$i] = _WinAPI_DuplicateHandle(Ptr(-1), $hEvent, Ptr(-1), 0, 0, 2) If @error Or $aHandles[$i] = 0 Then SetError(-1, @error, 0) ExitLoop EndIf Next If @error Then If $aHandles[$i] = 0 Then ReDim $aHandles[$i - 1] EndIf Return SetError(2, @extended, $aHandles) EndIf Return $aHandles EndFunc
  5. That's very odd. Well, give the new UDF a try and see what the output is! Update: 2014-09-26: Added: Multiple processes and/or types allowed in filters, plus exclusion flag Changed: NTKernelObjectsInspect example now allows multiple filters and/or processes Also, Object totals are displayed where possible (elevated rights necessary for some processes) Added: $NTOBJ_QUERYBY_PROCESSNAME Filter -> Must be used with processes as names Improved: DLL Handles used for DLLCalls() Note that the biggest time-sink is still the API call NtQuerySystemInformation which grabs ALL system handles in one fell swoop (used inside _NTObjGetHandlesUD) Added: Internal functions which rely on DLLHandles for speed improvements Added: $g_NTKO_bNamedPipeProtect which can be toggled to prevent certain attributes from being scanned when "File" types are found. This is toggled ON for XP/2003 and under, OFF for Vista+ Added: $g_NTKO_sFileAttribSkipList -> assign additional Skip-Attributes with this if desired Added: 'BadMasks' check in collection function which flags certain Attributes which have previously caused Thread timeouts. Use $g_NTKO_bSkipBadMasks to toggle this OFF (ON by default) Changed: HKCU detection relies on a different technique than _Security__LookupAccountName() to get SID Added: Smaller simpler example (NTKernelObjectsCollectExample) Misc: Other misc. changes, additions, fixes which shouldn't affect UDF usage
  6. step887, thanks for the output. I'm curious why HKEY_CURENT_USER isn't being detected for you properly. The line that reads "HKEY_USERSS-1-5-21...Software" should be converted, so I'm assuming the call to _Security__LookupAccountName() isn't returning the proper SID for you. Just out of curiosity, try running this and see if the SID matches the one used in that line: #include <Security.au3> ConsoleWrite("SID = " & _Security__LookupAccountName(@UserName)[0] & @CRLF) But anyway, I'm releasing a new version of the UDF shortly which uses a different method of getting the SID, so we'll see if that one works for you
  7. JScript: thanks for the compliment DanyfireX, much appreciated! Are you running a Spanish (or otherwise) language version of the O/S?
  8. Glad to hear it! Btw, I'd like to hear if the Registry keys, File paths, and NamedPipes/Mailslots etc are being detected properly. (excluding those reporting negative values in the 'ExInfo' column) Also, hmm.. it appears when running from Scite, querying one of the 'AutoIt3Wrapper' processes fails for certain File types, probably NamedPipes.. the thread querying these doesn't even terminate properly. I may have to force protection for that specific attribute even on Windows 7
  9. trancexx, Actually I've had parts of the code laying around for years now. I was intending to release something like this last year when I first showed Decipher a >function on getting Object names using a thread. I've done a bit of work in C++ with multithreading using Semaphores and Mutexes, which gave me the need to query those objects too. Why MS never 'officially' exposed these non-mutating query API's (NtQueryMutant, NtQuerySemaphore, etc), I'll never know.. they are pretty important tools for debugging. Additionally, I've also seen a few random people asking about handles, files, and other system objects that made me think it might be worth something to someone to release this. As an unexpected result of this, I've also been able to see why certain processes are eating thousands of handles, sometimes for no good reason (khalmnpr.exe - you dirty little slut). Ah, but back to that HotKeys thread.. that actually did get me digging, but instead of finding an answer, I went and released that '>Atom Tables' UDF a month or so ago, as some hotkey strings are occasionally stored there. Man, I dunno about you.. but with this project, I'm pretty much done with the Undocumented Windows world. haha With the Processes and Threads, Atom Tables, multi-CPU Usage and other UDF's I've released, I think I've had my fill.
  10. So, for the Map types to become a really well integrated part of the language, there needs to be some support for string literal assignments. Without this kind of support, its very tedious to use Maps in many situations. When I talk literals, I mean something like this: $mMap [] = { "val" : 1, "valB" : $nVal, "name" : "Bob Smith", "array" : $myArray} There's other ways to do the above, depending on the language, but I think that is one of the easiest ways to format it. Anyway, for anyone looking to add *very* simplistic Map-literal assignment/addition to a Map, here's an example of how it could be done for the simplest of types (alphanumeric kinds of assignments): ; ================================================================================================= ; Func _MapCreateFromStringLiteral($sMapLits) ; ; Given a string literal, creates and adds key/values to a new map which is returned. ; Note this is VERY simplistic and is really more for alphanumeric types of assignments. ; ; String literal for Maps is in the form of Javascript & D, except with quotes in place of brackets ; Example: ; "[valA : mapVal, valB : 123, 3 : 4]" is the equivalent of: ; Map["valA"] = "mapVal" ; Map["valB"] = "123" ; Map["3"] = "4" ; ; Note that currently there is no numeric conversion ("123" doesn't get converted to 123), ; however comparisons like ("123" = 123" work in AutoIt ; ; Returns: ; Success: new Map ; Failure: "" with @error set ; ; Author: Ascend4nt ; ================================================================================================= Func _MapCreateFromStringLiteral($sMapLits) Local $mMap [] If Not _MapAddFromStringLiteral($mMap, $sMapLits) Then Return SetError(@error, @extended, "") Return $mMap EndFunc ; ================================================================================================= ; Func _MapAddFromStringLiteral(ByRef $mMap, $sMapLits) ; ; Given a string literal, creates and adds key/values to the given Map parameter. ; Note this is VERY simplistic and is really more for alphanumeric types of assignments. ; ; String literal for Maps is in the form of Javascript & D, except with quotes in place of brackets ; Example: ; "{valA : mapVal, valB : 123, 3 : 4}" is the equivalent of: ; Map["valA"] = "mapVal" ; Map["valB"] = "123" ; Map["3"] = "4" ; ; Note that currently there is no numeric conversion ("123" doesn't get converted to 123), ; however comparisons like ("123" = 123") work in AutoIt ; ; Returns True on success, False on failure with @error set ; ; Author: Ascend4nt ; ================================================================================================= Func _MapAddFromStringLiteral(ByRef $mMap, $sMapLits) If Not IsString($sMapLits) Or $sMapLits = "" Or Not IsMap($mMap) Then Return SetError(1, 0, False) ; Pull out "key:val" pairs. Optional whitespace surrounds each element (comma typically separates the pairs) Local $aMapVals = StringRegExp($sMapLits, "(\w+)\s*:\s*(\w+)", 3) If @error Then Return SetError(1, 0, False) For $i = 0 To (UBound($aMapVals) - 1) Step 2 ;ConsoleWrite("Next assignment: Key '" & $aMapVals[$i] & "' = " & $aMapVals[$i + 1] & @CRLF) $mMap[$aMapVals[$i]] = $aMapVals[$i + 1] Next Return True EndFunc ;; -= Map from Literals =- Local $mMapFromLits = _MapCreateFromStringLiteral("{byte:1, short:2, int:4, int64:8, float:4, double:8}") _MapAddFromStringLiteral($mMapFromLits, "{ char : 1 , wchar : 2 }") ConsoleWrite("# of Keys in $mMapFromLits = " & UBound($mMapFromLits) & ":" & @CRLF) For $kKey In MapKeys($mMapFromLits) ConsoleWrite("Map['" & $kKey & "'] = " & $mMapFromLits[$kKey] & @CRLF) Next
  11. Kernel Objects Information Sample output of Object Handles probing _ I've assembled a number of UDF's which use "undocumented" features of the O/S over the years. And this here would be the latest, and possibly the last (I hope?). The purpose of this UDF is to query kernel objects in the system. It's actually a pretty big UDF that ties together a lot of functionality, and hopefully makes it more accessible. With the UDF you can: Query a Kernel Object for 'hidden' information using its handle: Object Type and stats (_ObjectGetTypeInfoUD), Attributes and Access (_ObjectGetBasicInfoUD), Kernel Object Name (_ObjectGetNameUD), etc Query certain Kernel Event Objects for current states:Event, IoCompletion and Mutex ("Mutant") signal states (and more), Semaphore counts, Timer's remaining time, etc Get a list of opened File handles and filenames (there's already a few UDF's dedicated to that, though) Collect all the current handles held by the O/S and its processes, using specific filters, and get information on what the object is and its current state Kernel Objects Inspector script _ What's an Object you say? Whats a Kernel? Whats an NT? Gosh, maybe you shouldn't be here - go read Youtube. As Windows programmers, we make use of these Kernel Objects all the time... Object Types List Some of the most common System Objects: Token, Process, Thread, Event, Mutant (Mutex), Semaphore, Timer, File (includes NamedPipe and Mailslot), Key (Registry Key) Anytime you work with these objects, you are generating new objects at the kernel level. Luckily, the O/S allows above 16 million handles per process (see Pushing the Limits of Windows: Handles by Mark Russinovich), so this isn't a concern. However, if an individual process has in excess of 16K handles, there will be some trunacted values returned from the NT API call as it only returns 16-bit values for handles. See >this post where I try to describe this in better detail. However, this is no longer a problem with the latest update, which restores the upper bits of handles through a simple wraparound detection technique. There's more to say, but perhaps its best to show what functions are available. From the NTKernelObjectsInfo UDF Header: Querying time issues: Note that any call to query handles (_NTObjGetHandlesUD, _NTObjGetHandlesInfoEx) relies on a call to NtQuerySystemInformation, which gathers information on EVERY handle held by the system and it's processes. This can take a few seconds! Be patient. (Also, _NTObjBuildTypesIndexMap calls it indirectly) IMPORTANT: Be a little careful with looking for 'File' objects on Vista and Win7.. on XP there's already some safeguards which unfortunately prevent detecting certain objects. Newer versions of the O/S don't seem to have problems with threaded probing of File objects, but there may be some cases.. The Console output is still a bit noisy, but its good for analyzing where there's problems in reading handles, or analyzing "File" handles which can cause major problems, especially in the case of NamedPipes. Some example UDFs are included: NTSystemObjectsList: displays a list of System Object Types NTKernelObjectsCollectExample: A collection query at its simplest (see below for this example) NTKernelObjectsSelfExamine: creates a number of different Objects before listing everything NTKernelObjectsInspect: Inspect Kernel Objects with Filtering options from a GUI This GUI needs work! Notice that with the ArrayDisplay function, there is a 'Run User Func' option which will display any extra info retrieved for the object (see ExInfo column). NTKernelObjectsSpam: Creates a crapload of Kernel Objects. This is mostly useless, but its here to demonstrate how NTKernelObjectsInspect now is able to report correct handle values beyond 65,536 NTKernelObjectsCollectExample In this example I query only 2 processes for handles, and use exclusion criteria to remove "File" and "EtwRegistration" from the resultant list. ; =========================================================================================================== ; <NTKernelObjectsCollectExample.au3> ; ; Pretty barebones example of NTKernelObjectsInfo, showing the ease with which objects can be collected ; Uses multipe query types, multiple processes, and multiple Object Types with exclusion rules ; ; Author: Ascend4nt ; =========================================================================================================== #include "NTKernelObjectsInfo.au3" #include <Array.au3> ; -= FLAGS to Tweak Object Querying =- ; Force Win2000/XP Attribute skipping (must appear AFTER #include): ;$g_NTKO_bNamedPipeProtect = True ; Alternatively set own: ;$g_NTKO_sFileAttribSkipList = "0x0012019F|" ; Additionally, can force BadMask Skipping to OFF (not recommended): ;$g_NTKO_bSkipBadMasks = False ; Other queries available, although less often used: ; $NTOBJ_QUERYBY_PID (example: @AuotItPID), $NTOBJ_QUERYBY_OBJTYPE (ex: 28), and $NTOBJ_QUERYBY_HANDLE (actual object handle) $aRet = _NTObjGetHandlesInfoEx($NTOBJ_QUERYBY_PROCESSNAME, "firefox.exe|autoit3.exe", _ $NTOBJ_QUERYBY_OBJTYPENAME + $NTOBJ_QUERY_EXCLUDE, "File|EtwRegistration") ConsoleWrite("Errors: " & @error & ", @extended = " & @extended & @CRLF) _ArrayDisplay($aRet, "_NTObjGetHandlesInfoEx") Thanks for testing this out! Change History: NTKernelObjects.zip ~prev Downloads: 55
  12. guinness, kudos on the good work. Expanding macros and certain AutoIt expressions is pretty sweet, and I would dare say it would encourage more proper coding practices
  13. Thanks for that. That's pretty ugly syntax, but I get that the parser has issues with it otherwise. Anyway, I've updated my example after testing it on various embedded datatypes. I was actually surprised to find out that DLLStructs are passed by reference (in stable and beta), rather than a copy being made.. interesting. Also, the embedded-array access issue is a bit annoying, but that problem has been present with arrays-within-arrays as well. Also, since Jon was cool enough to update the Beta docs, here's a link for anyone that hasn't played with the Beta's yet: Overall: https://www.autoitscript.com/autoit3/files/beta/autoit/docs/ (currently, the Variables section has an overview of Maps, and Functions lists the Mapxx functions) Also, the Map Management section: https://www.autoitscript.com/autoit3/files/beta/autoit/docs/functions/Map%20Management.htm #Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Version=Beta #AutoIt3Wrapper_Outfile=MapTests.exe #AutoIt3Wrapper_Change2CUI=y #AutoIt3Wrapper_Run_AU3Check=n #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** ; ------------------------------------------------------------------------------------------- ; Map Tests ; ; Requires AutoIt beta (v3.3.13.18 tested) ; ------------------------------------------------------------------------------------------- ; NOTES: ; ----- ; - Embedding other complex datatypes: ; Anything inserted in Arrays and Maps are COPIES of the objects, ; EXCEPT for DLLStruct's, which are reference types (at least in current AutoIt versions) ; So, modifying embedded Arrays and Maps will NOT alter the originals ; ; - Calling functions: ; Must be done using Map["func"]() or (Map.func)() ; Functions in Arrays work with Arr[i]() ; ; - Arrays within Maps/Arrays: ; Write access: ; No manner of subscript access works, and parentheses around an expression causes a copy ; to be made, which discards the value at the end of the statement ; A function taking an Array ByRef can be used to workaround the problem, however ; ; Read access: ; dot-member access works: ; Map.arr[0] ; However for both Maps and arrays, using subscript operators requires parentheses: ; (Map["arr"])(0) ; (Arr[0])[0] ; ; - Maps inside maps: ; Read/Write access: ; Map["map"]["val"] ; Map.map.val ; ; - Maps within Arrays: ; Write access: ; Arr[3].data ; Read access: ; Arr[3].data ; (Arr[3])["data"] ; ; - DLLStructs' produce references to the original structure (in current AutoIt versions) ; Read/Write access depends on Array or Map container: ; Arr[i].stVal ; Map["struct"].stVal ; Map.struct.stVal ; ; ------------------------------------------------------------------------------------------- Func MyFunc() ConsoleWrite("Called MyFunc"&@LF) EndFunc Func ShowString(Const ByRef $mMap) If MapExists($mMap, "myString") Then ConsoleWrite("Map string = " & $mMap.myString & @LF) EndFunc Func ModifyArrayValue(ByRef $aArr, Const $i, Const ByRef $vData) If IsArray($aArr) Then $aArr[$i] = $vData EndFunc Func TestMaps() Local $aArr[4], $mMap[], $mMap2[] ;Dim $mMap2[] ; Also works for declaring/redeclaring variable as Map ;; -= Function call Tests -= ; Indirect function call using array $aArr[0] = MyFunc $aArr[0]() ; Indirect function call using Map & array-style access $mMap["func"] = MyFunc $mMap["func"]() ; Indirect function call using Map & member/property access $mMap2.func = MyFunc ; Awkward syntax which works (Au3Check fails to handle this, however) ($mMap2.func)() ;$mMap2.func() ; Preferred syntax (which doesn't work) ;; -= Value assignment Tests =- ; array-style assign & access: $mMap["val"] = 1 ConsoleWrite("Map1 value = " & $mMap["val"] & @LF) ; member/property version $mMap2.val = 4 ConsoleWrite("Map2 value = " & $mMap2.val & @LF) ; More values $mMap["myString"] = "aMap string" $mMap2.myString = "aMap2 string" ; Passing maps as parameters ShowString($mMap) ShowString($mMap2) ;; -= Embedded Array tests =- Local $aEmbedArr[2] = [1, 2] ; Makes *COPY* of array inside Map $mMap.arr = $aEmbedArr ; Modifying embedded array: ; Member/property access doesn't work: ;$mMap.arr[0] = 20 ; Parentheses around a value forces a copy to be made [per Jon], not a reference: ;~ ($mMap.arr)[0] = 20 ; copy is made, and discarded ; array-style access doesn't work either: ;$mMap["arr"][0] = 20 ; Indirect workaround (pass array as reference): ModifyArrayValue($mMap.arr, 0, 20) ConsoleWrite("Map:Embedded-Array elements:"&@LF) ; array-style access For $i = 0 To UBound($mMap["arr"]) - 1 ; Note Awkward syntax for getting internal array element: ; ($mMap["arr"])($i) ConsoleWrite("#" & $i & " = " & ($mMap["arr"])[$i] & @LF) Next ; Member/property access: ConsoleWrite("[alternate member/property access]:"&@LF) For $i = 0 To UBound($mMap.arr) - 1 ConsoleWrite("#" & $i & " = " & $mMap.arr[$i] & @LF) Next ; .. 'EmbeddedArray' value remains the same as its initial assignment: ConsoleWrite("..aEmbedArr[0] = " & $aEmbedArr[0] & @LF) ;; - Embedded array in array (no Map) - ; Makes *COPY* of EmbedArray inside Array $aArr[1] = $aEmbedArr ; Doesn't work: ;$aArr[1]0] = 40 ; Parentheses around a value forces a copy to be made [per Jon], not a reference: ;~ ($aArr[1])[0] = 40 ; copy is made, and discarded ; Indirect workaround (pass array as reference): ModifyArrayValue($aArr[1], 0, 40) ConsoleWrite("Array:Embedded-Array elements:"&@LF) ; array-style access For $i = 0 To UBound($aArr[1]) - 1 ; Note Awkward syntax for getting internal array element: ; ($aArr[1])[$i] ConsoleWrite("#" & $i & " = " & ($aArr[1])[$i] & @LF) Next ; .. 'EmbeddedArray' value remains the same as its initial assignment: ConsoleWrite("..aEmbedArr[0] = " & $aEmbedArr[0] & @LF) ;; -= Structures =- Local $tStruct = DllStructCreate("int MyInt;") ; Structure normal access: DllStructSetData($tStruct, "MyInt", 10) ; Structure property-access: $tStruct.myInt = 20 ; Assign structure to Map (actually creates a reference rather than a copy) $mMap.struct = $tStruct ; Modify structure data (both ways work) $mMap.struct.myInt = 40 $mMap["struct"].myInt = 60 ConsoleWrite("mMap.struct.myInt = " & $mMap.struct.myInt & @LF) ; Struct Inside array (creates reference to original struct) $aArr[3] = $tStruct $aArr[3].myInt = 80 ConsoleWrite("aArr[3].myInt = " & $aArr[3].myInt & @LF) ; Original Structure is modified by all operations above (even embedded in Maps or Arrays) ConsoleWrite("tStruct.myInt = " & $tStruct.myInt & @LF) ConsoleWrite("DLLStructGetData($tStruct, 'myInt') = " & DllStructGetData($tStruct, "myInt") &@LF) ;; -= Maps within Maps =- Local $mEmbedMap[] $mEmbedMap.innerVal = 4 ; This causes a *COPY* of $mEmbedMap to be added to $mMap: $mMap.embeddedMap = $mEmbedMap ; Modifying the embedded map doesn't affect the external one $mMap.embeddedMap.innerVal = 10 ConsoleWrite("Map.embeddedMap.innerVal = " & $mMap.embeddedMap.innerVal & @LF) ; Alternate ways of writing the above line: ; (Note how 2 subscripts can be used here, as opposed to embedded arrays): ConsoleWrite("Map[embeddedMap][innerVal] = " & $mMap["embeddedMap"]["innerVal"] & @LF) ConsoleWrite("Map[embeddedMap].innerVal = " & $mMap["embeddedMap"].innerVal & @LF) ; .. 'EmbedMap' value remains the same as its initial assignment: ConsoleWrite("..EmbedMap.innerVal = " & $mEmbedMap.innerVal & @LF) ;; -= Maps within Arrays =- ; Creates a *COPY* of Map inside array $aArr[3] = $mEmbedMap $aArr[3].innerVal = 20 ; Parentheses around a value forces a copy to be made [per Jon], not a reference: ;~ ($aArr[3])["innerVal"] = 40 ; copy is made, and discarded ConsoleWrite("Arr[embeddedMap].innerVal = " & $aArr[3].innerVal &@LF) ; Note Awkward syntax for getting internal array element: ; ($aArr[3])["innerVal"] ConsoleWrite("(Arr[embeddedMap])[innerVal] = " & ($aArr[3])["innerVal"] &@LF) ; .. 'EmbedMap' value remains the same as its initial assignment: ConsoleWrite("..EmbedMap.innerVal = " & $mEmbedMap.innerVal & @LF) EndFunc TestMaps()
  14. I made some small changes, and added an example script which lets you invert the colors on the screen (Win7+) via Tray controls. The last bit was inspired by a post long ago by gil900 about Inversion being better than the Windows Dimmer/Shade/Spotlight eyestrain solutions that I've put up. 2014-08-25: - Changed: WinMagnifier UDF is now separate from the examples - Changed: MagnifierExperiments is a separate script - Changed: Full-screen Scale example now adjusts correctly for Win8+ - Added: MagnifierScreenInverter example - invert screen colors via Tray - Added: _MagnifierColorEffectIsEqual() - compare two Color Matrices for Equality
×
×
  • Create New...