エクスプローラのコピー、切り取りを判定したい。

解決


いか  2003-08-16 11:18:03  No: 108127  IP: [192.*.*.*]

エクスプローラなどで、ファイル、フォルダを切り取り、貼り付けるようなことをVBで行いたいと思うのですが、

VBアプリ単体なら、できるのですが、エクスプローラと自アプリ間で切り取り、貼り付けを行いたいと考えています。以下の処理ができればと考えているのですが。

1)クリックボードからのデータの取得。
VBのクリップボードメソッドでは、取得できなかったのですが?
エクスプローラ上で切り取り、コピーは、単純にパスを覚えているだけのような気がしますが。

2)エクスプローラ上で切り取りをしたのか、コピーしたのかの判定

編集 削除
魔界の仮面弁士  2003-08-16 21:39:31  No: 108128  IP: [192.*.*.*]

> VBのクリップボードメソッドでは、取得できなかったのですが?

VB.NETですか? ならばこんな感じで。

Dim DataObject As System.Windows.Forms.IDataObject
DataObject = Clipboard.GetDataObject()

Dim DropEffect As Object = DataObject.GetData("Preferred DropEffect")
If TypeOf DropEffect Is System.IO.MemoryStream Then
  Dim Stm As System.IO.MemoryStream = DirectCast(DropEffect, System.IO.MemoryStream)
  Dim Effect As Integer = BitConverter.ToInt32(Stm.ToArray(), 0)

  Const DROPEFFECT_NONE As Integer = 0
  Const DROPEFFECT_COPY As Integer = 1
  Const DROPEFFECT_MOVE As Integer = 2
  Const DROPEFFECT_LINK As Integer = 4

  If (Effect And DROPEFFECT_COPY) <> 0 Then
    Trace.WriteLine("コピー")
  End If
  If (Effect And DROPEFFECT_MOVE) <> 0 Then
    Trace.WriteLine("切り取り")
  End If
  If (Effect And DROPEFFECT_LINK) <> 0 Then
    Trace.WriteLine("ショートカット")
  End If
End If

Dim Files As Object = DataObject.GetData(DataFormats.FileDrop)
If TypeOf Files Is String() Then
  Dim X As String
  For Each X In DirectCast(Files, String())
    Trace.WriteLine(X)
  Next
End If


VB6以下の場合は、Clipboardオブジェクトからは取得できません。
クリップボードにあるかどうかの確認だけならば、
   Const CF_HDROP = 15
   If Clipboard.GetFormat(15) Then
などで行えますが、どうしてもファイル名を取得したいなら、
クリップボード系のAPIを使う必要があります。

ただし、『FolderItemVerbオブジェクト』を使えば、
「コピー」「切り取り」「貼り付け」を実行する事はできますので、
VB6でも、APIを使わずに済ます事は可能です。


> エクスプローラ上で切り取り、コピーは、単純にパスを覚えているだけのような気がしますが。

ファイル名の一覧は、CF_HDROP形式のクリップボードデータとして保持されています。
また、これらとは別に、切り取りかコピーか(そしてショートカットを作成可能か)の情報として、
"Preferred DropEffect"というデータも同時に保持されています。

編集 削除
いか  2003-08-17 08:28:15  No: 108129  IP: [192.*.*.*]

たいへんありがとうございます。

もしよろしけらば、VB6を使っているので、そのへんのさわりだけでもお教えて頂きたいのですが。

編集 削除
魔界の仮面弁士  2003-08-18 04:23:40  No: 108130  IP: [192.*.*.*]

