{*******************************************************}
{                                                       }
{       nrCommLib Communication Library for Delphi      }
{       WinUSB Driver Installation                      }
{       Copyright (c) 1997-2011 Roman Novgorodov        }
{       v9.27                                           }
{       last update: 10 Jan 2010                        }
{                                                       }
{       e-mail:noro@deepsoftware.com                    }
{       DeepSoftware                                    }
{       http://www.deepsoftware.com/                    }
{                                                       }
{*******************************************************}
{$I nrcommlib.inc}
unit nrusbdriver;

interface
Uses Windows, nrclasses, classes, Dialogs, nrwin32, SysUtils, nrusb;

Type

  TnrUsbDriver = class(TComponent)
  private
    fnrUsb:TnrUSB;
    fIdVendor,fIdProduct:cardinal;
    fOnFatalError: TOnFatalErrorEvent;
    fVersion:string;
    fDriverDate:TDateTime;
    fInterfaceGUID:TGUID;
    fInterfaceGUIDString:string;
    fLog: TnrCustomLogFile;
    fCaption : string;
    slFiles:TStringList;
    fWinUSB : boolean;
    fShowSplash:boolean;
    function GetIdProduct: cardinal;
    function GetIdVendor: cardinal;
    function GetOnFatalError: TOnFatalErrorEvent;
    procedure SetIdProduct(const Value: cardinal);
    procedure SetIdVendor(const Value: cardinal);
    procedure SetOnFatalError(const Value: TOnFatalErrorEvent);
    function GetUSB: TnrUSB;
    procedure SetUSB(const Value: TnrUSB);
    function GetVersion: string;
    procedure SetVersion(const Value: string);
    function GetDate: TDateTime;
    function GetInterfaceGUID: string;
    procedure SetDate(const Value: TDateTime);
    procedure SetInterfaceGUID(const Value: string);
    function  SaveDriverFiles(const aDir:string):boolean;
    procedure DeleteDriverFiles(const aDir:string);
    function GetHardwareID:string;

    function GetLog: TnrCustomLogFile;
    procedure SetLog(const Value: TnrCustomLogFile);
    function GetCaption: string;
    procedure SetCaption(const Value: string);
    function GetWinUSB: boolean;
    procedure SetWinUSB(const Value: boolean);
    function GetShowProgress: boolean;
    procedure SetShowProgress(const Value: boolean);
  protected
    fDriverFileName:string;
    fResName:string;
    procedure DoFatalErrors(Detail: cardinal; Msg: string);
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure Logging(Det:TDetailLevel;Mess:string);
  public
    constructor Create(AOwner:TComponent); override;
    destructor Destroy; override;
    procedure Install;
  published
    property OnFatalError:TOnFatalErrorEvent read GetOnFatalError write SetOnFatalError;
    property IdVendor:cardinal read GetIdVendor write SetIdVendor;
    property IdProduct:cardinal read GetIdProduct write SetIdProduct;
    property nrUsb:TnrUSB read GetUSB write SetUSB;
    property DriverVersion:string read GetVersion write SetVersion;
    property DriverDate:TDateTime read GetDate write SetDate;
    property InterfaceGUID:string read GetInterfaceGUID write SetInterfaceGUID;
    property Log:TnrCustomLogFile read GetLog write SetLog;
    property DriverCaption:string read GetCaption write SetCaption;
    property WinUSB:boolean read GetWinUSB write SetWinUSB;
    property ShowProgress:boolean read GetShowProgress write SetShowProgress;
  end;

implementation
uses nrwin32di, nrusbman, nrsysutils, nrprogressdlg;

{$R ..\Res\nrdusb.res}

Const
  CS_DefaultDriverVersion       = '9.0.0.1';
  CS_DefaultFileList =
     // x32 files ...
     'nrusbbin_x32=i386\WinUSBCoInstaller2.dll'#13#10+
     'nrusbwdf_x32=i386\WdfCoInstaller01009.dll'#13#10+
     'nrusbwdfup_x32=i386\WUDFUpdate_01009.dll'#13#10+
     // AMD64 files ...
     'nrusbbin_x64=amd64\WinUSBCoInstaller2.dll'#13#10+
     'nrusbwdf_x64=amd64\WdfCoInstaller01009.dll'#13#10+
     'nrusbwdfup_x64=amd64\WUDFUpdate_01009.dll'#13#10+
     // IA64 files ...
     'nrusbbin_i64=ia64\WinUSBCoInstaller2.dll'#13#10+
     'nrusbwdf_i64=ia64\WdfCoInstaller01009.dll'#13#10+
     'nrusbwdfup_i64=ia64\WUDFUpdate_01009.dll';

  CS_NrUsbFileList =
     // x32 files ...
     'nrusbsys_x86=x86\nrusb.sys'#13#10+
     // AMD64 files ...
     'nrusbsys_x64=amd64\nrusb.sys'#13#10+
     // IA64 files ...
     'nrusbsys_ia64=ia64\nrusb.sys';

  CS_driver_inf = 'nrusb.inf';
  CS_x64stub_command =  '"%snrX64stub.exe" "%s" "%s"';

constructor TnrUsbDriver.Create(AOwner:TComponent);
begin
  inherited Create(AOwner);
  slFiles := TStringList.Create;
  slFiles.Text := CS_DefaultFileList;
  fInterfaceGUIDString := '{4266AEE3-A7D8-4DD2-B979-D64FB674F68A}';
  fVersion := CS_DefaultDriverVersion;
  fDriverDate := Now;
  fCaption := '';
//  fWinUSB := True;
end;

destructor TnrUsbDriver.Destroy;
begin
  slFiles.Free;
  inherited Destroy;
end;

procedure TnrUsbDriver.DoFatalErrors(Detail: cardinal; Msg: string);
var fRaise:boolean;
begin
  fRaise:=True;
  if Assigned(fLog) then fLog.AddLogFmt(dlSimple,'Fatal Error: %u, %s',[Detail,Msg]);
  if Assigned(fOnFatalError) then fOnFatalError(Self,ECC_NRUSBDRV,Detail,Msg,fRaise);
  if fRaise then raise Exception.Create(Msg);
end;

function TnrUsbDriver.GetIdProduct: cardinal;
begin
  Result := fIdProduct;
end;

function TnrUsbDriver.GetIdVendor: cardinal;
begin
  Result := fIdVendor;
end;

procedure TnrUsbDriver.SetIdProduct(const Value: cardinal);
begin
  fIdProduct := Value;
end;

procedure TnrUsbDriver.SetIdVendor(const Value: cardinal);
begin
  fIdVendor := Value;
end;

function TnrUsbDriver.GetOnFatalError: TOnFatalErrorEvent;
begin
  Result := fOnFatalError;
end;

{$IFDEF TRIAL}
Const
  //You are using nrComm Lib trial version!
  //Please register! xor d
  esi: Ansistring=
#$3D#$0B#$11#$44#$05#$16#$01#$44#$11#$17#$0D#$0A#$03#$44#$10#$16 +
#$0D#$05#$08#$44#$0A#$16#$27#$0B#$09#$09#$28#$0D#$06#$44#$12#$01 +
#$16#$17#$0D#$0B#$0A#$45#$69#$6E#$34#$08#$01#$05#$17#$01#$44#$16 +
#$01#$03#$0D#$17#$10#$01#$16#$45;
{$ENDIF}

procedure TnrUsbDriver.SetOnFatalError(const Value: TOnFatalErrorEvent);
begin
  fOnFatalError := Value;
end;

function TnrUsbDriver.GetUSB: TnrUSB;
begin
  Result :=  fnrUsb;
end;

procedure TnrUsbDriver.SetUSB(const Value: TnrUSB);
begin
  fnrUsb := Value;
end;

function TnrUsbDriver.GetHardwareID: string;
var Vid,Pid:cardinal;
begin
  Result := '';
  Vid := fIdVendor;
  Pid := fIdProduct;

  if (fnrUsb <> nil) and (fnrUsb.UsbDevice <> nil) then begin
    Vid := fnrUsb.UsbDevice.VendorID;
    Pid := fnrUsb.UsbDevice.ProductID;
  end;

  if (Vid = 0) or (Pid = 0) then begin
    DoFatalErrors(ECCE_NOT_SELECTED_DEVICE,'Device is not selected.');
    exit;
  end;

  Result := Format('USB\VID_%.4x&PID_%.4x',[Vid,Pid]);

end;

{$IFDEF TRIAL}
function eax:string;
var i:integer;
begin
  Result:='';
  for i := 1 to 56 do Result := Result + Char(Byte(esi[i]) xor Byte('d'));
end;
{$ENDIF}

function TnrUsbDriver.SaveDriverFiles(const aDir: string):boolean;
var sInf, sd, sDescr, sHid, sRes, sF:string;
    stmp : AnsiString;
    sz , hF:dword;
    i, cnt:integer;
begin
  Result := False;

  try
    fInterfaceGUIDString := Trim(fInterfaceGUIDString);
    if fInterfaceGUIDString = ''
      then CreateGUID(fInterfaceGUID)
      else fInterfaceGUID := StringToGUID(fInterfaceGUIDString);
  except
    DoFatalErrors(ECCE_INTERNAL, 'Interface GUID is not correct.');
    exit;
  end;

  if not DirectoryExists(aDir) then begin
    DoFatalErrors(ECCE_INTERNAL,'Creating of temporary path is failed.');
    exit;
  end;

  // extract binary driver files from resources ...

  ForceDirectories(aDir + 'i386\');
  ForceDirectories(aDir + 'amd64\');
  ForceDirectories(aDir + 'ia64\');
  ForceDirectories(aDir + 'x86\');

  if WinUSB
    then slFiles.Text := CS_DefaultFileList
    else slFiles.Text := CS_NrUsbFileList;

  cnt := 0;
  for i := 0 to slFiles.Count - 1 do begin
    sRes := slFiles.Names[i];
    sF := slFiles.Values[sRes];
    if CreateFileFromRes(sRes, aDir + sF) then begin
      Inc(cnt);
      Logging(dlDebug, 'Driver file has been created: ' + slFiles[i]);
    end;
  end;

  Logging(dlDetail, 'Create files:' + IntToStr(cnt));

  // extract INF driver file from resources ...

  sDescr := '';

  if fCaption <> ''
    then sDescr := ' ' + fCaption
    else
      if (fnrUsb <> nil) and (fnrUsb.UsbDevice <> nil)
         then sDescr := ' ' + fnrUsb.UsbDevice.NameFriendly;

  sHid := GetHardwareID; //Format('USB\VID_%.4x&PID_%.4x',[Vid,Pid]);

  if WinUSB
    then stmp := AnsiString(GetResAsStr('nrusbinf'))   // winusb inf file
    else stmp := AnsiString(GetResAsStr('nrusbinf2')); // lib usb inf file

  stmp := AnsiString(StringReplace(String(stmp), '#HARDWAREID#'{'%NR_HID%'},sHid,[rfReplaceAll,rfIgnoreCase]));
  stmp := AnsiString(StringReplace(String(stmp), '%NR_CLASS%','USB',[rfReplaceAll,rfIgnoreCase]));
//  stmp := StringReplace(stmp,'Class     = "libusb-win32 devices"','Class=USB',[rfReplaceAll,rfIgnoreCase]);
  stmp := AnsiString(StringReplace(String(stmp), GUIDToString(GUID_DEVCLASS_LIB_USB), GUIDToString(GUID_DEVCLASS_USB),[rfReplaceAll,rfIgnoreCase]));
  stmp := AnsiString(StringReplace(String(stmp), '#BASE_FILENAME#','nrUsb',[rfReplaceAll,rfIgnoreCase]));
  stmp := AnsiString(StringReplace(String(stmp), '#CAT_FILENAME#','',[rfReplaceAll,rfIgnoreCase]));

  {$IFDEF TRIAL}
  sDescr := sDescr + ' ' + StringReplace(eax,#13#10,' ', [rfReplaceAll]);
  {$ENDIF}
  DateTimeToString(sd,'mm|dd|yyyy', DriverDate);
  sd := StringReplace(sd, '|', '/' ,[rfReplaceAll,rfIgnoreCase]);
  stmp := AnsiString(StringReplace(String(stmp),'#DRIVER_DATE#' {'%NR_VER_DATE%'}, sd ,[rfReplaceAll,rfIgnoreCase]));
  stmp := AnsiString(StringReplace(String(stmp),'#DRIVER_VERSION#' {'%NR_VER_NUMBER%'},fVersion,[rfReplaceAll,rfIgnoreCase]));
  stmp := AnsiString(StringReplace(String(stmp),'#DEVICE_DESCRIPTION#'{'%NR_DEV_DESCR%'},Trim(sDescr),[rfReplaceAll,rfIgnoreCase]));
  stmp := AnsiString(StringReplace(String(stmp),'#DEVICE_INTERFACE_GUID#'{'%NR_INTF_GUID%'},GUIDToString(fInterfaceGUID),[rfReplaceAll,rfIgnoreCase]));
  stmp := AnsiString(StringReplace(String(stmp),'#DEVICE_MANUFACTURER#','nrUsb DeepSoftware',[rfReplaceAll,rfIgnoreCase]));

  sInf := aDir + CS_driver_inf;
  hF := CreateFile(PChar(sInf),GENERIC_WRITE,0,nil,CREATE_ALWAYS,0,0);
  if hF = INVALID_HANDLE_VALUE then begin
    DoFatalErrors(ECCE_FILE_IO,'Creating file error.');
    exit;
  end;

  WriteFile(hF, PAnsiChar(stmp)^, Length(stmp), sz, nil);
  CloseHandle(hF);

  // extract x64stub app from resources ...

  CreateFileFromRes('nrx64stub', aDir + CS_x64stub_exe);

  Result := True;

end;

procedure TnrUsbDriver.DeleteDriverFiles(const aDir: string);
var i:integer;
    sF:string;
begin
  for i := 0 to slFiles.Count - 1 do begin
    sF := slFiles.Values[slFiles.Names[i]];
    if Length(sF) > 0 then DeleteFile(aDir + sF);
  end;
  DeleteFile(aDir + CS_x64stub_exe);
  DeleteFile(aDir + CS_driver_inf);
end;

procedure TnrUsbDriver.Install;
label _exit;
var sTmpFolder, sInf, sError:string;
    len :integer;
    res : boolean;
    errCode:dword;
begin

{  if not TryEnterCriticalSection(csUsbReloadLock) then begin
    DoFatalErrors(ECCE_INTERNAL,'USB Enumerator is working. Please try again later.');
    exit;
  end;}
  if ShowProgress
    then ProgressShow('Usb Driver installation. Please wait ...');

  errCode := 0;
  try
    Logging(dlDetail, 'Start WinUSB driver installation ...');
    sTmpFolder := '';
    SetLength(sTmpFolder,MAX_PATH);

    {$IFDEF TRIAL}
    try
      if (GetTickCount and 1) = 1 then ShowMessage(eax);
    except
    end;
    {$ENDIF}

    len := GetTempPath(MAX_PATH,PChar(sTmpFolder)); // buffer for path
    if (len <= 0) or (len > MAX_PATH) then begin
      DoFatalErrors(ECCE_INTERNAL,'Temporary path is wrong.');
      goto _exit;
    end;
    SetLength(sTmpFolder,len);
    sTmpFolder := Format('%snrCommLib.Tmp.%x\',[sTmpFolder,GetTickCount]);
    ForceDirectories(sTmpFolder);
    SaveDriverFiles(sTmpFolder);

    Logging(dlDetail, 'Files are saved. Try to install ...');

    sleep(50);
    res := false;

    try
      try
        sInf := sTmpFolder + CS_driver_inf;
        res := InstallDriver(GetHardwareID, sInf );

        if (not res) then begin
          errCode := GetLastError;
          if (errCode = ERROR_IN_WOW64) then begin
            Logging(dlDebug, 'Driver installation needs x64 mode.');
            RunProcess(GetMainWindowHandle,
               Format(CS_x64stub_command, [sTmpFolder, GetHardwareID, sInf]),
               sTmpFolder, True, True);
            errCode := GetLastError();
            res := errCode = ERROR_SUCCESS;
            Logging(dlDebug, 'x64 stub finished.');
          end;
          //res := InstallINF(sTmpFolder + 'nrusb.inf');
          Logging(dlDetail, 'Instalaltion is finished.');
        end;
      except
        on E:Exception do DoFatalErrors(ECC_NRUSBDRV, E.Message);
      end;
    finally
      DeleteDriverFiles(sTmpFolder);
      DeleteFile(sTmpFolder);
    end;

    if not res then begin
      if errCode <> ERROR_SUCCESS then begin
        sError := SysErrorMessage(errCode);
        if Length(sError) = 0 then sError := GetSetupError(errCode);
      end
        else sError := '';

      DoFatalErrors(ECCE_INTERNAL, 'Driver installation failed.' + sError);
    end;

_exit:

  finally
    //LeaveCriticalSection(csUsbReloadLock);
    ProgressHide;
  end;

end;

function TnrUsbDriver.GetVersion: string;
begin
  Result := fVersion;
end;

procedure TnrUsbDriver.SetVersion(const Value: string);
begin
  fVersion := Value;
end;

function TnrUsbDriver.GetDate: TDateTime;
begin
  Result := fDriverDate;
end;

function TnrUsbDriver.GetInterfaceGUID: string;
begin
  Result := fInterfaceGUIDString;
end;

procedure TnrUsbDriver.SetDate(const Value: TDateTime);
begin
  fDriverDate := Value;
end;

procedure TnrUsbDriver.SetInterfaceGUID(const Value: string);
begin
  fInterfaceGUIDString := Value;
end;

function TnrUsbDriver.GetCaption: string;
begin
  Result := fCaption;
end;

procedure TnrUsbDriver.SetCaption(const Value: string);
begin
  fCaption := Value;
end;

function TnrUsbDriver.GetLog: TnrCustomLogFile;
begin
  Result := fLog;
end;

procedure TnrUsbDriver.SetLog(const Value: TnrCustomLogFile);
begin
  fLog := Value;
end;

procedure TnrUsbDriver.Logging(Det: TDetailLevel; Mess: string);
begin
  if Assigned(Log) then Log.AddLog(Det,'nrUsbDriver: ' + Mess);
end;

procedure TnrUsbDriver.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if Operation = opRemove then begin
    if AComponent = fnrUsb then fnrUsb := nil;
    if AComponent = fLog then fLog := nil
  end;
end;

function TnrUsbDriver.GetWinUSB: boolean;
begin
  Result := fWinUSB;
end;

procedure TnrUsbDriver.SetWinUSB(const Value: boolean);
begin
  fWinUSB := Value;
end;

function TnrUsbDriver.GetShowProgress: boolean;
begin
  Result := fShowSplash;
end;

procedure TnrUsbDriver.SetShowProgress(const Value: boolean);
begin
  fShowSplash := Value;
end;

end.

