「クリップボードをアクセスが拒否されました。」

276 views
Skip to first unread message

Eiichiro Kamiya

unread,
Jul 18, 2023, 9:24:53 PM7/18/23
to Japan RAD Studio User Group
こんにちは、(昔の名前で)ek です。

クリップボードにテキストをコピーするプログラムが次のメッセージを出すようになりました :
 クリップボードをアクセスが拒否されました。を開けません。
Windows10 までは出たことがなく、Windows11 に上げてからもしばらくは出なかったような気がします。

ググってみたらかなり古くから出ている定番のトラブルのようで、対策として Sleep を嚙ませることが有効だとあり、ネット上のエレガントなサンプルを採用してプログラムを更新しました。しかし効果がありませんでした。

試行錯誤の末、Clipboard.Clear してからコピーすると取り敢えずはこのエラーが消えましたが、これもタイミングがあるようで単純には成功しなかったので、別の環境ではエラーが出るかもしれません。また Clear は履歴全体を消すでしょうから、使用環境によっては迷惑でしょう。もっと適切な解決策があればご教示ください。


========
神谷英一郎

細川淳

unread,
Jul 19, 2023, 1:32:11 AM7/19/23
to radstu...@googlegroups.com
こんにちは。
細川です。

恐らく VCL.Clipbrd の Clipboard だと思いますので、VCL の Clipboard について説明します。
VCL.Clipbrd.TClipboard の SetAsText (SetBuffer) の実装は横着していて
EmptyClipboard を呼んでいません。
本来は MS のサイト(※)でもあるように SetClipboardData を呼ぶ前に EmptyClipboard を呼ぶ必要があります。
EmptyClipboard を呼ばなくても概ね上手く行くのですが、それは「偶然」です。
例えば、Clipboard 追跡プログラムが入っている場合は失敗する可能性があります。
TClipboard.Clear を呼ぶと成功する(たまに失敗する)というのは Clear で EmpltyClipboard が呼ばれているからです。

絶対に成功させるためには OpenClipboard / CloseClipboard に囲まれた中で EmptyClipboard
呼び、SetClipboardData を呼ぶコードを自作しなければいけません。
たとえば、↓のような形になります。

procedure TClipboardHelper.SetTextToClip(const AText: String);
begin
if OpenClipboard(FHandle) then // FHandle は HWND
begin
try
EmptyClipboard;

var Data := GlobalAlloc(GMEM_MOVEABLE, Length(AText) *
SizeOf(Char) + 1);
if Data <> 0 then
begin
try
var DataPtr := GlobalLock(Data);
try
Move(PChar(AText)^, DataPtr^, GlobalSize(Data));
SetClipboardData(CF_UNICODETEXT, Data);
finally
GlobalUnlock(Data);
end;
except
GlobalFree(Data);
end;
end;
finally
CloseClipboard;
end;
end;
end;


https://learn.microsoft.com/ja-jp/windows/win32/dataxchg/using-the-clipboard

2023年7月19日(水) 10:24 Eiichiro Kamiya <eiichir...@gmail.com>:
> --
> このメールは Google グループのグループ「Japan RAD Studio User Group」に登録しているユーザーに送られています。
> このグループから退会し、グループからのメールの配信を停止するには radstudio-jp...@googlegroups.com にメールを送信してください。
> このディスカッションをウェブ上で閲覧するには https://groups.google.com/d/msgid/radstudio-jp/CAA1D39Q%3DEfhix7L9Zb%3Dpy0t2wLFbSdMeD8rd%2BAO1yGPSuPvd3g%40mail.gmail.com にアクセスしてください。

Eiichiro Kamiya

unread,
Jul 19, 2023, 4:21:50 AM7/19/23
to radstu...@googlegroups.com
ありがとうございます。
うまくいきそうです。

========
神谷英一郎


2023年7月19日(水) 14:32 細川淳 <j...@serialgames.co.jp>:
このメールは Google グループのグループ「Japan RAD Studio User Group」の登録者に送られています。
このグループから退会し、グループからのメールの配信を停止するには radstudio-jp...@googlegroups.com にメールを送信してください。
このディスカッションをウェブ上で閲覧するには、https://groups.google.com/d/msgid/radstudio-jp/CAEcB0P7Spd8CY98-vb8P6Bd2bJxKZopBaG4vRHsyV3kbgN60Ng%40mail.gmail.com にアクセスしてください。