私の回答では、『クリップボード系のAPI』を使う方法と、
『FolderItemVerbオブジェクト』を使う方法の2案を出しましたが、
この場合の「そのへんのさわり」とは、どちらの事でしょうか?(^^;


とりあえず、APIで取得するなら、

1. 『"Preferred DropEffect"形式のデータ』を取得する。
 (RegisterClipboardFormat APIを使って、この識別値を得ます)
  この形式のデータは、切り取りかコピーかを示すビットフラグです。
    &H1 が立っていれば、コピー
    &H2 が立っていれば、移動
    &H4 が立っていれば、ショートカットを作成可能

2. 『CF_HDROP形式のデータ』を取得する。
 (識別値の定義は、 Const CF_HDROP = 15 のようになります)
  この形式のデータは、ファイル名の一覧を示す文字列です。

の2手順にて、必要な情報を得る事ができますので、あとは、
SHFileOperation APIか、もしくはVBのファイル操作ステートメント等で
実際に、それらのファイル郡を操作する事になるでしょう。
(クリップボードAPIの操作方法に付いては、ご自身で調べてみて下さい)


一方、FolderItemVerbオブジェクトを使った方法は、要するに
  『  フォルダを右クリックした時のメニュー項目にある
      [コピー]や[貼り付け]の動作を、VBから指示する。  』
という処理です。

この方法の場合は、クリップボードの内容をVB側で確認する事はありません。
それゆえ、実際にどのファイルが移動(あるいは複写)されるのかまでは
VB側では把握しません(把握する必要がありません)。

FolderItemVerbを使う場合は、[Microsoft Shell Controls And Automation]を参照設定した後に、

Dim Path As String
Dim oShell As Shell32.Shell
Dim oFItem As Shell32.FolderItem
Dim oFVerb As Shell32.FolderItemVerb

Path = "C:\test\"

Set oShell = CreateObject("Shell.Application")
Set oFItem = oShell.NameSpace(CVar(Path)).Items().Item()
For Each oFVerb In oFItem.Verbs
    If oFVerb.Name Like "貼り付け*" Then
        oFVerb.DoIt
        Exit For
    End If
Next
Set oFItem = Nothing
Set oShell = Nothing

などのようなコードで、貼り付け動作を行います。

なお上記は、参照設定無しでも動作させられますが、その場合は、
  Dim oShell As Shell32.Shell
  Dim oFItem As Shell32.FolderItem
  Dim oFVerb As Shell32.FolderItemVerb
の部分を、全て  As Object  で宣言するようにしてください。

編集 削除
いか  2003-08-21 17:09:24  No: 108131  IP: [192.*.*.*]

ありがとうございます。

まずAPIの方なのですが、

1. 『"Preferred DropEffect"形式のデータ』を取得する。
 (RegisterClipboardFormat APIを使って、この識別値を得ます)
  この形式のデータは、切り取りかコピーかを示すビットフラグです。
    &H1 が立っていれば、コピー
    &H2 が立っていれば、移動
    &H4 が立っていれば、ショートカットを作成可能

dim kekka as long
kekka = RegisterClipboardFormat("Preferred DropEffect")
のようにやってみたのですが、
コピー、切り取りをエクスプローラでやって戻り値をみてみたのですが、
49324が返ってきて、変化ありませんでした。
たぶん、やり方がおかしいと思いますが。


FolderItemVerbを使ったサンプルでやってみたところうまく動きました。
ステップして追っていったところ、確かに右クリックメニューの文字が列挙されていました。
この場合に、複数のパス、ファイル名をコピー、切り取りということは、可能なのでしょうか?

編集 削除
魔界の仮面弁士  2003-08-21 17:26:45  No: 108132  IP: [192.*.*.*]

> kekka = RegisterClipboardFormat("Preferred DropEffect")
> のようにやってみたのですが、
> コピー、切り取りをエクスプローラでやって戻り値をみてみたのですが、
> 49324が返ってきて、変化ありませんでした。

RegisterClipboardFormatが返すのは、データ形式を表す識別番号です。
クリップボードの内容を示しているのではありません。(^_^;)

その、49324(別の番号になる可能性もあります)という値を使って、
クリップボードから、情報を取得するのです。

VB6のClipboardオブジェクトの場合も、クリップボードから情報を
(GetTextやGetDataで)取得するときには、識別値——例えば、
vbCFText(=&H1)、vbCFEMetafile(=&HE)、vbCFRTF(=&HBF01)など——を
指定しますよね。APIの場合もそれと同じ事です。

> この場合に、複数のパス、ファイル名をコピー、切り取りということは、可能なのでしょうか?
可能ですよ。詳細はPlatform SDKで調べて、ご自身で試して見てください。

編集 削除
魔界の仮面弁士  2003-08-21 21:47:34  No: 108133  IP: [192.*.*.*]

>> この場合に、複数のパス、ファイル名をコピー、切り取りということは、可能なのでしょうか?
> 可能ですよ。詳細はPlatform SDKで調べて、ご自身で試して見てください。

って、肝心な『手順』を書かないと、調べようが無いですよね。。。(^^;

複数のパスを指示したい場合は、ShellFolderViewオブジェクトを利用します。
ShellFolderViewのSelectItemメソッドで、サブフォルダやファイルを
選択状態にできます。また、選択状態にしたファイル/フォルダは、
SelectedItemsメソッドにて、FolderItemsコレクションとして取得できます。
FolderItemsとして取得したあとの処理は、先と同様、FolderItemVerbメソッドで処理すればOKです。


なお、これらのオブジェクトに関しては、下記を参照してください。
[Shell Objects for Scripting and Microsoft Visual Basic]
http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/reference/objects/objects.asp

編集 削除
いか  2003-08-29 15:26:33  No: 108134  IP: [192.*.*.*]

わざわざありがとうございます。
NSDNをみながらやってみたのですが、

    Dim Path(2) As String
    Dim lop As Integer
    
    Dim oShell As Shell32.Shell
    Dim oFItem As Shell32.FolderItem
    Dim oFItems As Shell32.FolderItems
    Dim oFVerb As Shell32.FolderItemVerb
    Dim oFView As Shell32.ShellFolderView
    
    Path(0) = "C:\test"
    Path(1) = "C:\test2"
    
    Set oShell = CreateObject("Shell.Application")
 
    For lop = 0 To 1
        Set oFItem = oShell.NameSpace(Path(lop)).Items.Item
        oFView.SelectItem oFItem, 1
    Next
    
    Set oFItems = oFView.SelectedItems
    
    For Each oFVerb In oFItems.Verbs
        If oFVerb.Name Like "貼り付け*" Then
            oFVerb.DoIt
            Exit For
        End If
    Next
    
    Set oFItem = Nothing
    Set oShell = Nothing


 oFView.SelectItem oFItem, 1の行で

実行時エラー'91':
オブジェクト変数またはWithブロック変数が設定されていません。

がでてさきに進みません。
SelectItemの引数が違うと思うのですが、

ShellFolderView.SelectItem(vItem, dwFlags)
Parameters

vItem Required. 
The FolderItem object for which the selection state will be set. 

VItemは、どのようにして取得するのでしょうか?
FolderItemでは、ない?

編集 削除
魔界の仮面弁士  2003-08-29 16:11:35  No: 108135  IP: [192.*.*.*]

>  oFView.SelectItem oFItem, 1の行で
> 実行時エラー'91':
> オブジェクト変数またはWithブロック変数が設定されていません。

オブジェクト変数oFViewが設定されていない(Nothing状態)だからです。

編集 削除
いか  2003-08-29 16:46:06  No: 108136  IP: [192.*.*.*]

>オブジェクト変数oFViewが設定されていない(Nothing状態)だからです。

たいへんありがとうございます。
MSDNをもう一度みてみたいと思います。

編集 削除
いか  2003-08-29 17:06:21  No: 108137  IP: [192.*.*.*]

みてみたのですが、わかりませんでした。
これ以上教えていただくのも悪いので、実現をあきらめます。

編集 削除
いか  2003-09-04 10:42:52  No: 108138  IP: [192.*.*.*]

>オブジェクト変数oFViewが設定されていない(Nothing状態)だからです。

このオブジェクトは、どのスクリプトで取得するのでしょうか?

編集 削除
魔界の仮面弁士  2003-09-04 12:51:36  No: 108139  IP: [192.*.*.*]

エクスプローラあるいはInternet Explorer等を起動して、
そこにフォルダを表示させてください。例えば、こんな感じ。

Private Sub Command1_Click()
    Dim W       As Object   'As InternetExplorer
    Dim oFView  As Object   'As ShellFolderView
    Dim oFItems As Object   'As FolderItems
    Dim oFItem  As Object   'As FolderItem

    
    Set W = CreateObject("InternetExplorer.Application")
    W.Navigate2 "C:\Program Files\"
    W.Visible = True    '表示/非表示の切り替え
    
    Do
       DoEvents
    Loop Until W.busy = 4
    
    Set oFView = W.Document
    Set oFItems = oFView.SelectedItems
    Set oFItem = oFView.FocusedItem
    Debug.Print "oFView : " & TypeName(oFView)
    Debug.Print "oFItems: " & TypeName(oFItems)
    Debug.Print "oFItem : " & TypeName(oFItem)
    
    Debug.Print "このフォルダの項目数", oFView.Folder.Items().Count
    Debug.Print "現在選択されている項目数", oFItems.Count
    If oFItem Is Nothing Then
        Debug.Print "フォーカスがありません。"
    Else
        Debug.Print oFItem.Name
    End If

    Set oFItem = Nothing
    Set oFItems = Nothing
    Set oFView = Nothing

    W.Quit  '終了
    Set F = Nothing
End Sub

編集 削除
魔界の仮面弁士  2003-09-04 12:54:54  No: 108140  IP: [192.*.*.*]

———訂正。m(_ _)m


>    Loop Until W.busy = 4
これは、 
     Loop While W.busy
もしくは、
     Loop Until W.readystate = 4
に。


>    Set F = Nothing
これは、Set W = Nothing で。

編集 削除