Map Stuff

Started by stanl, July 03, 2020, 02:26:23 PM

Previous topic - Next topic

stanl

Below is a simple lookup from WMI based on a WB Map Structure. I take it the map key is not 'typed', i.e is there a way to differentiate between a key of 1 and '1' in the same map:
Code (WINBATCH) Select


;Winbatch using Map to enumerate WMI Win Types
Types = $"0,PRODUCT_UNDEFINED
1,PRODUCT_ULTIMATE
2,PRODUCT_HOME_BASIC
3,PRODUCT_HOME_PREMIUM
4,PRODUCT_ENTERPRISE
5,PRODUCT_HOME_BASIC_N
6,PRODUCT_BUSINESS
7,PRODUCT_STANDARD_SERVER
8,PRODUCT_DATACENTER_SERVER
9,PRODUCT_SMALLBUSINESS_SERVER
10,PRODUCT_ENTERPRISE_SERVER
11,PRODUCT_STARTER
12,PRODUCT_DATACENTER_SERVER_CORE
13,PRODUCT_STANDARD_SERVER_CORE
14,PRODUCT_ENTERPRISE_SERVER_CORE
15,PRODUCT_ENTERPRISE_SERVER_IA64
16,PRODUCT_BUSINESS_N
17,PRODUCT_WEB_SERVER
18,PRODUCT_CLUSTER_SERVER
19,PRODUCT_HOME_SERVER
20,PRODUCT_STORAGE_EXPRESS_SERVER
21,PRODUCT_STORAGE_STANDARD_SERVER
22,PRODUCT_STORAGE_WORKGROUP_SERVER
23,PRODUCT_STORAGE_ENTERPRISE_SERVER
24,PRODUCT_SERVER_FOR_SMALLBUSINESS
25,PRODUCT_SMALLBUSINESS_SERVER_PREMIUM
26,PRODUCT_HOME_PREMIUM_N
27,PRODUCT_ENTERPRISE_N
28,PRODUCT_ULTIMATE_N
29,PRODUCT_WEB_SERVER_CORE
30,PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT
31,PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY
32,PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING
33,PRODUCT_SERVER_FOUNDATION
34,PRODUCT_HOME_PREMIUM_SERVER
35,PRODUCT_SERVER_FOR_SMALLBUSINESS_V
36,PRODUCT_STANDARD_SERVER_V
37,PRODUCT_DATACENTER_SERVER_V
38,PRODUCT_ENTERPRISE_SERVER_V
39,PRODUCT_DATACENTER_SERVER_CORE_V
40,PRODUCT_STANDARD_SERVER_CORE_V
41,PRODUCT_ENTERPRISE_SERVER_CORE_V
42,PRODUCT_HYPERV
43,PRODUCT_STORAGE_EXPRESS_SERVER_CORE
44,PRODUCT_STORAGE_STANDARD_SERVER_CORE
45,PRODUCT_STORAGE_WORKGROUP_SERVER_CORE
46,PRODUCT_STORAGE_ENTERPRISE_SERVER_CORE
47,PRODUCT_STARTER_N
48,PRODUCT_PROFESSIONAL
49,PRODUCT_PROFESSIONAL_N
50,PRODUCT_SB_SOLUTION_SERVER
51,PRODUCT_SERVER_FOR_SB_SOLUTIONS
52,PRODUCT_STANDARD_SERVER_SOLUTIONS
53,PRODUCT_STANDARD_SERVER_SOLUTIONS_CORE
54,PRODUCT_SB_SOLUTION_SERVER_EM
55,PRODUCT_SERVER_FOR_SB_SOLUTIONS_EM
56,PRODUCT_SOLUTION_EMBEDDEDSERVER
57,PRODUCT_SOLUTION_EMBEDDEDSERVER_CORE
58,PRODUCT_PROFESSIONAL_EMBEDDED
59,PRODUCT_ESSENTIALBUSINESS_SERVER_MGMT
60,PRODUCT_ESSENTIALBUSINESS_SERVER_ADDL
61,PRODUCT_ESSENTIALBUSINESS_SERVER_MGMTSVC
62,PRODUCT_ESSENTIALBUSINESS_SERVER_ADDLSVC
63,PRODUCT_SMALLBUSINESS_SERVER_PREMIUM_CORE
64,PRODUCT_CLUSTER_SERVER_V
65,PRODUCT_EMBEDDED
66,PRODUCT_STARTER_E
67,PRODUCT_HOME_BASIC_E
68,PRODUCT_HOME_PREMIUM_E
69,PRODUCT_PROFESSIONAL_E
70,PRODUCT_ENTERPRISE_E
71,PRODUCT_ULTIMATE_E
72,PRODUCT_ENTERPRISE_EVALUATION
76,PRODUCT_MULTIPOINT_STANDARD_SERVER
77,PRODUCT_MULTIPOINT_PREMIUM_SERVER
79,PRODUCT_STANDARD_EVALUATION_SERVER
80,PRODUCT_DATACENTER_EVALUATION_SERVER
84,PRODUCT_ENTERPRISE_N_EVALUATION
85,PRODUCT_EMBEDDED_AUTOMOTIVE
86,PRODUCT_EMBEDDED_INDUSTRY_A
87,PRODUCT_THINPC
88,PRODUCT_EMBEDDED_A
89,PRODUCT_EMBEDDED_INDUSTRY
90,PRODUCT_EMBEDDED_E
91,PRODUCT_EMBEDDED_INDUSTRY_E
92,PRODUCT_EMBEDDED_INDUSTRY_A_E
95,PRODUCT_STORAGE_WORKGROUP_EVALUATION_SERVE
96,PRODUCT_STORAGE_STANDARD_EVALUATION_SERVER
97,PRODUCT_CORE_ARM
98,PRODUCT_CORE_N
99,PRODUCT_CORE_COUNTRYSPECIFIC
100,PRODUCT_CORE_SINGLELANGUAGE
101,PRODUCT_CORE
103,PRODUCT_PROFESSIONAL_WMC
105,PRODUCT_EMBEDDED_INDUSTRY_EVAL
106,PRODUCT_EMBEDDED_INDUSTRY_E_EVAL
107,PRODUCT_EMBEDDED_EVAL
108,PRODUCT_EMBEDDED_E_EVAL
109,PRODUCT_NANO_SERVER
110,PRODUCT_CLOUD_STORAGE_SERVER
111,PRODUCT_CORE_CONNECTED
112,PRODUCT_PROFESSIONAL_STUDENT
113,PRODUCT_CORE_CONNECTED_N
114,PRODUCT_PROFESSIONAL_STUDENT_N
115,PRODUCT_CORE_CONNECTED_SINGLELANGUAGE
116,PRODUCT_CORE_CONNECTED_COUNTRYSPECIFIC
117,PRODUCT_CONNECTED_CAR
118,PRODUCT_INDUSTRY_HANDHELD
119,PRODUCT_PPI_PRO
120,PRODUCT_ARM64_SERVER
121,PRODUCT_EDUCATION
122,PRODUCT_EDUCATION_N
123,PRODUCT_IOTUAP
124,PRODUCT_CLOUD_HOST_INFRASTRUCTURE_SERVER
125,PRODUCT_ENTERPRISE_S
126,PRODUCT_ENTERPRISE_S_N
127,PRODUCT_PROFESSIONAL_S
128,PRODUCT_PROFESSIONAL_S_N
129,PRODUCT_ENTERPRISE_S_EVALUATION
130,PRODUCT_ENTERPRISE_S_N_EVALUATION
135,PRODUCT_HOLOGRAPHIC
138,PRODUCT_PRO_SINGLE_LANGUAGE
139,PRODUCT_PRO_CHINA
140,PRODUCT_ENTERPRISE_SUBSCRIPTION
141,PRODUCT_ENTERPRISE_SUBSCRIPTION_N
143,PRODUCT_DATACENTER_NANO_SERVER
144,PRODUCT_STANDARD_NANO_SERVER
145,PRODUCT_DATACENTER_A_SERVER_CORE
146,PRODUCT_STANDARD_A_SERVER_CORE
147,PRODUCT_DATACENTER_WS_SERVER_CORE
148,PRODUCT_STANDARD_WS_SERVER_CORE
149,PRODUCT_UTILITY_VM
159,PRODUCT_DATACENTER_EVALUATION_SERVER_CORE
160,PRODUCT_STANDARD_EVALUATION_SERVER_CORE
161,PRODUCT_PRO_WORKSTATION
162,PRODUCT_PRO_WORKSTATION_N
164,PRODUCT_PRO_FOR_EDUCATION
165,PRODUCT_PRO_FOR_EDUCATION_N
168,PRODUCT_AZURE_SERVER_CORE
169,PRODUCT_AZURE_NANO_SERVER
171,PRODUCT_ENTERPRISEG
172,PRODUCT_ENTERPRISEGN
175,PRODUCT_SERVERRDSH
178,PRODUCT_CLOUD
179,PRODUCT_CLOUDN
180,PRODUCT_HUBOS
182,PRODUCT_ONECOREUPDATEOS
183,PRODUCT_CLOUDE
184,PRODUCT_ANDROMEDA
185,PRODUCT_IOTOS
186,PRODUCT_CLOUDEN$"
WinTypes= MapCreate(Types,'',@lf)


