I hope I can explain this problem decently well!
I'm trying to implement a ReWire audio device as a Delphi .dll. If you don't know what ReWire is, don't worry about it. What's important is that my code compiles into a .dll, and I get calls from the ReWire system into my .dll to open up a display, check if the display is opened, and close it again.
When I get the call to launch, I do the following:
if not Assigned(form) then
form := TMyForm.Create(nil);
form.Show;
where form is a global variable inside of my Delphi library (maybe a problem?). I have hooked up MyForm's OnCreate event to do some interesting things like prepare an array of stuff I want to work with.
So far everything's good. My form has a little button in it that opens up a TOpenDialog. I find that as soon as that dialog closes, somehow the OnCreate event is firing again in my form!
I have checked that OnDestroy is not being called, so I have no idea why OnCreate is getting called again.
Unfortunately I'm not really sure what information is relevant, but here's the call stack the first time around (when the form is first set up):
As expected, ReWire is making a call into my .dll to Launch the Panel application, so I create my form. Great, things are looking good.
Then inside my form, I open up a little dialog, select a file, and do some operations. Out of left field, OnCreate is called again, and here's the call stack that time:
It's a crazy party of calls! Reaper (at the bottom) is the ReWire host I'm using to test my application, but I have no idea what's going on inside that stack trace because none of it is my code. Suddenly the event just gets called when I don't think it should, because OnDestroy wasn't even called.
The only other important thing I can think of is that if I print out the address of the Sender, it's different each time, so it's somehow getting Created again or something, but I've checked that I only call the MyForm.Create once.
Any ideas as to how this type of thing could happen?
In the first stack trace, the OnCreate call is preceded by a call to TCustomForm.Create(), which is correct behavior. In the second stack trace, the OnCreate call is preceded by a call to TObject.Create() instead, which is not correct behavior. That leads me to think that something in your button OnClick event handler is either constructing an object with a bad VMT, or is otherwise corrupting memory in general and causing a bad jump into code that just happens to be occupied by your TForm class. Either way, double check your OnClick logic for bugs.
Check on (any)where you are setting the form variable to nil. It may be that it's being set to nil without freeing the form it's pointing to, and so next time your launch code is called it's creating another instance of the form.
Related
I'm working on a custom cross platform UI library that needs a synchronous "ShowPopup" method that shows a popup, runs an event loop until it's finished and automatically cancels when clicking outside the popup or pressing escape. Keyboard, mouse and scroll wheel events need to be dispatched to the popup but other events (paint, draw, timers etc...) need to be dispatched to their regular targets while the loop runs.
Edit: for clarification, by popup, I mean this kind of menu style popup window, not an alert/dialog etc...
On Windows I've implemented this fairly simply by calling GetMessage/DispatchMessage and filtering and dispatching messages as appropriate. Works fine.
I've much less experience with Cocoa/OS X however and finding the whole event loop/dispatch paradigm a bit confusing. I've seen the following article which explains how to implement a mouse tracking loop which is very similar to what I need:
http://stpeterandpaul.ca/tiger/documentation/Cocoa/Conceptual/EventOverview/HandlingMouseEvents/chapter_5_section_4.html
but... there's some things about this that concern me.
The linked article states: "the application’s main thread is unable to process any other requests during an event-tracking loop and timers might not fire". Might not? Why not, when not, how to make sure they do?
The docs for nextEventMatchingMask:untilDate:inMode:dequeue: states "events that do not match one of the specified event types are left in the queue.". That seems a little odd. Does this mean that if an event loop only asks for mouse events then any pressed keys will be processed once the loop finishes? That'd be weird.
Is it possible to peek at a message in the event queue without removing it. eg: the Windows version of my library uses this to close the popup when it's clicked outside, but leaves the click event in the queue so that clicking outside the popup on a another button doesn't require a second click.
I've read and re-read about run loop modes but still don't really get it. A good explanation of what these are for would be great.
Are there any other good examples of implementing an event loop for a popup. Even better would be pseudo-code for what the built in NSApplication run loop does.
Another way of putting all this... what's the Cocoa equivalent of Windows' PeekMessage(..., PM_REMOVE), PeekMessage(..., PM_NOREMOVE) and DispatchMessage().
Any help greatly appreciated.
What exactly is a "popup" as you're using the term? That term means different things in different GUI APIs. Is it just a modal dialog window?
Update for edits to question:
It seems you just want to implement a custom menu. Apple provides a sample project, CustomMenus, which illustrates that technique. It's a companion to one of the WWDC 2010 session videos, Session 145, "Key Event Handling in Cocoa Applications".
Depending on exactly what you need to achieve, you might want to use an NSAlert. Alternatively, you can use a custom window and just run it modally using the -runModalForWindow: method of NSApplication.
To meet your requirement of ending the modal session when the user clicks outside of the window, you could use a local event monitor. There's even an example of just such functionality in the (modern, current) Cocoa Event Handling Guide: Monitoring Events.
All of that said, here are (hopefully no longer relevant) answers to your specific questions:
The linked article states: "the application’s main thread is unable to process any other requests during an event-tracking loop and
timers might not fire". Might not? Why not, when not, how to make
sure they do?
Because timers are scheduled in a particular run loop mode or set of modes. See the answer to question 4, below. You would typically use the event-tracking mode when running an event-tracking loop, so timers which are not scheduled in that mode will not run.
You could use the default mode for your event-tracking loop, but it really isn't a good idea. It might cause unexpected re-entrancy.
Assuming your pop-up is similar to a modal window, you should probably use NSModalPanelRunLoopMode.
The docs for nextEventMatchingMask:untilDate:inMode:dequeue:
states "events that do not match one of the specified event types are
left in the queue.". That seems a little odd. Does this mean that if
an event loop only asks for mouse events then any pressed keys will be
processed once the loop finishes? That'd be weird.
Yes, that's what it means. It's up to you to prevent that weird outcome. If you were to read a version of the Cocoa Event Handling Guide from this decade, you'd find there's a section on how to deal with this. ;-P
Is it possible to peek at a message in the event queue without removing it. eg: the Windows version of my library uses this to close
the popup when it's clicked outside, but leaves the click event in the
queue so that clicking outside the popup on a another button doesn't
require a second click.
Yes. Did you notice the "dequeue:" parameter of nextEventMatchingMask:untilDate:inMode:dequeue:? If you pass NO for that, then the event is left in the queue.
I've read and re-read about run loop modes but still don't really get it. A good explanation of what these are for would be great.
It's hard to know what to tell you without knowing what you're confused about and how the Apple guide failed you.
Are you familiar with handling multiple asynchronous communication channels using a loop around select(), poll(), epoll(), or kevent()? It's kind of like that, but a bit more automated. Not only do you build a data structure which lists the input sources you want to monitor and what specific events on those input sources you're interested in, but each input source also has a callback associated with it. Running the run loop is like calling one of the above functions to wait for input but also, when input arrives, calling the callback associated with the source to handle that input. You can run a single turn of that loop, run it until a specific time, or even run it indefinitely.
With run loops, the input sources can be organized into sets. The sets are called "modes" and identified by name (i.e. a string). When you run a run loop, you specify which set of input sources it should monitor by specifying which mode it should run in. The other input sources are still known to the run loop, but just ignored temporarily.
The -nextEventMatchingMask:untilDate:inMode:dequeue: method is, more or less, running the thread's run loop internally. In addition to whatever input sources were already present in the run loop, it temporarily adds an input source to monitor events from the windowing system, including mouse and key events.
Are there any other good examples of implementing an event loop for a popup. Even better would be pseudo-code for what the built in
NSApplication run loop does.
There's old Apple sample code, which is actually their implementation of GLUT. It provides a subclass of NSApplication and overrides the -run method. When you strip away some stuff that's only relevant for application start-up or GLUT, it's pretty simple. It's just a loop around -nextEventMatchingMask:... and -sendEvent:.
I`ve got a Cocoa UI app that needs to update its main window as fast as the rest of its UI event loop permits, so I implemented the main updater function via a CFRunLoopObserver. I also have an application-modal alert box and a context menu.
So, my problem is that when I have both an alert and a context menu on the screen, the updater gets stuck, presumably due to excess number of nested run loops above the one it has been attached to back at the start. Surprisingly, I can alleviate the issue by duplicating the observer, and it keeps running — one instance only, randomly chosen from the two existing when the «blocking» nested loop starts. When it ends (a menu item gets chosen, an alert exceeds its timeout or is dismissed manually — whatever), things get back to normal, and the blocked observer regains the ability to run.
Now the question: is there a way to have a single observer that gets executed no matter how deep the run loop stack is?
Sidestepping the runloop issue, have you looked at CVDisplayLink?
You set one up using CVDisplayLinkCreateWithActiveCGDisplays(), and then attach a callback using CVDisplayLinkSetOutputCallback(). It'll ask for data at the refresh rate of the display, so you'll be getting the callback as fast as you need.
We have a kiosk mode application for Windows Mobile 5 that was going strong for a year at least in production. It uses window subclassing through the SetWindowLong windows API function to override the behavior of the taskbar to prevent users from ever leaving our application or other explicitly allowed applications.
My callback overrides the handling of a few window messages and calls the default handler for other messages. This is done by storing the previous function pointer returned by SetWindowLong, and using it inside of the new function by calling CallWindowProc on it.
Then we had to update the application to be compatible with Windows Mobile 6.5.3, and started having a bunch of problems. I followed this article to disable the bottom menu buttons which are new on 6.5. On a single application, it works fine and the menu respects my callback. As soon as the user opens up another application through ours, that window seems to be recreated, and I had to devise a mechanism to detect foreground window changes and then "resubclass" the window again.
In my code, I don't have control of when exactly this window is recreated, so my first attempt was to use the GetWindowLong function to obtain the current callback address and test it against my own function address. I learned the hard way that I can't just compare the values like that, since it does not always return the function pointer.
Because I'm not able to test if my method is the current handler that way, I end up sometimes setting my method as the handler, and the previous handler is also my own method (in this case, the window was not recreated and thus already had my method set). This results in an infinite loop, since my callback ends up calling itself indefinitely.
How can I know if my custom function is the one being used by a certain window, so that I can avoid this infinite recursion?
Don't use SetWindowLong(GWL_WNDPROC) to subclass a window. Use SetWindowSubClass() instead (like Raymond Chen said):
Subclassing Controls
Safer subclassing
Amongst providing safer subclassing semantics (such as allowing multiple subclasses at a time), it also allows you to associate user-defined data with each subclass. So you could use GetWindowSubclass() to check if you have already subclassed the window or not.
Or, you could simply keep track of whether or not you have already subclassed the window. A simple boolean variable will suffice. Once your subclass is in place, you MUST remove the subclass before the window is fully destroyed. So, for instance, when the subclass receives the WM_NCDESTROY message, you can remove the subclass and clear your boolean at the same time, and then the next time you see the window again your boolean will tell you that you need to subclass the window.
Send the custom message to window. Handle this message in your custom function to return some value that will indicate that it was your custom handler.. and so on.
if(SendMessage(hwnd, mymsg, 0, 0) != myvalue)
;// It's not your handler
I'm implementing a Visual Studio Language Service for a custom scripting language used internally at my company, and I've run into an issue with the navigation bar implemented as a subclass of TypeAndMemberDropdownBars. The subclass is created by my LanguageService subclass' LanguageService.CreateDropDownHelper method.
In the OnSynchronizeDropdowns method I'm iterating through the types defined in the file and adding DropDownMembers to the passed-in array to fill out the navigation bar. The issue I'm seeing is that the first item in the array is being duplicated and placed at the end of the listing by code that I don't have access to. This extra item does not behave correctly when selected (nothing happens), but doesn't seem to cause any other issues; the rest of the items in the list work fine. Additionally, this only seems to happen for the type dropdown box - the members dropdown box does not display this behavior.
I'm hoping someone else has seen and resolved this issue and could provide some assistance. Thanks!
Turns out this was caused by me calling LanguageService.SynchronizeDropdowns from my LanguageService.ParseSource method, which was being called on a background thread. I've fixed the problem by setting a flag when ParseSource does a Check parse, and then implementing a check for that flag in my LanguageService.OnIdle function that will call SynchronizeDropdowns. It's now working as expected!
A better solution is to implement the LanguageService.OnParseComplete callback, and call SynchronizeDropdowns from there. OnParseComplete is always called from the main thread, so this prevents any synchronization issues from coming up, and also keeps you from having to keep track of whether or not you need to call SynchronizeDropdowns().
I am making a C++/CLI Forms application.
In the main window of my app I have a button. When I click that button I call the Load function. Below there is the C++/CLI code:
private: System::Void Button1_Click(System::Object^ sender, System::EventArgs^ e) {
Load();
}
The function Load() is a time-consuming function. It uses the cURL library to send several HTTP GET request to a website.
In the Form I also included a ProgressBar and a textLabel showing the current request being sended.
The problem is that when I click the button and call the function the Form just freezes. I can't see the progressBar and Textlabel changing it's value while the function Load() is called, the Form is just freezed. When the function Load() has finished sending request, suddenly the progressBar change It's value to 100%.
I hope I described my problem clearly enough to understand it.
Move your task to another thread, or call Application.DoEvents();, just after you updating your scrollbar value.
Either break the task into smaller parts (design a finite state machine or use continuations) or use a separate thread.
The first approach takes more getting used to, but it's easier for an experienced programmer to get right. Threading requires synchronization which is very detail-oriented and causes a lot of hidden sporadic bugs which are extremely difficult to debug.
Call Form1.Refresh() every time you update an element of the form (say Form1). It will show the results immediately.
Before any line command that make probably any load time ...Write This:
System::Windows::Forms::Application::DoEvents();