//-----------------------------------------------------------------------------
// File: GetDXVer.cpp
//
// Desc: Demonstrates how applications can detect what version of DirectX
//       is installed.
//
//    (C) Copyright 1995-1997 Microsoft Corp.  All rights reserved.
//
// converted by Ivo Steinmann (isteinmann@bluewin.ch)
//-----------------------------------------------------------------------------

unit GetDXVer;

interface

uses Windows, ActiveX,

  // DirectX
  DirectDraw, DirectMusic, DirectInput;

type
  TDirectInputCreate = function (hinst: THandle; dwVersion: DWORD; out ppDI: IDirectInput; punkOuter: IUnknown) : HResult; stdcall;
  TDirectDrawCreate = function (lpGUID: PGUID; out lplpDD: IDirectDraw; pUnkOuter: IUnknown) : HResult; stdcall;
  TDirectDrawCreateEx = function  (lpGUID: PGUID; out lplpDD: IDirectDraw7; const iid: TGUID; pUnkOuter: IUnknown) : HResult; stdcall;

  TDXVersion = (
    DXVER_NOTINSTALLED,
    DXVER_DIRECTX_1,
    DXVER_DIRECTX_2,
    DXVER_DIRECTX_3,
    DXVER_DIRECTX_5,
    DXVER_DIRECTX_6,
    DXVER_DIRECTX_61,
    DXVER_DIRECTX_7,
    DXVER_DIRECTX_8
  );

//-----------------------------------------------------------------------------
// Name: GetDXVersion()
// Desc: This function returns two arguments:
//          dwDXVersion:
//            0x0000 = No DirectX installed
//            0x0100 = DirectX version 1 installed
//            0x0200 = DirectX 2 installed
//            0x0300 = DirectX 3 installed
//            0x0500 = At least DirectX 5 installed.
//            0x0600 = At least DirectX 6 installed.
//            0x0601 = At least DirectX 6.1 installed.
//            0x0700 = At least DirectX 7 installed.
//            0x0800 = At least DirectX 8 installed.
//          dwDXPlatform:
//            0                          = Unknown (This is a failure case)
//            VER_PLATFORM_WIN32_WINDOWS = Windows 9X platform
//            VER_PLATFORM_WIN32_NT      = Windows NT platform
// 
//          Please note that this code is intended as a general guideline. Your
//          app will probably be able to simply query for functionality (via
//          QueryInterface) for one or two components.
//
//          Please also note:
//            "if (dxVer != 0x500) return FALSE;" is BAD. 
//            "if (dxVer < 0x500) return FALSE;" is MUCH BETTER.
//          to ensure your app will run on future releases of DirectX.
//-----------------------------------------------------------------------------

procedure GetDXVersion(var pdwDXVersion, pdwDXPlatform: DWORD);
function GetDXVersionEx: TDXVersion;

implementation

procedure GetDXVersion(var pdwDXVersion, pdwDXPlatform: DWORD);
var hr      : HResult;
    DDHinst : HInst;
    DIHinst : HInst;
    hD3D8DLL: HInst;
    pDDraw  : IDirectDraw;
    PDDraw2 : IDirectDraw2;
    osVer   : OSVERSIONINFO;
    pSurf   : IDirectDrawSurface;
    pSurf3  : IDirectDrawSurface3;
    pSurf4  : IDirectDrawSurface4;
    pDD7    : IDirectDraw7;
    pDMusic : IDirectMusic;
    ddsd    : TDDSURFACEDESC;
    DirectInputCreate : TDirectInputCreate;
    DirectDrawCreate  : TDirectDrawCreate;
    DirectDrawCreateEx: TDirectDrawCreateEx;
