unit PlugServ;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  Windows, ActiveX, Classes, ComObj, Filter, SysUtils, DriveType,
   CM2GenInterface, CM2AutoUpdateInterface, CM2EditorSupportInterface;

type
  TFormatTo = (ftPercent, ftString, ftBytes);
  TDiskSize = class(TComObject, ICM2GenPlug, ICM2AutoUpdate, ICM2EditorSupport10)
  Private
   LastErrMsg: String;
   Drive: Char;
   Folder: string;
   FormatTo : TFormatTo;
   Foo: String;
   Procedure Parser(Param: String);
   Function GetAll(Free: Boolean): Int64; 
  protected
   Function Name(OUT Name: Pchar): HResult; stdcall;
   Function Version(OUT Version: word): HResult; stdcall; // Using MMRB Notation
   Function RequiredCoreVersion(OUT RCV: Word): HResult; stdcall; // Using MMRB Notation - Please refer to CM2Plugtypes for details
   Function SetParentHandle(Handle: THandle): HResult; stdcall;
  // Declare Error commands
   Function GetLastErrorMsg(OUT Msg: PChar): HResult; stdcall;
  // Declare Info system
   Function GetInfoCount(OUT InfoCount: Integer): HResult; stdcall;
   Function GetInfo(Index: Integer; OUT InfoName: PChar; OUT InfoVar: PChar): HResult; stdcall;
  // Declare Sensor system
   Function GetSensorCount(OUT SensorCount: Integer): HResult; stdcall;
   Function GetSensorNameOnly(Index: Integer; OUT SensorName: PChar; OUT SensorStyle: Byte): HResult; stdcall; // Designed for speed polling at startup
   Function GetSensor(Index: Integer; Parameters: PChar; OUT SensorName: PChar; OUT SensorStyle: byte; OUT SensorString: PChar; OUT SensorValue: Double): HResult; stdcall;
  // Declare "Event" functions
   Function Init: HResult; stdcall;
   Function Terminating: HResult; stdcall;
   Function Setup: HResult; stdcall;
  // Declare Support types
   // Default (Required)
   Function GetDefaultCount(SensorIndex: Integer; OUT DefaultCount: Integer): HResult; stdcall;
   Function GetDefault(SensorIndex: Integer; Index: Integer; OUT Name: Pchar; OUT Value: PChar; OUT Description: PChar): HResult; stdcall;
   // Optional
   Function GetOptionalCount(SensorIndex: Integer; OUT OptionalCount: Integer): HResult; stdcall;
   Function GetOptional(SensorIndex: Integer; Index: Integer; OUT Name: Pchar; OUT Value: PChar; OUT Description: PChar): HResult; stdcall;
   // Auto update
   Function UpdateFileURL(OUT URL: Pchar): HResult; stdcall;
   // Editor Support
   Function ShowSensor(Index: Integer; OUT ShowAt: Byte): HResult; stdcall;
   Function ObjIns(SensorIndex: Integer; ParameterName: PChar; OUT ValueType: Byte; OUT ValueRange: PChar): HResult; stdcall;
  end;

const
  Class_DiskSize: TGUID = '{2B7EE5EA-164B-4848-A592-6DE7860CA93A}';
  AutoUpdateFile = 'http://www.coolmon.org/update/disksize.upd';
  RCVersion = 0500;
  PlugVersion = 0601;
  UpdateStr = 'June, 3rd 2004';

implementation

uses ComServ;

Function PathToOk(APath: String): String;
Begin
 If APath = '' Then begin
  Result := APath;
  Exit;
 End;
 If APath[length(APath)] <> '\' Then APath := APath + '\';
 Result := APath;
End;

Function GetFolderSize(Folder: String; OfSubDirs: Boolean = True; OfHidden: Boolean = False): Int64;
Var
 SR: TSearchRec;
 Res: Integer;
Begin
 Result := 0;
 If OfHidden then Res := FindFirst(PathToOk(Folder) + '*', faHidden + faReadonly + faArchive + faDirectory, SR)
  else Res := FindFirst(PathToOk(Folder) + '*', faReadonly + faArchive + faDirectory, SR);
 While Res = 0 do begin
  If (directoryexists(pathtook(Folder) + SR.Name)) and (OfSubDirs) Then begin
   If not((SR.Name = '.') or (SR.Name = '..')) then begin
    Result := Result + GetFolderSize(PathToOk(Folder) + sr.Name, OfSubDirs, OfHidden);
   end;
  end
   else
  begin
   Inc(Result, SR.Size);
  end;
  Res := FindNext(SR);
 end;
 FindClose(SR);
