-
-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MouseOverControl.cls file imported in Modules folder instead of the Class Modules folder. #28
Comments
Hi @NicoR45 , Thanks for raising the issue! Presumably this is an issue when downloading the raw file, which I will make it Windows-style as requsted. Can you please download the available zip and then import the MouseOverControl.cls module? Does it still import as a standard module or does it import properly as a class module? Thanks! |
I noticed the same behavior, using the zip file as you recommended indeed prevents this issue :) |
I never had to deal with this issue before so unfortunately no. |
Hi @Greedquest , Apologies for contacting you here, but do you have any idea how to solve this. It seems whatever I do does not fix the line endings when downloading the raw file. Thanks! |
Greedquest/ListObject-WithEvents#2 Is a workaround after cloning then I believe *.bas eol=crlf linguist-language=VBA
*.cls eol=crlf linguist-language=VBA
*.doccls eol=crlf linguist-language=VBA
*.twin linguist-language=VBA Is what I use in my gitattributes although let me just check if it downloads correctly |
More info: GitHub will perform the normalisation when you download as zip, even if the files are stored with LF only on github https://stackoverflow.com/a/76548787/6609896 Ok I'm re-reading and none of this is news to you. If you followed the steps to re-apply line endings in that help page you listed then I don't have any bright ideas I'm afraid. You could clone the repo, run that powerquery on it, commit changes. See in notepad++ whether the CRLF in gitattributes is actually being respected |
Thanks @Greedquest ! Darn. Lost hope now. I basically have: *.bas text eol=crlf linguist-language=VBA
*.cls text eol=crlf linguist-language=VBA
*.frm text eol=crlf linguist-language=VBA I only added the I just downloaded one of your You might be interested in this trick. It basically fixes the way the arguments are pushed on the stack for x64 by faking the delegate signature. This also solves it for SetTimer. The gain is that one can now step through the code in x64 whereas before that would have caused a crash. It still crashes when pressing |
@cristianbuse Yeah I just checked my own files have only the LF - although that is laziness since I expect users to download my .xlam or clone my repo, not download raw files individually. But linefeed correction is not mandatory in git, it is to avoid merge conflicts if one dev is on UNIX, one is on Windows, you would get a conflict of linefeed characters when both push the same file on github. So Git normalises all to
Then you commit all your But untested I don't understand that trick - what is the magic number and what is the argument alignment issue it solves? I remember you mentioning it but it was over my head at the time? But I got my asm working! I have a lightweight COM object implementing IEnumVariant using a simple VTABLE of vba function pointers. Allocated using CoTaskMemAlloc. Crucially, it has a custom IUnknown::Release written in asm rather than vba. This Release pops a messagebox and calls cotaskmemfree on the instance, even if End or a stop button is hit. So guaranteed cleanup |
It was indeed set to true but I was under the impression that .gitattributes takes precedence over autocrlf. It also seems the data on GitHub has the wanted crlf - just the download raw button is the issue.
Would definitely want to see that working because I tried following your SO posts and it's beyond my skill level.
The magic number is an offset. The Consider the following scenario:
Option Explicit
Private Declare PtrSafe Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hWnd As LongPtr) As Long
Private m_hWnd As LongPtr
Private Sub UserForm_Initialize()
IUnknown_GetWindow Me, VarPtr(m_hWnd)
End Sub
Public Property Get GetHWnd() As LongPtr
GetHWnd = m_hWnd
End Property
Option Explicit
Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
Private f As UserForm1
Sub TestTimer()
Set f = New UserForm1
SetTimer f.GetHWnd, 1, 100, AddressOf TimerProc
End Sub
Sub TimerProc(ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal nIDEvent As LongPtr, ByVal wTime As Long)
Stop 'BOOM. Crashes on x64, cannot step through code. Works fine on x32
KillTimer hWnd, nIDEvent
End Sub When running Now consider this variation of the standard module (no parameters in the callback): Option Explicit
Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
Private f As UserForm1
Private m_hwnd As LongPtr
Private m_nIDEvent As LongPtr
Sub TestTimer()
Set f = New UserForm1
m_hwnd = f.GetHWnd
m_nIDEvent = 1
SetTimer m_hwnd, m_nIDEvent, 100, AddressOf TimerProc
End Sub
Sub TimerProc()
Stop 'Works fine on both x64 and x32
KillTimer m_hwnd, m_nIDEvent
End Sub which proves that something is wrong with how the parameters are pushed on the stack. Finally, this fixes the issue: Option Explicit
Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
Private f As UserForm1
Sub TestTimer()
Set f = New UserForm1
Dim ptrFake As LongPtr: ptrFake = VBA.Int(AddressOf FakeProc) 'Points to a delegate object, not the function itself
Dim ptrProc As LongPtr: ptrProc = VBA.Int(AddressOf TimerProc)
'
Dim addrFake As LongPtr: addrFake = ptrFake + PTR_SIZE * 6 + 4 'PTR_SIZE * 6 + 4 = 52 on x64
Dim addrProc As LongPtr: addrProc = ptrProc + PTR_SIZE * 6 + 4
'
MemLongPtr(addrFake) = MemLongPtr(addrProc) 'Swap the function that the delegate actually calls from FakeProc to TimerProc
'
SetTimer f.GetHWnd, 1, 100, ptrFake 'Pass the address of the fake delegate which then points to the correct callback
End Sub
Sub FakeProc() 'Never called
End Sub
Sub TimerProc(ByVal hWnd As LongPtr, ByVal wMsg As Long, ByVal nIDEvent As LongPtr, ByVal wTime As Long)
Stop 'Works fine on both x64 and x32
KillTimer hWnd, nIDEvent
End Sub I'm sure it will make sense now. This obviously doesn't solve the issue when 'Reset' is pressed in the IDE but that's only an issue within the scope of the actual callback, hence I am working around that by postponing the processing to a I would of course want to get rid of any crashes once and for all hence very curious of what you've achieved with asm. Many thanks! |
OK so at What exactly goes on in those first 52 bytes of "prologue"? So you have the prologue from FakeProc, but the actual body from TimerProc. That prologue is the setup for a Sub() with zero args prologue rather than a Sub() with 4 args, which by trial and error you have found is what the debugger requires to not crash things. Strange. What's strange is that for 64 bit, the first 4 args are passed in registers not on the stack. So I'm more surprised you have weird stack misalignment in 64 bit than in 32 bit. I guess all we can say is that those 52 bytes of prologue code in the version with args are corrupting the registers or the stack somehow. Incidentally have you tried using a debugger. I have been experimenting with one called x64 debug and it's actually surprisingly easy. I solved all the asm questions pretty rapidly (was a bug in tB :'( I was stuck on for ages - my asm worked in VBA. Still tB is great for trial and error since it doesn't totally crash when you do something dumb) PS thanks for the thorough answer! |
They are identical. Seems like a signature thing for some kind of delegate object. I think a good analogy is that I am swapping a method address inside some weird virtual table.
Yes 😆 . Lots of trial and error
I read a while ago about this on x32 and x64 when doing the instance redirection in LibMemory so I am surprised as well. There are a lot of x64 bugs though - just one more.
No, I did not know there is such a thing for VBA. Can you please give me a link? Also, where can I find the finalized ASM bit? Many thanks! |
Went away, hence no reply! The debugger I've been using is called x64 dbg https://x64dbg.com/ . It's pretty cool, I tried winDBG or something like that which Microsoft makes and looks a lot like an office product, however if turned out to be command based rather than graphical like the vba debugger. The way I used it is launch excel or twinbasic. Open the debugger and attach to process excel.exe you need to learn the keyboard shortcut for "continue without breaking on exceptions". Run VBA and put a vba breakpoint somewhere in the code. Now use hex( adressof foo) or varptr(myassemblycode) to get a pointer to the stuff you are interested in in the immediate window. While VBA is paused, Back in the assem debugger, hit ctrl g and paste the hex to jump to that address in memory. Now put a breakpoint here (click in margin just like in vbide) and continue execution of the VBA. Now the breakpoint gets hit - vbide freezes but the debugger lets you step through. Inspect registers set values, jump to a line. Really good. That's my improvised approach anyway, and it helped me find some tricky bugs in my injected assembly (tB passes by ref the this ptr, but VBA passes by val! Nasty bug) I recommend this video it's only 8 mins and was the only thing I watched: https://youtu.be/vq_VkoCgk3c?si=WheQhnh_c8D-4bSd (I don't necessarily agree with what he's doing but the tutorial is pretty great if you have background knowledge) RE the code I've been working on, I need to do one more thing which is to call IUnknown:: Release - you can imagine this is important for a cleanup routine. But I've got it working to display a message box and call CoTaskMemFree after a stop button press or an end statement, which I'm pretty happy about. I also need to write the 32 bit version but that can wait. I'll probably post on CR a write up because it is quite interesting but I'm really out of my depth and need bug checking |
Many thanks @Greedquest for the detailed guidance. |
Hi @Greedquest , This might be of interest to you. |
Problem : in Microsoft 365 MSO (Version 2302 Build 16.0.16130.20690) 32-bit VBA Editor, when the MouseOverControl.cls file is imported, it is imported into the Modules folder instead of the Class Modules folder.
Solution : the issue appears to be line endings. When downloaded the MouseOverControl.cls file from Github, the line endings are UNIX-style (LF or \n) and from my local files they are Windows-style (CR + LF or \r\n). After switching the downloaded file with Windows-style line endings the file imported correctly as a Class.
Can you make the file download Windows-style please?
The text was updated successfully, but these errors were encountered: