2012-03-30 14 views
1

ich eine WinForms-Anwendung, die Verwendung eines TaskDialog library macht, dass die Vista-Stil Dialoge von ComCtl32.dll und für weniger OS nutzt nutzt es eine emulierte win Form ...Ausnahme vorkommendes nur auf Drag & Drop-

Aber das ist nicht die Problem ... Diese Bibliothek funktioniert gut und wir hatten nie Probleme damit. Bis jetzt ... In der Tat, wenn wir unter normalen Umständen einen Dialog starten, dann sieht es gut aus.

Allerdings habe ich einen Drag-Drop-Handler auf meinem Hauptformular hinzugefügt, um Dateipfade zu erfassen, die aus anderen Quellen stammen (z. B. Windows Explorer). Wenn das Drag & Drop-Handler das erste Mal ist, dass der Dialog gezeigt, dann erhalten wir die folgende Ausnahme:

können nicht einen Einstiegspunkt finden namens ‚TaskDialogIndirect‘ in DLL ‚Comctl32‘.

Dies tritt am dritten Aufruf der Partei Bibliothek:

/// <summary> 
    /// TaskDialogIndirect taken from commctl.h 
    /// </summary> 
    /// <param name="pTaskConfig">All the parameters about the Task Dialog to Show.</param> 
    /// <param name="pnButton">The push button pressed.</param> 
    /// <param name="pnRadioButton">The radio button that was selected.</param> 
    /// <param name="pfVerificationFlagChecked">The state of the verification checkbox on dismiss of the Task Dialog.</param> 
    [DllImport ("ComCtl32", CharSet = CharSet.Unicode, PreserveSig = false)] 
    internal static extern void TaskDialogIndirect (
     [In] ref TASKDIALOGCONFIG pTaskConfig, 
     [Out] out int pnButton, 
     [Out] out int pnRadioButton, 
     [Out] out bool pfVerificationFlagChecked); 

Wenn ein Dialog bereits gezeigt wurde, dann wird der Handler OK laufen.

Der DragDrop-Handler für das Formular zeigt nicht InvokeRequired und ich war vorsichtig, den Dialog über Form.Invoke sowieso zu erhöhen.

private void MainForm_DragDrop(object sender, DragEventArgs e) 
{ 
    if (e.Data.GetDataPresent(DataFormats.FileDrop)) 
    { 
     Array fileNames = (Array)e.Data.GetData(DataFormats.FileDrop); 
     if (fileNames != null && fileNames.OfType<string>().Any()) 
     { 
      foreach (var fileName in fileNames.OfType<string>()) 
      { 
       this.Invoke(new Action<string>(this.AttemptOpenFromPath), fileName); 
      } 
     } 
    } 
} 

Als Seite: Ich bin Kompilieren (und läuft) es auf einer 64-Bit-Maschine Windows 7, aber mit der "AnyCPU" Architektur Flagge.

Irgendwelche Gedanken/Lösungen, warum die Ausnahme nur ausgelöst wird, wenn der erste Aufruf an TaskDialogIndirect über den DragDrop-Handler ist ???

+0

Das hängt davon ab, wie die Bibliothek funktioniert. – SLaks

+0

Auf welchem ​​Betriebssystem tritt die Ausnahme auf? – SLaks

+0

Der Fehler besagt, dass Sie versuchen, die Funktion "TaskDialogIndirect" aufzurufen, wenn sie nicht verfügbar ist. Diese Funktion existiert erst ab Windows Vista, daher wird sie bei älteren Windows-Versionen fehlschlagen. Es hat nichts mit Drag and Drop zu tun. –

Antwort

3
[DllImport ("ComCtl32", ...)] 

Die Bibliothek ist eine ziemlich schwere Abkürzung nehmen die comctl32.dll Windows-DLL zu verwenden. Dies neigt dazu, aus Versehen zu einem guten Ende zu kommen, aber es fällt in Ihrem Code um. Die vollständige Erklärung ist ziemlich langatmig, ich werde versuchen, es kurz zu halten. Das Kernproblem ist, dass Windows zwei Versionen von comctl32.dll hat. Die in c: \ windows \ system32 ist eine Legacy Version, die die allgemeinen Steuerelemente so implementiert, wie sie in Windows 2000 und früher ausgesehen und gearbeitet haben. Windows XP hat visuelle Stile übernommen, wodurch diese Steuerelemente sehr unterschiedlich aussehen. Es gibt eine andere DLL, die diese visuellen Stile implementiert, wird im Side-by-Side-Windows-Cache (c: \ windows \ winsxs) gespeichert.