strComputer = "."
oWMI = GetObject( "winmgmts:\\" : strComputer : "\root\cimv2")
WQL = 'SELECT * FROM Win32_OperatingSystem'
oOP = oWMI.ExecQuery(WQL)
ForEach o in oOP
   sku=o.OperatingSystemSKU
   If MapKeyExist(WinTypes, sku) Then Message("OperatingSystemSKU: ":sku,WinTypes[sku])
Next


oOP=0
oWMI=0


Exit



td

By definition, maps or associative arrays use text as the key to each key-value pair.  Since map keys are equivalent to index numbers in WIL arrays,  use a regular WIL array when you want to access values by an integer value.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Should add that you can always adopt a convention of some kind.  For example, you could decide that digit string keys with a leading zero are treated as strings and those without a leading zero are treated as integers.

Code (winbatch) Select
mExample = MapCreate() ; Since first key could be a number need to create first.
mExample['01'] = 'whatever'
mExample[1]    = BinaryAlloc(64)

aKeys = MapkeysGet(mExample, 2, @lf) ; Requires 2020B beta.

Message('Map Keys', aKeys)

mExample = 0 ; This will free the binary buffer for you.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Quote from: td on July 04, 2020, 09:40:09 AM
aKeys = MapkeysGet(mExample, 2, @lf) ; Requires 2020B beta.
Message('Map Keys', aKeys)
mExample = 0 ; This will free the binary buffer for you.



