[Delphi:91163] フォルダ名の変更

846 views
Skip to first unread message

ron

unread,
Mar 12, 2009, 7:39:35 AM3/12/09
to Del...@ml.users.gr.jp
お世話になってます、ろんです。

現在Delphi2007(.NETモード)で、フォルダ名を取得して、そのフォルダ名を変更
させるプログラムを組んでるのですが、うまく動きません。何事もなくプログラ
ムが動いて、フォルダ名は全く変わっていません。

プログラムとしては、

SelectDirectory関数でフォルダ名を取得
RenameFile関数でフォルダ名を変更(したい)

という感じです。RenameFile関数でファイル名の変更はうまくいくのですが、
フォルダ名がダメです。

ブレークポイントを設定して、フォルダ名の文字列の並びは確認しました。ちゃんと

D:\hoehoe\old
D:\hoehoe\new

のようになっていました。

あとは、どうすればいいでしょうか。

よろしくお願いします。


--
ron
mailto:r...@ebetsu.arrow.jp

高木太郎

unread,
Mar 12, 2009, 8:39:23 AM3/12/09
to Del...@ml.users.gr.jp
 こんにちは、イマジオムの高木です。

ろんさん:
> フォルダ名を取得して、そのフォルダ名を変更させるプログラムを
> 組んでるのですが、うまく動きません。何事もなくプログラムが動いて、
> フォルダ名は全く変わっていません。
>
> プログラムとしては、
>
> SelectDirectory関数でフォルダ名を取得
> RenameFile関数でフォルダ名を変更(したい)

 RenameFile 関数の戻り値は False ですよね? たぶん SelectDirectory
関数でフォルダを選んだ時点で、カレントフォルダがそのフォルダに変わって
います。 カレントフォルダの名前を変更することはできなかったと思います。

 名前を変える前に、ChDir 手続きか何かで、カレントフォルダをたとえば
ドライブのルートに変えてみてはいかがでしょう? 
――――――――――――――――――――――――――――――――――――
株式会社イマジオム 代表取締役 高木太郎
〒316-0024 茨城県 日立市 水木町 1-11-10
電話:0294-28-0147
ファクシミリ:0294-28-0148
電子メール:tarou_...@imageom.co.jp
ホームページ:http://www.imageom.co.jp/

ron

unread,
Mar 12, 2009, 7:58:02 PM3/12/09
to Del...@ml.users.gr.jp
お世話になってます、ろんです。

高木太郎 wrote (2009/03/12 21:39):

>> SelectDirectory関数でフォルダ名を取得
>> RenameFile関数でフォルダ名を変更(したい)
>
>  RenameFile 関数の戻り値は False ですよね? たぶん SelectDirectory
> 関数でフォルダを選んだ時点で、カレントフォルダがそのフォルダに変わって
> います。 カレントフォルダの名前を変更することはできなかったと思います。

戻り値はFalseです。

