C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

複数ファイルをコピーする時に進捗ダイアログを表示する

[トピック内 16 記事 (1 - 16 表示)]  << 0 >>

■85029 / inTopicNo.1)  複数ファイルをコピーする時に進捗ダイアログを表示する
  
□投稿者/ カンガルー (1回)-(2017/09/05(Tue) 19:05:14)

分類:[.NET 全般] 

例えば、100MBのファイルを100個コピーすると、
ダイアログが表示され、
コピーの進捗を知ることができます。

これをVB.NETで行いたいのですが
どのようにすれば良いですか?

https://dobon.net/vb/dotnet/file/filecopy.html

My.Computer.FileSystemを使用すれば
ダイアログは表示されるのですが
ファイル全体ではなく
ファイル一つずつの進捗状況が表示されてしまいます。

一体どのようにすれば良いですか?


引用返信 編集キー/
■85033 / inTopicNo.2)  Re[1]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ Azulean (858回)-(2017/09/05(Tue) 22:13:49)
No85029 (カンガルー さん) に返信
> ファイル全体ではなく
> ファイル一つずつの進捗状況が表示されてしまいます。
>
> 一体どのようにすれば良いですか?

どこまで予想がつきますか?
なお、一撃でそこまでやってくれる便利なメソッドはないので、ファイルコピーや進捗表示含めて自作する必要がありますが、それでもチャレンジしたい状況ですか?

・バックグラウンドで処理を実行するということ
・コピー対象のファイルの列挙、コピー先の状況の確認が必要(上書き確認など)
・必要であれば自力でアニメーション表示
・ファイル数単位で進捗表示でよければ File.Copy、ダメなら自分で File を読み込んで書き込む処理の実装
・キャンセル対応
・エラー処理、エラーメッセージ表示

仮に続行する場合でも全部のやり方を教えてくれでは、「質問」ではなく「依頼」になるのでどこかの業者にお願いしてください。
それぞれのキーワードを元に調べ、試行錯誤した結果について、具体的な質問をするようにした方が答えを得られやすいでしょう。
引用返信 編集キー/
■85034 / inTopicNo.3)  Re[2]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ カンガルー (2回)-(2017/09/06(Wed) 01:19:36)
ご回答ありがとうございます。

いまやりたいことは
Windowsの標準機能を使いたい、だけなので
VSを使えば簡単にできるのではないかと思って質問したのですが
簡単にできるコマンドは存在せず
自分で、同じような挙動をするコードを作るしかない、ということですか?
 
引用返信 編集キー/
■85035 / inTopicNo.4)  Re[3]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ カンガルー (3回)-(2017/09/06(Wed) 01:22:50)
すいません、ご回答に、そう書かれてありましたね。

自分でコードの書き方が分からない部分は
複数のファイルをコピー中に
1つのファイルのバイトデータをコピーしている
進捗状況を取得するというところだけです。

そこだけで良いのでできればお教えいただけないでしょうか?

 
引用返信 編集キー/
■85036 / inTopicNo.5)  Re[4]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ Azulean (859回)-(2017/09/06(Wed) 07:21:03)
No85035 (カンガルー さん) に返信
> 自分でコードの書き方が分からない部分は
> 複数のファイルをコピー中に
> 1つのファイルのバイトデータをコピーしている
> 進捗状況を取得するというところだけです。
>
> そこだけで良いのでできればお教えいただけないでしょうか?

1 つのファイルコピーに絞るのであれば、Win32API を使ってコールバック関数に進捗を通知してもらうようにすれば良いでしょうね。
https://msdn.microsoft.com/ja-jp/library/windows/desktop/aa363852

My.Computer.FileSystem ではどうしようもありませんので、ファイルを検索する、進捗ダイアログを作るあたりの自作とセットです。
引用返信 編集キー/
■85037 / inTopicNo.6)  Re[3]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ とっちゃん (454回)-(2017/09/06(Wed) 11:05:23)
No85034 (カンガルー さん) に返信
> ご回答ありがとうございます。
>
> いまやりたいことは
> Windowsの標準機能を使いたい、だけなので
> VSを使えば簡単にできるのではないかと思って質問したのですが
> 簡単にできるコマンドは存在せず
> 自分で、同じような挙動をするコードを作るしかない、ということですか?
>  