Initially when Maps were announced I assmed a function like MapFind() with a default not-found value rather than circumnavigating with MapKeyExist().  So, MapkeysGet()...  I will probably need a better example to prove Map efficacy over lookups I can already perform since it is a binary relations and alternatives offer more robust return values.

td

What connection are you asserting between MapKeyExist and the MapKeysGet function?  The MapKeysGet function has many uses but simply using it to check for the existence of a key in a map is probably not one of them.  The combination of MapKeyExist and accessing a value using the square bracket operator is more than efficient enough for most script usage.  If it isn't efficient enough for you then just use your "alternatives".  The idea of creating a MapFind function is one that has been considered and will likely appear in a future release.  Not for some efficiency reason but because it makes map usage more intuitive and is easy to implement using existing code.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Should also add that the one problem with a function like MapFind is the "default not-found value".  It more or less doesn't exist because a map value of a key-value pair can be anything valid in a variable value other than a map/array.  That means that any default return value cannot be guaranteed to be nonexistent in a key-value pair.  So you are left with the question on function return; does the return value really mean that the key does not exist? The less-than-elegant solution is to add an optional parameter to the function that allows the user to indicate an alternative default not-found value. Of course, this places the onus on the user to have awareness of the stored values.

WIL like most programming languages offers multiple collection data structures; arrays, item lists, and maps.  None of them should be considered the be-all and end-all collection.  Each has its best uses and the one you use should be based on requirements.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Here are the results of one of the bench tests we use to monitor the performance of various WIL functions.  The map test consists of a call to MapExist and then accessing the value using map[searchkey] syntax. The scripting dictionary uses first the "Exists" method and then the "Item" property. The array test calls the ArraySearch function on a presorted array and then access the array element using array[foundindex,valuecolumn]  syntax.  The time to sort the array is not included in the total time for the array result.  The  500 lookup keys are selected randomly from a CSV file containing the data used to load each collection.  The data set contains 2000 pairs and the data contained in the three collections are identical.

