Game explorer: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
(+ msdn link)
(macro arguments, working removal and no more GameuxInstallHelper)
Line 2: Line 2:


== Description ==
== Description ==
Microsoft Windows Vista ships with a feature called Game Explorer that allows a developer to list games in a central explorer location. Following the useful code from kichik and Doca Cola in this thread: http://forums.winamp.com/showthread.php?s=&threadid=258969 I wrote an installer that adds an item to Game Explorer and removes it.
Microsoft Windows Vista ships with a feature called Game Explorer that allows a developer to list games in a central explorer location. This page contains code that allows a NSIS installer to add  and remove a game from the Game Explorer.


To integrate with Game Explorer you need to add a gdf xml file, icon and screenshot to the resources of an exe or dll that installs with your app. You can generate these with the Game Explorer tool that ships in the Microsoft DirectX SDK. You will also need that SDK for the GameuxInstallHelper.dll library these routines use to install the app.
Before using this code, a game with an implemented GDF must already be prepared. GDF stands for Game Definition File and is actually a XML embedded as a resource in the game executable. The resource type should be named DATA and the resource itself should be named __GDF_XML. For more information, see MSDN.


== The code ==
== Code ==
<highlight-nsis>
<highlight-nsis># user interface
  RequestExecutionLevel admin
 
!define GIS_CURRENT_USER 2
!define GameExplorer_AddGame    '!insertmacro GameExplorer_AddGame'
!define GIS_ALL_USERS 3
!define GameExplorer_UpdateGame '!insertmacro GameExplorer_UpdateGame'
!define GameExplorer_RemoveGame '!insertmacro GameExplorer_RemoveGame'
 
# internal stuff
 
!define CLSCTX_INPROC_SERVER 1
!define IID_IGameExplorer {E7B2FB72-D728-49B3-A5F2-18EBF5F1349E}
!define CLSID_GameExplorer {9A5EA990-3034-4D6F-9128-01F3C61022BC}
 
!define GIS_CURRENT_USER 2
!define GIS_ALL_USERS   3
 
!define IGameExplorer_QueryInterface 0
!define IGameExplorer_AddRef        1
!define IGameExplorer_Release        2
!define IGameExplorer_AddGame        3
!define IGameExplorer_RemoveGame    4
!define IGameExplorer_UpdateGame    5
!define IGameExplorer_VerifyAccess  6
 
# includes
 
!include LogicLib.nsh
 
# the actual code
 
!macro GameExplorer_GenerateGUID
 
  System::Call 'ole32::CoCreateGuid(g .s)'
 
!macroend
 
!macro GameExplorer_AddGame CONTEXT GDF INSTDIR EXE GUID
 
  !define __GAME_EXPLORER_UNIQUE "${__LINE__}${__FILE__}"


Function GameExplorer
   Push $0
   Push $0
  Push $1
   ; Check if we have Vista or later
 
  Push $R0
   Push $R1
  Push $R2
 
  Push "${GUID}"
  Push "${INSTDIR}"
  Push "${GDF}"
 
  Pop $R0 # == ${GDF}
  Pop $R1 # == ${INSTDIR}
  Pop $R2 # == ${GUID}
 
   ClearErrors
   ClearErrors
  ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
  IfErrors finished
  StrCpy $0 $0 1
  IntCmp $0 6 0 finished
 
  ; Register with Game Explorer if so
  SetOutPath $Temp
  File "GameuxInstallHelper.dll"
  ; Remove first to avoid duplication
  ReadRegStr $0 HKCU "Software\TITLE" "GameExplorer"
  StrCmp $0 '' skipUninstall
  System::Call "GameuxInstallHelper::RemoveFromGameExplorer(g r0)"
  System::Call "GameuxInstallHelper::RemoveTasks(g r0)"
  skipUninstall:
  ; Now add to GE
  System::Call "GameuxInstallHelper::GenerateGUID(g .r0)"
  WriteRegStr HKCU "Software\TITLE" "GameExplorer" $0
  System::Call "GameuxInstallHelper::AddToGameExplorer(t 'GDFEXE', t 'APPPATH', i ${GIS_ALL_USERS}, g r0 r0)"
  System::Call "GameuxInstallHelper::CreateTask(i ${GIS_ALL_USERS}, g r0, i 0, i 0, t 'Play', t 'PROGPATH', t 'PROGARGS')"
  System::Call "GameuxInstallHelper::CreateTask(i ${GIS_ALL_USERS}, g r0, i 1, i 0, t 'Support', t 'http://www.codev.co.uk/', t '')"