end;

Function SizetoString(Size : real) : String;
Var
 S1 : string;
 Size2 : Extended;
begin
  Result := 'Too large convertion';
  if size < 0 then begin
   Result := 'Size is smaller than zero [' + floattoStr(size) + ']';
  end;
  if size = 0 then begin
   Result := floattoStr(Size) + ' Bytes';
  end;
  if size = 1 then begin
   Result := floattoStr(Size) + ' Byte';
  end;
  if (size > 1) and (size < 1024) then begin
   Result := floattoStr(Size) + ' b.';
  end;
  if (size > 1023) and (size < 1048576) then begin
   Size2 := Size / 1024;
   S1 := Formatfloat('####.00',Size2);
   Result := s1 + ' Kb.';
  end;
  if (size > 1048575) and (size < 1073741825) then begin
   Size2 := Size / 1048576;
   S1 := Formatfloat('####.00',Size2);
   Result := s1 + ' Mb.';
  end;
  If Size > 1073741824 then begin
   Size2 := Size /1073741824;
//                 39068696576
   S1 := Formatfloat('####.00',Size2);
   Result := s1  + ' Gb.'
  end;
end;

{ TDiskSize }

function TDiskSize.GetDefault(SensorIndex, Index: Integer; out Name, Value,
  Description: PChar): HResult;
begin
 Result := S_OK;
 If sensorindex = 9 then Begin
  Name := 'Folder'; Value := 'C:\'; Description := 'The Folder you wish to obtain the size of';
  exit;
 end;
 Case Index of
  0: Begin Name := 'Drive'; Value := 'C'; Description := 'The letter of the drive you want to poll information'; End;
 End;
end;

function TDiskSize.GetDefaultCount(SensorIndex: Integer;
  out DefaultCount: Integer): HResult;
begin
 DefaultCount := 1;
 Result := S_OK;
end;

function TDiskSize.GetInfo(Index: Integer; out InfoName,
  InfoVar: PChar): HResult;
begin
 Result := S_OK;
 Case Index of
  0: Begin InfoName := 'Programmed By'; InfoVar := 'Christian Aaangel'; end;
  1: Begin InfoName := 'Created'; InfoVar := 'Feb. 19 2003'; end;
  2: begin InfoName := 'Updated'; InfoVar := Pchar(UpdateStr); end;
  3: Begin InfoName := 'Copyright'; InfoVar := '© 2003 - 2005, The CoolMon Project'; end;
  else Begin
   LastErrMsg := 'Index out of bounds';
   Result := E_FAIL;
  End;
 End;
end;

function TDiskSize.GetInfoCount(out InfoCount: Integer): HResult;
begin
 InfoCount := 4;
 Result := S_OK;
end;

function TDiskSize.GetLastErrorMsg(OUT Msg: PChar): HResult;
begin
 If LastErrMsg = '' Then Msg := 'No error'
  else Msg := PChar(LastErrMsg);
 Result := S_OK;
end;

function TDiskSize.GetOptional(SensorIndex, Index: Integer; out Name,
  Value, Description: PChar): HResult;
begin
 Result := S_OK;
 If ((SensorIndex < 3) or (SensorIndex > 6)) then begin
  If Index = 0 then begin
   Name := 'Format';
   Value := '%';
   Description := 'Output operator - "%" or "percent" for a percentage output, "String" for a value with postfix (b. mb. gb.)';
  End;
 End;
end;

function TDiskSize.GetOptionalCount(SensorIndex: Integer;
  out OptionalCount: Integer): HResult;
begin
 Result := S_OK;
 OptionalCount := 0;
 If ((SensorIndex < 3) or (SensorIndex > 6)) then OptionalCount := 1;
end;

function TDiskSize.GetSensor(Index: Integer; Parameters: PChar;
  out SensorName: PChar; out SensorStyle: byte; out SensorString: PChar;
  out SensorValue: Double): HResult;
Var
 DT: TDriveInfo;