Keep in mind that these values are not averages but the total time taken by each method to perform 500 lookups and value fetches.

   Mon 7/6/2020 3:50:54 PM
   WinBatch: 32-bit
   Lookups: 500

   Map Total: 0.1011857 seconds
   Scripting Dictionary Total: 0.1222651 seconds
   Array Total: 0.148151 seconds

The test was run on Windows 10.  Times are better on Windows 7 for some yet unknown reason.  Running the test with 64-bit WinBatch improves performance by about 10%.  However, in all cases, WIL Maps produce the best times.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

It takes practice.  ??? .  Maps are very useful for enumerations and as you proved fast ++
Below is an example to enumerate Windows SuiteMask. Probably a more elegant way to work with the bit masks but map worked
Code (WINBATCH) Select


;Winbatch using Map to enumerate SuiteMask
gosub udfs


Masks = $"10,SmallBusinessServer
9,Server2008Enterprise
8,BackOfficeComponents
7,CommunicationsServer           
6,TerminalServices 
5,SmallBusinessServerRestricted
4,WindowsEmbedded             
3,DatacenterEdition
2,TerminalServicesSingleSession
1,HomeEdition$"
SuiteMask= MapCreate(Masks,'',@lf)


strComputer = "."
oWMI = GetObject( "winmgmts:\\" : strComputer : "\root\cimv2")
WQL = 'SELECT * FROM Win32_OperatingSystem'
oOP = oWMI.ExecQuery(WQL)
ForEach o in oOP
   cap=o.Caption
   sm = Dec2Binary(o.SuiteMask)
   SuiteMasks = ""
   For i = 1 to 10
      If StrSub(sm,i,1)=="1" & MapKeyExist(SuiteMask,i) Then SuiteMasks = SuiteMasks:SuiteMask[i]:@LF
   Next
   Message("OperatingSystem: ":cap,sm:@LF:SuiteMasks)
Next


oOP=0
oWMI=0


Exit


:udfs
#DefineFunction Dec2Binary(number)
   binarray = ArrDimension(10)
   ArrInitialize (binarray, "")
   count = 0
   While(number!=0) ; keep looping until we cannot do any more calculations
      binarray[count] = number mod 2
      number=number/2
      count=count+1
   EndWhile
   binary = ""
   size = ArrInfo(binarray, 1)-1
   For x = size To 0 By -1
      binary = binary : binarray[x]
   Next
   Return binary
#EndFunction


Return



td

Here's a not particularly sophisticated example of a roll-your-own MapFind function.  Of course, using a UDF sacrifices some performances.

Code (winbatch) Select
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  _MapFind
;;
;; Parameters: _Map - m:WIL Map
;;             _Key - s:Key to lookup
;;             -pValue - pointer to variable
;;                       that contains the
;;                       value associated with
;;                       the _key.
;;
;;  Return: 1 if the _key exists in the map;
;;             otherwise, 0.
;;             The variable pointed to by
;;             _pValue is unchanged when the
;;             UDF returns 0.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#DefineFunction _MapFind( _Map, _Key, _pValue)
   nErrMode=ErrorMode(@Off)
   *_pValue=_Map[_key]
   bRet = !LastError()
   ErrorMode(nErrMode)
   return bRet
#EndFunction

