2

I have an application that will work with the HOSTS file in the Windows\System32\drivers\etc folder. However, I don't want to hard code the path to C:\Windows\System32, because Windows might not be installed on drive C:.

I tried using %WinDir%\system32\drivers\etc\hosts, but this doesn't get expanded when it's used in the variable in my code.

How can I use %WinDir%\system32\drivers\etc\hosts as the path to the hosts file so I don't have to hard-code the path?

Another problem is that on successful compilation I have received one warning as

[DCC Warning] ApplicationWizard01.pas(67): W1002 Symbol 'TFileAttributes' is specific to a platform.

Code is shown in the answer here

Here is my new code :

unit KoushikHalder01;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons, Vcl.ExtCtrls,
  Vcl.ComCtrls;
type
  TForm01 = class(TForm)
    Label01: TLabel;
    Edit01: TEdit;
    Edit02: TEdit;
    BitBtn01: TBitBtn;
    BitBtn02: TBitBtn;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormShow(Sender: TObject);
    procedure FormHide(Sender: TObject);
    procedure BitBtn01MouseEnter(Sender: TObject);
    procedure BitBtn02MouseEnter(Sender: TObject);
    procedure BitBtn01MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure BitBtn02MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure BitBtn01MouseLeave(Sender: TObject);
    procedure BitBtn02MouseLeave(Sender: TObject);
    procedure BitBtn02Click(Sender: TObject);
    procedure BitBtn01Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form01: TForm01;

implementation

{$R *.dfm}

uses System.IOUtils;

procedure TForm01.BitBtn01Click(Sender: TObject);
function GetSysDir: string;
function IncludeTrailingPathDelimiter(const S: string): string;
var
  Attributes: TFileAttributes;
  SL: TStringList;
  Idx: Integer;
  Buffer: array[0..MAX_PATH] of Char;
  PathAndFileName : String;
begin
   GetSystemDirectory(Buffer, MAX_PATH - 1);
   SetLength(Result, StrLen(Buffer));
   Result := Buffer;
   PathAndFileName := CheckTrailingPathDelimiter(GetSysDir) + 'drivers\etc\hosts`;
   Attributes := [];
   TFile.SetAttributes('PathAndFileName', Attributes);
   SL := TStringList.Create;
   try
      SL.LoadFromFile('PathAndFileName');

     if
        SL.IndexOf('10.220.70.34    VIRTSDP25') <> -1
     then
        begin
        Edit02.Font.Color := clRed;
        Edit02.Text := 'Your Host File Has Already Been Modified Successfully.';
        end;
     if
        SL.IndexOf('10.220.70.34    VIRTSDP25') = -1
     then
        begin
        SL.Add('10.220.70.34    VIRTSDP25');
        Edit02.Font.Color := clGreen;
        Edit02.Text := 'Your Host File Has Been Modified Successfully.';
        end;
     if
        SL.IndexOf('10.220.70.32    BSNLESDP25A') = -1
     then
        SL.Add('10.220.70.32    BSNLESDP25A');
     if
        SL.IndexOf('10.220.70.33    BSNLESDP25B') = -1
     then
        SL.Add('10.220.70.33    BSNLESDP25B');
     if
        SL.IndexOf('10.220.70.34    VIRTBSNLESDP25') = -1
     then
        SL.Add('10.220.70.34    VIRTBSNLESDP25');
     if
        SL.IndexOf('10.220.70.34    KOSDPTwentyfive.bsnl.in.net') = -1
     then
        SL.Add('10.220.70.34    KOSDPTwentyfive.bsnl.in.net');
     if
        SL.IndexOf('10.220.70.34    KOSDPTwentyfive.bsnl.net.in') = -1
     then
        begin
           SL.Add('10.220.70.34 KOSDPTwentyfive.bsnl.net.in');
           SL.SaveToFile('PathAndFileName');
        end;
     finally
       SL.Free;
   end;
    Include(Attributes, TFileAttribute.faSystem);
    Include(Attributes, TFileAttribute.faReadOnly);
    TFile.SetAttributes('PathAndFileName', Attributes);
end;
Community
  • 1
  • 1
Koushik Halder
  • 445
  • 1
  • 9
  • 15

2 Answers2

6

Don't use the %Windir%\System32. Use the Windows API's function designed specifically to find that folder, GetSystemDirectory. It's defined in the Windows unit; here's a quick wrapper (not tested on XE2, but works on XE):

Since you had problems with my previous answer, here's a fully-compiling copy of the code (I commented out your references to Edit02, so you'll need to uncomment them; everything else compiles just fine as is under XE2:

uses 
  System.IOUtils;

function GetSysDir: string;
var
  Buffer: array[0..MAX_PATH] of Char;