Eine Anwendung muss Windows explizit mitteilen, dass es die neue Version der DLL unterstützt. Es gibt zwei Möglichkeiten, dies zu tun, Sie können dies in einem Manifest tun (wie WPF es tut) oder Sie können einen Betriebssystemaufruf machen, die CreateActCtx() - Funktion (so wie es Winforms macht).

Die Art, wie die Bibliothek funktioniert ist, dass sie Hoffnungen hat, dass jemand eines dieser zwei Dinge getan hat. Und die richtige Version von comctl32.dll geladen, so dass pinvoking die Funktion [DllImport] lädt nicht tatsächlich die c: \ windows \ system32-Version. Das alte, das TaskDialogIndirect() nicht implementiert. Dies funktioniert durch Zufall, weil ein Code normalerweise funktioniert. Und die Tatsache, dass Windows nur den DLL-Namen interessiert und nicht wo er herkommt, um festzustellen, ob er eine DLL laden muss.

Ich kann ein wenig raten, wie Sie kein Glück hatten. Sie verwenden Control.Invoke(), etwas, das Sie immer nur tun müssen, wenn Sie Threads verwenden. Sie zeigen dieses Formular eindeutig in einem anderen Thread an, nicht im Hauptthread der Benutzeroberfläche. Dies ist im Allgemeinen eine wirklich schlechte Idee, der UI-Thread wurde bereits entworfen, um mit mehreren Fenstern umgehen zu können. Die einzige Sache, die nicht im UI-Thread passiert ist, ist der Aufruf von Application.EnableVisualStyles(). Der, der Windows mitteilt, dass Sie die neue Version von comctl32 haben möchten.

Sie können versuchen, es auf Ihrem Arbeitsthread aufzurufen. Könnte funktionieren, keine Ahnung. Bei weitem die beste Lösung ist, keine Fenster auf Worker-Threads zu erstellen. Sie können die wonky-Bibliothek mithilfe des Windows-API-Code Packs loswerden und einen Wrapper für Taskdialoge bereitstellen.

+0

Ich habe nicht speziell versucht, den Dialog auf dem Nicht-UI-Thread zu öffnen ... Allerdings schätze ich, dass DragDrop-Handler, die abgelegte Objekte von außerhalb dieses Threads abfangen (zB eine andere App wie Windows Explorer), ihn verarbeiten der UI-Thread – Reddog

+0

Verwenden von Windows API Code Pack - das ist _absolutely_ in unseren Plänen ... Es ist viel besser. Jetzt muss ich mir einfach die Zeit dafür nehmen! – Reddog

+1

Nein, Drag & Drop erstellt keine Threads. An einem bestimmten Punkt haben Sie eine InvalidOperationException erhalten und herausgefunden, dass Sie Control.Invoke verwenden müssen, um das Problem zu beheben. Das hat aber nicht geklappt, du hättest dich fragen sollen, "warum muss ich das umgehen!". Der übliche Fall ist eine Art von E/A-Abschluss-Callback, die auf einen Thread-Pool-Thread ausgelöst wird. Wie einer für einen Sockel. Sie können es in der Stapelüberwachung sehen. –

0

Es stellte sich heraus, dass in der Dragdrop-Handler sollte I unter Verwendung von BeginInvoke sein asynchron auf das Formular des UI-Thread den Anruf in die Warteschlange im Gegensatz zu synchron zum Warten es innerhalb des Behandlungsroutine abzuschließen ...

Deshalb ist es, wurde gelöst mit:

private void MainForm_DragDrop(object sender, DragEventArgs e) 
{ 
    if (e.Data.GetDataPresent(DataFormats.FileDrop)) 
    { 
     Array fileNames = (Array)e.Data.GetData(DataFormats.FileDrop); 
     if (fileNames != null && fileNames.OfType<string>().Any()) 
     { 
      foreach (var fileName in fileNames.OfType<string>()) 
      { 
       this.BeginInvoke(new Action<string>(this.AttemptOpenFromPath), fileName); 
      } 
     } 
    } 
} 

Ich bin mir nicht sicher, warum, obwohl ?? Kann ein Kommentator vielleicht einen Grund angeben?