VBS running code from string - vbscript

I have one string storing hexadecimal data (\xEA\x...). Is it anyway to run that code using vbs? Maybe doing some kind of casting to function pointer or similar.
The C version of what I'm trying to do would be:
unsigned char opcode[] = "\xc0\x...."
main()
{
int (*run)() = (int(*)())opcode;
run();
}
Thank you so much.

You can use function pointers (or function references) with the GetRef function:
dim fp : set fp = GetRef("ShowMessage")
call fp("Woosh")
function ShowMessage(msg)
msgbox msg
end function
To make this work for any string with normally illegal characters for function naming (like the backslash in hexadecimal data) you can use brackets in you function declaration:
dim fp : set fp = GetRef("99 problems")
call fp()
' note: functions normally cannot start with a digit or contain spaces
function [99 problems]()
msgbox "but this aint one"
end function
The only character you cannot use is a closing bracket: ]

Related

Decimal to hexadecimal conversion using CAPL

Is there any CAPL function for converting decimal value into hexadecimal value? I have already looked in help option of CAPL browser.
Assuming that you want the number to be converted to a string, that you can print out. Either to the write window, into the testreport, etc.
You can use snprintf like this:
snprintf(buffer,elcount(buffer),"%x",integervariable);
where buffer is a char array big enough.
This example is taken from the Vector knowledge base and was among the first result on google.
For hexadecimal equivalent value:
You can make use of _pow function (returns x to the power of y) and while in the following way, which would return you the hexadecimal equivalent value:
double decToHexEquivalent(int n)
{
int counter,remainder,decimal_number,hexadecimal_number = 0;
while(n!=0)
{
remainder = decimal_number % 16;
hexadecimal_number = hexadecimal_number + remainder * _pow(10, counter);
n=n/16;
++counter;
}
return hexadecimal_number;
}
you can call the above function in the following way:
testfunction xyz(int n)
{
write("Hexadecimal:%d", decToHexa(n));
}
Caution: not tested
For hexadecimal value
declare a global variable char buffer[100] in the variable section
variables
{
char buffer[100];
}
and then using snprintf function you can convert an integer variable to a character array, like this:
void dectohexValue(int decimal_number)
{
snprintf(buffer,elcount(buffer),"%02X",decimal_number);
}
then finally you can use the function as follows:
testfunction xyz(int n)
{
dectohexValue(n);
write("Hexadecimal:%s", buffer);
}

Why is the result of VarPtr(ByVal str) the same as StrPtr(str) ? (VB6)

In VB6 VarPtr should return the address of the variable, in this case the address of the variable str which is allocated on stack, and holds a pointer to a string in memory. StrPtr (or StrPtr) should return the address of the allocated string in the memory. ByVal should just create a copy, but in this case, it works strangely:
Dim str As String
str = "asd"
Debug.Print VarPtr(str)
Debug.Print VarPtr(ByVal str)
Debug.Print StrPtr(str)
The result is:
1636452
110882980
110882980
Why is the result of VarPtr(ByVal str) the same as StrPtr(str) ?
Strings passed ByVal pass the address of the first character of the containing C string in the BStr. StrPtr does the same.
There are two reasons that spring to mind for doing this. Passing Unicode to API calls and string building.
Passing Unicode to API calls
You could use StrPtr on a string rather than a byte array when sending Unicode strings to API functions.
Dim ByteArr() as Byte
Var1="My Text"
ByteArr = Var1
APICall(ByteArr(0))
APICall(StrPtr(Var1))
Should both pass a Unicode string to an API functions. Unicode strings are converted to ANSI strings when using the declare statement as Win 95 didn't do unicode.
String Building
On the other hand if you are string building, then that is built in to VBA using the Left, Right, and Mid statements, not functions (they are overloaded).
Sub Main()
Dim Var As String
Var = "gggggggggggg"
MsgBox StrPtr(Var)
Mid(Var, 1, 2) = "xx"
MsgBox StrPtr(Var) & " - " & Var
End Sub
ByVal Versus ByRef
Some authors like to say that the ByVal keyword is overloaded for
strings, meaning that it takes on a different meaning when applied to
strings than when applied to other variables. Frankly, I don't see it.
Writing:
ByVal str As String
tells VB to pass the contents of the BSTR (actually the ABSTR), which is the pointer to the character array. Thus, ByVal is acting
normally--it just happens that the content of the BSTR is a pointer to
another object, so this simulates a pass by reference. Similarly:
ByRef str As String
passes the address of the BSTR, as expected.
Win32 API Programming with Visual Basic, Chapter 6 Strings, O'Reilly, from MSDN Library October 2001
StrPtr
Strings in Visual Basic are stored as BSTR's. If you use the VarPtr on
a variable of type String, you will get the address of the BSTR, which
is a pointer to a pointer of the string. To get the address of the
string buffer itself, you need to use the StrPtr function. This
function returns the address of the first character of the string.
Take into account that Strings are stored as UNICODE in Visual Basic.
To get the address of the first character of a String, pass the String
variable to the StrPtr function.
Example:
Dim lngCharAddress as Long
Dim strMyVariable as String
strMyVariable = "Some String"
lngCharAddress = StrPtr(strMyVariable)
You can use this function when you need to pass a pointer to a
UNIOCODE string to an API call.
HOWTO: Get the Address of Variables in Visual Basic Q199824 Microsoft Knowledge Base, MSDN October 2001.
VarPtr is not part of the VBA/VB6 language, therefore companies that implement VBA (like Corel) may not implement it in their VBA. The VBA spec is here https://msdn.microsoft.com/en-us/library/dd361851.aspx

