unit FileFunctions; { [FileFunctions] [6.0] Delphi 2005 December 2005 LICENSE The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at "http://www.mozilla.org/MPL/" Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is "[FileFunctions.pas]". The Initial Developer of the Original Code is Martin Holmes (Victoria, BC, Canada, "http://www.mholmes.com/"). Copyright (C) 2005 Martin Holmes and the University of Victoria Computing and Media Centre. The code was co-developed for university and personal projects, and rights are shared by Martin Holmes and the University of Victoria. All Rights Reserved. } { This is a mass of general-purpose file-related functions developed over the years in various projects. Caveat user: many are obsolete or untested. Dependencies: JEDI Code Library (JCL) jclUnicode. ShBrowseU (Alan G. Lloyd) TntUnicodeControls (Troy Wolbrink) } interface uses Windows, Forms, SysUtils, Classes, ShellAPI, Dialogs, FileCtrl, ShlObj, URLMon, jclUnicode, TntClasses, ShBrowseU, TntDialogs, TntSysUtils; var Win2K: Boolean; function LoadFileToString(InFile: string; var OutString: string): Boolean; function SaveStringToFile(OutFile: string; InString: string): Boolean; function WLoadFileToString(InFile: string; var OutString: Widestring): Boolean; function WSaveStringToFile(OutFile: string; InString: WideString): Boolean; function WSaveStringToFileUTF8(OutFile: string; InString: WideString): Boolean; function WSaveStringToFileUTF8NoBOM(OutFile: string; InString: WideString): Boolean; function GetFileSize(FName: string): integer; function CompareTextFiles(FileName1, FileName2: string): integer; function LaunchFile(Path: string): Boolean; function LaunchFileW2K(Path: string): Boolean; function RunFile(FilePath: string; Wait: Boolean): Boolean; function LaunchWebsite(Path: string): Boolean; function GetRelativePath(SourcePath, TargetPath: string): string; function GetFullPathFromRelative(RootPath, RelativePath: string; var OutString: string): Boolean; function WGetFullPathFromRelative(RootPath, RelativePath: WideString; var OutString: WideString): Boolean; function MDHGetWindowsDir: string; // wrapper for GetWindowsDirectory API function MakePath(InString: string): string; function RemoveSlash(InString: string): string; function CreateDirPath(TargetPath: string): Boolean; //Parses a string into cascading dirs and creates each one in turn procedure GetWindowsVersion(var Major : integer; var Minor : integer); procedure SetWin2K; function GetFolder(var FolderPath: string; DialogTitle: string): Boolean; function GetFolder2(var FolderPath: string; DialogTitle: string): Boolean; function GetFolder3(var FolderPath: string; DialogTitle: WideString; LeftTop: TPoint): Boolean; function GetFileFromWeb(WebFile, SaveFile: string): Boolean; function ExecNewProcess(ProgramName: string; WaitFor: Boolean): Boolean; function ExecNewProcessTimed(ProgramName: string; WaitTime: integer): Boolean; function ExecCmdLine(const CmdLine: string; WindowState: Word): Boolean; function CopyFilesInDir(const Source, Dest, Mask: string; Subdirs: Boolean): Boolean; procedure GetFiles(const ADirectory: string; Mask: string; Files: TStringList; SubFolders: Boolean); procedure GetFoldersInDir(const ADirectory: string; var Folders: TStringList); function LoadSourceFile(Dir, FName: string; var Contents: string): Boolean; function HasWebPageExtension(FName: string): Boolean; function MakeWebFileName(FName: string): string; //This sets the current filename for a target dialog based on the input filename, //by changing its extension, meanwhile preserving the target dialog's current directory //if it has one. procedure SetParallelFileName(dlgTarget: TTntOpenDialog; ModelFileName: WideString); var msgFailedToRunProgram: string = 'Unable to run the program and load the file. Please try yourself to open this file:'; function GetVersionEx(lpOs : pointer) : BOOL; stdcall; external 'kernel32.dll' name 'GetVersionExA'; implementation //Loads a file and puts it into a string variable. //Returns true if successful. function LoadFileToString(InFile: string; var OutString: string): Boolean; var InList: TStringList; i: integer; begin InList := TStringList.Create; try try InList.LoadFromFile(InFile); OutString := ''; for i := 0 to InList.Count - 1 do begin Application.ProcessMessages; OutString := OutString + InList[i] + #13#10; end; Result := True; except Result := False; end; finally InList.Free; end; end; //Loads a unicode text file function WLoadFileToString(InFile: string; var OutString: Widestring): Boolean; var InList: TTntStringList; begin InList := TTntStringList.Create; try try InList.LoadFromFile(InFile); OutString := InList.Text; Result := True; except Result := False; end; finally InList.Free; end; end; //Saves a string to a text file function SaveStringToFile(OutFile: string; InString: string): Boolean; var OutList: TStringList; TokenPosition: integer; StartPoint: integer; begin OutList := TStringList.Create; try //Work through the string TokenPosition := Pos(#13#10, InString); while TokenPosition > 0 do begin //Get the string OutList.Add(Copy(InString, 1, TokenPosition - 1)); //Remove it from InString StartPoint := TokenPosition + 2; InString := Copy(InString, StartPoint, Length(InString) - (StartPoint - 1)); TokenPosition := Pos(#13#10, InString); end; //Add the last bit if Length(InString) > 0 then OutList.Add(InString); try OutList.SaveToFile(OutFile); Result := True; except Result := False; end; finally OutList.Free; end; end; //Saves a widestring to a Unicode text file function WSaveStringToFile(OutFile: string; InString: WideString): Boolean; var OutList: TTntStringList; begin OutList := TTntStringList.Create; try OutList.Text := InString; //This property missing from latest TTntStringList (was present in previous TTntWideStringList) // OutList.SaveUnicode := True; try OutList.SaveToFile(OutFile); Result := True; except Result := False; end; finally OutList.Free; end; end; function WSaveStringToFileUTF8(OutFile: string; InString: WideString): Boolean; var OutList: TTntStringList; begin OutList := TTntStringList.Create; try OutList.Text := InString; //This property missing from latest TTntStringList (was present in previous TTntWideStringList) // OutList.SaveUnicode := True; try OutList.AnsiStrings.SaveToFileEx(OutFile, CP_UTF8); Result := True; except Result := False; end; finally OutList.Free; end; end; function WSaveStringToFileUTF8NoBOM(OutFile: string; InString: WideString): Boolean; var OutList: TStringList; OutString: string; begin Result := False; //default OutList := TStringList.Create; try try OutList.Text := WideStringToUTF8(InString); OutList.SaveToFile(OutFile); Result := True; except //Returning false is sufficient end finally FreeAndNil(OutList); end; end; function GetFileSize(FName: string): integer; var SearchRec: TSearchRec; begin Result := 0;//default if FindFirst(FName, faAnyFile, SearchRec) = 0 then Result := SearchRec.Size; end; function CompareTextFiles(FileName1, FileName2: string): integer; var Text1, Text2: string; Size1, Size2: integer; i: integer; begin //First check to see if we can do this by size alone, which is quicker Size1 := GetFileSize(FileName1); Application.ProcessMessages; Size2 := GetFileSize(FileName2); Application.ProcessMessages; Result := Size1 - Size2; Application.ProcessMessages; if Result = 0 then begin LoadFileToString(FileName1, Text1); Application.ProcessMessages; LoadFileToString(FileName2, Text2); Application.ProcessMessages; Result := Length(Text1) - Length(Text2); if (Result = 0) and (Length(Text1) > 0) then for i := 1 to Length(Text1) do begin Application.ProcessMessages; if Text1[i] <> Text2[i] then begin Result := Ord(Text1[i]) - Ord(Text2[i]); Exit; end; end; end; end; //Tries to launch a file function LaunchFile(Path: string): Boolean; var rc: Integer; i: integer; p: Array[0..255] of Char; begin {if Win2K then begin Result := LaunchFileW2K(Path); Exit; end; } Result := False; Application.ProcessMessages; if not(FileExists(Path)) then begin MessageDlg(msgFailedToRunProgram + #13#10#13#10 + Path, mtWarning, [mbOK], 0); Exit; end; Application.ProcessMessages; StrPCopy(p, Path); rc := ShellExecute(0, 'open', p, nil, PChar(ExtractFilePath(Path)), SW_SHOWNORMAL); if rc <= 32 then raise Exception.Create( Format(msgFailedToRunProgram + #13#10#13#10 + ' %s. %s', [path, SysErrorMessage(rc)])) else Result := True; end; //Tries to launch a file on Windows 2000 function LaunchFileW2K(Path: string): Boolean; var rc: integer; i: integer; DestSize: integer; PWPath: PWideChar; begin Result := False; { if not(FileExists(Path)) then begin raise Exception.Create(msgFailedToRunProgram + #13#10#13#10 + Path); Exit; end; DestSize := 512; PWPath := StringToWideChar(Path, PWPath, DestSize); Application.ProcessMessages; rc := ShellExecute(0, nil, PWPath, nil, nil, 0); if rc <= 32 then raise Exception.Create( Format(msgFailedToRunProgram + #13#10#13#10 + ' %s. %s', [path, SysErrorMessage(rc)])) else Result := True; } end; function RunFile(FilePath: string; Wait: Boolean): Boolean; var sei: TShellExecuteInfo; begin try FillChar(sei, sizeof(sei), 0); sei.cbSize := sizeof(sei); sei.fMask := SEE_MASK_NOCLOSEPROCESS; sei.Wnd := Application.Handle; sei.lpVerb := StrNew(pchar('open')); sei.lpFile := StrNew(pchar(FilePath)); sei.lpParameters := StrNew(pchar('')); sei.nShow := sw_Normal; if ShellExecuteEx(@sei) then begin WaitForSingleObject(sei.hProcess,INFINITE); Result := True; end else begin // ShowMessage(SysErrorMessage(GetLastError)); Result := False; end; finally StrDispose(sei.lpVerb); StrDispose(sei.lpFile); StrDispose(sei.lpParameters); end; end; //Tries to launch a browser and go to a Website function LaunchWebsite(Path: string): Boolean; var p: array [0..255] of Char; rc: Integer; begin Result := False; StrPCopy(p, path); rc := ShellExecute(GetDesktopWindow, nil, p, nil, nil, SW_SHOWNORMAL); if rc <= 32 then raise Exception.Create( Format(msgFailedToRunProgram + #13#10#13#10 + ' %s. %s', [path, SysErrorMessage(rc)])) else Result := True; end; function GetRelativePath(SourcePath, TargetPath: string): string; var DirCount: integer; OutPath: string; i: integer; begin Result := ''; if UpperCase(SourcePath[1]) <> UpperCase(TargetPath[1]) then begin // MessageDlg('These files are on different drives, so no relative path can be created.', mtWarning, [mbOK], 0); for i := 1 to Length(TargetPath) do if TargetPath[i] = '\' then Result := Result + '/' else Result := Result + TargetPath[i]; Exit; end; i := 1; while UpperCase(SourcePath[i]) = UpperCase(TargetPath[i]) do inc(i); //Fix added here: " or (TargetPath[i] <> '\')" -- on 09/02/05 while (SourcePath[i] <> '\') or (TargetPath[i] <> '\') do dec(i); SourcePath := Copy(SourcePath, i+1, Length(SourcePath) - i); TargetPath := Copy(TargetPath, i+1, Length(TargetPath) - i); DirCount := 0; for i := 1 to Length(SourcePath) do if SourcePath[i] = '\' then inc(DirCount); for i := 1 to DirCount do OutPath := OutPath + '../'; for i := 1 to Length(TargetPath) do if TargetPath[i] = '\' then OutPath := OutPath + '/' else OutPath := OutPath + TargetPath[i]; GetRelativePath := OutPath; end; //This function uses a SysUtils function to re-compose a full path //from a relative path, and the path it's relative to. It returns //true if it's able to perform the operation and a valid path to an //existing file results. function GetFullPathFromRelative(RootPath, RelativePath: string; var OutString: string): Boolean; var CurDir: string; RootDir: string; i: integer; begin //Default return Result := False; //Check that the root path is valid if not FileExists(RootPath) then Exit; //Save the current directory, because we'll have to change it CurDir := GetCurrentDir; //Get the directory of the root path RootDir := ExtractFilePath(RootPath); //Set the current directory to the root path ChDir(RootDir); //Change any forward slashes to backslashes in the relative path //(in case it's relative in the URL sense) for i := 1 to Length(RelativePath) do if RelativePath[i] = '/' then RelativePath[i] := '\'; //Call the function to get the path OutString := ExpandFileName(RelativePath); //Check the validity of the return path if FileExists(OutString) then Result := True; //Set the current directory back to what it was ChDir(CurDir); end; function WGetFullPathFromRelative(RootPath, RelativePath: WideString; var OutString: WideString): Boolean; var CurDir: WideString; RootDir: WideString; i: integer; begin //Default return Result := False; //Check that the root path is valid if not FileExists(RootPath) then Exit; //Save the current directory, because we'll have to change it CurDir := GetCurrentDir; //Get the directory of the root path RootDir := ExtractFilePath(RootPath); //Set the current directory to the root path ChDir(RootDir); //Change any forward slashes to backslashes in the relative path //(in case it's relative in the URL sense) for i := 1 to Length(RelativePath) do if RelativePath[i] = WideChar('/') then RelativePath[i] := WideChar('\'); //Call the function to get the path OutString := ExpandFileName(RelativePath); //Check the validity of the return path if FileExists(OutString) then Result := True; //Set the current directory back to what it was ChDir(CurDir); end; function MDHGetWindowsDir: string; var WD: PChar; i: integer; begin WD := StrAlloc(255); i := GetWindowsDirectory(WD, 255); if i = 0 then Result := '' else Result := StrPas(WD); StrDispose(WD); end; function MakePath(InString: string): string; begin if Length(InString) < 1 then Exit; if InString[Length(InString)] <> '\' then InString := InString + '\'; MakePath := InString; end; function RemoveSlash(InString: string): string; begin if InString[Length(InString)] = '\' then InString := Copy(InString, 1, Length(InString) - 1); Result := InString; end; function CreateDirPath(TargetPath: string): Boolean; //Parses a string into cascading dirs and creates each one in turn var DirList: TStringList; FirstPos: integer; begin Result := True; if DirectoryExists(TargetPath) then Exit; DirList := TStringList.Create; try FirstPos := Pos('\', TargetPath) + 1; while FirstPos <= Length(TargetPath) do begin if TargetPath[FirstPos] = '\' then if not DirectoryExists(Copy(TargetPath, 1, FirstPos -1)) then try CreateDir(Copy(TargetPath, 1, FirstPos -1)); except MessageDlg('Unable to create directory: ' + Copy(TargetPath, 1, FirstPos -1), mtWarning, [mbOK], 0); Result := False; end; inc(FirstPos); end; if TargetPath[FirstPos] = '\' then if not DirectoryExists(TargetPath) then try CreateDir(TargetPath); except MessageDlg('Unable to create directory: ' + TargetPath, mtWarning, [mbOK], 0); Result := False; end; finally DirList.Free; end; end; procedure GetWindowsVersion(var Major : integer; var Minor : integer); var lpOS, lpOS2 : POsVersionInfo; begin GetMem(lpOS, SizeOf(TOsVersionInfo)); lpOs^.dwOSVersionInfoSize := SizeOf(TOsVersionInfo); while getVersionEx(lpOS) = false do begin GetMem(lpos2, lpos^.dwOSVersionInfoSize + 1); lpOs2^.dwOSVersionInfoSize := lpOs^.dwOSVersionInfoSize + 1; FreeMem(lpOs, lpOs^.dwOSVersionInfoSize); lpOS := lpOs2; end; Major := lpOs^.dwMajorVersion; Minor := lpOs^.dwMinorVersion; FreeMem(lpOs, lpOs^.dwOSVersionInfoSize); end; procedure SetWin2K; var Major, Minor: integer; begin GetWindowsVersion(Major, Minor); Win2K := (Major > 4); end; function GetFolder(var FolderPath: string; DialogTitle: string): Boolean; var lpItemID : PItemIDList; BrowseInfo : TBrowseInfo; DisplayName : array[0..MAX_PATH] of char; TempPath : array[0..MAX_PATH] of char; begin Result := False; try FillChar(BrowseInfo, sizeof(TBrowseInfo), #0); BrowseInfo.hwndOwner := Application.Handle; BrowseInfo.pszDisplayName := @DisplayName; BrowseInfo.lpszTitle := PChar(DialogTitle); BrowseInfo.ulFlags := BIF_RETURNONLYFSDIRS; lpItemID := SHBrowseForFolder(BrowseInfo); if lpItemId <> nil then begin SHGetPathFromIDList(lpItemID, TempPath); FolderPath := MakePath(TempPath); Result := True; GlobalFreePtr(lpItemID); end; except //do nothing -- result is false end; end; function GetFolder2(var FolderPath: string; DialogTitle: string): Boolean; var ShBrowse: TShBrowse; begin Result := False; ShBrowse := TShBrowse.Create; try with ShBrowse do begin UserMessage := DialogTitle; InitFolder := FolderPath; if Execute then begin FolderPath := MakePath(Folder); Result := True; end; end; finally ShBrowse.Free; end; end; function GetFolder3(var FolderPath: string; DialogTitle: WideString; LeftTop: TPoint): Boolean; var ShBrowse: TShBrowse; begin Result := False; ShBrowse := TShBrowse.Create; try with ShBrowse do begin Options := Options + [sboNewDialogStyle]; // Options := Options + [sboEditBox]; Options := Options - [sboNoNewFolderButton]; Left := LeftTop.X; Top := LeftTop.Y; UserMessage := DialogTitle; if Length(FolderPath) > 0 then InitFolder := FolderPath; if Execute then begin FolderPath := MakePath(Folder); Result := True; end; end; finally ShBrowse.Free; end; end; function GetFileFromWeb(WebFile, SaveFile: string): Boolean; begin Result := False; if URLDownloadToFile(nil, PChar(WebFile), PChar(SaveFile), 0, nil) <> 0 then MessageBox(Application.Handle, 'An error ocurred while downloading the file.', PChar(Application.Title), MB_ICONERROR or MB_OK) else Result := True; end; function ExecNewProcess(ProgramName : string; WaitFor : Boolean): Boolean; var StartInfo : TStartupInfo; ProcInfo : TProcessInformation; CreateOK : Boolean; begin { fill with known state } FillChar(StartInfo,SizeOf(TStartupInfo),#0); FillChar(ProcInfo,SizeOf(TProcessInformation),#0); StartInfo.cb := SizeOf(TStartupInfo); CreateOK := CreateProcess(nil, PChar(ProgramName), nil, nil,False, CREATE_NEW_PROCESS_GROUP+NORMAL_PRIORITY_CLASS, nil, nil, StartInfo, ProcInfo); // check to see if successful } // and if we should wait for it to finish if CreateOK and WaitFor then // wait for child processe to finish WaitForSingleObject(ProcInfo.hProcess, INFINITE); ExecNewProcess := CreateOK; end; function ExecNewProcessTimed(ProgramName: string; WaitTime: integer): Boolean; var StartInfo : TStartupInfo; ProcInfo : TProcessInformation; CreateOK : Boolean; begin { fill with known state } FillChar(StartInfo,SizeOf(TStartupInfo),#0); FillChar(ProcInfo,SizeOf(TProcessInformation),#0); StartInfo.cb := SizeOf(TStartupInfo); //Make sure it starts minimized StartInfo.dwFlags := STARTF_USESHOWWINDOW; StartInfo.wShowWindow := SW_HIDE; CreateOK := CreateProcess(nil, PChar(ProgramName), nil, nil,False, CREATE_NEW_PROCESS_GROUP+NORMAL_PRIORITY_CLASS, nil, nil, StartInfo, ProcInfo); // check to see if successful } // and if we should wait for it to finish if CreateOK then // wait for child processe to finish while WaitForSingleObject(ProcInfo.hProcess, WaitTime) = WAIT_TIMEOUT do Application.ProcessMessages; CloseHandle(ProcInfo.hProcess); ExecNewProcessTimed := CreateOK; end; function ExecCmdLine(const CmdLine: string; WindowState: Word): Boolean; var SUInfo: TStartupInfo; ProcInfo: TProcessInformation; begin { Enclose filename in quotes to take care of long filenames with spaces. } FillChar(SUInfo, SizeOf(SUInfo), #0); with SUInfo do begin cb := SizeOf(SUInfo); dwFlags := STARTF_USESHOWWINDOW; wShowWindow := WindowState; end; Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False, CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil {PChar(ExtractFilePath(Filename))}, SUInfo, ProcInfo); end; function CopyFilesInDir(const Source, Dest, Mask: string; Subdirs: Boolean): Boolean; var ts: TSearchRec; function FileWithPath(const Dir, FName: string): string; begin if (Length(Dir) > 0) and (Copy(Dir, Length(Dir), 1) <> '\' ) then Result := Dir + '\' + FName else Result := Dir + FName; end; begin Result := DirectoryExists(Dest); if not Result then Result := CreateDir(Dest); if not Result then Exit; if FindFirst(FileWithPath(Source, Mask), faAnyFile, ts) = 0 then repeat if not ((ts.name='.') or (ts.name='..')) then begin if ts.Attr and faDirectory > 0 then begin if SubDirs then Result := CopyFilesInDir(FileWithPath(Source, ts.name), FileWithPath(Dest, ts.name), Mask, SubDirs); end else Result := CopyFile(PChar(FileWithPath(Source, ts.name)), PChar(FileWithPath(Dest, ts.name)), False); if not Result then break; end; until FindNext(ts) <> 0; FindClose(ts); end; procedure GetFiles(const ADirectory: string; Mask: string; Files: TStringList; SubFolders: Boolean); // Helper function to remove any slashes or add them if needed function SlashSep(const Path, S: string): string; begin if Path[Length(Path)] <> '\' then Result := Path + '\' + S else Result := Path + S; end; var SearchRec: TSearchRec; nStatus: Integer; begin // First find all the files fitting the mask in the current directory //Set the mask in case it's not specified corrently if Length(Mask) < 3 then Mask := '*.*'; nStatus := FindFirst(PChar(SlashSep(ADirectory, Mask)), 0, SearchRec); while nStatus = 0 do begin Files.Add(SlashSep(ADirectory, SearchRec.Name)); nStatus := FindNext(SearchRec); end; FindClose(SearchRec); // Next look for subfolders and search them if required to do so if SubFolders then begin nStatus := FindFirst(PChar(SlashSep(ADirectory, Mask)), faDirectory, SearchRec); while nStatus = 0 do begin // If it is a directory, then use recursion if ((SearchRec.Attr and faDirectory) <> 0) then begin if ( (SearchRec.Name <> '.') and (SearchRec.Name <> '..') ) then GetFiles(SlashSep(ADirectory, SearchRec.Name), Mask, Files, SubFolders); end; nStatus := FindNext(SearchRec) end; FindClose(SearchRec); end; end; procedure GetFoldersInDir(const ADirectory: string; var Folders: TStringList); // Helper function to remove any slashes or add them if needed function SlashSep(const Path, S: string): string; begin if Path[Length(Path)] <> '\' then Result := Path + '\' + S else Result := Path + S; end; var SearchRec: TSearchRec; Path: string; nStatus: integer; begin //Add a slash to the directory if there isn't one there Path := SlashSep(ADirectory, '*'); // First find all the files fitting the mask in the current directory nStatus := FindFirst(PChar(Path), faDirectory, SearchRec); while nStatus = 0 do begin if SearchRec.Attr and faDirectory > 0 then //Don't include the folder itself or its parent if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then Folders.Add(SlashSep(ADirectory, SearchRec.Name)); nStatus := FindNext(SearchRec); end; FindClose(SearchRec); end; function LoadSourceFile(Dir, FName: string; var Contents: string): Boolean; begin Result := True; if FileExists(Dir + FName) then LoadFileToString(Dir+FName, Contents) else if FileExists(ExtractFilePath(Application.ExeName) + '\source\' + FName) then LoadFileToString(ExtractFilePath(Application.ExeName) + '\source\' + FName, Contents) else if FileExists(ExtractFilePath(Application.ExeName) + '\srcbackup\' + FName) then LoadFileToString(ExtractFilePath(Application.ExeName) + '\srcbackup\' + FName, Contents) else Result := False; end; function HasWebPageExtension(FName: string): Boolean; var WebExts, Ext: string; begin Result := False; WebExts := '.htm.html.shtml.xhtml.xml.php.asp.aspx.jsp.php3.php4.php5.'; Ext := ExtractFileExt(FName) + '.'; if Length(Ext) > 3 then if Pos(Ext, WebExts) > 0 then Result := True; end; function MakeWebFileName(FName: string): string; begin Result := FName; if HasWebPageExtension(FName) = False then Result := FName + '.htm'; end; procedure SetParallelFileName(dlgTarget: TTntOpenDialog; ModelFileName: WideString); var Dir, NewName: WideString; function AddSlash(InPath: WideString): WideString; begin Result := InPath; if Length(InPath) > 0 then if InPath[Length(InPath)] <> WideChar('\') then Result := InPath + '\'; end; begin NewName := WideExtractFileName(WideChangeFileExt(ModelFileName, '.' + dlgTarget.DefaultExt)); if Length(dlgTarget.FileName) > 0 then begin Dir := AddSlash(WideExtractFileDir(dlgTarget.FileName)); dlgTarget.FileName := Dir + NewName; end else begin if Length(dlgTarget.InitialDir) > 0 then begin dlgTarget.FileName := AddSlash(dlgTarget.InitialDir) + NewName; end else begin dlgTarget.FileName := AddSlash(WideExtractFileDir(ModelFileName)) + NewName; end; end; end; initialization SetWin2k; finalization end.