Skip to content
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

MouseMove Event in SStab control causes crash #1835

Open
dmrvb opened this issue Apr 26, 2024 · 20 comments
Open

MouseMove Event in SStab control causes crash #1835

dmrvb opened this issue Apr 26, 2024 · 20 comments

Comments

@dmrvb
Copy link

dmrvb commented Apr 26, 2024

Describe the bug
The MouseMove event in an SStab control will give a crash when referencing any of the passed parameters.

To Reproduce
Put a SStab control on a Form

Private Sub SSTab1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
On Error GoTo e
If Button = 0 Then ' ......this fails
End If
Exit Sub
e:
Debug.Print Err.Description
End Sub

The reference to "button" will give a run time access violation error.
The error trap does NOT catch this.

win11 tB 515

Note
Crashes in VB6 often result in everything just disappearing.
tB is pretty good at surviving and giving a message in the Debug Console, thankyou.

MouseMoveTab.zip

@fafalone
Copy link
Collaborator

fafalone commented Apr 26, 2024

When you create the procedure in VB6 does it have a ByVal?

Edit: Just got home to check; the answer is no. This is a curious situation here. It's ByRef in VB6 and works. When it's ByVal in twinBASIC, it works. How is this? I check VarPtr(Button) in VB6 and it's not using that as both pointer and value.

When it's ByRef in tB, it's crashing because VarPtr(Button) is 0. But in VB it's not 0 and points to the correct value.

This makes no sense. The interface appears weird in OLEVIEW too; it's missing in/out attributes. My thinking is this is a VB6 bug; since it's missing the in/out parameter, it uses the default (if you omit ByVal and ByRef, ByRef is used), but because it's passed by the control ByVal, it winds up working out somehow. [out] or [in, out] must be a pointer, and it's not-- short Button;,

So if true then the question is... should tB replicate this bug? For a long time I would have thought yes, but since in other circumstances now no matter how reliable undefined/incorrect behavior is in VB6, it won't be supported in tB, only Wayne can say.

In any case, the correct way to handle this is for KeyDown, KeyUp, MouseDown, MouseMove, and Click, all the arguments should be ByVal in tB. Which is how they're autogenerated.

It's VB6 that has the 'wrong' prototype here, even if it's somehow working out right behind the scenes.

Edit2: Confirmed VarPtr(Button) in twinBASIC gives the actual value, 0/1/2 depending on mouse button.

@dmrvb
Copy link
Author

dmrvb commented Apr 27, 2024

Looks like none of the mouse events in VB6 (for Form as well as SStab) specify Byval/byref.

tB is going to be a better language than VB, and although VB was intended to be friendly to total beginners I don't think this applies to tB, the world has moved on, even schoolkids have to work in C type langauges 😃
So, I am still not sure that 100% compatability is essential, especially where in requires replicating weaknesses. Hopefully things like this could be resolved by an "upgrade wizard" that runs as part of the VB6 import???

@fafalone
Copy link
Collaborator

So Eduardo- on VBF helped me figure out what's going on here.

VB6 has a signature override for specific dispids. If it encounters a method with a specific dispid, here for MouseMove 0xfffffda2, it takes the ByVal parameter, and turns it into ByRef. It's an open question as to WHY?

Proof of concept:

-Create new ActiveX control in tB, registered to local machine so VB6 sees it.

-Code;

[DispId(&Hfffffda2)]
Public Event z9MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As stdole.OLE_XPOS_PIXELS, ByVal Y As OLE_YPOS_PIXELS)
    
Private Sub UserControl_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single) Handles UserControl.MouseMove
    RaiseEvent z9MouseMove(Button, Shift, X, Y)
End Sub

-Compile. Open in OLEVIEW. The dispinterface tB generates correctly reflects the ByVal,

    [
      uuid(81D15170-64D1-4583-9A0C-0A7091FF6050),
      hidden,
      nonextensible
    ]
    dispinterface __z9testID {
        properties:
        methods:
            [id(0xfffffda2)]
            HRESULT z9MouseMove(
                            [in] short Button, 
                            [in] short Shift, 
                            [in] OLE_XPOS_PIXELS X, 
                            [in] OLE_YPOS_PIXELS Y);
    };

-New VB6 project, add control, add to form: VB6 creates a ByRef prototype, and it works, not crashing but instead producing the expected values:

Private Sub z9testID1_z9MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Debug.Print Button
End Sub

WHY is the big question here. Why not pass through the ByVal like normal? I haven't tested modifying the signature from the expected, what's going to happen then, how specific is this override mechanism?