finished:
  Pop $0
FunctionEnd


Function un.GameExplorer
  System::Call "ole32::CoCreateInstance( \
    g '${CLSID_GameExplorer}', i 0, \
    i ${CLSCTX_INPROC_SERVER}, \
    g '${IID_IGameExplorer}', *i .r1) i .r0"
 
  ${If} $0 != 0 # S_OK
 
    SetErrors
    Goto "done_${__GAME_EXPLORER_UNIQUE}"
 
  ${EndIf}
 
  !if ${CONTEXT} == all
 
    System::Call "$1->${IGameExplorer_AddGame}(w R0, w R1, i ${GIS_ALL_USERS}, g R2) i .r0"
 
  !else if ${CONTEXT} == user
 
    System::Call "$1->${IGameExplorer_AddGame}(w R1, w R0, i ${GIS_CURRENT_USER}, g R2) i .r0"
 
  !else
 
    !error "Invalid CONTEXT passed to GameExplorer_AddGame! Must be `user` or `all`."
 
  !endif
 
  ${If} $0 != 0 # S_OK
 
    SetErrors
 
  ${Else}
 
    # Create play task
 
    !if ${CONTEXT} == all
 
      SetShellVarContext all
 
    !else if if ${CONTEXT} == user
 
      SetShellVarContext user
 
    !endif
 
    CreateDirectory $APPDATA\Microsoft\Windows\GameExplorer\$R2\PlayTasks\0
    CreateShortcut $APPDATA\Microsoft\Windows\GameExplorer\$R2\PlayTasks\0\Play.lnk "${EXE}"
 
  ${EndIf}
 
  System::Call "$1->${IGameExplorer_Release}()"
 
  "done_${__GAME_EXPLORER_UNIQUE}:"
 
    Pop $R2
    Pop $R1
    Pop $R0
 
    Pop $1
    Pop $0
 
  !undef __GAME_EXPLORER_UNIQUE
 
!macroend
 
!macro _GameExplorer_GUID_Function Function GUID
 
  !define __GAME_EXPLORER_UNIQUE "${__LINE__}${__FILE__}"
 
   Push $0
   Push $0
  Push $1
   ; Check if we have Vista or later
  Push $2
  Push $3
  Push $4
  Push $5
 
  Push $R0
 
  Push "${GUID}"
 
  Pop $R0 # == ${GUID}
 
  System::Alloc 16
  Exch $R0
  System::Call "ole32::CLSIDFromString(w s, i R0)"
   System::Call "*$R0(i .r2, i .r3, i .r4, i .r5)"
  System::Free $R0
 
   ClearErrors
   ClearErrors
   ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
 
  IfErrors finished
   System::Call "ole32::CoCreateInstance( \
   StrCpy $0 $0 1
    g '${CLSID_GameExplorer}', i 0, \
  IntCmp $0 6 0 finished
    i ${CLSCTX_INPROC_SERVER}, \
    g '${IID_IGameExplorer}', *i .r1) i .r0"
   ; Remove from game explorer
 
  SetOutPath $Temp
   ${If} $0 != 0 # S_OK
   File "GameuxInstallHelper.dll"
 
   ReadRegStr $0 HKCU "Software\TITLE" "GameExplorer"
    SetErrors
   StrCmp $0 '' finished
    Goto "done_${__GAME_EXPLORER_UNIQUE}"
   System::Call "GameuxInstallHelper::RemoveFromGameExplorer(g r0)"
 
   System::Call "GameuxInstallHelper::RemoveTasks(g r0)"
   ${EndIf}
finished:
 
   System::Call "$1->${Function}(i r2, i r3, i r4, i r5) i .r0"
Pop $0
 
FunctionEnd
   ${If} $0 != 0 # S_OK
