VB declare function does not generate getlasterror code? (Hi,the Trick!)
Hi,the Trick:
I read your VBCDeclFix project description, very clever.
Then I recognize that the vb TypeLib import api can be annotated to disable getlasterror. Is it possible to go through something similar to VBCDeclFix so that the VB declare function doesn't generate getlasterror code?
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
...why? Isn't all it's doing filling in Err.LastDllError?
ps- How am I *not* supposed to mention tB supports both CDecl and the [UseGetLastError(True/False)] attribute for locally declared API calls when someone asks exactly for one of the language features tB adds that VB lacks
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by fafalone
...why?
Usually API error is not needed.
It forces vb to produce larger code and the overhead of two function calls, and is poorly suited for small utility functions that are called frequently!
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Use a typelib. You can use that attribute and when in a typelib, an API is placed in the IAT, rather than called dynamically. Only a noticeable difference on the first call, but you're chasing microseconds and a few bytes anyway.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by fafalone
Use a typelib. You can use that attribute and when in a typelib, an API is placed in the IAT, rather than called dynamically. Only a noticeable difference on the first call, but you're chasing microseconds and a few bytes anyway.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Code:
Option Explicit
Private Enum PTR
[_]
End Enum
Private Const PAGE_EXECUTE_READWRITE As Long = &H40&
Private Declare Function GetModuleHandle Lib "kernel32" _
Alias "GetModuleHandleW" ( _
ByVal lpModuleName As PTR) As PTR
Private Declare Function VirtualProtect Lib "kernel32" ( _
ByVal lpAddress As Long, _
ByVal dwSize As Long, _
ByVal flNewProtect As Long, _
ByRef lpflOldProtect As Long) As Long
Private Declare Sub GetMem4 Lib "msvbvm60" ( _
ByRef pAddr As Any, _
ByRef pDst As Any)
Private Declare Sub GetMemPtr Lib "msvbvm60" _
Alias "GetMem4" ( _
ByRef pAddr As Any, _
ByRef pDst As Any)
Private Declare Sub GetMem8 Lib "msvbvm60" ( _
ByRef pAddr As Any, _
ByRef pDst As Any)
Private Declare Sub PutMemPtr Lib "msvbvm60" _
Alias "PutMem4" ( _
ByRef pDst As Any, _
ByVal pVal As PTR)
Private Declare Sub PutMem2 Lib "msvbvm60" ( _
ByRef pDst As Any, _
ByVal iVal As Integer)
Private Function RemoveSystemError() As Boolean
Dim hVB6 As PTR
Dim pNTHdr As PTR
Dim pStart As PTR
Dim pEnd As PTR
Dim cSign As Currency
Dim lLength As Long
Dim lProt As Long
hVB6 = GetModuleHandle(StrPtr("vba6.dll"))
If hVB6 = 0 Then Exit Function
GetMem4 ByVal hVB6 + &H3C, pNTHdr
pNTHdr = pNTHdr + hVB6
GetMem4 ByVal pNTHdr + &H104, pStart
pStart = pStart + hVB6
GetMem4 ByVal pNTHdr + &H100, lLength
pEnd = pStart + lLength - 8
Do While pStart <= pEnd
GetMem8 ByVal pStart, cSign
If cSign = -356375250902713.1008@ Then
If VirtualProtect(pStart + &H10, 2, PAGE_EXECUTE_READWRITE, lProt) Then
PutMem2 ByVal pStart + &H10, &H9090
VirtualProtect pStart + &H10, 2, lProt, lProt
RemoveSystemError = True
End If
Exit Do
End If
pStart = pStart + 1
Loop
End Function
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Hi Trick.
Thank you very much for your fantastic reply and the well thought out code.
It is really good news that removing SetLastError is possible.
I have a couple more questions for you:
1, I can't find the VBA6.pdb symbol file, can you provide it to me?
2, We would like to enhance VB6 like vbcdeclfix instead of changing VB6.The ideal way would be to introduce a NoLastErr keyword in the declare statement, which controls whether each individual api generates SetLastError code. This ensures compatibility with the original VB compiler. If you add this feature to your vbcdeclfix plug-in is perfect!
3, What is the principle of this patch , when compiled to Pcode and Native Code exe is still valid?
Last edited by TomCatChina; May 4th, 2024 at 03:58 PM.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by VanGoghGaming
Is this better than overwriting the entry point for __vbaSetSystemError with an empty function from a BAS module?
It still forces vb to produce larger code and the overhead of two function calls, and is poorly suited for small utility functions that are called frequently! Also breaks compatibility with the original VB6.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by TomCatChina
What is the principle of this patch , when compiled to Pcode and Native Code exe is still valid?
It seems this patch only works in the IDE because "vba6.dll" is not used in executables. Here is a workaround for executables:
Code:
Dim lpProcAddress As Long, lOpCodeRet As Long
lOpCodeRet = &HC3 ' RET
lpProcAddress = GetProcAddress(GetModuleHandle("msvbvm60.dll"), "__vbaSetSystemError")
WriteProcessMemory -1, lpProcAddress, VarPtr(lOpCodeRet), 1, 0
DeleteFileW StrPtr(App.Path & "\abc.xyz") ' Try to delete a non-existent file to generate error 2 (File not found)
MsgBox Err.LastDllError
This code writes a single "RET" instruction at the address of "__vbaSetSystemError" so it effectively prevents the function from being executed and returns immediately.
The message box will print "2" in the IDE and "0" when compiled to EXE.
However there doesn't seem to be any speed gain by preventing "__vbaSetSystemError" from being executed...
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Did not achieve the desired effect, do I need to add permissions?
VirtualProtect ?
Private Declare Function VirtualProtect Lib "kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
now it's ok
Code:
Option Explicit
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function KillFile Lib "kernel32" Alias "DeleteFileW" (ByVal lpszFileName As Long) As Long
Private Declare Function VirtualProtect Lib "kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long
Private Const PAGE_EXECUTE_READWRITE = &H40
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Sub Command3_Click()
Dim lpProcAddress As Long, lOpCodeRet As Byte
lOpCodeRet = &HC3 ' RET
lpProcAddress = GetProcAddress(GetModuleHandle("msvbvm60.dll"), "__vbaSetSystemError")
WriteProcessMemory -1, ByVal lpProcAddress, lOpCodeRet, 1, 0 'good
KillFile StrPtr(App.Path & "\abc.xyz") ' Try to delete a non-existent file to generate error 2 (File not found)
MsgBox Err.LastDllError
End Sub
Private Sub Command1_Click()
KillFile StrPtr(App.Path & "\abc.xyz") ' Try to delete a non-existent file to generate error 2 (File not found)
MsgBox Err.LastDllError
End Sub
Private Sub Command2_Click()
Dim lpProcAddress As Long, lOpCodeRet As Byte
lOpCodeRet = &HC3 ' RET
lpProcAddress = GetProcAddress(GetModuleHandle("msvbvm60.dll"), "__vbaSetSystemError")
Dim OldProtect As Long
VirtualProtect ByVal lpProcAddress, 6, PAGE_EXECUTE_READWRITE, OldProtect '????????????
CopyMemory ByVal lpProcAddress, lOpCodeRet, 1 'good
'
VirtualProtect ByVal lpProcAddress, 6, OldProtect, OldProtect '????????????
KillFile StrPtr(App.Path & "\abc.xyz") ' Try to delete a non-existent file to generate error 2 (File not found)
MsgBox Err.LastDllError
End Sub
or use this:
Code:
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Private Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Public Sub KillvbaSetSystemError()
'msvbvm60.__vbaSetSystemError
Dim Code As Long
Dim DllFunAddr As Long
Dim hModule As Long, dwJmpAddr As Long
hModule = LoadLibrary("msvbvm60")
If hModule = 0 Then
Exit Sub
End If
DllFunAddr = GetProcAddress(hModule, "__vbaSetSystemError")
If DllFunAddr = 0 Then
Exit Sub
End If
Code = &HC3C3C3C3
WriteProcessMemory -1, ByVal DllFunAddr, Code, 4, ByVal 0
End Sub
Last edited by xiaoyao; May 5th, 2024 at 08:22 PM.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
"WriteProcessMemory" already has full "Write" privileges over the entire memory area of the process so there is no need for "VirtualProtect" as far I as know.
However, as I said before, a speed measurement didn't show any improvements when suppressing "__vbaSetSystemError" over 10000 deletion attempts so I don't see any benefits.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by TomCatChina
1, I can't find the VBA6.pdb symbol file, can you provide it to me?
There are no public symbols for vba6. I used vba5 symbols and manual analysis to match with vba6.
Originally Posted by TomCatChina
2, We would like to enhance VB6 like vbcdeclfix instead of changing VB6.The ideal way would be to introduce a NoLastErr keyword in the declare statement, which controls whether each individual api generates SetLastError code. This ensures compatibility with the original VB compiler. If you add this feature to your vbcdeclfix plug-in is perfect!
I have no free time to do this now. Each function / method has the attributes we can use it.
Originally Posted by TomCatChina
3, What is the principle of this patch , when compiled to Pcode and Native Code exe is still valid?
I didn't check but i think the patch should work both native and p-code form.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by VanGoghGaming
It seems this patch only works in the IDE because "vba6.dll" is not used in executables.
The patch affects to code-generation so you don't need it for an end-executable.
Originally Posted by VanGoghGaming
Here is a workaround for executables:
Code:
Dim lpProcAddress As Long, lOpCodeRet As Long
lOpCodeRet = &HC3 ' RET
lpProcAddress = GetProcAddress(GetModuleHandle("msvbvm60.dll"), "__vbaSetSystemError")
WriteProcessMemory -1, lpProcAddress, VarPtr(lOpCodeRet), 1, 0
DeleteFileW StrPtr(App.Path & "\abc.xyz") ' Try to delete a non-existent file to generate error 2 (File not found)
MsgBox Err.LastDllError
This code writes a single "RET" instruction at the address of "__vbaSetSystemError" so it effectively prevents the function from being executed and returns immediately.
The message box will print "2" in the IDE and "0" when compiled to EXE.
However there doesn't seem to be any speed gain by preventing "__vbaSetSystemError" from being executed...
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by The trick
The patch affects to code-generation so you don't need it for an end-executable.
Ah I understand now, so your function needs to be executed first and then compile the project to EXE from the IDE. It won't work when compiled from the command line...
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by The trick
I have no free time to do this now. Each function / method has the attributes we can use it.
The good news is that it works in principle, unfortunately you don't have the time and I don't have the ability! I think with vbcdeclfix you have done most of the research, the remaining work should be minimal, hopefully when you have time you can add this feature to the vbcdeclfix add-in.
I hope getting this function , is meaningful.
By hooking DllFunctionCall, I can bind a function symbol to a memory address with a declare statement.
This is the equivalent of providing VB6 with an elegant way to call function pointers with support for the "as any" parameter, as officially supported by VB6, and the code will be easy to understand and syntactic!
Combined with addressof, this gives VB6 full function pointer support!
Selectively removing SetLastError would eliminate the efficiency loss and could be the preferred option for VB6 calls to arbitrary memory addresses!
For now I'll use your hard patch and then manually call __vbaSetSystemError when LastDllError is needed, but of course your final solution will be the best!
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by The trick
There are no public symbols for vba6. I used vba5 symbols and manual analysis to match with vba6.
There's a vba6.dbg floating around; hex viewer shows vba6 functions; no good?
(As far as know .dbg files are safe and not executable, and there's no PE header at the start of this file, but it's a random internet file I found so due caution if there's any way it could be dangerous)
If this file is of interest the same folder also had a msvbvm60.dbg and vb6.dbg; I do know there's a msvbvm60.pdb out there that has version-not-lining-up issues.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by TomCatChina
The good news is that it works in principle, unfortunately you don't have the time and I don't have the ability! I think with vbcdeclfix you have done most of the research, the remaining work should be minimal, hopefully when you have time you can add this feature to the vbcdeclfix add-in.
I hope getting this function , is meaningful.
By hooking DllFunctionCall, I can bind a function symbol to a memory address with a declare statement.
This is the equivalent of providing VB6 with an elegant way to call function pointers with support for the "as any" parameter, as officially supported by VB6, and the code will be easy to understand and syntactic!
Combined with addressof, this gives VB6 full function pointer support!
Selectively removing SetLastError would eliminate the efficiency loss and could be the preferred option for VB6 calls to arbitrary memory addresses!
For now I'll use your hard patch and then manually call __vbaSetSystemError when LastDllError is needed, but of course your final solution will be the best!
Thanks for taking the time to discuss!
BTW there is MagicPointers by firehacker which works as you described.
Originally Posted by fafalone
There's a vba6.dbg floating around; hex viewer shows vba6 functions; no good?
(As far as know .dbg files are safe and not executable, and there's no PE header at the start of this file, but it's a random internet file I found so due caution if there's any way it could be dangerous)
If this file is of interest the same folder also had a msvbvm60.dbg and vb6.dbg; I do know there's a msvbvm60.pdb out there that has version-not-lining-up issues.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by TomCatChina
By hooking DllFunctionCall, I can bind a function symbol to a memory address with a declare statement.
This is the equivalent of providing VB6 with an elegant way to call function pointers with support for the "as any" parameter, as officially supported by VB6, and the code will be easy to understand and syntactic!
Combined with addressof, this gives VB6 full function pointer support!
Thanks for taking the time to discuss!
DllFunctionCall can bind to model1.bas function,address of "sum()"
You can also apply for a memory space, write assembly code to directly execute the cdecl API, and then clean up the parameter stack.
This results in almost no invocation overhead.
In this way, you can package multiple DLLs into one exe. hook api,Then change the address to the DLL API address in the resource
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by xiaoyao
DllFunctionCall can bind to model1.bas function,address of "sum()"
You can also apply for a memory space, write assembly code to directly execute the cdecl API, and then clean up the parameter stack.
This results in almost no invocation overhead.
In this way, you can package multiple DLLs into one exe. hook api,Then change the address to the DLL API address in the resource
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by VanGoghGaming
This sounds very interesting. Is this better than just using an empty function template in a BAS module, because of the "As Any" parameter type?
This kind of function pointer conforms to VB6 syntax, allows you to write clear source code, and does not require each function to be patched; after all, we need code that is easy to maintain, not voodoo magic.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Trick,
In IDE, VBA6.dll doesn't output __vbaSetSystemError function, but it implements something like __vbaSetSystemError with p-code, so I can't call __vbaSetSystemError manually in IDE, do you have any suggestion?
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by TomCatChina
This kind of function pointer conforms to VB6 syntax, allows you to write clear source code, and does not require each function to be patched; after all, we need code that is easy to maintain, not voodoo magic.
I dunno man, personally I see only advantages in using a "BAS function" vs a "Declare statement". The BAS function doesn't call "vbaSetSystemError" and can hook itself on the first run so it has just a little more code than writing a Declare statement. Another difference would be the lack of "As Any" parameters but those can be substituted with "ByVal Long" just the same...
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
You can replace the obj generated by the module file with the obj file generated by the masm.exe creation assembly code \n If you just call the cdecl function, using VBCDeclFix, 8000 lines of code, it's a bit of a waste. https://www.cnblogs.com/Jonlee/archi...08/313346.html
If you use VBDeclFix code, you can make VB6 support more new syntax, which is the most valuable.
But I can't understand the code in this project at all, and I don't know how it was invented.
Last edited by xiaoyao; May 8th, 2024 at 04:13 AM.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by xiaoyao
You can replace the obj generated by the module file with the obj file generated by the masm.exe creation assembly code \n If you just call the cdecl function, using VBCDeclFix, 8000 lines of code, it's a bit of a waste. https://www.cnblogs.com/Jonlee/archi...08/313346.html
If you use this code, you can make VB6 support more new syntax, which is the most valuable.
But I can't understand the code in this project at all, and I don't know how it was invented.
It's too tough, I just want to be a normal person, not a ninja.
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by TomCatChina
As Any has a special application when programming algorithms, allowing for some degree of general purpose programming!
By the way, I tried freebasic and am confused as to why it compiles so slowly?
It takes 7 seconds for a hello world program, which is like hell for me who is used to VB IDE!
Is there something wrong with my configuration? How can you guys stand this torture?
VB.NET,C# .NET8 AOT Editing also takes 10-50 seconds, Microsoft's development tools are becoming more powerful, editing also takes more time, and opening VS development tools and projects also takes 10-30 seconds.
FREEBASIC does take 5-10 seconds to compile, and TWINBASIC is now estimated to be pseudo-compiled, perhaps parsed, and completed in 30 milliseconds
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by xiaoyao
VB.NET,C# .NET8 AOT Editing also takes 10-50 seconds, Microsoft's development tools are becoming more powerful, editing also takes more time, and opening VS development tools and projects also takes 10-30 seconds.
FREEBASIC does take 5-10 seconds to compile, and TWINBASIC is now estimated to be pseudo-compiled, perhaps parsed, and completed in 30 milliseconds
I tested delphi7, purebasic, powerbasic, VC6, they are all native code compiled, small programs are basically compiled and linked in 1 to 2 seconds, I don't know what's wrong with freebasic!
Re: VB declare function does not generate getlasterror code? (Hi,the Trick!)
Originally Posted by TomCatChina
I tested delphi7, purebasic, powerbasic, VC6, they are all native code compiled, small programs are basically compiled and linked in 1 to 2 seconds, I don't know what's wrong with freebasic!
Cannot reproduce these longer compile-times here, using WinFBE 3.1.0 -
(even when including Win32-API and GDIPlus, producing 64Bit Windows-GUI-Apps).
I get 0.8 seconds to compile one of the GDIPlus-examples in 64Bit-Debug-mode.
(as can be seen in the ScreenShot below)...