how do you parse a string in vb6?

Some of us unfortunately are still supporting legacy app like VB6. I have forgotten how to parse a string.
Given a string:
Dim mystring As String = "1234567890"
How do you loop in VB6 through each character and do something like
for each character in mystring
debug.print character
next
In C# i would do something like
char[] myChars = mystring.ToCharArray();
foreach (char c in theChars)
{
//do something with c
}
Any ideas?
Thanks a lot
You can use the 'Mid' function to get at the individual characters:
Dim i As Integer
For i = 1 To Len(mystring)
Print Mid$(mystring, i, 1)
Next
Note this is untested.
There is no possibility to use foreach on strings.
Use
Dim i As Integer
For i = 1 To Len(YourString)
Result = Mid$(YourString, i, 1)
Next
note that the type of Result is a length-1 string, no char or byte type.
If performance is important, you'll have to convert the string to a bytearray fist (using StrConv) and then loop through it like this.
Dim i As Long
For i = 0 To UBound(Data)
Result = Data(i) ' Type is Byte '
Next
This is much more efficient.
The easiest way is to convert the string into an array of bytes and iterate over the byte array (converting each byte to a character).
Dim str As String
Dim bytArray() As Byte
Dim count As Integer
str = "This is a string."
bytArray = str
For count = 0 To UBound(bytArray)
Debug.Print Chr(bytArray(count))
Next
Don't loop; rather, set a reference to Microsoft VBScript Regular Expressions library and use regular expressions to achieve your 'do something' goal.

VB6 Can IsNumeric be wrong?

