Search code examples
registryinno-setupdriversftdi

How to detect FTDI driver in system


I have an app that requires FTDI D2XX Direct Drivers drivers to be installed in system. Now I am creating an installation script using Inno Setup. How can I get an information whether FTDI drivers are installed in system (Windows). Is there any path or registry entry that could be checked to gain unambiguous information about that?


Solution

  • There were two ways I was thinking of when posting this answer, but I've decided to show my preferred one that time. For the sake of completeness, I'm including also a generic way to check whether a library is present in the system.

    1. Try to call an FTDI driver library function

    I think the best you can do is trying to call a function from the driver's library itself using delayed loading (delayload option). A good function to call is e.g. the FT_GetLibraryVersion function, by which you can get the library version, and which does not require opened device. In the following example code is shown just how to test whether the library is present in the system:

    [Code]
    type
      ULONG = Cardinal;
      FT_STATUS = ULONG;
    
    function FT_GetLibraryVersion(out lpdwVersion: DWORD): FT_STATUS;
      external '[email protected] stdcall delayload';
    
    function IsFTD2xxInstalled: Boolean;
    var
      Version: DWORD;
    begin
      Result := True;
      try
        FT_GetLibraryVersion(Version);
      except
        Result := False;
      end;
    end;
    

    It's just a probe if a library function can be called. Hence there is no result nor error checking.

    2. Check the library presence in a generic way

    Since the functions like LoadDLL and FreeDLL are deprecated (and there's no function to check if the library is already loaded), a generic way for checking whether a library is present in the system requires importing a few WinAPI functions. The following function LibraryExists checks whether a library of a given name is already loaded by Inno Setup and if not, it tries to load and unload it:

    [Code]
    #ifdef UNICODE
      #define AW "W"
    #else
      #define AW "A"
    #endif
    
    const
      ERROR_MOD_NOT_FOUND = 126;
    
    type
      HINSTANCE = THandle;
      HMODULE = HINSTANCE;
    
    function LoadLibrary(lpLibFileName: string): HMODULE;
      external 'LoadLibrary{#AW}@kernel32.dll stdcall';
    function FreeLibrary(hLibModule: HMODULE): BOOL;
      external '[email protected] stdcall';
    function GetModuleHandle(lpModuleName: string): HMODULE;
      external 'GetModuleHandle{#AW}@kernel32.dll stdcall';
    
    procedure RaiseLastError(const Message: string; const Exclude: array of LongInt);
    var
      I: Integer;
      LastError: LongInt;
    begin
      LastError := DLLGetLastError;
      for I := 0 to GetArrayLength(Exclude) - 1 do
        if Exclude[I] = LastError then
          Exit;
      RaiseException(Format('%s' + #13#10 + '%d: %s', [Message, LastError,
        SysErrorMessage(LastError)]));
    end;
    
    function IsModuleLoaded(const Name: string): Boolean;
    begin
      Result := GetModuleHandle(Name) <> 0;
      if not Result then
        RaiseLastError('GetModuleHandle call failed.', [ERROR_MOD_NOT_FOUND]);
    end;
    
    function LibraryExists(const Name: string): Boolean;
    var
      Handle: HMODULE;
    begin
      Result := IsModuleLoaded(Name);
      if not Result then
      begin
        Handle := LoadLibrary(Name);
        if Handle <> 0 then
        begin
          Result := True;
          FreeLibrary(Handle);
        end
        else
          RaiseLastError('LoadLibrary call failed.', [ERROR_MOD_NOT_FOUND]);
      end;
    end;
    

    And a possible usage for your case:

    function IsFTD2xxInstalled: Boolean;
    begin
      Result := LibraryExists('ftd2xx.dll');
    end;