.NET からクリップボードを設定すると CLIPBRD_E_CANT_OPEN エラーが発生する

StackOverflow https://stackoverflow.com/questions/68666

  •  09-06-2019
  •  | 
  •  

質問

次のコードにより、内容が「CLIPBRD_E_CANT_OPEN」の例外が発生する場合があるのはなぜですか:

Clipboard.SetText(str);

これは通常、アプリケーションでクリップボードが初めて使用されるときに発生し、それ以降は発生しません。

役に立ちましたか?

解決

実はこれだと思うのですが、 Win32 API の障害.

クリップボードにデータを設定するには、次のことを行う必要があります。 それを開く 初め。クリップボードを開くことができるのは、一度に 1 つのプロセスだけです。したがって、別のプロセスがクリップボードを開いているかどうかを確認するときに、 何らかの理由で, 、開こうとしても失敗します。

たまたま、ターミナル サービスがクリップボードを追跡しているため、古いバージョンの Windows (Vista 以前) では、中身を確認するにはクリップボードを開かなければなりません...それはあなたをブロックすることになります。唯一の解決策は、ターミナル サービスがクリップボードを閉じるまで待って、再試行することです。

ただし、これはターミナル サービスに固有のものではないことを認識することが重要です。それは何にでも起こり得ることです。Win32 でクリップボードを操作すると、大きな競合状態が発生します。ただし、設計上、ユーザー入力に応じてクリップボードを操作することのみが想定されているため、通常はこれで問題は発生しません。

他のヒント

これは、ターミナル サービスのクリップボード (およびその他の可能性のあるもの) のバグ/機能と、クリップボードの .NET 実装が原因で発生します。クリップボードを開くのが遅れるとエラーが発生しますが、通常は数ミリ秒以内に解決します。

解決策は、ループ内で複数回試行し、その間にスリープすることです。

for (int i = 0; i < 10; i++)
{
    try
    {
        Clipboard.SetText(str);
        return;
    }
    catch { }
    System.Threading.Thread.Sleep(10);
} 

この質問が古いことは承知していますが、問題はまだ存在します。前述したように、この例外はシステム クリップボードが別のプロセスによってブロックされている場合に発生します。残念ながら、Windows のクリップボードをブロックする可能性のある切り取りツール、スクリーンショット用のプログラム、ファイル コピー ツールが多数あります。したがって、使用しようとするたびに例外が発生します Clipboard.SetText(str) そのようなツールが PC にインストールされている場合。

解決:

決して使用しないでください

Clipboard.SetText(str);

代わりに使用してください

Clipboard.SetDataObject(str);

実際には別の問題が存在する可能性があります。フレームワーク呼び出し (WPF と winform フレーバーの両方) は次のようなものを呼び出します (コードはリフレクターからのものです)。

private static void SetDataInternal(string format, object data)
{
    bool flag;
    if (IsDataFormatAutoConvert(format))
    {
        flag = true;
    }
    else
    {
        flag = false;
    }
    IDataObject obj2 = new DataObject();
    obj2.SetData(format, data, flag);
    SetDataObject(obj2, true);
}

この場合、SetDataObject は常に true で呼び出されることに注意してください。

これにより、内部的には win32 API への 2 つの呼び出しがトリガーされます。1 つはデータを設定し、もう 1 つはアプリからデータをフラッシュして、アプリが閉じた後に使用できるようにします。

クリップボード イベントをリッスンするアプリ (いくつかの Chrome プラグインやダウンロード マネージャー) をいくつか見てきました。最初の呼び出しがヒットするとすぐに、アプリはクリップボードを開いてデータを調べますが、2 番目のフラッシュ呼び出しは失敗します。

直接 win32 API を使用する独自のクリップボード クラスを作成するか、アプリ終了後にデータを保持するために false を指定して setDataObject を直接呼び出すこと以外に、良い解決策が見つかりませんでした。

私はネイティブの Win32 関数を使用して自分のアプリでこの問題を解決しました。OpenClipboard()、CloseClipboard()、および SetClipboardData()。

以下は私が作成したラッパークラスです。誰でもできますか お願いします それを見直して、 それが正しいかどうか教えてください. 。特にマネージ コードが x64 アプリとして実行されている場合 (プロジェクト オプションで Any CPU を使用しています)。 x64 アプリから x86 ライブラリにリンクするとどうなりますか?

ありがとう!

コードは次のとおりです。

public static class ClipboardNative
{
    [DllImport("user32.dll")]
    private static extern bool OpenClipboard(IntPtr hWndNewOwner);

    [DllImport("user32.dll")]
    private static extern bool CloseClipboard();

    [DllImport("user32.dll")]
    private static extern bool SetClipboardData(uint uFormat, IntPtr data);

    private const uint CF_UNICODETEXT = 13;

    public static bool CopyTextToClipboard(string text)
    {
        if (!OpenClipboard(IntPtr.Zero)){
            return false;
        }

        var global = Marshal.StringToHGlobalUni(text);

        SetClipboardData(CF_UNICODETEXT, global);
        CloseClipboard();

        //-------------------------------------------
        // Not sure, but it looks like we do not need 
        // to free HGLOBAL because Clipboard is now 
        // responsible for the copied data. (?)
        //
        // Otherwise the second call will crash
        // the app with a Win32 exception 
        // inside OpenClipboard() function
        //-------------------------------------------
        // Marshal.FreeHGlobal(global);

        return true;
    }
}

これは私の WPF アプリケーションで発生しました。OpenClipboard が失敗しました (HRESULT からの例外:0x800401D0 (CLIPBRD_E_CANT_OPEN))。

私が使う

ApplicationCommands.Copy.Execute(null, myDataGrid);

解決策は、まずクリップボードをクリアすることです

Clipboard.Clear();
ApplicationCommands.Copy.Execute(null, myDataGrid);
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top