"Failed to create dialog. Incorrect DLGTEMPLATE?" sanity check

The issue I’ll describe here only occurs on Windows, not macOS. (I have not tried on Linux.) I can’t make a simple runnable example; I’ve tried but this appears to be an interaction of multiple factors, and requires a hundred short videos to trigger it. At this point, I’d like to describe my issue, describe my current “solution,” and see if anyone has better ideas for me to try.

I write video analysis software for academics. As part of it, I use ffmpeg to strip audio from video files so I can draw waveform diagrams. That’s usually done one file at a time, but I also have a tool that will scan one or more selected directories for appropriate video files and perform the audio extraction task in bulk. Because this extraction can take a long time for a large data set, I provide a Progress Dialog for each extraction. My code detects the number of computer cores available and allows that many extractions to be running at a time. (The version of ffmpeg I used when I first wrote this did one extraction per core. That’s no longer true, but I have not removed that logic because too many extractions at once slows them all down, so doing them in smaller batches still makes sense.)

On a large data set, my extraction code will fail with the “Failed to create dialog. Incorrect DLGTEMPLATE?” error message at a reliable point. (This is a wx._core.wxAssertionError exception.) That is, if you set the same file list, it will fail at the same point, although if you specify directories in a different order, it will fail in a different place, with no detectable pattern. It’s not related to the media files themselves or the number of files processed or the time the task has been running. It seems worse when a lot of short files are processed in a row, like a batch of 100 30-second TV commercials.

I’ve tried so many different things, all of which have failed. If I just create a Progress Dialog that doesn’t extract audio, I have no problems with 10,000 progress bars. Eventually, I developed the hypothesis that this problem arose when there were too many dialog creation requests made too quickly. (Perhaps ffmpeg needs extra time to clean up?) After considerable experimentation, I’ve settled on the following:

I track the duration that a Progress Dialog has been shown. If it has been shown for less than 5 seconds, I call wx.MilliSleep for 2 seconds following the close of the dialog box, thus delaying the creation of the next Progress dialog in the set. This seems to be helping. (MilliSleep for 1 second did not help.) At least, I’ve passed a thousand consecutive extractions where I used to run into trouble with as few as 50 - 100 files. I tried wx.CallLater(2000, fxName), but that did not work. So it appears there is something about putting the application to sleep that makes a different, but that’s just a guess.

Anyway, I can now get completely through my two smaller test data sets that would fail, and my gigantic do-every-file-on-my-drive test takes 3 or 4 requests instead of too many to ever have the patience to complete.

Does this make sense? Does it provide any hints to anyone else who’s wrested with this problem that there might be some tips and trick to try?

Thanks in advance,
David

It is from Windows-specific code, so no need to worry about the other platforms for this issue. Here is the code fragment where the error is coming from:

bool wxTopLevelWindowMSW::CreateDialog(const void *dlgTemplate,
                                       const wxString& title,
                                       const wxPoint& pos,
                                       const wxSize& size)
{
    // static cast is valid as we're only ever called for dialogs
    wxWindow * const
        parent = static_cast<wxDialog *>(this)->GetParentForModalDialog();

    m_hWnd = (WXHWND)::CreateDialogIndirect
                       (
                        wxGetInstance(),
                        static_cast<const DLGTEMPLATE*>(dlgTemplate),
                        parent ? GetHwndOf(parent) : NULL,
                        (DLGPROC)wxDlgProc
                       );

    if ( !m_hWnd )
    {
        wxFAIL_MSG(wxT("Failed to create dialog. Incorrect DLGTEMPLATE?"));

        wxLogSysError(wxT("Can't create dialog using memory template"));

        return false;
    }
    ...

The call to wxLogSysError should include the Windows error code in the log message, so if you can set up a wx.Log target that saves those messages somewhere it might give some more clues help to track down the source of the issue.

Regarding your workaround, it wouldn’t surprise me if there was some sort of timing issue, so adding strategic delays would be the right direction to go. But it’s weird enough that I’m not sure I would be satisfied with just blaming the timing…

Rather than lots of progress dialogs have you considered doing something like a single window with a list-like collection of wx.Gauge widgets (and whatever else needs to be displayed for each)?

It is from Windows-specific code

m_hWnd = (WXHWND)::CreateDialogIndirect
(
wxGetInstance(),
static_cast<const DLGTEMPLATE*>(dlgTemplate),
parent ? GetHwndOf(parent) : NULL,
(DLGPROC)wxDlgProc
);

Guessing into the blue: I vaguely remember there being some
limit as to the number of usable “window handles” on Windows ?

Does that ring any bell ?

Perhaps one might ask Eryk Syn over there on the Python list…

Karsten

That came to my mind as well (running out of window handles on Windows).

Scott