Access violation in vstHookWndProc


#1

Hi

I'm getting a confusing access violation in the vstHookWndProc of juce_VSTPluginFormat

Now it takes some pretty specific actions to reproduce it, so I'll need to explain how I got this error. I have been able to reproduce this in the pluginHost example, just to make sure it is not related to something else in my project, but I have only tested on windows 7.

 

I'm using the ApplicationCommandManager and it's key mappings.

If you do nothing, key commands are not recognized after editing a plugin's GUI, as the keys are not relayed to the command manager. (I want this to work as a user might tweak a knob then hit ctrl+s to save.)

So to make the key commands work when the plugin UI has focus I added this to the pluginwindow's constructor

addKeyListener(getCommandManager().getKeyMappings());

Now if this is in some way super stupid then please stop reading and tell me why.

This solves the problem with key commands, and works fine EXCEPT.

If you click on the plugin UI then trigger a key command that deletes the window. (For instance I have a short cut for toggling the plugin UI). This always causes an access violation.

As far as I can tell, what happens is that after the plugin UI is closed and the window has been deleted,

the vstHookWndProc is called on some rubbish data. It is like if the keyboard hook has not been removed before the editor closed (But I doubt Jules would make that mistake).

I'm thinking that I need to call something before closing the window.

calling

commandManager->removeKeyListener(pluginWindow)

before closing does nothing, as the problem seems to be unrelated to that.

Any help on this or how you solved the issue with keyboard focus in the plugin gui would be great.

 

Regards

Nikolai

 

PS

I'm getting more and more happy/exited by this framework, it is a work of art.

For any newcomers not instantly getting comfy migrating from their old environment:

It takes some research effort to get up and running, and you might need to rethink some of your old habits but it is sooo worth it.


#2

Bit hard to figure out what's going on ther.. What's the stack trace when it crashes?


#3

Hi Thank you for getting back to me.

Here is some debug info, hope it helps:

The log + AV:


Closing VST UI: B4 II
First-chance exception at 0xFEEEFEEE in PluginHost.exe: 0xC0000005: Access violation executing location 0xFEEEFEEE.
Unhandled exception at 0xFEEEFEEE in PluginHost.exe: 0xC0000005: Access violation executing location 0xFEEEFEEE.

If you press the key, the vstHookWndProc callback gets called twise. first time is fine, command gets executed, widnow deleted. Then it is called again and the access violation happens when this line fires:


return CallWindowProc ((WNDPROC) w->originalWndProc,
                                       (HWND) w->pluginHWND,
                                       message, wParam, lParam);

 

The Call Stack looks like this:


     feeefeee()    Unknown
     [Frames below may be incorrect and/or missing]    
     [External Code]    
>    PluginHost.exe!juce::VSTPluginWindow::vstHookWndProc(HWND__ * hW, unsigned int message, unsigned int wParam, long lParam) Line 2396    C++
     [External Code]    
     PluginHost.exe!juce::MessageManager::dispatchNextMessageOnSystemQueue(bool returnIfNoPendingMessages) Line 135    C++
     PluginHost.exe!juce::MessageManager::runDispatchLoopUntil(int millisecondsToRunFor) Line 94    C++
     PluginHost.exe!juce::MessageManager::runDispatchLoop() Line 82    C++
     PluginHost.exe!juce::JUCEApplicationBase::main() Line 239    C++
     PluginHost.exe!WinMain(void * __formal, void * __formal, const char * __formal, int __formal) Line 82    C++
     [External Code]    

And the local watches:


-        w    0x00495588 {plugin={effect=??? module={referencedObject=??? } extraFunctions={object=??? } ...} isOpen=...}    const juce::VSTPluginWindow * const
+        juce::AudioProcessorEditor    {owner=0xfeeefeee {wrapperType=??? playHead=??? listeners={data={elements={...} numAllocated=??? } numUsed=...} ...} }    juce::AudioProcessorEditor
+        juce::ComponentMovementWatcher    {component={holder={referencedObject=0xfeeefeee {owner=??? } } } lastPeerID=4277075694 registeredParentComps=...}    juce::ComponentMovementWatcher
+        juce::Timer    {countdownMs=-17891602 periodMs=-17891602 previous=0xfeeefeee {countdownMs=??? periodMs=??? previous=...} ...}    juce::Timer
+        plugin    {effect=??? module={referencedObject=??? } extraFunctions={object=??? } ...}    juce::VSTPluginInstance &
        isOpen    true (238)    bool
        recursiveResize    true (254)    bool
        pluginWantsKeys    true (238)    bool
        pluginRefusesToResize    true (254)    bool
        alreadyInside    true (238)    bool
+        pluginHWND    0xfeeefeee {unused=??? }    HWND__ *
        originalWndProc    0xfeeefeee    void *
        sizeCheckCount    -17891602    int
        leakDetector2616    {...}    juce::LeakedObjectDetector<juce::VSTPluginWindow>
        i    0    int
-        hW    0x005f05e0 {unused=??? }    HWND__ *
        unused    <Unable to read memory>    
        message    258    unsigned int
        wParam    5    unsigned int
        lParam    1179649    long

This looks like a mess, but the main thing I can read from it is that the pluginHWND is not valid (address 
0xfeeefeee) and that it looks like the whole method is called on a object that has been deleted.


#4

Ok, so I took some time to try and actually understand what was going on.

the loop inside vstHookWndProc reffers to a VSTPluginWindow pointer w.

This is pulled like this:

const VSTPluginWindow* const w = activeVSTWindows.getUnchecked (i);

Now this line (if i'm correct) relays the keycommand to the parent window:

                    SendMessage ((HWND) w->getTopLevelComponent()->getWindowHandle(),
                                 message, wParam, lParam);

It is then deleted for some reason (like a command is triggered)

The next line:

                    return CallWindowProc((WNDPROC)w->originalWndProc,
                        (HWND)w->pluginHWND,
                        message, wParam, lParam);

Now has a dangeling pointer to the deleted editor, and the loop variable i might be out of range as the activeVSTWindows array has gotten smaler.

This hack/bodge seems to fix the issue:


        for (int i = activeVSTWindows.size(); --i >= 0;)
        {
            const VSTPluginWindow* const w = activeVSTWindows.getUnchecked (i);
            if (w->pluginHWND == hW)
            {
                if (message == WM_CHAR
                    || message == WM_KEYDOWN
                    || message == WM_SYSKEYDOWN
                    || message == WM_KEYUP
                    || message == WM_SYSKEYUP
                    || message == WM_APPCOMMAND)
                {
                    SendMessage ((HWND) w->getTopLevelComponent()->getWindowHandle(),
                                 message, wParam, lParam);
                }
                //** Changes**
                const VSTPluginWindow* const x = activeVSTWindows[i]; //Try to get the pointer to the window again
                if (x != nullptr) //Check that it is valid and do the same as before
                {
                    return CallWindowProc((WNDPROC)x->originalWndProc,
                        (HWND)x->pluginHWND,
                        message, wParam, lParam);
                }
            }
        }
        return DefWindowProc (hW, message, wParam, lParam);

 


#5

Ah, good work! Thanks for the detailed description!

I've commited a change that should do the job in a robust way now - let me know if it still gives you any trouble!


#6

Hi

That seems to do the trick, thank you for fixing that. Your solution was a bit more elegant, had forgotten about safepointer.

Thanks again