Is it possible to test a string with IsNumeric() and for it to return true, but when you cast that same string to an integer using CInt() and assign it to a variable of type integer that it will give a type mismatch error?
I ask because I was getting a type mismatch error, so I used IsNumeric() to check the string was numeric before trying to cast it, but I still get the error.
I am tearing my hair out with this.
Here is the code in question.
iGlobalMaxAlternatives = CInt(strMaxAlternatives) is where the error is occuring.
Dim strMaxAlternatives As String
Dim iGlobalMaxAlternatives As Integer
iGlobalMaxAlternatives = 0
bSurchargeIncInFare = True
strMaxAlternatives = ReadStringKeyFromRegistry("Software\TL\Connection Strings\" & sConn & "\HH", "MaxAlt")
If IsNumeric(strMaxAlternatives) And strMaxAlternatives <> "" Then
iGlobalMaxAlternatives = CInt(strMaxAlternatives)
End If
You may have an overflow due the maximum integer size; the currency type actually does very well for large numbers (but beware of any regional issues). See edits below for Int64 discussion.
According to MSDN documentation on IsNumeric:
IsNumeric returns True if the data
type of Expression is Boolean, Byte,
Decimal, Double, Integer, Long,
SByte, Short, Single, UInteger,
ULong, or UShort, or an Object that
contains one of those numeric types.
It also returns True if Expression is
a Char or String that can be
successfully converted to a number.
IsNumeric returns False if Expression
is of data type Date or of data type
Object and it does not contain a
numeric type. IsNumeric returns False
if Expression is a Char or String
that cannot be converted to a number.
Since you are getting a Type Mismatch, perhaps a Double is interfering with the conversion. The IsNumeric does not guarantee it is an Integer, just that it matches one of the numeric possibilities. If the number is a double, perhaps regional settings (comma versus period and so on) are causing the exception.
You might try converting it to a double and then to an integer.
' Using a couple of steps
Dim iValue As Integer
Dim dValue As Double
dValue = CDbl(SourceValue)
iValue = CInt(iValue)
' Or in one step (might make debugging harder)
iValue = CInt(CDbl(SourceValue))
EDIT: After your clarification, it appears you are getting an overflow conversion. First try using a Long and CLng() instead of CInt(). There is still a chance the entry is Int64 though, which is more difficult using VB6.
I have used the following code for the LARGE_INTEGER and Integer8 types (both Int64), but it may not work for your situation:
testValue = CCur((inputValue.HighPart * 2 ^ 32) + _
inputValue.LowPart) / CCur(-864000000000)
This example was from an LDAP password expiration example, but like I said it may or may not work in your scenario. If you don't have the LARGE_INTEGER type, it looks like:
Private Type LARGE_INTEGER
LowPart As Long
HighPart As Long
End Type
Search for LARGE_INTEGER and VB6 for more information.
EDIT: For debugging, it may be useful to temporarily avoid error handling and then turn it back on after passing the troubling lines:
If IsNumeric(strMaxAlternatives) And strMaxAlternatives <> "" Then
On Error Resume Next
iGlobalMaxAlternatives = CInt(strMaxAlternatives)
If Err.Number <> 0 Then
Debug.Print "Conversion Error: " & strMaxAlternatives & _
" - " & Err.Description
EndIf
On Error Goto YourPreviousErrorHandler
End If
Yes, "3.41" would be numeric but not an integer.
VB6 doesn't provide good methods to guarantee CInt won't fail. I've found the simplest way is to just use error-handling.
function TryParse(byval text as string, byref value as integer) as boolean
on error resume next
value = CInt(text)
TryParse = (err.number = 0)
endfunction
Of course your error-handling preferences may vary.
Yes. Try this:
If IsNumeric("65537") Then
Dim i As Integer
i = CInt("65537") 'throws an error on this line!
End If
This one's an overflow, but I think it illustrates the unreliability of IsNumeric() in general (especially for ints - for doubles it's much more reliable).
According to the VB6 documentation, "IsNumeric returns True if the data type of Expression is Boolean, Byte, Decimal, Double, Integer, Long, SByte, Short, Single, UInteger, ULong, or UShort, or an Object that contains one of those numeric types. It also returns True if Expression is a Char or String that can be successfully converted to a number."
Many of those cannot be converted to an Integer. For example "1.5" is numeric but it's not an integer. So, you can convert it to a number, but not necessarily an integer.
The following code works without a Type Mismatch error in Visual BASIC 6
Dim I As Integer
I = CInt("3.41")
The same for this variant
Dim I As Integer
Dim TempS As String
TempS = "3.41"
I = CInt(TempS)
Posting the code in question would help answer your question. Basically there are several function in VB6 that are used to convert strings into number.
CInt and Int convert into number but handle rounding different. Direct assignment works and equivalent to using CInt. Howver I recommend continuing to use CInt to make the operation clear to you and your fellow developers in the future.
CInt works on number with commas like "3,041.41" However VB6 has problem handling region settings so if you are using notation other than standard American English you will get strange results and errors.
Your best bet is to start logging the errors with the actual values it's working with so you can figure out whats going on.
IsNumeric will return true if it can convert the string to a number. Even if there are non-numeric characters in the string. I always loop though the string one character at a time and test each character. If one character fails then I can return an error.
Just found this nugget. If you run the following, script #1 returns TRUE but script #2 & #3 will fail:
SELECT ISNUMERIC('98,0') AS isNum -- Fails
SELECT CONVERT(INT, '98,0') -- Fails
SELECT CONVERT(NUMERIC(11,4), '98,0') -- Fails
Two options...
Change
If IsNumeric(strMaxAlternatives) And strMaxAlternatives <> "" Then
iGlobalMaxAlternatives = CInt(strMaxAlternatives)
End If
To
If IsNumeric(strMaxAlternatives) And strMaxAlternatives <> "" Then
iGlobalMaxAlternatives = CDbl(strMaxAlternatives) ' Cast to double instead'
End If
Or
If IsNumeric(strMaxAlternatives) And strMaxAlternatives <> "" Then
If CDbl(strMaxAlternatives) Mod 1 = 0 Then ' Make sure there\'s no decimal points'
iGlobalMaxAlternatives = CInt(strMaxAlternatives)
End If
End If