begin
 If Self.GetSensorNameOnly(Index, SensorName, SensorStyle) = E_FAIL Then begin
  Result := E_FAIL;
  Exit;
 End;
 Parser(Parameters);
 DT := GetDriveInfo(Drive);
 Case Index Of
 0: Begin
     If FormatTo = ftPercent Then FormatTo := ftBytes;
     Case FormatTo of
      ftString: Begin
                 SensorString := PChar(SizetoString(DT.DiskCapasity));
                 SensorStyle := 1;
                End;
      ftBytes: Begin
                SensorValue := DT.DiskCapasity;
                SensorStyle := 0;
               End;
     End;
    End;
 1: Begin
     Case FormatTo of
      ftPercent: Begin
                  SensorValue := Round((DT.DiskFree/DT.DiskCapasity)*100);
                  SensorStyle := 0;
                 End;
      ftString: Begin
                 If DT.DiskCapasity = -1 then DT.DiskFree := 0;
                 SensorString := PChar(SizetoString(DT.DiskFree));
                 SensorStyle := 1;
                End;
      ftBytes: Begin
                SensorValue := DT.DiskFree;
                SensorStyle := 0;
               End;
     End;
    End;
 2: Begin
     Case FormatTo of
      ftPercent: Begin
                  SensorValue := Round((DT.DiskUsed/DT.DiskCapasity)*100);
                  SensorStyle := 0;
                 End;
      ftString: Begin
                 SensorString := PChar(SizetoString(DT.DiskUsed));
                 SensorStyle := 1;
                End;
      ftBytes: Begin
                SensorValue := DT.DiskUsed;
                SensorStyle := 0;
               End;
     End;
    End;
 3: Begin
     SensorStyle := 1;
     If DT.DiskCapasity = -1 then Foo := '[No Disk]'
      else Foo := GetVolumeLabel(Drive);
     SensorString := Pchar(foo);
    End;
 4: Begin
     SensorStyle := 1;
     If uppercase(DT.Drivetype) <> 'FIXED' then
      SensorString := Pchar(DT.Drivetype)
     else
      SensorString := PChar(DT.FileSystem);
    End;
 5: Begin
     SensorStyle := ssNonNumeric;
     If DriveType.NTypeofDrive(drive) = 1 then Foo := 'No'
      else Foo := 'Yes';
     SensorString := Pchar(Foo);
    End;
 6: Begin
     SensorStyle := ssNonNumeric;
     SensorString := Pchar(DT.FileSystem);
    end;
 7: Begin
     // SizeTotal
     if FormatTo = ftPercent then FormatTo := ftBytes;
     Case FormatTo of
      ftString: Begin
                 SensorString := PChar(GetAll(false));
                 SensorStyle := 1;
                End;
      ftBytes: Begin
                SensorValue := GetAll(False);
                SensorStyle := 0;
               End;
     End;
    End;
 8: Begin
     // TotalFree
     Case FormatTo of
      ftPercent: Begin
                  SensorValue := Round((GetAll(True)/GetAll(False))*100);
                  SensorStyle := 0;
                 End;
      ftString: Begin
                 SensorString := PChar(GetAll(True));
                 SensorStyle := 1;
                End;
      ftBytes: Begin
                SensorValue := GetAll(True);
                SensorStyle := 0;
               End;
     End;
    End;
 9: Begin
     // FolderSize
     Case FormatTo of
      ftPercent: Begin
                  Drive := Folder[1];
                  DT := DriveType.GetDriveInfo(Drive);
                  SensorValue := Round((GetFolderSize(Folder)/DT.DiskCapasity)*100);
                  SensorStyle := 0;
                 End;
      ftString: Begin
                 Foo := SizeToString(GetFolderSize(Folder));
                 SensorString := Pchar(Foo);
                 SensorStyle := 1;
                End;
      ftBytes: Begin
                SensorValue := GetFolderSize(Folder);
                SensorStyle := 0;
               End;
     end;
    End;
 End;
 Result := S_OK;
end;

function TDiskSize.GetSensorCount(out SensorCount: Integer): HResult;
begin
 SensorCount := 10;
 Result := S_OK;
end;

function TDiskSize.GetSensorNameOnly(Index: Integer; out SensorName: PChar;
  out SensorStyle: Byte): HResult;