</highlight-nsis>
 
    SetErrors
 
   ${EndIf}
 
   System::Call "$1->${IGameExplorer_Release}()"
 
   "done_${__GAME_EXPLORER_UNIQUE}:"
 
    Pop $R0
 
    Pop $5
    Pop $4
    Pop $3
    Pop $2
    Pop $1
    Pop $0
 
  !undef __GAME_EXPLORER_UNIQUE
 
!macroend
 
!macro GameExplorer_UpdateGame GUID
 
  !insertmacro _GameExplorer_GUID_Function ${IGameExplorer_UpdateGame} "${GUID}"
 
!macroend
 
!macro GameExplorer_RemoveGame GUID
 
  !insertmacro _GameExplorer_GUID_Function ${IGameExplorer_RemoveGame} "${GUID}"
 
!macroend</highlight-nsis>


== Usage ==
== Usage ==
Just put ''Call GameExplorer'' in your install section and ''Call un.GameExplorer'' in your uninstall section.


Replace TITLE with the name of your app, GDFAPP with the executable containing the GDF resources, APPPATH with the application path, PROGPATH with the program to run when the user double clicks your game (you can add arguments in the place of PROGARGS) and your application's website in the place of http://www.codev.co.uk
=== Adding Games ===
 
First, an instance GUID for the game installation must be created with '''GameExplorer_GenerateGUID''' or any other GUID generation method. The generated instance GUID must be saved for later usage as it's required for the removal and updating of the game.
 
<highlight-nsis>${GameExplorer_GenerateGUID}
Pop $0 # instance GUID saved to $0</highlight-nsis>
 
Next, simply call '''GameExplorer_AddGame''' with all the details.
 
<highlight-nsis>${GameExplorer_AddGame} all $INSTDIR\GDF.dll $INSTDIR $INSTDIR\Game.exe $0
  # `all` means installation for all users
  # `$INSTDIR\GDF.dll` is the path to the executable containing the GDF
  # `$INSTDIR` is the installation directory
  # `$INSTDIR\Game.exe` is the path to the main game executable
  # `$0` is the instance GUID</highlight-nsis>
 
The error flag is set in case of an error.
 
=== Additional Tasks ===
 
'''GameExplorer_AddGame''' creates only one task by default - the ''Play'' task. To create more, a shortcut must be created in a numbered directory under ''$APPDATA\Microsoft\Windows\GameExplorer\<instance GUID>\PlayTasks\<task number>'' or ''$APPDATA\Microsoft\Windows\GameExplorer\<instance GUID>\SupportTasks\<task number>''. The ''Play'' task is numbered 0 under PlayTasks, so the next play task should be numbered 1, while the next support task should be numbered 0.
 
<highlight-nsis># restore instance GUID to $0 somehow
CreateDirectory $APPDATA\Microsoft\Windows\GameExplorer\$0\PlayTasks\1
CreateShortcut "$APPDATA\Microsoft\Windows\GameExplorer\$0\PlayTasks\1\Network Play.lnk" \
  "$INSTDIR\Game.exe" "-network"
 
CreateDirectory $APPDATA\Microsoft\Windows\GameExplorer\$0\SupportTasks\0
CreateShortcut "$APPDATA\Microsoft\Windows\GameExplorer\$0\SupportTasks\0\Network Play.lnk" \
  "$INSTDIR\Support.exe"</highlight-nsis>
 
=== Updating Games ===
 
Restore the instance GUID and pass it to ''GameExplorer_UpdateGame''.
 
<highlight-nsis># restore instance GUID to $0 somehow
${GameExplorer_UpdateGame} $0</highlight-nsis>
 
The error flag is set in case of an error.
 
=== Removing Games ===
 
Restore the instance GUID and pass it to ''GameExplorer_RemoveGame''.
 
<highlight-nsis># restore instance GUID to $0 somehow
${GameExplorer_RemoveGame} $0</highlight-nsis>
 
The error flag is set in case of an error.
 
== Related MSDN Pages ==