begin
  pointer(pDDraw) := nil;
  pointer(pDDraw2) := nil;
  pointer(pDD7) := nil;
  pointer(pSurf) := nil;
  pointer(pSurf3) := nil;
  pointer(pSurf4) := nil;
  pointer(pDMusic) := nil;

  // First get the windows platform
  ZeroMemory(@osVer, Sizeof(osVer));
  osVer.dwOSVersionInfoSize := Sizeof(osVer);
  if not GetVersionEx(osVer) then
  begin
    pdwDXVersion := 0;
    pdwDXPlatform := 0;
    Exit;
  end;

  if osVer.dwPlatformId = VER_PLATFORM_WIN32_NT then
  begin
    pdwDXPlatform := VER_PLATFORM_WIN32_NT;

    // NT is easy... NT 4.0 is DX2, 4.0 SP3 is DX3, 5.0 is DX5
    // and no DX on earlier versions.
    if osVer.dwMajorVersion < 4 then
    begin
      pdwDXVersion := 0; // No DX on NT3.51 or earlier
      Exit;
    end;

    if (osVer.dwMajorVersion = 4) then
    begin
      // NT4 up to SP2 is DX2, and SP3 onwards is DX3, so we are at least DX2
      pdwDXVersion := $200;

      // We're not supposed to be able to tell which SP we're on, so check for dinput
      DIHinst := LoadLibrary('DINPUT.DLL');
      if DIHinst = 0 then
      begin
        // No DInput... must be DX2 on NT 4 pre-SP3
        Exit;
      end;

      DirectInputCreate := GetProcAddress(DIHinst, 'DirectInputCreateA');
      FreeLibrary(DIHinst);

      if not Assigned(DirectInputCreate) then
      begin
        // No DInput... must be pre-SP3 DX2
        Exit;
      end;

      // It must be NT4, DX2
      pdwDXVersion := $300;  // DX3 on NT4 SP3 or higher
    end;
    // Else it's NT5 or higher, and it's DX5a or higher: Drop through to
    // Win9x tests for a test of DDraw (DX6 or higher)
  end else begin
    // Not NT... must be Win9x
    pdwDXPlatform := VER_PLATFORM_WIN32_WINDOWS;
  end;

  // Now we know we are in Windows 9x (or maybe 3.1), so anything's possible.
  // First see if DDRAW.DLL even exists.
  DDHinst := LoadLibrary('DDRAW.DLL');
  if DDHinst = 0 then
  begin
    pdwDXVersion := 0;
    pdwDXPlatform := 0;
    FreeLibrary(DDHinst);
    Exit;
  end;

  // See if we can create the DirectDraw object.
  DirectDrawCreate := GetProcAddress(DDHinst, 'DirectDrawCreate');
  if not Assigned(DirectDrawCreate) then
  begin
    pdwDXVersion := 0;
    pdwDXPlatform := 0;
    FreeLibrary(DDHinst);
    Exit;
  end;

  hr := DirectDrawCreate(nil, pDDraw, nil);
  if FAILED(hr) then
  begin
    pdwDXVersion := 0;
    pdwDXPlatform := 0;
    FreeLibrary(DDHinst);
    Exit;
  end;

  // So DirectDraw exists.  We are at least DX1.
  pdwDXVersion := $100;

  // Let's see if IID_IDirectDraw2 exists.
  hr := pDDraw.QueryInterface(IID_IDirectDraw2, pDDraw2);
  if FAILED(hr) then
  begin
    // No IDirectDraw2 exists... must be DX1
    pDDraw := nil;
    FreeLibrary(DDHinst);
    Exit;
  end;

  // IDirectDraw2 exists. We must be at least DX2
  pDDraw2 := nil;
  pdwDXVersion := $200;

  ///////////////////////////////////////////////////////////////////////////
  // DirectX 3.0 Checks
  ///////////////////////////////////////////////////////////////////////////

  // DirectInput was added for DX3
  DIHinst := LoadLibrary('DINPUT.DLL');
  if DIHinst = 0 then
  begin
    // No DInput... must not be DX3
    pDDraw := nil;
    FreeLibrary(DDHinst);
    FreeLibrary(DDHinst);
    Exit;
  end;

  DirectInputCreate := GetProcAddress(DIHinst, 'DirectInputCreateA');

  if not Assigned(DirectInputCreate) then
  begin
    // No DInput... must be DX2
    pDDraw := nil;
    FreeLibrary(DDHinst);
    FreeLibrary(DIHinst);
    Exit;
  end;

  // DirectInputCreate exists. We are at least DX3
  pdwDXVersion := $300;
  FreeLibrary(DIHinst);

  // Can do checks for 3a vs 3b here


  ///////////////////////////////////////////////////////////////////////////
  // DirectX 5.0 Checks
  ///////////////////////////////////////////////////////////////////////////

  // We can tell if DX5 is present by checking for the existence of
  // IDirectDrawSurface3. First, we need a surface to QI off of.

  ZeroMemory(@ddsd, sizeof(ddsd));
  ddsd.dwSize         := sizeof(ddsd);
  ddsd.dwFlags        := DDSD_CAPS;
  ddsd.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE;

  hr := pDDraw.SetCooperativeLevel(0, DDSCL_NORMAL);
  if FAILED(hr) then
  begin
    // Failure. This means DDraw isn't properly installed.
    pDDraw := nil;
    FreeLibrary(DDHinst);
    pdwDXVersion := 0;
    Exit;
  end;

  hr := pDDraw.CreateSurface(ddsd, pSurf, nil);
  if FAILED(hr) then
  begin
    // Failure. This means DDraw isn't properly installed.
    pDDraw := nil;
    FreeLibrary(DDHinst);
    pdwDXVersion := 0;
    Exit;
  end;

  // Query for the IDirectDrawSurface3 interface
  if FAILED(pSurf.QueryInterface(IID_IDirectDrawSurface3, pSurf3)) then
  begin
    pDDraw := nil;
    FreeLibrary(DDHinst);
    Exit;
  end;

  // QI for IDirectDrawSurface3 succeeded. We must be at least DX5
  pSurf3 := nil;
  pdwDXVersion := $500;


  ///////////////////////////////////////////////////////////////////////////
  // DirectX 6.0 Checks
  ///////////////////////////////////////////////////////////////////////////

  // The IDirectDrawSurface4 interface was introduced with DX 6.0
  if FAILED(pSurf.QueryInterface(IID_IDirectDrawSurface4, pSurf4)) then
  begin
    pDDraw := nil;
    FreeLibrary(DDHinst);
    Exit;
  end;

  // IDirectDrawSurface4 was create successfully. We must be at least DX6
  pdwDXVersion := $600;
  pSurf4 := nil;
  pSurf := nil;
  pDDraw := nil;


  ///////////////////////////////////////////////////////////////////////////
  // DirectX 6.1 Checks
  ///////////////////////////////////////////////////////////////////////////

  // Check for DMusic, which was introduced with DX6.1
  CoInitialize(nil);
  hr := CoCreateInstance(CLSID_DirectMusic, nil, CLSCTX_INPROC_SERVER, IID_IDirectMusic, pDMusic);  // Hier ist ein Memoryloch
  if FAILED(hr) then
  begin
    FreeLibrary(DDHinst);
    CoUninitialize;
    Exit;
  end;

  // DirectMusic was created successfully. We must be at least DX6.1
  pdwDXVersion := $601;
  pDMusic := nil;
  CoUninitialize;


  ///////////////////////////////////////////////////////////////////////////
  // DirectX 7.0 Checks
  ///////////////////////////////////////////////////////////////////////////

  // Check for DirectX 7 by creating a DDraw7 object
  DirectDrawCreateEx := GetProcAddress(DDHinst, 'DirectDrawCreateEx');
  if not Assigned(DirectDrawCreateEx) then
  begin
    FreeLibrary(DDHinst);
    Exit;
  end;

  if FAILED(DirectDrawCreateEx(nil, pDD7, IID_IDirectDraw7, nil)) then
  begin
    FreeLibrary(DDHinst);
    Exit;
  end;

  // DDraw7 was created successfully. We must be at least DX7.0
  pdwDXVersion := $700;
  pDD7 := nil;

  //-------------------------------------------------------------------------
  // DirectX 8.0 Checks
  //-------------------------------------------------------------------------

  // Simply see if D3D8.dll exists.
  hD3D8DLL := LoadLibrary('D3D8.DLL');
  if hD3D8DLL = 0 then
  begin
    FreeLibrary(DDHinst);
    Exit;
  end;

  // D3D8.dll exists. We must be at least DX8.0
  pdwDXVersion := $800;


  ///////////////////////////////////////////////////////////////////////////
  // End of checks
  ///////////////////////////////////////////////////////////////////////////

  // Close open libraries and return
  FreeLibrary(DDHinst);
  FreeLibrary(hD3D8DLL);
end;

function GetDXVersionEx: TDXVersion;
var pdwDXVersion, pdwDXPlatform: DWORD;
begin
  GetDXVersion(pdwDXVersion, pdwDXPlatform);
  case pdwDXVersion of
    $0000: Result := DXVER_NOTINSTALLED;
    $0100: Result := DXVER_DIRECTX_1;
    $0200: Result := DXVER_DIRECTX_2;
    $0300: Result := DXVER_DIRECTX_3;
    $0500: Result := DXVER_DIRECTX_5;
    $0600: Result := DXVER_DIRECTX_6;
    $0601: Result := DXVER_DIRECTX_61;
    $0700: Result := DXVER_DIRECTX_7;
    $0800: Result := DXVER_DIRECTX_8;
    else Result := DXVER_NOTINSTALLED;
  end;
end;

end.