Windowsの標準機能というよりは、エクスプローラの挙動と同じことができればよい
ということですかね?

.NET Framework から使うとなるといろいろ面倒なところはありますが(COM全般の問題)
IFileOperation という COM Interface が、エクスプローラのファイル操作全般を扱う
インターフェースとなっています。

ざっくりですが

http://d.hatena.ne.jp/Tan90909090/20160111/1452441478

のブログで紹介されていたのでリンクを張っておきます。

多分探せばほかにもあると思いますが、詳しく探してないのでわかりません。


.NET Framework から COM インターフェースを利用するというのはそんなに単純な話ではないところもあるので
これを簡単とみなすか?といわれるとかなり微妙ですが、自分でファイルをコピーするなどの
実装は必要ないので、そういう意味ではだいぶ楽ではある。。。かもしれませんw

なお、ごみ箱に移動の処理はこのインターフェースを使うことになります。
ここだけは覚えておくとよいと思います。
(System.IO.File.Delete などではごみ箱に移動させることはできません)

引用返信 編集キー/
■85091 / inTopicNo.7)  Re[4]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ カンガルー (4回)-(2017/09/11(Mon) 11:44:39)

No85037 (とっちゃん さん) に返信


http://www.asahi-net.or.jp/~fq7y-krsk/vba_v1.2/Win32API_VBA/xSHFileOperation.html

ありがとうございます。

上記ページに書かれた方法でうまく実現することができました。

ところで、一つ疑問なのですが、
pFromに単一のファイルパスを指定すると、pToはファイルパスとして引数を受け取ります。
一方で、vbNullcharでファイルパスを連結して
複数ファイルパスをpFromに渡すと、
pToはフォルダーパスとして引数を受け取ります。

このように、コピー元のファイルパスの数によって
コピー先のパスがファイルになったりフォルダーになったりして
使いづらいので、コピー元が単一ファイルであっても
コピー先がフォルダーになるようにして使いたいのですが
そのようなことは可能でしょうか?

FOF_MULTIDESTFILES
を試してみましたが駄目でした。

.pFrom = sPath & vbNullChar sPath & vbNullChar

というようにして、
同じファイルを二回コピーするようにすると
確かに所望のことはできるのですが
冗長的になってしまいます。

良い方法があれば教えてください。



引用返信 編集キー/
■85093 / inTopicNo.8)  Re[5]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ 魔界の仮面弁士 (1409回)-(2017/09/11(Mon) 12:12:28)
No85091 (カンガルー さん) に返信
> コピー元が単一ファイルであっても
> コピー先がフォルダーになるようにして使いたいのですが
> そのようなことは可能でしょうか?

pTo の指定がどうなっているか分かりませんが、
コピー先を「末尾が \ で終わるフルパス」で指定したら
フォルダーへのコピーになると思います。

この場合、コピー先のフォルダーは事前に用意しておく必要がありますが。


> http://www.asahi-net.or.jp/~fq7y-krsk/vba_v1.2/Win32API_VBA/xSHFileOperation.html
> 上記ページに書かれた方法でうまく実現することができました。
上記は VBA の場合ですよね。

SHFILEOPSTRUCT 構造体のアライメントは、本来は 1 バイトパッキングです。
提示 URL の VBA コードだと fFlags の後続メンバーの配置がずれますのでご注意ください。

VB.NET に移植する場合は、StructLayoutAttribute で Pack:=1 を指定すればすむのですが、
VBA(x86) は 4 バイト単位で配置される仕様なので…。
http://www.gizcollabo.jp/vbtomo/log/archive/vbqanda_41630_0.html

# そういえば、x64 版の VBA はどうなんだろう。
引用返信 編集キー/
■85094 / inTopicNo.9)  Re[6]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ カンガルー (5回)-(2017/09/11(Mon) 12:20:53)

No85093 (魔界の仮面弁士 さん) に返信

ありがとうございます。

\を付けることでうまくいきました。

実際には、上記のVBAのコードを参考にしつつ
VB.NETで書かれたコードを流用して使っているので問題は起きていないのですが

