• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 400
  • Last Modified:

Rename files with special char in filename

I need to rename a lot of files automatically, that have been copied over the network from a volume with macintosh namespace.
Some of them had a '/' in their filename which displays as a black box in the explorer.
When you read the list of filenames, this character display as '?'
The following methods did not work:
- Windows.RenameFile/MoveFile
- SHFileOperation(..FO_Rename..) (returns Error 114).

These methods do work:
- the shell's ren command
- rename these files with the
Windows NT Explorer.

How do I rename files in code like the Windows explorer (probably something with IShellXXXX interface).


Thanks,
EmmDieh

0
EmmDieh
Asked:
EmmDieh
  • 5
  • 4
1 Solution
 
hubdogCommented:
Because I do not have  mac files to test,the code below may not work.

procedure TForm1.Button1Click(Sender: TObject);
var
  P: PWideChar;
  NewPIDL,RenPIDL,PathPIDL: PItemIDList;
  Flags,
  NumChars: LongWord;
  NewShellFolder: IShellFolder;
  Value,PathValue:String;
begin
  SHGetDesktopFolder(FIDesktopFolder);
  Value:='hubdog.txt';//change c:\hubdog.txt to c:\newhubdog.txt
  PathValue:='C:\';
  NumChars := Length(PathValue);
  Flags := 0;
  P := StringToOleStr(PathValue);

  OLECheck(
    FIDesktopFolder.ParseDisplayName(
      Application.Handle,
      nil,
      P,
      NumChars,
      PathPIDL,
      Flags)
   );

  OLECheck(
    FIDesktopFolder.BindToObject(
            PathPIDL,
            nil,
            IID_IShellFolder,
            Pointer(NewShellFolder))
   );

  NumChars := Length(Value);
  Flags := 0;
  P := StringToOleStr(Value);

  OLECheck(
    NewShellFolder.ParseDisplayName(
      Application.Handle,
      nil,
      P,
      NumChars,
      NewPIDL,
      Flags)
   );

  if NewShellFolder.SetNameOf(
            Application.Handle,
            NewPIDL,
            StringToOleStr('newhubdog.txt'),
            128,
            RenPIDL
            )=noerror then
    showmessage('ok');
end;

good luck

hubdog
0
 
hubdogCommented:
I do not test the code on mac files so it may not work.

procedure TForm1.Button1Click(Sender: TObject);
var
  P: PWideChar;
  NewPIDL,RenPIDL,PathPIDL: PItemIDList;
  Flags,
  NumChars: LongWord;
  //Index: Integer;
  NewShellFolder: IShellFolder;
  Value,PathValue:String;
begin
  SHGetDesktopFolder(FIDesktopFolder);
  Value:='hubdog.txt';//change c:\hubdog.txt to c:\newhubdog.txt
  PathValue:='C:\';
  NumChars := Length(PathValue);
  Flags := 0;
  P := StringToOleStr(PathValue);

  OLECheck(
    FIDesktopFolder.ParseDisplayName(
      Application.Handle,
      nil,
      P,
      NumChars,
      PathPIDL,
      Flags)
   );

  OLECheck(
    FIDesktopFolder.BindToObject(
            PathPIDL,
            nil,
            IID_IShellFolder,
            Pointer(NewShellFolder))
   );

  NumChars := Length(Value);
  Flags := 0;
  P := StringToOleStr(Value);

  OLECheck(
    NewShellFolder.ParseDisplayName(
      Application.Handle,
      nil,
      P,
      NumChars,
      NewPIDL,
      Flags)
   );

  if NewShellFolder.SetNameOf(
            Application.Handle,
            NewPIDL,
            StringToOleStr('newhubdog.txt'),
            128,
            RenPIDL
            )=noerror then
    showmessage('ok');
  //SHChangeNotify(SHCNE_RENAMEITEM,SHCNF_FLUSH,NewPIDL,nil);
end;

good luck

hubdog
0
 
MadshiCommented:
Well, the question is: How is the filename stored in the FAT? Have you tried enumerating the files that were copied with FindFirstFile? I guess the APIs like RenameFile, MoveFile and CopyFile will work if you give in the right string. You "simply" need to find the right string... Well, at least that is what I guess. I don't have such files here, so I can't test it...

Regards, Madshi.
0
2018 Annual Membership Survey

Here at Experts Exchange, we strive to give members the best experience. Help us improve the site by taking this survey today! (Bonus: Be entered to win a great tech prize for participating!)

 
EmmDiehAuthor Commented:
Hello Madshi,

I know how the file is stored in the fat, because when I right-click it, I have an additional tab "Mac Properties".

That is either because of NT or because of the driver from MacOpener 4.1.
Funny thing is, whe I deactivate the driver I can still see the Mac Properties tab sheet.

On this tab sheet it says for example:
BKK14017?S_88_4 (on NTFS)
Macintosh-Name: BKK14017/S_88_4
Datapath: xxx Byte
Resourcepath: yyy Byte
Type/Creator:   AAAA / BBBB
Attributes:..... and so on

But neither the NTFS Filename nor the Mac-Filename did work with the standard API Funktions.

Thanks,
Michael
0
 
EmmDiehAuthor Commented:

The System Control Pack on
http://www.delphifreestuff.com/delphi/
contains a tdfsSystemListView that
is able to rename the special files I mentioned:

li := dfsSystemListView.Items[i];
if li.Caption = 'BKK14014?S_88_3' then
  dfsSystemListView.RenameItem(dfsSystemListView.Items[i], 'BKK14014_S_88_3');

Unfortunately this is A LOT more than I need.


0
 
EmmDiehAuthor Commented:
Hello Hubdog,

I took your second post and tried it.

VERY GOOD, I will grant you the points, if you comment your piece of code.
(don't forget to declare the variable FIDesktopFolder).

You can test it with arbitrary file names like I also did, before I tried it on live data.

What memory/handles etc. do you need to release ?
I suppose you need to free the memory that was reserved by StringToOleStr ?

Here is how I read your code:
First you retrieve a pointer to the desktop shell folder.
From there you retrieve a new shell folder that points to the specified directory.
Last you change the name of the file in this shell folder.

For manipulation of files/folders etc. you always need Item Identifier Lists.


Thanks,
Michael

0
 
hubdogCommented:
Hi Michael
sorry for I forgot to declare the variable FIDeskTopFolder

var
  FIDeskTopFolder:IShellFolder;

I think what you understand is right.
And the item identifier List must be free like this
procedure FreeIDL(IDL:PItemIdList);
var
  ShellMalloc: IMalloc;
begin
  if IDL=nil then exit;
  SHGetMalloc(ShellMalloc);
  ShellMalloc.Free(IDL);
end;

And no need to free StringToOleStr
you can grep the source of Delphi Units
all them not free the StringToOleStr

good luck

hubdog

0
 
hubdogCommented:
>>First you retrieve a pointer to the desktop shell folder.
>>From there you retrieve a new shell >>folder that points to the specified directory.
>>Last you change the name of the file in this shell folder.

>>For manipulation of files/folders etc. you always need Item Identifier Lists.

That is what my code means

remember free the pitemidlist after you have used it.

0
 
EmmDiehAuthor Commented:
Hello hubdog,

Looking at the VCL-Source for StringToOleStr, I can see that this function uses the Win-API function SysAllocStringLen to reserve the memory.
The Win-SDK says you'll have to use SysFreeString to later free this string.

That is what I inserted into your code after each usage:
SysFreeString(P);
And Windows did not complain so far.....

Thanks,
Michael
0
 
hubdogCommented:
Hi,Michael

You may be right.I didn't pay much attention to the memory leak.So my programs always have lots of bugs
:(

thanks

hubdog
0

Featured Post

The 14th Annual Expert Award Winners

The results are in! Meet the top members of our 2017 Expert Awards. Congratulations to all who qualified!

  • 5
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now