To replicate, tB is going to have to reinterpret not just this dispid, but all the others too... at a minimum:

           [id(0xfffffda7), helpstring("Occurs when you press and release a mouse button and then press and release it again over an object."), helpcontext(0x00046d03)]
            void DblClick();
            [id(0xfffffda6), helpstring("Occurs when the user presses a key while an object has the focus."), helpcontext(0x00046d04)]
            void KeyDown(
                            short KeyCode, 
                            short Shift);
            [id(0xfffffda5), helpstring("Occurs when the user presses and releases an ANSI key."), helpcontext(0x00046d05)]
            void KeyPress(short* KeyAscii);
            [id(0xfffffda4), helpstring("Occurs when the user releases a key while an object has the focus."), helpcontext(0x00046d06)]
            void KeyUp(
                            short KeyCode, 
                            short Shift);
            [id(0xfffffda3), helpstring("Occurs when the user presses the mouse button while an object has the focus."), helpcontext(0x00046d07)]
            void MouseDown(
                            short Button, 
                            short Shift, 
                            OLE_XPOS_PIXELS X, 
                            OLE_YPOS_PIXELS Y);
            [id(0xfffffda2), helpstring("Occurs when the user moves the mouse."), helpcontext(0x00046d08)]
            void MouseMove(
                            short Button, 
                            short Shift, 
                            OLE_XPOS_PIXELS X, 
                            OLE_YPOS_PIXELS Y);
            [id(0xfffffda1), helpstring("Occurs when the user releases the mouse button while an object has the focus."), helpcontext(0x00046d09)]
            void MouseUp(
                            short Button, 
                            short Shift, 
                            OLE_XPOS_PIXELS X, 
                            OLE_YPOS_PIXELS Y);
            [id(0xfffffda8), helpstring("Occurs when the user presses and then releases a mouse button over an object."), helpcontext(0x00046d0a)]
            void Click(short PreviousTab);

@fafalone
Copy link
Collaborator

Another question is why tB is not raising a signature mismatch error but instead letting it crash.

@dmrvb
Copy link
Author

dmrvb commented Apr 27, 2024

I suspect that ByVal and ByRef are probably the first bit of "unwelcome computer science" that non-programmers encounter when using VB.
In my younger days I knew quite a few engineers who used VB (or probably GWBasic) to write some quite big programs without any understanding at all of computer science concepts.
I propose that Microsoft decided that most of the standard event routines in VB should thus hide ByVal and ByRef from the user and use more devious means to resolve things.
So, can tB give a warning, or just add ByRef/ByVal as required for the events? All the srtandard events could be done from a look-up table, and unknown ActiveX components could just give a warning on unknown events.
I've just had a look at a non-Microsoft ActiveX control that I am using and that does (mostly) specify ByVal/ByRef on its custom events, but not on standard events like mousemove.

@dmrvb
Copy link
Author

dmrvb commented Apr 27, 2024

I confess that I am not fully understanding this stuff (but then I do try to be a high level language programmer which is why I like VB)
In VB if ByVal/ByRef is not specified then the default is ByRef.
Mousemove does not specify but if I make it ByVal then I get a compile error (procedure definition does not match.....)
In a form imported from VB I get the crash but can fix this by specifying ByVal.

If I create a Form in tB (without a Tab Control) then ByVal/Ref is again not specified in Form_Mousemove.
I can speciy either ByRef or ByVal without getting any errors but only ByRef works correctly, ByVal gives incorrect numbers.,

@fafalone
Copy link
Collaborator

fafalone commented Apr 27, 2024

Form_MouseMove is a bit different than an external control; tB didn't have to comply with any besides VB6, so the original event definition is ByRef. And Single for x,y... that's another issue. The original control is x, y as long. It's converting stdole.OLE_XPOS_PIXELS, where

    typedef [uuid(66504302-BE0F-101A-8BBB-00AA00300CAB), public]
    long OLE_XPOS_PIXELS;

I think there though it's got an actual reason; convert to twips or whatever?

I was trying to dig around the IDL def for VB.Form but couldn't find one. I suspect if it did exist it would be ByVal.

@wqweto
Copy link

wqweto commented Apr 28, 2024

I was trying to dig around the IDL def for VB.Form but couldn't find one. I suspect if it did exist it would be ByVal.

It is actually [in, out] single *X, [in, out] single *Y on MouseDown event of FormEvents dispinterface from the decompiled VB6.OLB typelib resource ID 3.