それで、ChDir('D:\')という文を一行加えてみたのですが、やっぱりダメです。

自分的には割と簡単なコーディングだと思っていたので、完全にトホホな状況です。


--
ron
mailto:r...@ebetsu.arrow.jp

高木太郎

unread,
Mar 12, 2009, 9:14:22 PM3/12/09
to Del...@ml.users.gr.jp
 ろんさん、こんにちは。 イマジオムの高木です。

>> たぶん SelectDirectory 関数でフォルダを選んだ時点で、カレント
>> フォルダがそのフォルダに変わって
>> います。
>
> ChDir('D:\')という文を一行加えてみたのですが、やっぱりダメです。

 SelectDirectory のヘルプを見たら「ダイアログがカレントディレクトリの
値を変更することはありません。」としっかり書いてありました。 勘違いで
申しわけありません。

 やってみたら、RenameFile 関数で、Windows エラー32( ERROR_SHARING_
VIOLATION )が発生していますね。 また SelectDirectory を使わずに
直接フォルダの名前を入力した場合には問題ないこともわかりました。
また ShBrowseForFolder API関数を使ってフォルダを選択した場合(下記)、
問題なくリネームができました。

 どうも SelectDirectory が、ディレクトリをつかんで放さないようです。
SelectDirectory は結構クセがあるので、下記のコードを使ったほうがいいかも
しれません。
――――――――――――――――――――――――――――――――――――
uses Windows,Classes,SysUtils,ShlObj,ActiveX,StrLib;

type TSelectDirectoryOption=(SDOForComputer,SDOForPrinter,SDOLocalFolder,
SDOAncestor,SDODirectory,SDOFixPosition);

TSelectDirectoryOptionSet=set of TSelectDirectoryOption;

var FixDialogPosition:Boolean;
DialogCaption:string;

function BFFCallBackProc(WindowHandle:HWnd; Msg:LongWord;
LParam,DataPtr:LongInt):LongInt; stdcall;

var Rect:TRect;

begin
if Msg=BFFM_INITIALIZED then
begin
SetWindowText(WindowHandle,PChar(DialogCaption));
if FixDialogPosition then
begin
GetWindowRect(WindowHandle,Rect);
SetWindowPos(WindowHandle,0,
(Screen.Width div 2)-((Rect.Right -Rect.Left) div 2),
(Screen.Height div 3)-((Rect.Bottom-Rect.Top ) div 3),
0,0,SWP_NOSIZE or SWP_NOZORDER);
end;
if DataPtr<>0 then
SendMessage(WindowHandle,BFFM_SETSELECTION,1,DataPtr);
end;
Result:=0;
end;


procedure SelectDirectory(OwnerWindowHandle:HWnd; Caption,Text:string;
RootDirName:string;
OptionSet:TSelectDirectoryOptionSet; var DirName:string);


function PathToIdListPtr(OwnerWindowHandle:HWnd; Path:string):PItemIdList;

var Eaten,Attributes:LongWord;
FolderInterface:IShellFolder;
OLEPath:array[0..MAX_PATH-1] of TOLEChar;

begin
if ShGetDesktopFolder(FolderInterface)=NOERROR then
begin
MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,PChar(Path),-1,OLEPath,MAX_PATH);
FolderInterface.ParseDisplayName(OwnerWindowHandle,nil,OLEPath,
Eaten,Result,Attributes);
end;
end;


var BrowseInfo:TBrowseInfo;
RootIdListPtr,ItemIdListPtr:PItemIdList;
CharBuffer:array[0..MAX_PATH-1] of Char;

begin
RootIdListPtr:=nil;
ItemIdListPtr:=nil;
try
RootIdListPtr:=PathToIdListPtr(OwnerWindowHandle,RootDirName);
with BrowseInfo do
begin
HWndOwner:=OwnerWindowHandle;
PIdLRoot:=RootIdListPtr;
PSZDisplayName:=@CharBuffer;
LPSZTitle:=PChar(Text);
ULFlags:=0;
if SDOForComputer in OptionSet then ULFlags:=ULFlags+BIF_BROWSEFORCOMPUTER;
if SDOForPrinter in OptionSet then ULFlags:=ULFlags+BIF_BROWSEFORPRINTER;
if SDOLocalFolder in OptionSet then ULFlags:=ULFlags+BIF_DONTGOBELOWDOMAIN;
if SDOAncestor in OptionSet then ULFlags:=ULFlags+BIF_RETURNFSANCESTORS;
if SDODirectory in OptionSet then ULFlags:=ULFlags+BIF_RETURNONLYFSDIRS;
LPFn:=BFFCALLBACK(@BFFCallBackProc);
LParam:=LongInt(PChar(DirName));
IImage:=0;
end;
if SDOFixPosition in OptionSet then FixDialogPosition:=True
else FixDialogPosition:=False;
DialogCaption:=Caption;
ItemIdListPtr:=ShBrowseForFolder(BrowseInfo);
if ItemIdListPtr<>nil then
begin
ShGetPathFromIdList(ItemIdListPtr,@CharBuffer);
DirName:=IncludeTrailingBackslash(string(CharBuffer));
end
else
DirName:='';
finally
CoTaskMemFree(RootIdListPtr);
CoTaskMemFree(ItemIdListPtr);
end;
end;


procedure TMainForm.Button1Click(Sender:TObject);

var DirName:string;

begin
SelectDirectory(Handle,'フォルダを開く','フォルダを選んでください。',
'\\',[SDOFixPosition],DirName);
RenameFile(DirName,'Renamed');
end;
――――――――――――――――――――――――――――――――――――
〒316-0024 茨城県 日立市 水木町 1-11-10 高木太郎
電話・ファクシミリ:0294-52-6787
電子メール:aaa1...@pop06.odn.ne.jp

ron

unread,
Mar 12, 2009, 10:26:30 PM3/12/09
to Del...@ml.users.gr.jp
お世話になってます、ろんです。

高木太郎 wrote (2009/03/13 10:14):

> ――――――――――――――――――――――――――――――――――――
> uses Windows,Classes,SysUtils,ShlObj,ActiveX,StrLib;

中略。m(_ _;)m

>
> begin
> SelectDirectory(Handle,'フォルダを開く','フォルダを選んでください。',
> '\\',[SDOFixPosition],DirName);
> RenameFile(DirName,'Renamed');
> end;

ども、わざわざコードありがとうございます。しかし難しくてよくわかりませ
ん。あとで勉強がてらゆっくり解析させていただきますので、許してください。
m(_ _;)m

フォルダ名変更の方はSHFileOperationとかいうものでうまくいくことがわかり
ました。これも良くわからないのですが、呪文と思って割り切ることにします。

プログラムとしては、

①SelectDirectory関数でフォルダ名を取得
②SHFileOperationの呪文を唱える

です。SHFileOperationは以下のような方法で使用することが出来ました。

uses ShellAPI

var
foStruct : TSHFileOpStruct;

begin
with foStruct do
begin
wnd := Handle; WFunc := FO_RENAME;
pFrom := '変更前のフォルダ名' + #0;
pTo := '変更したいフォルダ名' + #0;
end;

SHFileOperation(foStruct);
end;

まぁさしあたってhandleとか#0とかいうのがよくわからないですが、これでフォ
ルダ名の変更が可能でした。


--
ron
mailto:r...@ebetsu.arrow.jp

高木太郎

unread,
Mar 12, 2009, 11:11:48 PM3/12/09
to Del...@ml.users.gr.jp
ろんさん、こんにちは。 高木です。

 まずは解決したそうで、良かったです。

> フォルダ名変更の方はSHFileOperationとかいうものでうまくいくことが
> わかりました。これも良くわからないのですが、呪文と思って割り切る
> ことにします。

 SHFileOperation だと、SelectDirectory の後でもうまくいくのですね。

  SelectDirectory+RenameFile     ×
  SelectDirectory+SHFileOperation   ○
  SHBrowseForFolder+RenameFile     ○

よくわかりません……


 ついでですので、下記説明します。 よくわからないコードを使うのは
不安ですよね。

> begin
> with foStruct do
> begin
> wnd := Handle; WFunc := FO_RENAME;
> pFrom := '変更前のフォルダ名' + #0;
> pTo := '変更したいフォルダ名' + #0;
> end;
>
> SHFileOperation(foStruct);
> end;
>
> まぁさしあたってhandleとか#0とかいうのがよくわからないですが、
> これでフォルダ名の変更が可能でした。

 Handle は進捗を表示するダイアログボックスのオーナウィンドウを指定する
ための引数です。 上記は「ダイアログボックスを、絶対に Form1 の手前に
表示させろ」という指示です。

 #0 は、文字列の末尾にヌル文字を2個付けるための手法です。 ここで
指定する文字配列には複数のファイル名を書くことができ、それぞれの
ファイル名はヌル文字で区切ることになっています。 そして最後の
ファイル名の後にはヌル文字を2個置くことになっています。 Delphi の
長い文字列は、そのまま PChar 型にキャストすることができるように、
メモリの中では文字列本体の後にヌル文字が一つ付けられて保持されて
いるのですが、今回は2個のヌル文字が必要なので、意識的にヌル文字を
付加しているというわけです。
――――――――――――――――――――――――――――――――――――
株式会社イマジオム 代表取締役 高木太郎


〒316-0024 茨城県 日立市 水木町 1-11-10

ron

unread,
Mar 13, 2009, 12:23:07 AM3/13/09
to Del...@ml.users.gr.jp
お世話になってます、ろんです。

高木太郎 wrote (2009/03/13 12:11):

>  まずは解決したそうで、良かったです。

ありがとうございます。m(_ _;)m


>  Handle は進捗を表示するダイアログボックスのオーナウィンドウを指定する
> ための引数です。 上記は「ダイアログボックスを、絶対に Form1 の手前に
> 表示させろ」という指示です。

なるほど。処理はほとんど一瞬で終わるのでダイアログは出ないも同然なのです
が、私としては、ダイアログは表示されなくてもいいと思ってます。

となると、少々まずいコードを書いてますね・・・。

>  #0 は、文字列の末尾にヌル文字を2個付けるための手法です。 ここで
> 指定する文字配列には複数のファイル名を書くことができ、それぞれの
> ファイル名はヌル文字で区切ることになっています。 そして最後の
> ファイル名の後にはヌル文字を2個置くことになっています。 Delphi の
> 長い文字列は、そのまま PChar 型にキャストすることができるように、
> メモリの中では文字列本体の後にヌル文字が一つ付けられて保持されて
> いるのですが、今回は2個のヌル文字が必要なので、意識的にヌル文字を
> 付加しているというわけです。

了解です。

ありがとうございました。m(_ _;)m


--
ron
mailto:r...@ebetsu.arrow.jp

Reply all
Reply to author
Forward
0 new messages