> SHFILEOPSTRUCT 構造体のアライメントは、本来は 1 バイトパッキングです。
> 提示 URL の VBA コードだと fFlags の後続メンバーの配置がずれますのでご注意ください。

これってどういう意味ですか?



引用返信 編集キー/
■85096 / inTopicNo.10)  Re[7]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ 魔界の仮面弁士 (1410回)-(2017/09/11(Mon) 13:02:08)
No85094 (カンガルー さん) に返信
>>SHFILEOPSTRUCT 構造体のアライメントは、本来は 1 バイトパッキングです。
>>提示 URL の VBA コードだと fFlags の後続メンバーの配置がずれますのでご注意ください。
> これってどういう意味ですか?

紹介した URL で解説していますが、ファイルコピーがユーザによってキャンセルされた場合、
SHFILEOPSTRUCT の fAnyOperationsAborted メンバーに 1 がセットされる仕様なのです。
(先のサンプルでは、キャンセルを判定するようなコードにはなっていないですけれどね)

32bit環境においては、SHFILEOPSTRUCT 構造体のメモリ配置は、
 0x00-0x03 → HWND 型 hWnd
 0x04-0x07 → UINT 型 wFunc
 0x08-0x0B → PCZZTSTR 型 pFrom
 0x0C-0x0F → PCZZTSTR 型 pTo
 0x10-0x11 → FILEOP_FLAGS 型 fFlags
 0x12-0x15 → BOOL 型 fAnyOperationsAborted
 0x16-0x19 → LPVOID 型 hNameMappings
 0x1A-0x1D → PCTSTR 型 lpszProgressTitle
と並んでいます。


ところが VBA は、4バイト単位に再配置して呼び出す仕様です。

0x00、0x04、0x08、0x0C というのは 4 バイト単位の位置に並んでいるので問題ないのですが、
fAnyOperationsAborted が配置されているのは、「0x10-0x13 の後半」と「0x14-0x17 の前半」に
またがるメモリ位置になっています。

そこで VBA は、API の呼び出し時に自動的に 2 バイト分調整し、 No85091 の構造体を
 0x00-0x03 → Long 型 hWnd
 0x04-0x07 → Long 型 wFunc
 0x08-0x0B → String 型 pFrom
 0x0C-0x0F → String 型 pTo
 0x10-0x11 → Integer 型 fFlags
 0x12-0x13 → 【2 バイトのパディング】
 0x14-0x17 → Long 型 fAnyOperationsAborted
 0x18-0x1B → Long 型 hNameMappings
 0x1C-0x1F → String 型 lpszProgressTitle
のメモリ配置に変換して渡すようになっています。


ところが、API がキャンセル結果を書き込むのは
0x12-0x15 のメモリ位置なので、結果を正しく受け取れなくなるわけです。

受け取れないだけならまだ良いですが、ポインタである
lpszProgressTitle に文字列を渡してから呼び出せば、
クラッシュしてしまう可能性すらあるわけです。


一方 VB.NET の場合は、StructLayoutAttibute 属性を付与することで、
このアライメント調整を明示的に指定することができるようになっています。


'SHFileOperationA を呼び出す場合は、CharSet.Ansi をセットします。
'SHFileOperationW を呼び出す場合は、CharSet.Unicode をセットします。
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode, Pack:=1)> _
Public Structure SHFILEOPSTRUCT
  Public hwnd As IntPtr
  <MarshalAs(UnmanagedType.U4)> Public wFunc As Integer '(Enum FILEOP As Int32)
  <MarshalAs(UnmanagedType.LPWStr)> Public pFrom As String
  <MarshalAs(UnmanagedType.LPWStr)> Public pTo As String
  <MarshalAs(UnmanagedType.U2)> Public fFlags As Short '(Enum FILEOP_FLAGS As Int16)
  <MarshalAs(UnmanagedType.Bool)> Public fAnyOperationsAborted As Boolean
  Public hNameMappings As IntPtr
  <MarshalAs(UnmanagedType.LPWStr)> Public lpszProgressTitle As String
