ディレクトリのコピーにSHFileOperationを利用するとき

解決


どら  2004-12-28 23:29:26  No: 55850

またまたすみません。
ディレクトリを丸まるコピーするのに、SHFileOperationを使って行おうと思っています。

外部の関数として作成していて、WinMain関数でiniファイルからコピー元とコピー先を取得し以下の関数のなかで、コピーを実行しています。

BOOL CopyDirectory(HWND hWnd, LPCTSTR From, LPCTSTR To)
{
  SHFILEOPSTRUCT TSHFileOpStruct;

  ZeroMemory(&TSHFileOpStruct,sizeof(SHFILEOPSTRUCT));
  MessageBox(NULL, From, To, MB_OK);
  //構造体の初期設定
  TSHFileOpStruct.hwnd    = hWnd;          //親ウインドウのハンドル
  TSHFileOpStruct.wFunc  = FO_COPY;        //フラグ
  lstrcpy((LPTSTR)From, TSHFileOpStruct.pFrom);        //対象元パス
  lstrcpy((LPTSTR)To, TSHFileOpStruct.pTo);        //対象先パス
  TSHFileOpStruct.fFlags = FOF_MULTIDESTFILES;  //フラグ
  MessageBox(NULL, TSHFileOpStruct.pFrom, TSHFileOpStruct.pTo, MB_OK);
  
  //実行する
  if(SHFileOperation(&TSHFileOpStruct) != 0)
  {
    return FALSE;
  }

  if(TSHFileOpStruct.fAnyOperationsAborted == TRUE)
  {
    return FALSE;
  }

  return TRUE;
}

現在ではデバッグのためにFromとToがSHFILEOPSTRUCT構造体にわたっているかを見る用のMessageBox関数があります(将来的には消します)。

このMessageBoxでは、FromおよびToは正しく表示されているのですが、いざSHFileOperationを実行すると、パスがうまくわたっていないらしく、「送り側のファイルまたはディスクから読み取れません」と出てしまいます。

なお、この送り側のディレクトリを直接
    TSHFileOpStruct.pFrom = "送り側のディレクトリ";
と記述するとコピーできます。
また
    TSHFileOpStruct.pFrom = From;
とやっても、同様のエラーが出ました。
ちなみに、コピー元のディレクトリはサーバ上にあり
    \\サーバ名\ディレクトリ名\
となっています。

何か原因が分かる方いらっしゃいませんか?
よろしければ、ご教授いただければと思います。


シャノン  2004-12-28 23:40:19  No: 55851

問題が3つあります。

1.これで引数から構造体に文字列をコピーしているわけですか?

> lstrcpy((LPTSTR)From, TSHFileOpStruct.pFrom);
> lstrcpy((LPTSTR)To, TSHFileOpStruct.pTo);
> MessageBox(NULL, TSHFileOpStruct.pFrom, TSHFileOpStruct.pTo, MB_OK);

lstrcpy は、どっちからどっちにコピーする関数でしょう…?

2.FILEOPSTRUCT の pFrom および pTo はポインタです。
  上のコードでは、それはどこを指すポインタなんでしょうか?

3.FILEOPSTRUCT の説明をよく読んでください。
  pFrom および pTo の末尾には、何かくっつけなければいけないはずです。


どら  2004-12-29 00:53:39  No: 55852

> lstrcpy は、どっちからどっちにコピーする関数でしょう…?

