chkaufmann 17 Posted March 2, 2021 When I use TStringList.SaveToFile() on a hidden text file, I get a create error. Even if I do this as administrator. So the only solution I see is to read the attributes with GetFileAttributes, then save the file and then use SetFileAttributes to hide the file again. Or do I miss something? Christian Share this post Link to post
KodeZwerg 54 Posted March 2, 2021 (edited) IMHO you are absolut right. Or from SysUtils FileGetAttr and FileSetAttr. I do prefer WinApi way like you wrote. Edited March 2, 2021 by KodeZwerg Share this post Link to post
KodeZwerg 54 Posted March 2, 2021 23 minutes ago, chkaufmann said: So the only solution I see is to read the attributes with GetFileAttributes, then save the file and then use SetFileAttributes to hide the file again. Or do I miss something? Yes you miss something. Get attribs, set attribs without readonly/hidden flag, save file, restore original attribs. Share this post Link to post
Lajos Juhász 293 Posted March 2, 2021 You're right. A hidden file can be replaced only with a hidden file. Unfortunately you cannot create a hidden file as in FileCreate the attribute is hard coded to be FILE_ATTRIBUTE_NORMAL. Share this post Link to post
KodeZwerg 54 Posted March 2, 2021 47 minutes ago, Lajos Juhász said: FILE_ATTRIBUTE_NORMAL Hmm... that I need to try. I would thought that it had ARCHIVE flag set, NORMAL would be no attribute. To be continued... Share this post Link to post
Guest Posted March 2, 2021 5 hours ago, Lajos Juhász said: A hidden file can be replaced only with a hidden file. Unfortunately you cannot create a hidden file as in FileCreate the attribute is hard coded to be FILE_ATTRIBUTE_NORMAL. maybe help you https://stackoverflow.com/questions/13506986/how-to-make-files-really-hidden-in-a-directory hug Share this post Link to post
Lajos Juhász 293 Posted March 2, 2021 1 minute ago, emailx45 said: https://stackoverflow.com/questions/13506986/how-to-make-files-really-hidden-in-a-directory Please read the posts and think about them before you start a google search and copy some content that has nothing to do with the question and the thread you paste. So what is the relation with you stackoverflow post and System.SysUtils.FileCreate?! Share this post Link to post
Remy Lebeau 1394 Posted March 2, 2021 6 hours ago, KodeZwerg said: Get attribs, set attribs without readonly/hidden flag, save file, restore original attribs. From the CreateFile documentation: Quote If CREATE_ALWAYS and FILE_ATTRIBUTE_NORMAL are specified, CreateFile fails and sets the last error to ERROR_ACCESS_DENIED if the file exists and has the FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_SYSTEM attribute. To avoid the error, specify the same attributes as the existing file. So, you don't have to actually remove the attributes, just match the existing attributes. Which FileCreate() can't do. But you could call CreateFile() directly, and then wrap the HANDLE in a THandleStream if needed. 1 Share this post Link to post
KodeZwerg 54 Posted March 2, 2021 (edited) Thank you @Remy Lebeaueven if I dont understand why telling me. TStringList.SaveToFile() uses internal TFileStream.Create() with fmCreate flag where normal users have no access to. If you say my answer is wrong please explain to learn from. To make myself clear, my steps: 0. "Var VariableX: DWORD" 1. Get attributes "VariableX := GetFileAttributes(PChar(FileName))" 2. Clear attributes to have write access "SetFileAttributes(PChar(FileName), FILE_ATTRIBUTE_NORMAL)" 3. Invoke "TSringList.SaveToFile(FileName)" 4. Restore SetFileAttributes(PChar(FileName), VariableX)" If I typed correct this is how it work, no? Missing things like checking for existance and that it is a file. Edited March 2, 2021 by KodeZwerg Spelling Share this post Link to post
Lajos Juhász 293 Posted March 2, 2021 Thanks Remy I missed the part that you can give a Handle to the TFileStream constructor. You can replace the file using this code. Of course you've to make sure first the file exists: procedure TForm1.FormCreate(Sender: TObject); var lStrList: TStringList; lfStream: TFileStream; lFileName: PChar; begin lFileName:='D:\temp\0104.txt'; lStrList:=TStringList.Create; lfStream:=TFileStream.Create(Createfile(lFileName, GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_ALWAYS, GetFileAttributes(lFileName), 0)); try lStrList.SaveToStream(lfStream); finally lfStream.Free; end; end; 2 Share this post Link to post
KodeZwerg 54 Posted March 2, 2021 I got a flashing bulb over my head 💡 Now I do understand what you both wrote, I was not aware that you had a TStringList.SaveToFile() replacement in mind. By reading CreateFileW() doc I wonder what happen with created Handle. Wouldnt it be better to split it up and call CloseHandle() afterwards? Share this post Link to post
Lajos Juhász 293 Posted March 2, 2021 4 minutes ago, KodeZwerg said: By reading CreateFileW() doc I wonder what happen with created Handle. Wouldnt it be better to split it up and call CloseHandle() afterwards? The answer is here, the TFileStream will take over the ownership and close it in the destructor (FileClose will call CloseHandle): destructor TFileStream.Destroy; begin if FHandle <> INVALID_HANDLE_VALUE then FileClose(FHandle); inherited Destroy; end; 1 Share this post Link to post
KodeZwerg 54 Posted March 2, 2021 (edited) Kisses @Lajos Juhász Edited March 2, 2021 by KodeZwerg Share this post Link to post
Remy Lebeau 1394 Posted March 2, 2021 1 hour ago, Lajos Juhász said: Thanks Remy I missed the part that you can give a Handle to the TFileStream constructor. I said to use THandleStream, not TFileStream. TFileStream does not have a constructor that accepts an external THandle as input. Unless that is a recent addition that has not been documented yet. 1 hour ago, Lajos Juhász said: Of course you've to make sure first the file exists Sure, eg: procedure TForm1.FormCreate(Sender: TObject); var lFile: THandle; lStrList: TStringList; lStream: THandleStream; lFileName: PChar; lAttrs: DWORD; begin lStrList := TStringList.Create; try ... lFileName := 'D:\temp\0104.txt'; lAttrs := GetFileAttributes(PChar(lFileName)); if lAttrs = INVALID_FILE_ATTRIBUTES then begin if GetLastError() <> ERROR_FILE_NOT_FOUND then RaiseLastOSError; lAttrs = FILE_ATTRIBUTE_NORMAL; end; lFile := CreateFile(lFileName, GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_ALWAYS, lAttrs, 0); if lFile = INVALID_HANDLE_VALUE then RaiseLastOSError; try lStream := THandleStream.Create(lFile); try lStrList.SaveToStream(lStream); finally lStream.Free; end; finally CloseHandle(lFile); end; finally lStrList.Free; end; end; 1 Share this post Link to post
Remy Lebeau 1394 Posted March 2, 2021 2 hours ago, KodeZwerg said: If you say my answer is wrong please explain to learn from. I didn't say it was wrong, just that it does not follow what Microsoft says to do. Share this post Link to post
Lajos Juhász 293 Posted March 2, 2021 3 minutes ago, Remy Lebeau said: I said to use THandleStream, not TFileStream. TFileStream does not have a constructor that accepts an external THandle as input. TFileStream is derived from THandleStream and unlike the THandleStream it will close the file in the destructor as I showed in my earlier post, so it's just a try ... finally less to write. Share this post Link to post
Remy Lebeau 1394 Posted March 2, 2021 (edited) 4 hours ago, Lajos Juhász said: TFileStream is derived from THandleStream and unlike the THandleStream it will close the file in the destructor I'm aware of that. But I have never seen an example of anyone ever passing an existing file handle to a TFileStream constructor, only to a THandleStream constructor. Edited March 2, 2021 by Remy Lebeau Share this post Link to post
Virgo 18 Posted March 3, 2021 (edited) Strange. Passing handle to TFIleStream.Create does not work in Delphi 5, but works in Delphi XE. It also does not compile in FPC 3.2.0. And I do not understand, why it compiles with Delphi XE. After all, TComponent.Create; does not compile.... Edited March 3, 2021 by Virgo fixing missing not Share this post Link to post
KodeZwerg 54 Posted March 3, 2021 16 hours ago, Remy Lebeau said: Attrs := GetFileAttributes(PChar(lFileName)); Sorry for another beginner question. Why you call it that way? Isnt lFileName not already a PChar at that moment? Share this post Link to post
Virgo 18 Posted March 3, 2021 Quote A method can be redeclared using the overload directive. In this case, if the redeclared method has a different parameter signature from its ancestor, it overloads the inherited method without hiding it. Calling the method in a descendent class activates whichever implementation matches the parameters in the call. So it appears, that calling TFileStream.Create with handle parameter works because, TFIleStream has constructors with overload; That is something, that I newer knew. Works in all Delphi ja FPC versions. Just in FPC TFileStream constructors are not with overload; Share this post Link to post
Remy Lebeau 1394 Posted March 3, 2021 1 hour ago, Virgo said: So it appears, that calling TFileStream.Create with handle parameter works because, TFIleStream has constructors with overload I checked, and overload was added to TFileStream in Delphi 6, when the 3-parameter constructor for passing in Rights was introduced. Share this post Link to post
Remy Lebeau 1394 Posted March 3, 2021 3 hours ago, KodeZwerg said: Why you call it that way? Isnt lFileName not already a PChar at that moment? Yes, it is. But I didn't notice that initially, I thought it was a String instead (which it should be). Share this post Link to post
Virgo 18 Posted March 3, 2021 12 minutes ago, Remy Lebeau said: I checked, and overload was added to TFileStream in Delphi 6, when the 3-parameter constructor for passing in Rights was introduced. Right. And fpc allows in objfpc mode multiple constructors without overload, which explains why TFileStream in fpc has two constructors (2 parameters and 3 parameters), but does not allow creating TFileStream with THandleStream constructor. Share this post Link to post