1

ComCtl32.dll の Vista スタイルのダイアログを利用するTaskDialog ライブラリを利用する WinForms アプリケーションがあり、下位の OS ではエミュレートされた win フォームを使用します...

しかし、それは問題ではありません...このライブラリは正常に動作し、問題が発生したことはありません. 今までは... 実際、通常の状況でダイアログを起動すると、問題ないように見えます。

ただし、他のソース (Windows エクスプローラーなど) からドロップされたファイル パスをキャプチャするために、メイン フォームにドラッグ アンド ドロップ ハンドラーを追加しました。そのドラッグ アンド ドロップ ハンドラがダイアログが初めて表示された場合、次の例外が発生します。

DLL 'ComCtl32' で 'TaskDialogIndirect' という名前のエントリ ポイントが見つかりません。

これは、サード パーティ ライブラリの次の呼び出しで発生します。

    /// <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 );

ダイアログがすでに表示されている場合、ハンドラーは正常に実行されます。

フォームの DragDrop ハンドラは表示されませんが、とにかくInvokeRequiredダイアログを表示するように注意しました。Form.Invoke

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);
            }
        }
    }
}

余談ですが、64ビットのWindows 7マシンでコンパイル(および実行)していますが、「AnyCPU」アーキテクチャフラグを使用しています。

最初の呼び出しがTaskDialogIndirectDragDrop ハンドラーを介した場合にのみ例外が発生する理由についての考え/解決策???

4

2 に答える 2

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

ライブラリは、comctl32.dll Windows dll を使用するためにかなり重いショートカットを使用しています。これは、たまたま良い結果に終わる傾向がありますが、コードでは失敗します。完全な説明はかなり長くなりますので、簡潔にしようと思います。

中心的な問題は、Windowsに comctl32.dll の2 つのバージョンがあることです。c:\windows\system32 にあるものはレガシーバージョンであり、Windows 2000 以前の外観と機能を備えた共通コントロールを実装しています。Windows XP はビジュアル スタイルを取得したため、これらのコントロールの外観は大きく異なります。これらのビジュアル スタイルを実装する別のDLLがあり、Windows のサイド バイ サイド キャッシュ (c:\windows\winsxs) に格納されます。

アプリケーションは、新しいバージョンの DLL をサポートしていることを Windows に明示的に通知する必要があります。これを行うには 2 つの方法があります。マニフェストで行う方法 (WPF の方法) か、オペレーティング システムの呼び出しである CreateActCtx() 関数 (Winforms の方法) を作成する方法です。

ライブラリの仕組みは、誰かがこれら 2 つのことのいずれかを行ったことを期待するというものです。[DllImport] 関数をピンボークしても実際には c:\windows\system32 バージョンが読み込まれないように、正しいバージョンの comctl32.dll を読み込みました。TaskDialogIndirect() を実装していない古いもの。一部のコードは通常動作するため、これは偶然に動作します。また、Windows は DLL 名のみを考慮し、DLL をロードする必要があるかどうかを判断するために、その名前がどこから来たのかは気にしません。

あなたがどうやって運を使い果たしたのか、なんとなく推測できます。Control.Invoke() を使用しています。これは、スレッドを使用しているときにのみ行う必要があることです。明らかに、メインの UI スレッドではなく、別のスレッドでこのフォームを表示しています。これは一般的に非常に悪い考えです。UI スレッドは、複数のウィンドウを処理できるように設計されています。通常は UI スレッドで発生し、Application.EnableVisualStyles() 呼び出しは発生しませんでした。comctl32 の新しいバージョンが必要であることを Windows に伝えるもの。

ワーカースレッドで呼び出してみてください。うまくいくかもしれませんが、わかりません。断然最善の解決策は、ワーカー スレッドにウィンドウを作成しないことです。Windows API Code Pack を使用して不安定なライブラリを取り除くことができます。これは、タスク ダイアログのラッパーを提供します。

于 2012-03-30T21:20:53.373 に答える
0

BeginInvokeDragDrop ハンドラー内で、呼び出しがハンドラー内で完了するのを同期的に待機するのではなく、フォームの UI スレッドへの呼び出しを非同期的にキューに入れる必要があることが判明しました...

したがって、次の方法で解決しました。

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);
            }
        }
    }
}

なぜだかわからないけど!?? コメンターはおそらく理由を提供できますか?

于 2012-03-30T20:50:30.927 に答える