58

次のコードで「CLIPBRD_E_CANT_OPEN」という内容の例外が発生することがあるのはなぜですか?

Clipboard.SetText(str);

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

4

7 に答える 7

42

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

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

for (int i = 0; i < 10; i++)
{
    try
    {
        Clipboard.SetText(str);
        return;
    }
    catch { }
    System.Threading.Thread.Sleep(10);
} 
于 2008-09-16T03:07:04.067 に答える
41

実は、これはWin32 APIのせいだと思います。

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

たまたまターミナル サービスがクリップボードを追跡しており、古いバージョンの Windows (Vista より前) では、クリップボードを開いて中身を確認する必要があります。唯一の解決策は、ターミナル サービスがクリップボードを閉じるまで待ってから、再試行することです。

ただし、これはターミナル サービスに固有のものではないことを理解することが重要です。どのようなものでも発生する可能性があります。Win32 でクリップボードを操作すると、巨大な競合状態になります。しかし、設計上、ユーザー入力に応じてクリップボードを操作することだけが想定されているため、通常、これは問題になりません。

于 2008-09-16T02:21:31.710 に答える
26

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

解決:

決して使わない

Clipboard.SetText(str);

代わりに使用

Clipboard.SetDataObject(str);
于 2016-08-24T13:46:06.440 に答える
10

実際には別の問題が発生している可能性があります。フレームワーク呼び出し (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 を直接使用する独自のクリップボード クラスを作成するか、setDataObject を false で直接呼び出して、アプリの終了後にデータを保持する以外に、適切な解決策が見つかりませんでした。

于 2012-07-30T16:57:43.207 に答える
9

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

私が作ったラッパークラスの下。誰でもレビューして、それが正しいかどうか教えてください。特にマネージ コードが x64 アプリとして実行されている場合 (私はプロジェクト オプションで任意の 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;
    }
}
于 2015-05-11T10:45:36.950 に答える
2

WinForms バージョンを使用します (はい、WPF アプリケーションで WinForms を使用しても害はありません)。必要なものはすべて処理されます。

System.Windows.Forms.SetDataObject(yourText, true, 10, 100);

これは yourText をクリップボードにコピーしようとします。コピーはアプリが存在した後も残り、最大 10 回試行され、試行ごとに 100 ミリ秒待機します。

参考文献 https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.clipboard.setdataobject?view=netframework-4.7.2#System_Windows_Forms_Clipboard_SetDataObject_System_Object_System_Boolean_System_Int32_System_Int32 _

于 2020-11-04T18:26:30.430 に答える