[helpstring("Occurs when the user presses the mouse button while an object has the focus."), helpcontext(0x000dfb5f)]
HRESULT _stdcall MouseDown(
                [in, out] short* Button, 
                [in, out] short* Shift, 
                [in, out] single* X, 
                [in, out] single* Y);

@fafalone
Copy link
Collaborator

Ah, first place I looked but I missed it.

But yeah... ByRef by nature, not associated with the dispid 0xfffffda2, so no compatibility issues there or behind the scenes reinterpreting going on in VB.

@dmrvb
Copy link
Author

dmrvb commented Apr 28, 2024

For mousemove on a form I can optionally specify ByRef in the Form_mousemove (etc) definitions,
But, in SStab_Mousemove definitions I have to declare ByVal for it to work correctly.

@fafalone
Copy link
Collaborator

fafalone commented Apr 28, 2024

Yes ByRef is implied. foo(bar As Long) and foo(ByRef bar As Long) are identical in function.

There's only a difference when ByVal is there.

@dmrvb
Copy link
Author

dmrvb commented May 3, 2024

It looks like you have done some work on this but its still not right.
I now get a Diagnostics Error TB5018 "Unable to match handler to its event member" (good, thank you)
mousedown(byval button as interger, byval shift as integer, byval x as single, byval y as OLE_YPOS_Pixels).
Y should be single.
Note that this also occurs UPDownCursor and ToolBar Mouseevents (and probably others too).

@WaynePhillipsEA
Copy link
Collaborator

No, I haven't yet touched anything specific in this regard.

@dmrvb
Copy link
Author

dmrvb commented May 3, 2024

How odd, stuff that was compiling fine but giving errors at run time is now giving the 5018 error.
I might investigate more, but then there is a wisdom that its better to just get things fixed rather than to spend time understanding the problem.

@dmrvb
Copy link
Author

dmrvb commented May 3, 2024

I don't keep or even install every new release, they do come thick and fast, but something has changed somewhere between 515 and 529.

@fafalone
Copy link
Collaborator

fafalone commented May 3, 2024

I'm still not getting an in 529.

Are you still looking at SSTab_MouseMove? Remember not all controls used the specific dispid that triggers the issue.

@dmrvb
Copy link
Author

dmrvb commented May 4, 2024

I am seeing a 5018 error on a Toolbar.mousedown where the toolbar is directly on the Form with no SStab involved.
Its wants everything to be ByVal and "ByVal y as OLE_YPOS_Pixels".

Most errors do relate to SStabs, and controls on tabs, but this is because I use SStab extensively.
This is a recent change, no errors on same code in tB515. Its a big lump of code but if Wayne can't sort it I will do a test prog.
The warning is good, its the OLE_YPOS_Pixels that is trouble.

@dmrvb
Copy link
Author

dmrvb commented May 4, 2024

Just put a toolbar on a form, that will get the 5018 error in tB529.

I did this in VB6 and imported it to tB, did not try doing it directly in tB, but importing VB projects/testing compatability is my priority.

@dmrvb
Copy link
Author

dmrvb commented May 4, 2024

OK, if I create the form in tB there is no error because the mousedown definition includes byval, and y as ...Pixels.

Private Sub Toolbar1_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As stdole.OLE_YPOS_PIXELS)

I assume that this is wrong as y should be single to be VB compatible???
I can do this:
DIM s as single
s=y
But I don't like that, and having a varaiable in Pixels when the form scalemode is twips is not nice.

@fafalone
Copy link
Collaborator

fafalone commented May 4, 2024

Yes that's the bug here.

Looking at the ocx, the IToolbarEvents dispinterface has one of those odd dispids (id(0xfffffda3)) for MouseDown that VB must be seeing and rewriting behind the scenes.

This issue will occur with any control using these dispids for their standard events.

tB is technically correct in that it's not rewriting the original:

        [id(0xfffffda3), helpstring("Occurs when the user presses the mouse button while an object has the focus."), helpcontext(0x000334fe)]
        void MouseDown(
                        short Button, 
                        short Shift, 
                        OLE_XPOS_PIXELS x, 
                        OLE_YPOS_PIXELS y);

But since VB is rewriting it, tB will need to do the same for compatibility. VB knows the real definition, which is why it works, it's doing the neccessary conversion. But if you use VB's definition, tB doesn't know to rewrite it yet, so no conversion is done and boom.

What's VB doing with the arguments? Is it converting pixels to twips? Or to current scale mode? tB will have to replicate that too.

One thing we could do to help Wayne with this is gather a list of dispid/event definitions for all the events that need to be rewritten. Compare the VB definition to the typelib definition (by opening the ocx with OLEView's 'View typelib' option).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants