喫茶XRAYトップページ


[10859] ファイルのクリップボードへのコピー返信 削除
2017/2/22 (水) 17:25:13 Mr.XRAY
ZeosLib と宇宙人が一段落ついたので次です.

ファイルのリストをクリップボードにコピーする参考コードです.
TClipboard.SetAsHandle で行う場合です.

関数にしてみました.


implementation

uses
  ShlObj,
  ClipBrd;

{$R *.dfm}

//-----------------------------------------------------------------------------
//  引数の文字列配列(ファイル名のリスト)をクリップボードに送る
//  クリップボードの形式はCF_HDROP
//
//  動作確認は Windows 7 U64(SP1) + Delphi XE(UP1) Pro
//  usesにShlObj,ClipBrdが必要
//-----------------------------------------------------------------------------
procedure CopyFilesToClipboard(const FileNames: array of string);
var
  LFileListText : string;
  LValue        : string;
  LpDropFiles   : PDropFiles;
  LhGlobal      : THandle;
  LLen          : Integer;
begin
  //CF_HDROP形式のファイルのコピーは,ファイル名の末尾に#0を付加
  //最後は#0#0とする
  LFileListText := '';
  for LValue in FileNames do begin
    LFileListText := LFileListText + LValue + #0;
  end;
  LFileListText := LFileListText + #0;


  LLen := ByteLength(LFileListText);
  LhGlobal := GlobalAlloc(GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,
                          SizeOf(TDropFiles) + LLen);

  if (LhGlobal = 0) then begin
    //エラー
  end else begin
    try
      LpDropFiles := GlobalLock(LhGlobal);
      if (LpDropFiles = nil) then begin
        //エラー
      end else begin
        try
          LpDropFiles^.pFiles := SizeOf(TDropFiles);
          LpDropFiles^.fWide  := True;

          if LFileListText <> '' then begin
            Move(LFileListText[1], (PByte(LpDropFiles) + SizeOf(TDropFiles))^, LLen);
          end;
        finally
          GlobalUnlock(LhGlobal);
        end;
        Clipboard.SetAsHandle(CF_HDROP, LhGlobal);
      end;
    except
      GlobalFree(LhGlobal);
    end;
  end;
end;

//=============================================================================
//  コピーするファイル名のセット
//  文字列配列は他にもいろいろあるので適当に
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFileNameArray : array of string;
begin
  SetLength(LFileNameArray, 2);

  //ファイル名のチェックはなし
  //ファイルが1つでも存在しないとファイルを誤認識してコピーしない
  LFileNameArray[0] := ExpandFileName('..\CCC\DDD');       //フォルダ
  LFileNameArray[1] := ExpandFileName('..\CCC\AA1.txt');   //ファイル

  CopyFilesToClipboard(LFileNameArray);
end;

//=============================================================================
//  クリップボードを空にする
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  Clipboard.Clear;
end;



[10860] Re:ファイルのクリップボードへのコピー返信 削除
2017/2/22 (水) 17:57:46 Mr.XRAY
> ファイルのリストをクリップボードにコピーする参考コードです.
> TClipboard.SetAsHandle で行う場合です.


参考コードは以下.

[...Copy Files to the Windows clipboard?]
http://www.swissdelphicenter.ch/en/showcode.php?id=1038

↑ のコードは Ansi 版の delphi の時代のものです.


[10861] Re:ファイルのクリップボードへのコピー返信 削除
2017/2/22 (水) 22:25:05 Mr.XRAY
> ファイルのリストをクリップボードにコピーする参考コードです.
> TClipboard.SetAsHandle で行う場合です.


ハイ.この件は早くも終了 !!
いつも,こうありたいですね.


[10862] Re2:ファイルのクリップボードへのコピー返信 削除
2017/2/22 (水) 22:53:39 taka
▼ Mr.XRAYさん
> > ファイルのリストをクリップボードにコピーする参考コードです.
> > TClipboard.SetAsHandle で行う場合です.
>
> ハイ.この件は早くも終了 !!
> いつも,こうありたいですね.


え、別コードで 切り捨てですか?! もったいない

Delphi 2005 で実行できるように いじらせてもらいました。
(10.1も最後に実行してみました)

変更点
・眼に見えない操作なので、クリップボードは呼び出した時点でクリアしました
・個数が0の時は、クリップボードにコピーしない
・'' は無視
・LpDropFilesは、#0で初期化
・古いDelphiで構文エラーがでたのでその修正 ByteLength , Move

[エラー] Unit1.pas(56): E2003 未定義の識別子 : 'ByteLength'
[エラー] Unit1.pas : E2015 この型には指定した演算子は使えません

  SetLength(LFileNameArray, 1);
  LFileNameArray[0] := Application.ExeName;   //ファイル
  に置き換えてコピーできるか動作確認しました。

procedure CopyFilesToClipboard(const FileNames: array of string);
var
  LFileListText : string;
  LValue        : string;
  LpDropFiles   : PDropFiles;
  LhGlobal      : THandle;
  LLen          : Integer;
begin
  //CF_HDROP形式のファイルのコピーは,ファイル名の末尾に#0を付加
  //最後は#0#0とする
  LhGlobal := 0;
  LFileListText := '';
  Clipboard.Clear;

  for LValue in FileNames do begin
    if (LValue <> '') then begin
      LFileListText := LFileListText + LValue + #0;
    end
  end;
  LFileListText := LFileListText + #0;

  LLen := {$IFDEF UNICODE}ByteLength{$ELSE}Length{$ENDIF}(LFileListText);
  if (LLen = 1) then
    Exit;

  LhGlobal := GlobalAlloc(GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,
                          SizeOf(TDropFiles) + LLen);

  if (LhGlobal = 0) then begin
    //エラー
  end else begin
    try
      LpDropFiles := GlobalLock(LhGlobal);
      if (LpDropFiles = nil) then begin
        //エラー
      end else begin
        try
          FillChar(LpDropFiles^, SizeOf(LpDropFiles^), #0);
          LpDropFiles^.pFiles := SizeOf(TDropFiles);
          {$IFDEF UNICODE}LpDropFiles^.fWide  := True;{$ENDIF}

          if LFileListText <> '' then begin
{$IF CompilerVersion >= 18.0}
            Move(LFileListText[1], (PByte(LpDropFiles) + SizeOf(TDropFiles))^, LLen);
{$ELSE}
            Move(LFileListText[1], Ptr(Integer(LpDropFiles) + SizeOf(TDropFiles))^, LLen);
{$IFEND}
          end;
        finally
          GlobalUnlock(LhGlobal);
        end;
        Clipboard.SetAsHandle(CF_HDROP, LhGlobal);
      end;
    except
      GlobalFree(LhGlobal);
    end;
  end;
end;

[10863] Re3:ファイルのクリップボードへのコピー返信 削除
2017/2/22 (水) 23:53:41 Mr.XRAY
▼ takaさん

> え、別コードで 切り捨てですか?! もったいない
>
> Delphi 2005 で実行できるように いじらせてもらいました。
> (10.1も最後に実行してみました)


わわわっ,ゴメンナさい.
ZeosLib とか SQLite は他のバージョンの Delphi でも使いたい人が
いるかと思い,いろいろ確認しておいた方がいいと思いまして.
このコードは,実はメールでの質問の返信用なんです.例によって.

> 変更点
> ・眼に見えない操作なので、クリップボードは呼び出した時点でクリアしました
> ・個数が0の時は、クリップボードにコピーしない
> ・'' は無視
> ・LpDropFilesは、#0で初期化
> ・古いDelphiで構文エラーがでたのでその修正 ByteLength , Move
>
> [エラー] Unit1.pas(56): E2003 未定義の識別子 : 'ByteLength'
> [エラー] Unit1.pas : E2015 この型には指定した演算子は使えません
>
>   SetLength(LFileNameArray, 1);
>   LFileNameArray[0] := Application.ExeName;   //ファイル
>   に置き換えてコピーできるか動作確認しました。


おおっと,初期化しないと... ハハハツ.
ByteLength はしょうがないととして,PByte が...
なるほど,Ptr(Integer ですか.ですよね.
昔は PChar をポインタ代わりに使用するコードが多かったですね.
勉強させて頂きました.いつもスミマせん.



[10864] Re4:ファイルのクリップボードへのコピー返信 削除
2017/2/22 (水) 23:56:56 Mr.XRAY
> わわわっ,ゴメンナさい.
> ZeosLib とか SQLite は他のバージョンの Delphi でも使いたい人が
> いるかと思い,いろいろ確認しておいた方がいいと思いまして.
> このコードは,実はメールでの質問の返信用なんです.例によって.


でも,ここに書いたことによって taka さんに教えて頂いたのでよかった !!
個人あてのメールよりも,コミニュティの場で,というのはこういうことなんですけどね.

これももったいないから,どこかに記事として掲載しようかな...


[10865] Re5:ファイルのクリップボードへのコピー返信 削除
2017/2/23 (木) 00:09:22 Mr.XRAY
> これももったいないから,どこかに記事として掲載しようかな...

ZeosLib 関係は,以下の [備考 4] に勝手に,この喫茶XRAYの記事に
リンクを貼らせていただきました.

http://mrxray.on.coocan.jp/Delphi/plSamples/564_ZeosLib_DelphiBerlin.htm#01


[10866] Re4:ファイルのクリップボードへのコピー返信 削除
2017/2/23 (木) 17:41:14 taka
▼ Mr.XRAYさん
> ByteLength はしょうがないととして,PByte が...
> なるほど,Ptr(Integer ですか.ですよね.
> 昔は PChar をポインタ代わりに使用するコードが多かったですね.


Integer ?

ごめんなさい!!
64bit環境考慮していませんでした(^^ ;

Move(LFileListText[1], PByte(LongInt(LpDropFiles) + SizeOf(TDropFiles))^, LLen);

です

・ ポインタアドレスのサイズと同じサイズの整数は
  プラットフォーム依存の整数の LongInt型
   (LongWordではない理由は不明.)
   Integer は、常に32 ビットなので LongIntに置き換える。

・「型なしポインタ」の逆参照は 通常使ってはいけないらしいので、PByteに戻しました。

アドレス計算は、LongInt型にあてないといけないようです。
Freepascalには、PtrInt, PtrUIntという型があり、アドレス計算しやすいようになっているようです。
PtrInt型 : http://www.freepascal.org/docs-html/rtl/system/ptrint.html

Unicode版はCharが2バイトになったので、 混乱するので PByteのほうがいいですね。
古いdelphi(Version 5とか)は type PByte = ^Byte; を宣言してもらう


[10867] Re5:ファイルのクリップボードへのコピー返信 削除
2017/2/23 (木) 18:56:03 Mr.XRAY
▼ takaさん

> 64bit環境考慮していませんでした(^^ ;

64 ビット環境の考慮でしたら,前の taka さんのコードでいいと思います.
何故なら,Delphi 2007 以前では 64 ビットのアプリの開発ができませんから (^^:

>    Integer は、常に32 ビットなので LongIntに置き換える。

えっと,LongInt は Windows の環境では.
32 ヒットのアプリでも 64 ビットのアプリでも 4 バイトだったような.
NativeInt であれば変化します.
まだ,記事作成の用意ができていないので,後で確認してみますです.

> Freepascalには、PtrInt, PtrUIntという型があり、アドレス計算しやすいようになっているようです。
> PtrInt型 : http://www.freepascal.org/docs-html/rtl/system/ptrint.html


ずるい !! (^^


> Unicode版はCharが2バイトになったので、 混乱するので PByteのほうがいいですね。
> 古いdelphi(Version 5とか)は type PByte = ^Byte; を宣言してもらう


御意 !!
それがいいと思います.


> 64bit環境考慮していませんでした(^^ ;

この 64 ビット環境への考慮なんですが,
そうなんですよ.ポインタは 64 ビットを考慮しないといけないんですよね.
それでですね.サイトのサンプルは Delphi XE 使っているんです.
これなら,32 ヒット限定できから.

64 ビットのアプリ作成で使う場合は,もし,質問がきたら,そのサンプルだけは
チェックしようと思っています.
今のところ,そのような質問は,記憶ではありません.


[10868] Re6:ファイルのクリップボードへのコピー返信 削除
2017/2/23 (木) 19:14:58 taka
▼ Mr.XRAYさん
> えっと,LongInt は Windows の環境では.
> 32 ヒットのアプリでも 64 ビットのアプリでも 4 バイトだったような.
> NativeInt であれば変化します.


LongInt の範囲は
ここを参考にしました
http://docwiki.embarcadero.com/RADStudio/Berlin/ja/単純型(Delphi)
プラットフォーム依存の整数型

たしかに、よく見るとWindowsでは、4ビットとかいてありますね。
NativeIntになります。

DelphiにもPtrIntが必要ですね・・・。

> まだ,記事作成の用意ができていないので,後で確認してみますです.
>
> この 64 ビット環境への考慮なんですが,
> そうなんですよ.ポインタは 64 ビットを考慮しないといけないんですよね.
> それでですね.サイトのサンプルは Delphi XE 使っているんです.
> これなら,32 ヒット限定できから.

そうだったんですね。



[10869] Re7:ファイルのクリップボードへのコピー返信 削除
2017/2/23 (木) 20:01:02 Mr.XRAY
▼ takaさん

> > この 64 ビット環境への考慮なんですが,
> > そうなんですよ.ポインタは 64 ビットを考慮しないといけないんですよね.
> > それでですね.サイトのサンプルは Delphi XE 使っているんです.
> > これなら,32 ヒット限定できから.


> そうだったんですね。

一応名誉のためにお断りしておきますが,
Delphi XE5, XE7 もインストールだけはしています... (^^;  (だからどうした ?)


[10870] Re8:ファイルのクリップボードへのコピー返信 削除
2017/2/23 (木) 20:30:01 taka
▼ Mr.XRAYさん
> Delphi XE5, XE7 もインストールだけはしています... (^^;  (だからどうした ?)
いっぱい持っていますね。
私は、Delphi2, 2005 と 昨年ただで入手した 10.1 starterだけです。
(Delphi6はウイルス騒ぎがあったときに破棄しました)

10年の間にDelphiいろいろ変わりましたね。
今日は、Delphiには、ポインタ算術演算が実装されている
ということを覚えました。

ポインタのアドレスの加減算のまとめ

新しめのDelphi
 C言語のように直接ポインタアドレスに加減算の処理ができる
 ポインタ算術演算が実装されている

とても古いDelphi
  ポインタ算術演算が実装されていないため
 Integerにキャストして整数にして加減算の処理してポインターにキャストして戻す


[10871] Re9:ファイルのクリップボードへのコピー返信 削除
2017/2/23 (木) 20:50:16 Mr.XRAY
▼ takaさん

> 10年の間にDelphiいろいろ変わりましたね。

ほんとに変わりました.実感です.
Delphi 7 を起動して作業する時,時々

http://mrxray.on.coocan.jp/Delphi/Others/A_IDETechnique.htm#10

んことやって,あれ ? なんて思う時あります.

> 今日は、Delphiには、ポインタ算術演算が実装されている
> ということを覚えました。


これも便利です.ハッキリ言って.
でも,結構整数でキャストしちゃってますが (^^;


[10872] Re10:ファイルのクリップボードへのコピー返信 削除
2017/2/23 (木) 22:31:38 taka
▼ Mr.XRAYさん
> ▼ takaさん
>
> > 10年の間にDelphiいろいろ変わりましたね。
>
> ほんとに変わりました.実感です.
> Delphi 7 を起動して作業する時,時々
>
> http://mrxray.on.coocan.jp/Delphi/Others/A_IDETechnique.htm#10
>  ( Delphi 2007 以降 )

選択行全体のコメントアウトとコメント解除
は、Delphi2005にもあります

目次 タブ
Borlrand Help : Delphi 2005(共通) : ファーストステップ : コードエディタ
を開くと
ブロックコメントという名前で掲載されてまして
> コードエディタでコードを選択し,〔Ctrl〕+〔/〕(スラッシュ)を押せば,
> ...

とあります

コメントアウトのショートカットキーが
Delphi2005
NEW! 変数やフィールドの宣言、メソッドやリソース文字列の抽出、名称変更などを可能とするDelphiリファクタリング
とあるので、これに含まれているのか
そうではなくて、その前の、Delphi8からあるのかは謎です。
Delphi7にはないってことは、8か2005からってことですね。

同期編集モードは使っているのですが
ブロック選択 は、存在を知りませんでした。

個人的には、インデントのショートカットの時に
よく間違って選択コードをびしぃっと消しちゃいます


[10873] Re11:ファイルのクリップボードへのコピー返信 削除
2017/2/24 (金) 09:30:43 Mr.XRAY
▼ takaさん

> は、Delphi2005にもあります

あら,そうでしたから.失礼しました.
後で修正しておこう !!


[10874] サンプル掲載しました返信 削除
2017/2/24 (金) 10:57:27 Mr.XRAY
以下です.

[825_TClipboard.SetAsHandle を使用したファイルのコピー]
http://mrxray.on.coocan.jp/Delphi/plSamples/825_FilesCooyToClipboard.htm

ファイル名の長さの検出が間違っていましたので修正しました.
最後に必ず #0 か #0#0 があるので... (^^;

それから,修正ではないですが,話題にしたポインタですが,
PByte または Ptr は,64 ビットでは自動的に 64 ビットになるので,
そのままで OK のようです.
Integer(LpDropFiles) がどの程度の値になるかにもよると思いますが,
NativeInt にすると Delphi 6 で使えないので...

taka さん,ありがとうございます.
これでやっと終了かな ?


INCM/CMT
Cyclamen v3.82