What's the best way to determine if a character is a letter in VB6?

Need a function that takes a character as a parameter and returns true if it is a letter.
This was part of the code posted by rpetrich in response to a question by Joel Spolsky. I felt it needed a post specific to the problem it solves. It really is brilliant.
Private Function IsLetter(ByVal character As String) As Boolean
IsLetter = UCase$(character) <> LCase$(character)
End Function
You may be thinking to yourself, "Will this always work?" The documentation on the UCase and LCase functions, confirms that it will:
UCase Function Only lowercase letters are converted to uppercase;
all uppercase letters and nonletter characters remain unchanged.
LCase Function Only uppercase letters are converted to lowercase;
all lowercase letters and nonletter characters remain unchanged.
Seanyboy's IsCharAlphaA answer is close. The best method is to use the W version like so:
Private Declare Function IsCharAlphaW Lib "user32" (ByVal cChar As Integer) As Long
Public Property Get IsLetter(character As String) As Boolean
IsLetter = IsCharAlphaW(AscW(character))
End Property
Of course, this all rarely matters as all of VB6's controls are ANSI only
Private Function IsLetter(Char As String) As Boolean
IsLetter = UCase(Char) Like "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]"
End Function
What's wrong with the following, which doesn't rely on obscure language behaviour?
Private Function IsLetter(ByVal ch As String) As Boolean
IsLetter = (ch >= "A" and ch <= "Z") or (ch >= "a" and ch <= "z")
End Function
I believe we can improve upon this a little more. rpetrich's code will work, but perhaps only by luck. The API call's parameter should be a TCHAR (WCHAR here actually) and not a Long. This also means no fiddling with converting to a Long or masking with &HFFFF. This by the way is Integer and adds an implicit conversion to Long here too. Perhaps he meant &HFFFF& in this case?
On top of that it might be best to explictly call the UnicoWS wrapper for this API call, for Win9X compatibility. The UnicoWS.dll may need to be deployed but at least we gain that option. Then again maybe from VB6 this is automagically redirected, I don't have Win9X installed to test it.
Option Explicit
Private Declare Function IsCharAlphaW Lib "unicows" (ByVal WChar As Integer) As Long
Private Function IsLetter(Character As String) As Boolean
IsLetter = IsCharAlphaW(AscW(Character))
End Function
Private Sub Main()
MsgBox IsLetter("^")
MsgBox IsLetter("A")
MsgBox IsLetter(ChrW$(&H34F))
MsgBox IsLetter(ChrW$(&HFEF0))
MsgBox IsLetter(ChrW$(&HFEFC))
End Sub
Looking around a bit came up with the following...
Private Declare Function IsCharAlphaA Lib "user32" Alias "IsCharAlphaA" (ByVal cChar As Byte) As Long
I believe IsCharAlphaA tests ANSI character sets and IsCharAlpha tests ASCII. I may be wrong.
Private Function IsAlpha(ByVal vChar As String) As Boolean
Const letters$ = "abcdefghijklmnopqrstuvwxyz"
If InStr(1, letters, LCase$(vChar)) > 0 Then IsAlpha = True
End Function
I use this in VBA
Function IsLettersOnly(Value As String) As Boolean
IsLettersOnly = Len(Value) > 0 And Not UCase(Value) Like "*[!A-Z]*"
End Function
It doesn't exactly document itself. And it may be slow. It's a clever hack, but that's all it is. I'd be tempted to be more obvious in my checking. Either use regex's or write a more obvious test.
public bool IsAlpha(String strToCheck)
{
Regex objAlphaPattern=new Regex("[^a-zA-Z]");
return !objAlphaPattern.IsMatch(strToCheck);
}
public bool IsCharAlpha(char chToCheck)
{
return ((chToCheck=>'a') and (chToCheck<='z')) or ((chToCheck=>'A') and (chToCheck<='Z'))
}

Resources