user6828073
user6828073

Reputation:

How write to a txt file using SysUtils.FileWrite api?

I'm want write name of my pc to a txt file using SysUtils.FileWrite api, in my last attempt is wrote with sucess, but the trouble is that visually is cutting some characters, but size of text inside file have exactly the same size as if string is complete visually.

Eg: My pc is called of "TESTE-PC" (Without double quotes). The string "TESTE-PC" (Without double quotes) have exactly 8 bits, but SysUtils.FileWrite writes only "TEST" and size of file after is 8 bits. Very strange! :(

Thank you for any suggestion.

uses
 Registry;

...

function GetCompName: string;
var
  Reg: TRegistry;
begin
  Reg := TRegistry.Create;
  try
    Reg.rootKey := HKEY_LOCAL_MACHINE;
    if Reg.OpenKey('SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName', false) then
    begin
      Result := Reg.ReadString('ComputerName');
      Reg.CloseKey;
    end;
  finally
    Reg.Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
 hFile: THandle;
 Str: PWideChar;
begin

if not fileexists('test.txt') then
begin
  Str := PWideChar(GetCompName);
  hFile:=  CreateFile('test.txt', GENERIC_WRITE, 0, nil, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0);
  FileWrite(hFile, Str^, Length(Str));
  CloseHandle(hFile);
end;

end;

Upvotes: 2

Views: 1313

Answers (3)

Remy Lebeau
Remy Lebeau

Reputation: 596256

First off, using the Registry to get the computer name is wrong. Use the GetComputerName() function instead:

uses
  Windows;

...

function GetCompName: string;
var
  CompName: array[0..MAX_COMPUTERNAME_LENGTH] of Char;
  Size: DWORD;
begin
  Size := Length(CompName);
  if GetComputerName(CompName, Size) then
    SetString(Result, CompName, Size-1)
  else
    Result := '';
end;

Second, your FileWrite() code fails because you are not handling character encodings correctly. FileWrite() operates on raw bytes only, but you are working with Unicode strings and not taking into account that SizeOf(WideChar) is 2, not 1 like your code assumes.

You should also be using the RTL's FileCreate() function with FileWrite(). If you use the Win32 CreateFile() function directly, you should be using the Win32 API WriteFile() directly as well.

And no matter how you choose to write the file, you should be using an absolute path to the file, never a relative path.

Try something more like this:

procedure TForm1.FormCreate(Sender: TObject);
var
  FileName: string
  hFile: THandle;
  Str: string;
begin
  FileName := 'C:\path to\test.txt';
  if not FileExists(FileName) then
  begin
    Str := GetCompName;
    hFile := FileCreate(FileName);
    if hFile <> INVALID_HANDLE_VALUE then
    begin
      FileWrite(hFile, PChar(Str)^, Length(Str) * SizeOf(Char));
      FileClose(hFile);
    end;
  end;

Note that the code above will create the file in UTF-16 encoding. If you wanted to use UTF-8 instead, it would look like this:

procedure TForm1.FormCreate(Sender: TObject);
var
  FileName: string;
  hFile: THandle;
  Str: UTF8String;
begin
  FileName := 'C:\path to\test.txt';
  if not FileExists(FileName) then
  begin
    Str := UTF8String(GetCompName);
    hFile := FileCreate(FileName);
    if hFile <> INVALID_HANDLE_VALUE then
    begin
      FileWrite(hFile, PAnsiChar(Str)^, Length(Str));
      FileClose(hFile);
    end;
  end;

Or any other encoding, for that matter:

procedure TForm1.FormCreate(Sender: TObject);
var
  FileName: string;
  hFile: THandle;
  Enc: TEncoding;
  Str: TBytes;
begin
  FileName := 'C:\path to\test.txt';
  if not FileExists(FileName) then
  begin
    Enc := TEncoding.GetEncoding('desired encoding');
    try
      Str := Enc.GetBytes(GetCompName);
    finally
      Enc.Free;
    end;
    hFile := FileCreate(FileName);
    if hFile <> INVALID_HANDLE_VALUE then
    begin
      FileWrite(hFile, PByte(Str)^, Length(Str));
      FileClose(hFile);
    end;
  end;

Whatever encoding you decide to use, a simpler solution would be to use the IOUtils.TFile.WriteAllText() method instead:

uses
  IOUtils;

procedure TForm1.FormCreate(Sender: TObject);
var
  FileName: string;
begin
  FileName := 'C:\path to\test.txt';
  if not FileExists(FileName) then
  begin
    TFile.WriteAllText(FileName, GetCompName, TEncoding.UTF8); // or TEncoding.Unicode, etc...
  end;
end;

Upvotes: 5

MBo
MBo

Reputation: 80187

If you need to write wide chars, take their size into account:

FileWrite(hFile, Str^, Length(Str) * SizeOf(Char));

Upvotes: 0

RepeatUntil
RepeatUntil

Reputation: 2320

Change the type of str to RawByteString instead of PWideChar

procedure TForm1.FormCreate(Sender: TObject);
var
   hFile:  THandle;
   sFileName: string;
   Str: RawByteString;
begin
     Str := PWideChar(GetCompName);
     sFileName := 'Test.txt';
    if fileExists(sFileName) then
      hFile := fileOpen(sFileName,fmOpenReadWrite)
    else
      hFile := fileCreate(sFileName);
     try
       FileWrite(hFile,
        PChar(Str)^, Length(Str));
     finally
       FileClose(hFile);
     end;
end;

Upvotes: -1

Related Questions