Eiichiro Kamiya

unread,
Jul 20, 2023, 3:49:51 AM7/20/23
to radstu...@googlegroups.com
細川さん、ek です。お世話になっております。

昨日の簡単なテストではうまくいったように見えたのですがやはりエラーが消えませんでした。

EmptyClipboard だけを行うメソッドを追加し、これを呼んだ後でご教示いただいた SetTextToClip を呼ぶようにしたところエラーが出なくなりましたので、これで「使い込んで」みようと思います。

甘えて申し訳ございませんが、もしよろしければ GetClipboardData を実行するサンプルをいただけないでしょうか。知識不足にて動くものが作れませんでした。
こちらは vcl の Clipboard.AsText で今のところは支障なさそうですが、・・・。

========
神谷英一郎


2023年7月19日(水) 14:32 細川淳 <j...@serialgames.co.jp>:
こんにちは。
このメールは Google グループのグループ「Japan RAD Studio User Group」の登録者に送られています。
このグループから退会し、グループからのメールの配信を停止するには radstudio-jp...@googlegroups.com にメールを送信してください。
このディスカッションをウェブ上で閲覧するには、https://groups.google.com/d/msgid/radstudio-jp/CAEcB0P7Spd8CY98-vb8P6Bd2bJxKZopBaG4vRHsyV3kbgN60Ng%40mail.gmail.com にアクセスしてください。

細川淳

unread,
Jul 20, 2023, 4:47:57 AM7/20/23
to radstu...@googlegroups.com
ek さん、こんにちは。
細川です。

不思議ですね。
先のコードでは OpenClipboard が成功していれば EmptyClipboard を呼び出して Clipboard
の所有権を取得するのですが、そこが実行されていない(OpenClibpboard または EmptyCliboard
が失敗)可能性があります。
各 API の戻り値などを確認してみてください。

https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-openclipboard
https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-emptyclipboard

ちなみにテキストを Clipboard から取得する時は以下のようなコードになるかと思います(抜粋したものなので動作未確認)。

procedure TClipboardHelper.GetTextFromClip: String;
type
TPackedDWordArray = packed array of DWORD;
begin
Result := '';

var List: TPackedDWordArray := [CF_UNICODETEXT, CF_TEXT];
var Format := GetPriorityClipboardFormat(List[0], Length(List));

if OpenClipboard(FHandle) then
begin
try
var Data := GetClipboardData(Format);
if Data <> 0 then
begin
var TextData := GlobalLock(Data);
try
if Format = CF_UNICODETEXT then
Result := PChar(TextData)
else
Result := String(PAnsiChar(TextData));
finally
GlobalUnlock(Data);
end;
end;
finally
CloseClipboard;
end;
end;
end;

2023年7月20日(木) 16:49 Eiichiro Kamiya <eiichir...@gmail.com>:
> このディスカッションをウェブ上で閲覧するには https://groups.google.com/d/msgid/radstudio-jp/CAA1D39Sr8_v1tYkrocTLigYN%3D5__aWAOpQ6pWk-33%3DfCGVPVyA%40mail.gmail.com にアクセスしてください。

Eiichiro Kamiya

unread,
Jul 20, 2023, 9:08:23 PM7/20/23
to radstu...@googlegroups.com
細川さん、ek です。お世話になっております。

GetTextFromClip は問題なく動きました。

これを含めて少しコードをいじった部分がありますが、たいした変更はしていないつもりなのにダミーの EmptyCliboard なしにエラーが出なくなりました(「不思議」です)。基本はこれでうまくいきそうです。
ありがとうございました。

========
神谷英一郎


2023年7月20日(木) 17:47 細川淳 <j...@serialgames.co.jp>:
このディスカッションをウェブ上で閲覧するには、https://groups.google.com/d/msgid/radstudio-jp/CAEcB0P6NFMg2pWon3ULTq1Fz6FjeMbjgm60_81_WwL_orCydDA%40mail.gmail.com にアクセスしてください。
Reply all
Reply to author
Forward
0 new messages