begin
 Result := S_OK;
 Case Index of
  0: Begin
      SensorName := 'Disksize'; SensorStyle := 2;
     End;
  1: Begin
      SensorName := 'Diskfree'; SensorStyle := 2;
     End;
  2: Begin
      SensorName := 'Diskused'; SensorStyle := 2;
     End;
  3: Begin
      SensorName := 'Volume Label'; SensorStyle := 1;
     End;
  4: Begin
      SensorName := 'Drive Type'; SensorStyle := 1;
     End;
  5: Begin
      SensorName := 'Drive Present'; SensorStyle := ssNonNumeric;
     end;
  6: Begin
      SensorName := 'Filesystem'; SensorStyle := ssNonNumeric;
     End;
  7: Begin
      SensorName := 'TotalSize'; sensorStyle := 2;
     end;
  8: Begin
      SensorName := 'TotalFree'; SensorStyle := 2;
     end;
  9: Begin
      SensorName := 'FolderSize'; SensorStyle := 2;
     end; 
  else Begin
   LastErrMsg := 'Index out of bounds';
   Result := E_FAIL;
  End;
 End;
end;

function TDiskSize.Init: HResult;
begin
 Drive := 'C';
 Folder := 'C:\';
 FormatTo := ftBytes;
 Result := S_OK;
end;

function TDiskSize.Name(out Name: Pchar): HResult;
begin
 Name := 'Disksize';
 Result := S_OK;
end;

procedure TDiskSize.Parser(Param: String);
VAr
 I: Integer;
 N, V, S: String;
begin
 For I := 1 to CountFilt(param, '|') do begin
  S := GetFilt(Param, I, '|');
  N := uppercase(GetFilt(S, 1, '='));
  V := GetFilt(S, 2, '=');
  If N = 'DRIVE' Then Begin
   If Length(V) > 0 Then Drive := V[1];
  End;
  If N = 'FORMAT' Then Begin
   FormatTo := ftBytes;
   If (Uppercase(V) = 'PERCENT') or (V = '%') then FormatTo := ftPercent;
   If (Uppercase(V) = 'STRING') then FormatTo := ftString;
   If (Uppercase(V) = 'BYTES') then FormatTo := ftBytes;
  End;
  If N = 'FOLDER' then begin
   Folder := V;
  end;
 End;
end;

function TDiskSize.RequiredCoreVersion(out RCV: Word): HResult;
begin
 RCV := RCversion;
 Result := S_OK;
end;

function TDiskSize.SetParentHandle(Handle: THandle): HResult;
begin
 Result := S_OK;
end;

function TDiskSize.Setup: HResult;
begin
 Result := E_NOTIMPL;
end;

function TDiskSize.Terminating: HResult;
begin
 Result := S_OK;
end;

function TDiskSize.Version(out Version: word): HResult;
begin
 Version := PlugVersion;
 Result := S_OK;
end;

function TDiskSize.UpdateFileURL(out URL: Pchar): HResult;
begin
 URL := PChar(AutoUpdateFile);
 Result := S_OK;
end;

function TDiskSize.ObjIns(SensorIndex: Integer; ParameterName: PChar;
  out ValueType: Byte; out ValueRange: PChar): HResult;
begin
 Result := S_OK;
 ValueType := vtUnknown;
 If (SensorIndex < 3) or (sensorIndex > 6) then begin
  If uppercase(ParameterName) = 'FORMAT' then begin
   ValueType := vtFixed;
   ValueRange := 'Bytes|Percent|String';
  end;
 end;
end;

function TDiskSize.ShowSensor(Index: Integer; out ShowAt: Byte): HResult;
begin
 Result := S_OK;
 ShowAt := saDesignTime;
end;

function TDiskSize.GetAll(Free: Boolean): Int64;
Var
 I: Integer;
 List: String;
 DT: TDriveInfo;
begin
 List := Drivetype.BuildDriveList(false, true, false, false, false);
 Result := 0;
 For I := 1 to countfilt(List, ';') do begin
  DT := GetDriveInfo(GetFilt(List, I, ';')[1]);
  If Free then Result := Result + DT.DiskFree
   else Result := Result + DT.DiskCapasity;
 end;
end;

initialization
  TComObjectFactory.Create(ComServer, TDiskSize, Class_DiskSize,
    'DiskSize', 'CM2 Gen Disk Size', ciMultiInstance, tmApartment);
end.