End Structure
引用返信 編集キー/
■85097 / inTopicNo.11)  Re[8]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ カンガルー (7回)-(2017/09/11(Mon) 13:20:28)
処理をキャンセルした時に違いが見られることは分かりましたが
よく内容が理解できませんでした。

要はStructureのところの宣言の違いだけですよね?

https://social.msdn.microsoft.com/Forums/vstudio/en-US/b31b603a-627a-4660-9a51-90094f73572f/how-to-invoke-the-interface-ifileoperation-in-vbnet?forum=vbgeneral

このページを参考に


Private Structure SHFILEOPSTRUCT
Dim hwnd As Integer
Dim wFunc As Integer
Dim pFrom As String
Dim pTo As String
Dim fFlags As Short
Dim fAnyOperationsAborted As Boolean
Dim hNameMappings As Integer
Dim lpszProgressTitle As String
End Structure

と宣言していますが、問題ありませんでしょうか?



引用返信 編集キー/
■85100 / inTopicNo.12)  Re[9]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ 魔界の仮面弁士 (1411回)-(2017/09/11(Mon) 16:25:00)
2017/09/11(Mon) 16:36:24 編集(投稿者)

No85097 (カンガルー さん) に返信
> よく内容が理解できませんでした。

ざっくり言えば、「StructLayoutAttribute.Pack フィールドに 1 を指定するべき」ということです。

そのための正しい宣言例は既に No85096 で示しております。ファイルの先頭に
「Imports System.Runtime.InteropServices」を記述しておくことも忘れずに。


実際、構造体のサイズを
Dim sz As Integer = System.Runtime.InteropServices.Marshal.SizeOf(GetType(SHFILEOPSTRUCT))
で調べてみると、2 バイトのパディングが入ることにより、
No85096 の宣言が、x86ビルドで 30、x64 ビルドで 48 を返すのに対し、
No85097 の宣言は、x86ビルドで 32、x64 ビルドで 50 になってしまいます。


> 要はStructureのところの宣言の違いだけですよね?

いえ。Pack 指定以外にも、Ansi/Unicode 指定や IntPtr の扱いなど、
大切なことは他にもあります。


問題となりそうな箇所は 3 つ。

(1) Shift_JIS に無い文字、たとえば「立方メートル」記号を含んだファイル名を正しく処理できるか?
(2) x86 だけでなく、x64 でも動作するコードになっているか?
(3) fAnyOperationsAborted や lpszProgressTitle を利用できるような形になっているか?


上記 3 点を満たせるかどうか、という点で考えてみると、
No85097 の宣言では明らかに NG であると言えます。

しかしながら、特定の条件下…具体的には、
 ・Shift_JIS 前提のファイルパスしか扱わない。
 ・x86 ビルドでコンパイルする。
 ・fAnyOperationsAborted以降のメンバーを読み書きしない。
という前提を満たせる場合に限っては No85097 の間違った構造体宣言の
ままであっても、一見、問題なく呼び出せてしまうかも知れません。
(SHFileOperation をどのように宣言し、それをどのように
 呼び出しているのかにもよりますが)


そうした状況を問題ないと捉えるか、問題ありと考えるかは、
カンガルーさん自身の判断にお任せします。
引用返信 編集キー/
■85103 / inTopicNo.13)  Re[10]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ カンガルー (8回)-(2017/09/11(Mon) 16:50:09)
ありがとうございます。


詳細な原理は理解できていませんが、
とりあえず、
No85096 で示してくださったStructureをコピーして使ってみました。

しかし、この方法だとファイルがコピーされなくなりました。

x86でコンパイルしており、ファイルパスは全て半角文字を使っています。

コピー用のFunctionは以下の物を用いています。

何が原因でしょうか?




    Private Declare Function SHFileOperation Lib "shell32.dll" Alias "SHFileOperationA" (ByRef lpFileOp As SHFILEOPSTRUCT) As Integer


    Public Function SH_Copy(ByVal sPath As String, ByVal dPath As String) As Integer

        Dim shf As SHFILEOPSTRUCT

        With shf
            .wFunc = FO_COPY
            .pFrom = sPath & vbNullChar
            .pTo = dPath & vbNullChar
            .fFlags = FOF_ALLOWUNDO Or FOF_NOCONFIRMATION Or FOF_NOCONFIRMMKDIR
            .lpszProgressTitle = "Sending " & sPath & " to " & dPath
        End With

        Return SHFileOperation(shf)

    End Function



引用返信 編集キー/
■85105 / inTopicNo.14)  Re[11]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ とっちゃん (458回)-(2017/09/11(Mon) 17:02:13)
No85103 (カンガルー さん) に返信
> 何が原因でしょうか?
>
>
>
>
> Private Declare Function SHFileOperation Lib "shell32.dll" Alias "SHFileOperationA" (ByRef lpFileOp As SHFILEOPSTRUCT) As Integer
>
ここですね。

SHFileOperationA を指定しているので、文字コードはANSI形式が要求されます。
.NET から使う場合、よっぽどの理由がない限りは、SHFileOperationW など、UNICODE形式の文字コードを要求するバージョンの
APIを使うことをお勧めします。


引用返信 編集キー/
■85107 / inTopicNo.15)  Re[11]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ 魔界の仮面弁士 (1412回)-(2017/09/11(Mon) 17:23:03)
2017/09/11(Mon) 17:36:22 編集(投稿者)

No85103 (カンガルー さん) に返信
> No85096 で示してくださったStructureをコピーして使ってみました。

SHFILEOPSTRUCT を Structure で宣言する場合は、ByRef な引数で渡すようにし、
SHFILEOPSTRUCT を Class で宣言する場合は、ByVal な引数で渡す必要があります。
今回は前者を選択されたのですね。


> Private Declare Function SHFileOperation Lib "shell32.dll" Alias "SHFileOperationA" (ByRef lpFileOp As SHFILEOPSTRUCT) As Integer

No85096 に、
 'SHFileOperationA を呼び出す場合は、CharSet.Ansi をセットします。
 'SHFileOperationW を呼び出す場合は、CharSet.Unicode をセットします。
と書いておいた筈ですが、そもそも読んですらもらえていないのかな…。orz

〜A 系の関数に渡すのであれば、StructLayout での CharSet:= 指定を
CharSet.Ansi に変更する必要があります。
ただしこの場合、Shift_JIS 相当の文字しか扱えなくなります。


Unicode の文字を使えるようにする場合は、構造体宣言の方を
CharSet:=CharSet.Unicode としておいた上で、関数宣言の方を
 Private Declare Unicode Function SHFileOperation Lib "shell32" Alias "SHFileOperationW" (ByRef lpFileOp As SHFILEOPSTRUCT) As Integer
もしくは
 <DllImport("shell32", SetLastError:=True, CharSet:=CharSet.Unicode)> _
 Private Shared Function SHFileOperation(ByRef lpFileOp As SHFILEOPSTRUCT) As Integer
 End Function
のようにします。
(後者を Module 内に書く場合は、"Private Shared Function" を "Private Function" にしてください)


どちらの記述か迷った場合は No85105 でとっちゃんさんが書かれたように、
基本的には Unicode 版の組み合わせを選択するべきです。


--- 以下蛇足 ---
古い VBA コードは、Win98 世代の古い OS 向けのアプリを作っていた頃の名残で
SHFileOperationA などの 〜A 系 API を呼びだしているサンプルが非常に多いのですが、今となっては
SHFileOperationW などの 〜W 系 API への呼び出しに改めるのが望ましいです。

Win98 世代も意識した書きかたを考えるなら、下記のように
書き分けも必要ですが、今となってはその必要も無いでしょうし。
(下記は別の API での例ですが、一応参考情報として)
http://www5b.biglobe.ne.jp/~yone-ken/VBNET/Reference/ref2_GetPrivateProfileString.html
http://www5b.biglobe.ne.jp/~yone-ken/VBNET/special/sp06_GetPrivateProfileString.html
引用返信 編集キー/
■85196 / inTopicNo.16)  Re[12]: 複数ファイルをコピーする時に進捗ダイアログを表示する
□投稿者/ カンガルー (10回)-(2017/09/19(Tue) 11:15:38)
皆様ご回答ありがとうございます。

細かいところはこれから勉強したいと思いますが
とりあえずうまくいきました。
 
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