== MSDN Pages ==
*[http://msdn2.microsoft.com/en-us/library/bb206359.aspx#Integrating_into_an_Installer Integrating Windows Game Explorer into an Installer]
[http://msdn2.microsoft.com/en-us/library/bb206359.aspx#Integrating_into_an_Installer Integrating Windows Game Explorer into an Installer]
*[http://msdn2.microsoft.com/en-us/library/bb173435.aspx Game Definition File Editor]


[[Category:Code Examples]]
[[Category:Code Examples]]

Revision as of 15:43, 13 April 2007

Author: codev (talk, contrib)


Description

Microsoft Windows Vista ships with a feature called Game Explorer that allows a developer to list games in a central explorer location. This page contains code that allows a NSIS installer to add and remove a game from the Game Explorer.

Before using this code, a game with an implemented GDF must already be prepared. GDF stands for Game Definition File and is actually a XML embedded as a resource in the game executable. The resource type should be named DATA and the resource itself should be named __GDF_XML. For more information, see MSDN.

Code

# user interface
 
!define GameExplorer_AddGame    '!insertmacro GameExplorer_AddGame'
!define GameExplorer_UpdateGame '!insertmacro GameExplorer_UpdateGame'
!define GameExplorer_RemoveGame '!insertmacro GameExplorer_RemoveGame'
 
# internal stuff
 
!define CLSCTX_INPROC_SERVER 1
!define IID_IGameExplorer  {E7B2FB72-D728-49B3-A5F2-18EBF5F1349E}
!define CLSID_GameExplorer {9A5EA990-3034-4D6F-9128-01F3C61022BC}
 
!define GIS_CURRENT_USER 2
!define GIS_ALL_USERS    3
 
!define IGameExplorer_QueryInterface 0
!define IGameExplorer_AddRef         1
!define IGameExplorer_Release        2
!define IGameExplorer_AddGame        3
!define IGameExplorer_RemoveGame     4
!define IGameExplorer_UpdateGame     5
!define IGameExplorer_VerifyAccess   6
 
# includes
 
!include LogicLib.nsh
 
# the actual code
 
!macro GameExplorer_GenerateGUID
 
  System::Call 'ole32::CoCreateGuid(g .s)'
 
!macroend
 
!macro GameExplorer_AddGame CONTEXT GDF INSTDIR EXE GUID
 
  !define __GAME_EXPLORER_UNIQUE "${__LINE__}${__FILE__}"
 
  Push $0
  Push $1
 
  Push $R0
  Push $R1
  Push $R2
 
  Push "${GUID}"
  Push "${INSTDIR}"
  Push "${GDF}"
 
  Pop $R0 # == ${GDF}
  Pop $R1 # == ${INSTDIR}
  Pop $R2 # == ${GUID}
 
  ClearErrors
 
  System::Call "ole32::CoCreateInstance( \
    g '${CLSID_GameExplorer}', i 0, \
    i ${CLSCTX_INPROC_SERVER}, \
    g '${IID_IGameExplorer}', *i .r1) i .r0"
 
  ${If} $0 != 0 # S_OK
 
    SetErrors
    Goto "done_${__GAME_EXPLORER_UNIQUE}"
 
  ${EndIf}
 
  !if ${CONTEXT} == all
 
    System::Call "$1->${IGameExplorer_AddGame}(w R0, w R1, i ${GIS_ALL_USERS}, g R2) i .r0"
 
  !else if ${CONTEXT} == user
 
    System::Call "$1->${IGameExplorer_AddGame}(w R1, w R0, i ${GIS_CURRENT_USER}, g R2) i .r0"
 
  !else
 
    !error "Invalid CONTEXT passed to GameExplorer_AddGame! Must be `user` or `all`."
 
  !endif
 
  ${If} $0 != 0 # S_OK
 
    SetErrors
 
  ${Else}
 
    # Create play task
 
    !if ${CONTEXT} == all
 
      SetShellVarContext all
 
    !else if if ${CONTEXT} == user
 
      SetShellVarContext user
 
    !endif
 
    CreateDirectory $APPDATA\Microsoft\Windows\GameExplorer\$R2\PlayTasks\0
    CreateShortcut $APPDATA\Microsoft\Windows\GameExplorer\$R2\PlayTasks\0\Play.lnk "${EXE}"
 
  ${EndIf}
 
  System::Call "$1->${IGameExplorer_Release}()"
 
  "done_${__GAME_EXPLORER_UNIQUE}:"
 
    Pop $R2
    Pop $R1
    Pop $R0
 
    Pop $1
    Pop $0
 
  !undef __GAME_EXPLORER_UNIQUE
 
!macroend
 
!macro _GameExplorer_GUID_Function Function GUID
 
  !define __GAME_EXPLORER_UNIQUE "${__LINE__}${__FILE__}"
 
  Push $0
  Push $1
  Push $2
  Push $3
  Push $4
  Push $5
 
  Push $R0
 
  Push "${GUID}"
 
  Pop $R0 # == ${GUID}
 
  System::Alloc 16
  Exch $R0
  System::Call "ole32::CLSIDFromString(w s, i R0)"
  System::Call "*$R0(i .r2, i .r3, i .r4, i .r5)"
  System::Free $R0
 
  ClearErrors
 
  System::Call "ole32::CoCreateInstance( \
    g '${CLSID_GameExplorer}', i 0, \
    i ${CLSCTX_INPROC_SERVER}, \
    g '${IID_IGameExplorer}', *i .r1) i .r0"
 
  ${If} $0 != 0 # S_OK
 
    SetErrors
    Goto "done_${__GAME_EXPLORER_UNIQUE}"
 
  ${EndIf}
 
  System::Call "$1->${Function}(i r2, i r3, i r4, i r5) i .r0"
 
  ${If} $0 != 0 # S_OK
 
    SetErrors
 
  ${EndIf}
 
  System::Call "$1->${IGameExplorer_Release}()"
 
  "done_${__GAME_EXPLORER_UNIQUE}:"
 
    Pop $R0
 
    Pop $5
    Pop $4
    Pop $3
    Pop $2
    Pop $1
    Pop $0
 
  !undef __GAME_EXPLORER_UNIQUE
 
!macroend
 
!macro GameExplorer_UpdateGame GUID
 
  !insertmacro _GameExplorer_GUID_Function ${IGameExplorer_UpdateGame} "${GUID}"
 
!macroend
 
!macro GameExplorer_RemoveGame GUID
 
  !insertmacro _GameExplorer_GUID_Function ${IGameExplorer_RemoveGame} "${GUID}"
 
!macroend

Usage

Adding Games

First, an instance GUID for the game installation must be created with GameExplorer_GenerateGUID or any other GUID generation method. The generated instance GUID must be saved for later usage as it's required for the removal and updating of the game.

${GameExplorer_GenerateGUID}
Pop $0 # instance GUID saved to $0

Next, simply call GameExplorer_AddGame with all the details.

${GameExplorer_AddGame} all $INSTDIR\GDF.dll $INSTDIR $INSTDIR\Game.exe $0
  # `all` means installation for all users
  # `$INSTDIR\GDF.dll` is the path to the executable containing the GDF
  # `$INSTDIR` is the installation directory
  # `$INSTDIR\Game.exe` is the path to the main game executable
  # `$0` is the instance GUID

The error flag is set in case of an error.

Additional Tasks

GameExplorer_AddGame creates only one task by default - the Play task. To create more, a shortcut must be created in a numbered directory under $APPDATA\Microsoft\Windows\GameExplorer\<instance GUID>\PlayTasks\<task number> or $APPDATA\Microsoft\Windows\GameExplorer\<instance GUID>\SupportTasks\<task number>. The Play task is numbered 0 under PlayTasks, so the next play task should be numbered 1, while the next support task should be numbered 0.

# restore instance GUID to $0 somehow
CreateDirectory $APPDATA\Microsoft\Windows\GameExplorer\$0\PlayTasks\1
CreateShortcut "$APPDATA\Microsoft\Windows\GameExplorer\$0\PlayTasks\1\Network Play.lnk" \
  "$INSTDIR\Game.exe" "-network"
 
CreateDirectory $APPDATA\Microsoft\Windows\GameExplorer\$0\SupportTasks\0
CreateShortcut "$APPDATA\Microsoft\Windows\GameExplorer\$0\SupportTasks\0\Network Play.lnk" \
  "$INSTDIR\Support.exe"

Updating Games

Restore the instance GUID and pass it to GameExplorer_UpdateGame.

# restore instance GUID to $0 somehow
${GameExplorer_UpdateGame} $0

The error flag is set in case of an error.

Removing Games

Restore the instance GUID and pass it to GameExplorer_RemoveGame.

# restore instance GUID to $0 somehow
${GameExplorer_RemoveGame} $0

The error flag is set in case of an error.

Related MSDN Pages