SHFileOperationを正常に動作させるには?

解決


ひろ  2007-01-18 01:46:39  No: 134890

VB.NET2003で、ファイルの移動/コピーを行いたいため、SHFileOperationを利用しました。

すると、以下のような症状が出て正常に動作しません。
  何通りか、変えて、調査しましたが、エラーとなるときの症状は、すべて同じです。

パターン1
  APIを呼び出す部分を、イベントプロシージャ内に置いたとき
  (コピー元をフォルダで指定、最後に"*.*"をつけない)  →  正常動作

パターン2
  APIを呼び出す部分を、イベントプロシージャ内に置いたとき
  (コピー元をワイルドカードで指定、最後に"*.*"をつる)  →  エラー

パターン3
  APIを呼び出す部分を、標準モジュール内に置き、イベント
  プロシージャからコールしたとき
  (コピー元をフォルダで指定、最後に"*.*"をつけない)  →  正常動作

パターン4
  APIを呼び出す部分を、標準モジュール内に置き、イベント
  プロシージャからコールしたとき
  (コピー元をワイルドカードで指定、最後に"*.*"をつる)  →  エラー

パターン5
  APIを呼び出す部分を、DLLとして作成し、イベントプロシー
  ジャからコールしたとき  →  エラー

パターン6
  パターン1と同様、ただし、モジュール内で、DLLを使用する
  ための宣言"Private aa As New bb.clsCc"をしたとき  →  エラー

※エラーの症状
    1.実行すると、APIの中から、エラーメッセージ
        "ファイルをコピーできません。送り側のファイルまたは
                                  ディスクから読み取れません。"
      が表示されます。

    2.再度、実行すると、メッセージもでないで、正常に動作します。
      PGは、フォーム上に実行のためのボタンを置き、このボタンの
      クリックのイベントで、実行するようになっています。
      再度実行とは、エラーメッセージを"OK"ボタンで消し、実行の
      ボタンを再クリックすることです。PGを停止して、再起動し、
      再実行させたときは、同様のエラーとなります。

・パターン6は、この処理とは、直接関係のないDLLを宣言しても同様です。
・コピー元のフォルダは、ローカル上の、フロッピーを含む、どのドライ
  ブ上にあるものを指定しても、同様です。
・同じ、操作で正常に動作するため、ファイルの使用中などは、ないと思
  います。
・もともと、VB5で同様にこのAPIを利用していましたが、このような症状
  は、有りませんでした。  

ソースは、以下のようになっています。
★宣言部
    Public Declare Function SHFileOperation Lib "shell32" Alias "SHFileOperationA" _
             (ByRef lpFileOp As SHFILEOP) As Integer

    Public Structure SHFILEOP
        Public hwnd As Integer
        Public wFunc As Integer
        Public pFrom As String
        Public pTo As String
        Public fFlags As Short
        Public fAnyOperationsAborted As Integer
        Public hNameMappings As Integer
        Public lpszProgressTitle As String
    End Structure

呼出部
        'フォルダコピー(移動)に関する情報を指定
        With ShellOp
            .hwnd = Me.Handle.ToInt32
            .wFunc = FO_COPY        'コピーモード
            .pFrom = in_mot_fol     'コピー(移動)元のフォルダ
            .pTo = in_ski_fol       'コピー(移動)先のフォルダ
            '.fFlags = flg           'オプションスイッチ
        End With

        ret = SHFileOperation(ShellOp)

            よろしくお願いします


魔界の仮面弁士  2007-01-18 03:49:12  No: 134891

ざっと見て気になるのが、マーシャリングの指定ですかね。
MarshalAs や StructLayout などの属性指定が抜け落ちているようですし。
"構造体"や"文字列"や"配列"等を API に渡す場合、旧VB とは扱いが異なりますので、
単に「Long→Integerにすればよい」という物でも無いので、ご注意あれ。

で。SHFILEOPSTRUCT 構造体は、シングルパックのアライメントを要求しますが、
VB5 は4バイト単位なので、fFlags 以降のパラメータを利用する場合には、
32bit境界に対するアライメントの調整が必要でしたよね?
.NET の場合も同様に、アライメントを考慮する必要がありますので、
http://msdn2.microsoft.com/ja-jp/library/system.runtime.interopservices.structlayoutattribute.pack%28VS.80%29.aspx
などを 1 に指定する必要があるでしょう。

>  .hwnd = Me.Handle.ToInt32
いわゆる「ハンドル」は、わざわざ Int32 に直して渡すのではなく、
そのまま、IntPtr 型で定義して使うべきかと。


ひろ  2007-01-19 01:09:10  No: 134892

構造体の宣言を次のように修正しましたが症状は、変わりませんでした。
    '引数構造体
    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)> _
    Public Structure SHFILEOP
        Public hwnd As IntPtr                           '呼び出し元ウィンドウハンドル
        Public wFunc As Integer                         '処理内容決定フラグ
        <MarshalAs(UnmanagedType.LPStr)> _
            Public pFrom As String                      'コピー元/移動元/削除のファイル名/フォルダ名
        <MarshalAs(UnmanagedType.LPStr)> _
            Public pTo As String                        'コピー/移動先のファイル名/フォルダ名
        Public fFlags As Short                          '追加パラメータ
        Public fAnyOperationsAborted As Boolean         '処理が中断された場合、Falseが返される
        Public hNameMappings As IntPtr                  '処理前後のファイル名リストが返される
        <MarshalAs(UnmanagedType.LPStr)> _
            Public lpszProgressTitle As String          '進行状況表示ダイアログのタイトル
    End Structure

しかし、いろいろな情報を集めていると、
  "pFrom","pTo"には、末尾に"NULL"を2つつけなければならない
ということが、わかったため、"pFrom","pTo"の設定を次のように変更したところ、
正常に動作するようになりました。

        'フォルダコピー(移動)に関する情報を指定
        With ShellOp
            .hwnd = Me.Handle                       'ウィンドウハンドル
            .wFunc = FO_COPY                        'コピーモード
            .pFrom = in_mot_fol & Chr(0) & Chr(0)   'コピー(移動)元のフォルダ
            .pTo = in_ski_fol & Chr(0) & Chr(0)     'コピー(移動)先のフォルダ
            .fFlags = flg                           'オプションスイッチ
        End With

        ret = SHFileOperation(ShellOp)

でも、このNULLが原因だとしたら、VB5やVB.NETの特定の場合正常に動作していた理由が、
よくわかりません。

魔界の仮面弁士さん、ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加