mCntrCodes = MapCreate($"Canada,CA
China,CN
Finland,FI
Germany,DE
Japan,JP
Luxembourg,LU
Norway,NO
United Kingdom of Great Britain and Northern Ireland,GB
United States of America,US
$", '',@lf)

strTest = ''
Code = ''
if _MapFind(mCntrCodes, 'germany', &Code) then strTest := 'Country: Germany Code: ':Code
else strTest := 'Country: Germany Code: ':'*not found*'
if _MapFind(mCntrCodes, 'France', &Code) then strTest := @lf:'Country: France Code: ':Code
else strTest := @lf:'Country: France Code: ':'*not found*'

Message('_MapFind Test', strTest)
 
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Since our function bench tests are easy to add functions to, here are the results for the _MapFind UDF compared to other results:

   WinBatch: 32-bit
   Lookups: 500

   Map Total: 0.1129097 seconds
   Scripting Dictionary Total: 0.122154 seconds
   Array Total: 0.1661312 seconds
   _MapFind Total: 0.170167 seconds
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

My curiosity with Maps involved creating .NET enum lookups for use with CLR scripts. I have attached part 1 [zipped .csv] of common assembly enums which should be easily parsed in multiple maps.

stanl

part 2: .NET enums

td

An impressive list of enumeration values.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Probably something I should know but if I parsed into individual udfs in a wbc file could each Map array be called from a UDF in the beginning of a script. 

JTaylor


td

While waiting for a family member I took a couple of minutes to write two quick and dirty scripts to test the concept out.

First step was to create a single cvs file that can be loaded directly into a map:
Code (winbatch) Select
#definefunction AddCLREnums(_menums, _file)
   aEnums = ArrayFileGetCSV(DirScript():_file, 0)
   nMax = Arrinfo(aEnums, 1)
   for i=0 to nMax - 1
      _menums[aEnums[i,0]:'.':aEnums[i,1]] = aEnums[i,2]
   next
   return 1
#EndFunction

mEnums = MapCreate()

AddCLREnums(mEnums, 'enums_1.csv')
AddCLREnums(mEnums, 'enums_2.csv')
MapFilePutCsv('CLREnums.csv', mEnums)
exit


Note: removed " if IsFloat(aEnums[i,2])" from original posted script.

A modified help file example to test the concept:
Code (winbatch) Select
;; Load the enum map.
mEnums = MapFileGetCsv(Dirscript():'CLREnums.csv')

ObjectClrOption("useany", "System")
ObjectClrOption("useany", "System.Net")
Uri = "https://www.winbatch.com/about.html" ; Your URL here.
objUri = ObjectClrNew('System.Uri', Uri)
Encoding = ObjectClrNew( 'System.Text.Encoding' )
WebRequest = ObjectClrNew('System.Net.WebRequest')
objSvcManager = ObjectClrNew('System.Net.ServicePointManager')
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Assuming your site uses TLS 1.2 which is the integer value 3072:
;; https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=netframework-4.7.2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Since SecurityProtocol is expecting an enumeration value,
;; use ObjectTypeGet to give the integer value an enumeration
;; type the property expects.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  Tls12

; Instead of this.
;objSvcManager.SecurityProtocol = ObjectClrType("System.Net.SecurityProtocolType",3072)

; Used this. .Net is type picky and the CSV file value is placed in the map as a string
; so 0 is added to convert the stored value to a number.  This could also be done when
; building the map but this approach is more efficient.
value = mEnums['System.Net.SecurityProtocolType.Tls12'] + 0
objSvcManager.SecurityProtocol = ObjectClrType("System.Net.SecurityProtocolType",value)

objSvcPoint = objSvcManager.FindServicePoint(objUri)
WebRequest = WebRequest.Create(Uri)
WebRequest.Timeout = WebRequest.Timeout * 6
WebResponse = WebRequest.GetResponse()
ResponseStream = WebResponse.GetResponseStream
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Pipes the stream to a higher level stream reader with
;; the required code point encoding.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
StreamReader = ObjectClrNew("System.IO.StreamReader", ResponseStream, Encoding.UTF8)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Display the site's source.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Message("Site HTML", StreamReader.ReadToEnd())
ResponseStream.Close()
StreamReader.Close()
Exit
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

I had to break the csv into 2 files due to upload limits here. Nice udf to create the large map. Had to think a bit about the + 0 in the second script. I'm used to * 1 from converting Excel data.


[EDIT]; your if needs an endif or a Then in udf

td

Quote from: stanl on July 10, 2020, 08:31:36 AM
I had to break the csv into 2 files due to upload limits here. Nice udf to create the large map. Had to think a bit about the + 0 in the second script. I'm used to * 1 from converting Excel data.

+0 is slightly more efficient in the WinBatch world.

Quote
[EDIT]; your if needs an endif or a Then in udf

Yup. It was a quick and dirty...

[Edit] Actually, the if statement isn't needed if the files are correct.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

If you don't mind the overhead, a UDF would clean things up a bit.

Code (winbatch) Select
#definefunction GetdotNetEnumVal(_menums, _strenum, _strvalue)
   nValue = _menums[_strenum:'.':_strvalue] + 0
   return ObjectClrType(_strenum, nValue)
#EndFunction
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Worth asking, but
System.Diagnostics.Eventing.Reader.StandardEventKeywords.WdiContext,562949953421312


is a huge-math kinda number. Would that need to be transformed to a .NET type if used in script - if ever.

td

Hadn't taken the time to look closely at the data but that brings up an interesting point.  The best way to handle 64-bit integers for dotNet usage would be to use ObjectType to make the value an i8 variant.  Of course, you can't store i8 variant in a csv file because the files only store data in text format with the type-specific information being lost.  You can store i8 variants or any other valid WIL variable type except an array in a map value.  But in order to build the map with variant values, you would have to spin throw ~17000 rows of the array created with the csv file in a for loop.  Obviously, that is a slow process.

Another approach would be to detect the 64-bit values using a lookup UDF. 

The simplest approach would be to change the value after you load the map.
Code (winbatch) Select
;; Load the enum map.
; Untested!
mEnums = MapFileGetCsv(Dirscript():'CLREnums.csv')
strKey = 'System.Diagnostics.Eventing.Reader.StandardEventKeywords.WdiContext'
mEnums[strKey ] = ObjectType('i8', mEnums[strKey ])
 
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

I was thinking of adding some strlen() logic to your #definefunction GetdotNetEnumVal[_menums, _strenum, _strvalue) where if the length exceeded a specific value the appropriate objecttype could be returned, assuming it would also apply to large negative values.

[EDIT] -  distinct lengths

1
2
3
4
5
6
7
8
9
10
11
15
16
17
19

td

I had given that some thought too.  Of course, the problem is that two numbers can have the same length in digits with one being a 32-bit integer and the other a 64-bit integer.  It is possible to always distinguish between strings that represent 32-bit and 64-bit integers but as you add more logic to the "find" function the more you slow it down. That said, taking a less resource-intensive, heuristic approach like string length might be good enough.

This touches on a WIL language enhancement that has been under consideration for a long time.  Native support for 64-bit integers. It is surprisingly complicated to implement, however,
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

ChuckC

https://docs.microsoft.com/en-us/dotnet/api/system.enum.getunderlyingtype?view=netframework-4.8

System.Enum.GetUnderlyingType() is what would be most appropriate to make use of.  Given that enumerated types in .Net can have specific numeric types other than the default "int" [System.Int32], it is the best way to query the particular enumerated type in question and know what the integer size & [un]signed characteristics are for the numeric values that you're going to obtain for each value in the enumeration.

td

In the interest of performance integers at lease can be check for conversion to I8 by doing this:

Code (winbatch) Select
strNum = "2147483648"
bIs64Bit = StrCmp(strNum, strNum+0)


WinBatch represents dotNet type as COM variants so conversion to a variant is sufficient.  It would take some additional checking to avoid messing with floating-point numbers.  That said, using dotNet type information is a safer if a possibly slower option.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade