Unicode, win32 NT, FileExists

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Borut
Beiträge: 2
Registriert: So 28. Mär 2010, 08:56

Unicode, win32 NT, FileExists

Beitrag von Borut »

Hallo!

In Abständen von einigen Jahren kommt mir immer wieder danach, die Möglichkeiten rund um Unicode zu testen. Es geht um Windows 2000 als Ziel- und Entwicklungsplattform, also die Win NT Schiene, die intern Unicode-basierend ist. Versuche habe ich mit Lazarus-0.9.29-24216-fpc-2.4.1-20100326-win32 gemacht.

Ich habe mir erhofft, dass ich z.B. die Existenz einer Datei L:\uib\examples\lazarus\ĆevapčićiDžabe\Радничка.fb programmatisch feststellen kann. Der Beispielname will zeigen, dass die Existenz einer Systemcodepage, die alle Sonderzeichen abdecken würde, nicht gegeben ist. Genau darum geht es mir: um "Globalität" zu erreichen.

Nun, blauäugig, wie ich bin, habe ich gedacht, dass der Aufruf der API-funktion abzufangen ist, in etwa so (durch die Verwendung meiner eigenen Funktion MyFileExistsUTF8 - hier die Auszüge aus der Unit mit der Definition):

Code: Alles auswählen

interface
// [...]
function MyGetFileAttributes(lpFileName:LPCWSTR):DWORD; external 'kernel32' name 'GetFileAttributesW';
function MyFileExistsUTF8(const Filename: UTF8string): boolean;
function Bool2Str(b:Boolean):ShortString;
// [...]
implementation
// [...]
function UTF8ToWideString(const s : AnsiString) : WideString;
// Based on Mike Lischke's function (Unicode.pas unit, http://www.delphi-gems.com)
// [...]
begin
// [...]
end;
// [...]
Function MyFileExists (Const FileName : WideString) : Boolean;
var
  Attr:Dword;
  WS : WideString;
begin
  WS:=FileName;
  Attr:=MyGetFileAttributes(@WS[1]);
  if Attr <> $ffffffff then
    Result:= (Attr and FILE_ATTRIBUTE_DIRECTORY) = 0
  else
    Result:=False;
end;
 
function MyFileExistsUTF8(const Filename: UTF8string): boolean;
begin
// Result:=FileExists(UTF8ToSys(Filename));
  Result:=MyFileExists(UTF8ToWideString(Filename));
 
  ShowMessage('FileExistsUTF8 Debug:'
  + LineEnding + Filename
  + LineEnding + UTF8ToSys(Filename)
  + LineEnding + Bool2Str(Result)
  );
end;

Nun, leider ist das zwar kompilierbar und lauffähig, liefert aber für solche Dateinamen immer False zurück und zwar *immer*, also auch dann, wenn es im Dateinamen keine Sonderzeichen gibt. Dadurch denke ich mir, dass ich hier evtl. einen Anfängerfehler mache. (Ich habe auch bemerkt, dass @WS[1] beim Debuggen anscheinend auf die zweite Zeichenkettenposition zeigt (was falsch wäre). Wenn ich aber, durch Tricks, mit @WS[0] arbeite, erhalte ich böse Runtime Fehler).

Hätte jemand einen Rat für mich? Sorry, wenn das Ganze eine dumme Fragestellung ist.
Danke!
Borut

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Unicode, win32 NT, FileExists

Beitrag von mschnell »

Es wird kaum jemand Lust haben sich mit so etwas altem wie Win 2000 zu beschäftigen, vor allem wenn es um so etwas neues wie Unicode geht, das noch eine ganze Zeit lang ein "Moving Target" ist (die "neuen Delphi"- Strings mit dynamischer Codierung werden immer noch heiß diskutiert). Ich würde da momentan die Finger von lassen. :(

-Michael

Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: Unicode, win32 NT, FileExists

Beitrag von theo »

Win2k ist das beste Windows was es je gab. Hör nicht auf den mschnell, der muss sich immer zu Unicode äussern, obwohl er gar nichts davon verstehen will.

Du verwendest viele eigene Funktionen (UTF8ToWideString, MyGetFileAttributes, Bool2Str). Das ist alles mit Bordmitteln machbar.
Probier mal genau so:

Code: Alles auswählen

uses Windows, Classes .....
 
function MyFileExists(const FileName: WideString): boolean;
var
  Attr: Dword;
begin
  Attr := Windows.GetFileAttributesW(@FileName[1]);
  if Attr <> $ffffffff then
    Result := (Attr and FILE_ATTRIBUTE_DIRECTORY) = 0
  else
    Result := False;
end;
 
function MyFileExistsUTF8(const Filename: UTF8string): boolean;
begin
  Result := MyFileExists(UTF8Decode(Filename));
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
    Caption := BoolToStr(MyFileExistsUTF8(OpenDialog1.FileName), True);
end;

Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: Unicode, win32 NT, FileExists

Beitrag von theo »

Was auch möglich ist: Einfach mit kurzen Namen arbeiten.
Das habe ich nur mal so hingehackt und funzt bei mir auch (Win2k). Bitte noch überprüfen.
Dann kannst du mit den normalen Funktionen arbeiten.

Code: Alles auswählen

function ShortFileName (const FileName: WideString): Widestring;
var aTmp: WideString;
begin
  SetLength(aTmp,255);
  if Windows.GetShortPathNameW (PWideChar (FileName), @aTmp[1], 254) = 0
  then
    Result:= FileName
  else
    Result:=aTmp;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var sn:String;
begin
 If OpenDialog1.Execute then
 begin
   sn:=ShortFileName(UTF8Decode(OpenDialog1.FileName));
   Caption:=sn;
   if FileExists(sn) then Memo1.Lines.LoadFromFile(sn);
 end;
end;

Borut
Beiträge: 2
Registriert: So 28. Mär 2010, 08:56

Re: Unicode, win32 NT, FileExists

Beitrag von Borut »

Hallo Theo,

besten Dank - war sachlich, verständlich, korrekt, anwendbar und schnell. Habe beide Varianten ausprobiert - klappt Alles.

Bei der Funktion ShortFilename habe ich probeweise unter else auch SetLength(Result,Pos(#00,Result)-1) ausprobiert - so kann der Kurzname per Bedarf gleich schöner dargestellt werden.

Deine Vorschläge haben mir jetzt die Augen für die geeigneten Bordmittel geöffnet - danke dafür auch.

Ich erlaube mir bei dieser Gelegenheit gleich noch eine Frage stellen... Ich habe von Linux Null Ahnung (und habe auch nicht vor, das als Zielplattform zu nehmen). Wenn man aber das ganze portabel gestalten wolle (im Sinne, dass das gleiche Sourcecode für WindowsMitUnicode und Linux(Gtk2?) gestalten wird), wie soll das dann in etwa aussehen?

Danke und liebe Grüße aus Österreich,
Borut

Nebenbei bemerkt: W2K soll in diesem Zusammenhang vor allem als ein Beispiel der "WindowsMitUnicode"-Schiene sein (also auch für XP, Vista, 7), wobei ich aber Deine positive Meinung über W2K teile. Ich verwende das W2K in einer virtuellen Maschine unter Virtualbox, da ich so - so die Theorie - nach einem evtl. katastrophalen Versagen der Hardware in kürzester Zeit unter mehr oder mindern beliebigem neuen Host Hardware und Betriebssystem genau dort weitermachen kann, wo ich vor dem letzten Backup der VM aufgehört habe.

Hitman
Beiträge: 512
Registriert: Mo 25. Aug 2008, 18:17
OS, Lazarus, FPC: ArchLinux x86, WinVista x86-64, Lazarus 0.9.29, FPC 2.4.1
CPU-Target: x86
Wohnort: Chemnitz

Re: Unicode, win32 NT, FileExists

Beitrag von Hitman »

Borut hat geschrieben:Ich erlaube mir bei dieser Gelegenheit gleich noch eine Frage stellen... Ich habe von Linux Null Ahnung (und habe auch nicht vor, das als Zielplattform zu nehmen). Wenn man aber das ganze portabel gestalten wolle (im Sinne, dass das gleiche Sourcecode für WindowsMitUnicode und Linux(Gtk2?) gestalten wird), wie soll das dann in etwa aussehen?

Verzichte auf WinAPI Aufrufe (dürfte nicht all zu schwer sein :D). Vermeide außerdem, Pfad-Angaben Windows-spezifisch zu schreiben. Nutze keine \, sondern nimm DirectorySeparator oder gleich die Funktion IncludeTrailingPathDelimiter.

Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: Unicode, win32 NT, FileExists

Beitrag von theo »

Borut hat geschrieben:Ich erlaube mir bei dieser Gelegenheit gleich noch eine Frage stellen... Ich habe von Linux Null Ahnung (und habe auch nicht vor, das als Zielplattform zu nehmen). Wenn man aber das ganze portabel gestalten wolle (im Sinne, dass das gleiche Sourcecode für WindowsMitUnicode und Linux(Gtk2?) gestalten wird), wie soll das dann in etwa aussehen?


Bei heutigem Linux ergibt sich das Problem eigentlich gar nicht, da es mit UTF-8 arbeitet, wie die LCL auch.
Im Prinzip müssten die Windows "W" Funktionen eigentlich in die RTL, da wird aber noch dran gearbeitet.
Bis es soweit ist, musst du Umwege machen, a la:

Code: Alles auswählen

{$ifdef windows}
sn:=ShortFileName(UTF8Decode(OpenDialog1.FileName));
{$else}
sn:=OpenDialog1.FileName;
{$end}


Liebe Grüße aus der Schweiz.

Antworten