・・・あ、大ボケですね(^^;
ここを修正してもダメでした。

> 2.FILEOPSTRUCT の pFrom および pTo はポインタです。
  上のコードでは、それはどこを指すポインタなんでしょうか?

・・・えっと、不勉強でごめんなさい。
ちょっと意味を理解していないです。
最初にSHFILEOPSTRUCT構造体にをZeroMemoryで初期化しているので、最初はどこも指していないのではないんでしょうか??
それに、渡したい文字列のポインタを渡そうと思っているのですが・・・
これではまずいのでしょうか?

> 3.FILEOPSTRUCT の説明をよく読んでください。
  pFrom および pTo の末尾には、何かくっつけなければいけないはずです。

これまた不勉強でごめんなさい(^^;
「pFrom および pTo の末尾には、何かくっつけなければいけないはずです。」
の末尾とは??

SHFILEOPSTRUCT構造体を見ると、これらはLPCTSTR型になっています。
これより末尾に何かくっつけることって・・・あるんでしょうか?

あるサイトにあるサンプルソースをコピペしているので、色々とあるのかもしれません・・・。
サンプルソースでは、スタティックに値を渡しているので、その通りにするとうまくいくんですが・・・

すみません、何も分かっていなくて・・・
よろしくお願いいたします。


どら  2004-12-29 04:30:27  No: 55853

関数にして渡すところで問題があったみたいです。
グローバル変数で

char From[64], To[64];

と宣言し、関数の引数からこれらを削除したところ、問題なく動作しました。
文字列変数の渡し方に問題があるんですかね・・・

ちなみに、main関数の引数で上記文字列を宣言し、
BOOL CopyDirectory(HWND hWnd, LPCTSTR From, LPCTSTR To)
BOOL CopyDirectory(HWND hWnd, char* From, char* To)
どちらで関数を作成しても、

lstrcpy(TSHFileOpStruct.pFrom, From);                //対象元パス
lstrcpy(TSHFileOpStruct.pTo, To);                //対象先パス

または
TSHFileOpStruct.pFrom = From;
TSHFileOpStruct.pTo = To;

と行った上で

MessageBox(NULL, TSHFileOpStruct.pFrom, TSHFileOpStruct.pTo, MB_OK);

とすると、必要な値が登録されているにもかかわらず。

if(SHFileOperation(&TSHFileOpStruct) != 0)
{
    return FALSE;
}

を行うと、最初にいった通りのエラーが出てきます。

考えるの面倒になっちゃったので、このままグローバルで宣言するか、クラス化しちゃうことにしました。

シャノンさん、ありがとうございました。


ほげほげ  2004-12-29 04:43:52  No: 55854

>最初にSHFILEOPSTRUCT構造体にをZeroMemoryで初期化しているので、最初は>どこも指していないのではないんでしょうか??
そこにlstrcpyなんてことしちゃ駄目です^^;
素直にpFrom=From等としてあげましょう(これでも後述の問題がありますが^^;)。
> 3.FILEOPSTRUCT の説明をよく読んでください。
確かにLPCTSTR型ですが、扱いがちょっと特殊です。
二つ以上渡したいときの為にNULLが区切りとして解釈されます。
hoge,fooの二つをコピーする場合
hoge(NULL)foo(NULL)(NULL)
のようにする必要があります。((NULL)は文字列ではなくてNULLです。)
つまりNULLが二つ必要ってことです。
From,To変数を使いまわしたりした場合想定外のファイルまで指定してしまうことになるかもしれません^^;


シャノン  2004-12-29 05:53:54  No: 55855

> それに、渡したい文字列のポインタを渡そうと思っているのですが・・・
> これではまずいのでしょうか?

lstrcpy はポインタをコピーする関数ではなく、ポインタが指す実体をコピーする関数です。
ポインタのコピーは、単に代入

> 素直にpFrom=From等としてあげましょう

でいいです。

> 「pFrom および pTo の末尾には、何かくっつけなければいけないはずです。」
> の末尾とは??

言い方に語弊がありました。すいません。
正確には「pFrom および pTo が指す先の文字列の実体の末尾」です。

> SHFILEOPSTRUCT構造体を見ると、これらはLPCTSTR型になっています。
> これより末尾に何かくっつけることって・・・あるんでしょうか?

説明をよく読んでくださいと申し上げましたのに…
英語だからというだけで忌避しないでくださいませね。

pFrom(一部抜粋)
Although this member is declared as a null-terminated string, it is used as a buffer to hold multiple file names. Each file name must be terminated by a single NULL character. An additional NULL character must be appended to the end of the final name to indicate the end of pFrom. 

テキトーな日本語訳
このメンバはヌル終端文字列として宣言されていますが、複数のファイル名を保持することが出来ます。各ファイル名は、ひとつのヌル文字で終わっていなければなりません。pFrom の終端を示すために、最後のファイル名の後に、もうひとつヌル文字を付加しなければなりません。

pTo(一部抜粋)
Like pFrom, the pTo member is also a double-null terminated string and is handled in much the same way.

pFrom と同じように、pTo メンバも2つのヌル文字列で終わっていなければならず、ほとんど pFrom と同じように扱われます。


どら  2004-12-29 18:40:24  No: 55856

シャノンさん、丁寧な解説、ありがとうございます。
正直・・・LPSTR、LPCSTR、LPCTSTRあたりの区別が余るよく分かっていないです。
しかも、今回はNULLがふたつ必要だとか・・・

う〜ん、混乱してきた・・・

シャノンさんと、ほげほげさんのおかげで、無事解決できました☆
本当にありがとうございます。


シャノン  2004-12-29 19:36:13  No: 55857

> 正直・・・LPSTR、LPCSTR、LPCTSTRあたりの区別が余るよく分かっていないです。

LPSTR は char *
LPCSTR は const char *
LPCTSTR は UNICODE かどうかによって const char * か const wchar_t * のどちらかになります。
これらの違いは、最後にヌル文字が1つか2つかという問題とは無関係です。


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

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






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