begin
   GetSystemDirectory(Buffer, MAX_PATH - 1);
   SetLength(Result, StrLen(Buffer));
   Result := Buffer;
end;

{$WARN SYMBOL_PLATFORM OFF}
procedure TForm2.Button1Click(Sender: TObject);
var
  Attributes: TFileAttributes;
  SL: TStringList;
  Idx: Integer;
  PathAndFileName : String;
begin
   PathAndFileName := IncludeTrailingPathDelimiter(GetSysDir) + 'drivers\etc\hosts';
   Attributes := [];
   TFile.SetAttributes(PathAndFileName, Attributes);
   SL := TStringList.Create;
   try
     SL.LoadFromFile(PathAndFileName);

     if SL.IndexOf('10.220.70.34    VIRTSDP25') <> -1 then
     begin
       //Edit02.Font.Color := clRed;
       //Edit02.Text := 'Your Host File Has Already Been Modified Successfully.';
     end;

     if SL.IndexOf('10.220.70.34    VIRTSDP25') = -1 then
     begin
       SL.Add('10.220.70.34    VIRTSDP25');
       //Edit02.Font.Color := clGreen;
       //Edit02.Text := 'Your Host File Has Been Modified Successfully.';
     end;

     if SL.IndexOf('10.220.70.32    BSNLESDP25A') = -1 then
       SL.Add('10.220.70.32    BSNLESDP25A');
     if SL.IndexOf('10.220.70.33    BSNLESDP25B') = -1 then
       SL.Add('10.220.70.33    BSNLESDP25B');
     if SL.IndexOf('10.220.70.34    VIRTBSNLESDP25') = -1 then
       SL.Add('10.220.70.34    VIRTBSNLESDP25');
     if SL.IndexOf('10.220.70.34    KOSDPTwentyfive.bsnl.in.net') = -1 then
       SL.Add('10.220.70.34    KOSDPTwentyfive.bsnl.in.net');
     if SL.IndexOf('10.220.70.34    KOSDPTwentyfive.bsnl.net.in') = -1 then
       SL.Add('10.220.70.34 KOSDPTwentyfive.bsnl.net.in');
     SL.SaveToFile(PathAndFileName);
   finally
       SL.Free;
   end;
    Include(Attributes, TFileAttribute.faSystem);
    Include(Attributes, TFileAttribute.faReadOnly);
    TFile.SetAttributes(PathAndFileName, Attributes);
end;
{$WARN SYMBOL_PLATFORM ON}
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • I have tried as your suggession. But I have received the following errors "[DCC Error] KoushikHalder01.pas(58): E2003 Undeclared identifier: 'CheckTrailingPathDelimiter'" on "PathAndFileName := CheckTrailingPathDelimiter(GetSysDir) + 'drivers\etc\hosts`;" Line and "[DCC Error] KoushikHalder01.pas(59): E2066 Missing operator or semicolon" on "Attributes := [];" Line. Please help me. I have added my code in above. – Koushik Halder Jan 04 '12 at 09:11
  • 1
    I've re-written it (including fixing the compiler warning I told you how to fix before) and updated my answer. The issue with the trailing backslash function was mine; I remembered the function name wrong, and added it on the fly to my prior answer anticipating another problem you might run into. Fixed to use right function as well; it's a built-in function in the RTL, so you don't need to add code for it or declare it. Just use it as it is - it compiles perfectly. – Ken White Jan 04 '12 at 12:17
  • 1
    If the app is compiled for 32bit and then run on a 64bit Windows, `GetSystemDirectory()` will still report the path as `%WINDIR%\System32`, but the [File System Redirector](http://msdn.microsoft.com/en-us/library/windows/desktop/aa384187.aspx) will redirect access to `%WINDIR%\SysWOW64` instead. There is no `HOSTS` file under `%WINDIR%\SysWOW64`. To access the real `System32` folder under WOW64, you will have to skip `GetSystemDirectory()` and use WOW64's special `%WINDIR%\Sysnative` path instead, ie use `GetWindowsDirectory()` to find the Windows folder and then append `Sysnative` to it. – Remy Lebeau Jan 09 '15 at 19:05
0

Simply :

function WindowsPath: string; begin SetLength(Result, MAX_PATH); SetLength(Result, GetWindowsDirectory(@Result[1], MAX_PATH)); end;

  1. set's result length to 255 bytes or 512 for widesting and fills whole buffer with null chars #0
  2. requests windows folder from OS to pointer of first char, and sets length of buffer to chars length, after this when string no more used cleaner will destroy string content to first null byte, then destroys string. Actualy first nullbyte is next char of string because first line fills everything with nullchars.

    ps: Everything in string after nullbyte ignored, nullbyte char including!