Hello! I did not succeed in making check-in or fork to Wix source code. So
I'll try to send source code directly. Hope there would be some use for Wix
project.

 

Current Wix release (3.5) does not allow per-user game registration through
Game Explorer. Installation Scope GIS_ALL_USERS is hard coded in source
code. It works until app is not installed in Windows 7 with properties
ALLUSERS=2 and MSIINSTALLPERUSER=1 when installation process is not
elevated. Then an error of writing shortcuts to ProgramData folder happens.

 

So I try to achieve better flexibility and to add an new feature -
registering game in GIS_CURRENT_USER context.

 

http://msdn.microsoft.com/ru-ru/library/ee417686.aspx:

"Create a subdirectory with a name that matches the GameInstanceID, enclosed
in braces, in one of the following locations:

The common task directory, if the game is installed for all users

The per-user task directory, if the game is installed for the current user
only"

 

·         \src\ext\GamingExtension\wixlib\GamingExtension.wxs:

 

<CustomAction Id="WixExecGameExplorerPerUser" BinaryKey="WixGamingCA"
DllEntry="ExecGameExplorer" Execute="deferred" Return="ignore"
SuppressModularization="yes" />

<CustomAction Id="WixRollbackGameExplorerPerUser" BinaryKey="WixGamingCA"
DllEntry="ExecGameExplorer" Execute="rollback" Return="ignore"
SuppressModularization="yes" />

 

// These are new 2 CA with Impersonate="yes". They are called when a game is
installed per current user and we run CA in his context. 

 

·         \src\ext\GamingExtension\ca\gaming.cpp: 

 

(attachment)

 

Code finds out game installation scope from ALLUSERS and MSIINSTALLPERUSER
properties and used this value in call to IGameExplorer->AddGame(). Also
this code schedules per-machine or per-user deferred custom actions.

 

·         \src\ext\GamingExtension\wixext\GamingCompiler.cs:

 

The line

string rootDirectoryPath = String.Format(CultureInfo.InvariantCulture,
@"[CommonAppDataFolder]Microsoft\Windows\GameExplorer\{0}\", gameId);

for per-user install need to be:

string rootDirectoryPath = String.Format(CultureInfo.InvariantCulture,
@"[LocalAppDataFolder]Microsoft\Windows\GameExplorer\{0}\", gameId);

 

Because this code is executed during msi build process and installation
scope could be changed during installation process, at compile time we
cannot be sure what folder do we need to create. I don't know best
workaround for this. I've made two custom actions, one for per-user folder
and one for per-machine, and set conditions for them so one and only one
action should be performed at installation time. Maybe you should know
better solution.

 

Hope this helps!

//-------------------------------------------------------------------------------------------------
// <copyright file="gaming.cpp" company="Microsoft">
//    Copyright (c) Microsoft Corporation.  All rights reserved.
//    
//    The use and distribution terms for this software are covered by the
//    Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php)
//    which can be found in the file CPL.TXT at the root of this distribution.
//    By using this software in any fashion, you are agreeing to be bound by
//    the terms of this license.
//    
//    You must not remove this notice, or any other, from this software.
// </copyright>
// 
// <summary>
//    Game Explorer custom action code.
// </summary>
//-------------------------------------------------------------------------------------------------

#include "precomp.h"

LPCWSTR vcsGameuxQuery =
    L"SELECT `WixGameExplorer`.`InstanceId`,   `WixGameExplorer`.`File_`,   
`File`.`Component_` "
    L"FROM `WixGameExplorer`, `File` "
    L"WHERE `WixGameExplorer`.`File_` = `File`.`File`";
enum eGameuxQuery { egqInstanceId = 1, egqFile, egqComponent };


/******************************************************************
 ExtractXMLFromGDFBinary - extract gdf xml from the gdf binary
 
********************************************************************/
extern "C" HRESULT ExtractXMLFromGDFBinary( 
    __in LPCWSTR sczGDFBinPath,  
    __out IXMLDOMNode** ppIXMLDOMNode
    )
{
    Assert(sczGDFBinPath);
    Assert(ppIXMLDOMNode);

    HRESULT hr = S_OK;
    HMODULE hGDFDll = NULL;
    HGLOBAL hgResourceCopy = NULL;
    HGLOBAL hgResource = NULL;
    DWORD dwGDFXMLSize = 0;
    IPersistStreamInit* pPersistStreamInit = NULL;
    IStream* piStream = NULL;
    IXMLDOMDocument* pDoc = NULL;
    const BYTE* pResourceBuffer = NULL;

    // Extract the GDF XML from the GDF binary 
    hGDFDll = ::LoadLibraryW(sczGDFBinPath);
    if (NULL == hGDFDll)
    {
        ExitWithLastError(hr, "failed to load GDF binary");
    }

    // Find resource will pick the right ID_GDF_XML_STR based on the current 
language
    HRSRC hrsrc = ::FindResourceExW(hGDFDll, L"DATA", ID_GDF_XML_STR, 
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
    ExitOnNullWithLastError(hrsrc, hr, "Failed to find resource.");

    hgResource = ::LoadResource(hGDFDll, hrsrc);
    ExitOnNullWithLastError(hgResource, hr, "Failed to LoadResource.");

    pResourceBuffer = (const BYTE*)::LockResource(hgResource);
    ExitOnNullWithLastError(pResourceBuffer, hr, "Failed to LockResource.");

    dwGDFXMLSize = ::SizeofResource(hGDFDll, hrsrc);
    if (0 == dwGDFXMLSize)
    {
        ExitWithLastError(hr, "failed to SizeofResource");
    }

    // HGLOBAL from LoadResource() needs to be copied for 
CreateStreamOnHGlobal() to work
    hgResourceCopy = ::GlobalAlloc(GMEM_MOVEABLE, dwGDFXMLSize);
    ExitOnNullDebugTrace1(hgResourceCopy, hr, E_OUTOFMEMORY, "failed to 
GlobalAlloc %S", sczGDFBinPath);

    LPVOID pCopy = ::GlobalLock(hgResourceCopy);
    ExitOnNullWithLastError(pCopy, hr, "failed to global lock");

    memcpy(pCopy, pResourceBuffer, dwGDFXMLSize);
    ::GlobalUnlock(hgResourceCopy);

    hr = ::CreateStreamOnHGlobal(hgResourceCopy, TRUE, &piStream);
    ExitOnFailure(hr, "Failed to allocate stream from global memory.");

    // Load the XML into a IXMLDOMDocument object
    hr = ::CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, 
IID_IXMLDOMDocument, (void**)&pDoc);
    ExitOnFailure(hr, "failed to CoCreateInstance for CLSID_DOMDocument");

    hr = pDoc->QueryInterface(IID_IPersistStreamInit, 
(void**)&pPersistStreamInit);
    ExitOnFailure(hr, "failed to QueryInterface IID_IPersistStreamInit");

    hr = pPersistStreamInit->Load(piStream);
    ExitOnFailure(hr, "failed to Load IStream");

    // Get the root node to the XML doc and store it 
    hr = pDoc->QueryInterface(IID_IXMLDOMNode, (void**)ppIXMLDOMNode);
    ExitOnFailure(hr, "failed to QueryInterface for IID_IXMLDOMNode");

LExit:
    if (NULL != hgResourceCopy)
    {
        ::GlobalFree(hgResourceCopy);
    }

    if (NULL != hGDFDll)
    {
        ::FreeLibrary(hGDFDll);
    }

    ReleaseObject(pDoc);
    ReleaseObject(piStream);
    ReleaseObject(pPersistStreamInit);

    return hr;
}

/******************************************************************
 IsV2GDF - test the GDF version
 
********************************************************************/
extern "C" HRESULT IsV2GDF (
    __in LPCWSTR sczGDFBinPath, 
    __out BOOL* pfV2GDF
    )
{
    Assert(pfV2GDF);

    *pfV2GDF = FALSE;
    IXMLDOMNode* pIXMLDOMNode = NULL;
    IXMLDOMNode* pPrimaryPlayTasksNode = NULL;

    HRESULT hr = ExtractXMLFromGDFBinary(sczGDFBinPath, &pIXMLDOMNode);
    ExitOnFailure(hr, "failed to ExtractXMLFromGDFBinary");

    hr = XmlSelectSingleNode(pIXMLDOMNode, 
L"//GameDefinitionFile/GameDefinition/ExtendedProperties/GameTasks/Play/Primary",
 &pPrimaryPlayTasksNode);
    if (S_OK == hr)
    {
        *pfV2GDF = TRUE;
    }
    else if (S_FALSE == hr)
    {
        *pfV2GDF = FALSE;
        hr = S_OK;
    }
    else
    {
        hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
   }

LExit:
    ReleaseObject(pPrimaryPlayTasksNode);
    ReleaseObject(pIXMLDOMNode);

    return hr;
}

/******************************************************************
 GetBaseKnownFolderCsidl - get known folder Csidl from guid
 
********************************************************************/
extern "C" HRESULT GetBaseKnownFolderCsidl(
    __in LPCWSTR sczBaseKnownFolder, 
    __out int* pcsidl
    )
{
    Assert(sczBaseKnownFolder);
    Assert(pcsidl);

    HRESULT hr = S_OK;

    if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, 
sczBaseKnownFolder, -1, L"{905e63b6-c1bf-494e-b29c-65b732d3d21a}", -1))
    {
        *pcsidl =  CSIDL_PROGRAM_FILES;
    }
    else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, 
sczBaseKnownFolder, -1, L"{F7F1ED05-9F6D-47A2-AAAE-29D317C6F066}", -1))
    {
        *pcsidl =  CSIDL_PROGRAM_FILES_COMMON;
    }
    else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, 
sczBaseKnownFolder, -1, L"{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}", -1))
    {
        *pcsidl =  CSIDL_DESKTOP;
    }
    else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, 
sczBaseKnownFolder, -1, L"{FDD39AD0-238F-46AF-ADB4-6C85480369C7}", -1))
    {
        *pcsidl =  CSIDL_MYDOCUMENTS;
    }
    else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, 
sczBaseKnownFolder, -1, L"{C4AA340D-F20F-4863-AFEF-F87EF2E6BA25}", -1))
    {
        *pcsidl =  CSIDL_COMMON_DESKTOPDIRECTORY;
    }
    else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, 
sczBaseKnownFolder, -1, L"{ED4824AF-DCE4-45A8-81E2-FC7965083634}", -1))
    {
        *pcsidl =  CSIDL_COMMON_DOCUMENTS;
    }
    else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, 
sczBaseKnownFolder, -1, L"{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}", -1))
    {
        *pcsidl =  CSIDL_SYSTEM;
    }
    else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, 
sczBaseKnownFolder, -1, L"{F38BF404-1D43-42F2-9305-67DE0B28FC23}", -1))
    {
        *pcsidl =  CSIDL_WINDOWS;
    }
    else
    {
        hr = E_INVALIDARG ;
    }

    return hr;
}

/******************************************************************
 SaveShortcut - save a shortcut for one play task or support task
 
********************************************************************/
extern "C" HRESULT SaveShortcut(
    __in_z LPCWSTR sczLaunchPath, 
    __in_z_opt LPCWSTR sczCommandLineArgs,
    __in LPCWSTR sczShortcutFilePath, 
    __in BOOL bFileTask)
{
    Assert(sczLaunchPath);
    Assert(sczShortcutFilePath);

    DWORD cch = 0;
    IShellLinkW* psl = NULL;
    IPersistFile* ppf = NULL;

    HRESULT hr = ::CoInitialize(NULL);
    ExitOnFailure(hr, "failed to ::CoInitialize");

    hr = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, 
IID_IShellLinkW, (LPVOID*)&psl);
    ExitOnFailure(hr, "failed to CoCreateInstance for IID_IShellLinkW");

    // Setup shortcut
    hr = psl->SetPath(sczLaunchPath);
    ExitOnFailure(hr, "failed to set shorcut path");

    if (sczCommandLineArgs)
    {
        hr = psl->SetArguments(sczCommandLineArgs);
        ExitOnFailure(hr, "failed to set commandline rguments");
    }

    if (bFileTask)
    {
        // Set working dir to path of launch exe
        WCHAR strFullPath[MAX_PATH];
        WCHAR* strExePart;
        cch = ::GetFullPathNameW(sczLaunchPath, countof(strFullPath), 
strFullPath, &strExePart);
        if (0 == cch)
        {
            ExitWithLastError1(hr, "Failed to get full path for string: %S", 
sczLaunchPath);
        }

        if (strExePart) 
        {
            *strExePart = L'\0';
        }

        hr = psl->SetWorkingDirectory(strFullPath);
        ExitOnFailure(hr, "failed to set working directory");
    }

    // Save shortcut to file
    hr = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
    ExitOnFailure(hr, "failed to QueryInterface for IID_IPersistFile");

    hr = ppf->Save(sczShortcutFilePath, TRUE);
    ExitOnFailure(hr, "failed to Save shortcut");
    
LExit:
    ReleaseObject(ppf);
    ReleaseObject(psl);

    ::CoUninitialize();

    return hr;
}

/******************************************************************
 SubCreateSingleShorcut - sub function for CreateSingleShorcut
 
********************************************************************/
extern "C" HRESULT SubCreateSingleShortcut(
    __in GAME_INSTALL_SCOPE gisInstallScope,         // Either GIS_CURRENT_USER 
or GIS_ALL_USERS 
    __in LPCWSTR sczGDFBinPath,                    // valid GameInstance GUID 
that was passed to AddGame()
    __in LPCWSTR sczInstanceId,
    __in LPCWSTR sczTaskName,                      // Name of task.  Ex "Play"
    __in LPCWSTR sczLaunchPath,                    // Path to exe.  Example: 
"C:\Program Files\Microsoft\MyGame.exe"
    __in_opt LPCWSTR sczCommandLineArgs,               // Can be NULL.  
Example: "-windowed"
    __in int nTaskID,                             // ID of task
    __in BOOL bSupportTask,                       // if TRUE, this is a support 
task otherwise it is a play task
    __in BOOL bFileTask)                          // if TRUE, this is a file 
task otherwise it is a URL task
{
    Assert(sczGDFBinPath);
    Assert(sczTaskName);
    Assert(sczLaunchPath);

    WCHAR wzPath[MAX_PATH];
    WCHAR wzCommonFolder[MAX_PATH];
    WCHAR wzShortcutFilePath[MAX_PATH];
    HRESULT hr = S_OK;

    hr = ::SHGetFolderPathW(NULL, GIS_CURRENT_USER == gisInstallScope ? 
CSIDL_LOCAL_APPDATA : CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, 
wzCommonFolder);
    ExitOnFailure(hr, "failed to SHGetFolderPathW for APP data");

    // Create dir path for shortcut
    hr = ::StringCchPrintfW(
        wzPath, 
        MAX_PATH, 
        L"%s\\Microsoft\\Windows\\GameExplorer\\%s\\%s\\%d",
        wzCommonFolder, 
        sczInstanceId, 
        (bSupportTask) ? L"SupportTasks" : L"PlayTasks", 
        nTaskID);
    ExitOnFailure(hr, "failed to set dir path for shortcut");

    // Create the directory and all intermediate directories
    if (ERROR_SUCCESS == ::SHCreateDirectoryExW(NULL, wzPath, NULL))
    {
        // Create full file path to shortcut 
        hr = ::StringCchPrintfW(wzShortcutFilePath, MAX_PATH, L"%s\\%s.lnk", 
wzPath, sczTaskName);
        ExitOnFailure(hr, "failed to set full file path for shortcut");

        // Save shortcut
        hr = SaveShortcut(sczLaunchPath, sczCommandLineArgs, 
wzShortcutFilePath, bFileTask);
        ExitOnFailure(hr, "failed to save shortcut");
    }

LExit:

    return hr;
}

/******************************************************************
 CreateSingleShortcut - create a shortcut for one task
 
********************************************************************/
extern "C" HRESULT CreateSingleShortcut(
    __in IXMLDOMNode* pTaskNode, 
    __in LPCWSTR sczGDFBinPath, 
    __in LPCWSTR sczInstanceId,
    __in LPCWSTR sczGameInstallPath, 
    __in GAME_INSTALL_SCOPE gisInstallScope,
    __in BOOL bPrimaryTask, 
    __in BOOL bSupportTask)
{
    Assert(pTaskNode);
    Assert(sczGDFBinPath);
    Assert(sczGameInstallPath);

    HRESULT hr = S_OK;
    IXMLDOMNode* pFileTaskNode = NULL;
    IXMLDOMNode* pURLTaskNode = NULL;
    BSTR bstrTaskName = NULL;
    BSTR bstrTaskID = NULL;
    BSTR bstrPath = NULL;
    BSTR bstrCommandLineArgs = NULL;
    BSTR bstrBaseKnownFolderID = NULL;
    BSTR bstrURLPath = NULL;

    if (bPrimaryTask)
    {
        bstrTaskName = SysAllocString(L"Play");
        ExitOnNull(bstrTaskName, hr, E_OUTOFMEMORY, "Failed to allocate 
string.");
    }
    else
    {
        hr = XmlGetAttribute(pTaskNode, L"name", &bstrTaskName);
        if (S_FALSE == hr)
        {
            hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        }
        ExitOnRootFailure(hr, "failed to get name attribute");
    }

    if (bPrimaryTask)
    {
        bstrTaskID = SysAllocString(L"0");
        ExitOnNull(bstrTaskID, hr, E_OUTOFMEMORY, "Failed to allocate string.");
    }
    else
    {
        hr = XmlGetAttribute(pTaskNode, L"index", &bstrTaskID);
        if (S_FALSE == hr)
        {
            hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        }
        ExitOnRootFailure(hr, "failed to get index attribute");
    }

    int nTaskID = _wtoi(bstrTaskID);

    hr = XmlSelectSingleNode(pTaskNode, L"FileTask", &pFileTaskNode);
    if (S_OK == hr)
    {
        hr = XmlGetAttribute(pFileTaskNode, L"path", &bstrPath);
        if (S_FALSE == hr)
        {
            hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        }
        ExitOnRootFailure(hr, "failed to get path attribute");

        hr = XmlGetAttribute(pFileTaskNode, L"arguments", &bstrCommandLineArgs);
        ExitOnRootFailure(hr, "failed to get arguments attribute");

        hr = XmlGetAttribute(pFileTaskNode, L"baseKnownFolderID", 
&bstrBaseKnownFolderID);
        if (S_OK == hr)
        {
            int nCsidl;
            hr = GetBaseKnownFolderCsidl(bstrBaseKnownFolderID, &nCsidl);
            if (S_FALSE == hr)
            {
                hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
            }
            ExitOnFailure(hr, "Failed to get baseKnown folder Csidl");

            WCHAR wzFolderPath[MAX_PATH] = {0};
            hr = ::SHGetFolderPathW(NULL, nCsidl, NULL, SHGFP_TYPE_CURRENT, 
wzFolderPath);
            ExitOnFailure(hr, "failed to get folder path from nCsidl");

            WCHAR wzLaunchPath[MAX_PATH] = {0};
            hr = ::StringCchPrintfW(wzLaunchPath, MAX_PATH, L"%s\\%s", 
wzFolderPath, bstrPath);
            ExitOnFailure(hr, "failed to set launch path");

            hr = SubCreateSingleShortcut(gisInstallScope, sczGDFBinPath, 
sczInstanceId, bstrTaskName,wzLaunchPath, bstrCommandLineArgs, nTaskID, 
bSupportTask, true);
            ExitOnFailure(hr, "failed to create a shortcut for one  single 
task");
        }
        else if (S_FALSE == hr)         // no 'baseKnownFolderID'
        {
            WCHAR wzLaunchPath[MAX_PATH] = {0};
            ::StringCchPrintfW(wzLaunchPath, MAX_PATH, L"%s%s", 
sczGameInstallPath, bstrPath);
            hr = SubCreateSingleShortcut(gisInstallScope, sczGDFBinPath, 
sczInstanceId, bstrTaskName, wzLaunchPath, bstrCommandLineArgs, nTaskID, 
bSupportTask, true);
        }

        ExitOnRootFailure(hr, "failed to get baseKnownFolderID Attribute");
    }
    else if (S_FALSE == hr)         // no 'FileTask'
    {
        hr = XmlSelectSingleNode(pTaskNode, L"URLTask", &pURLTaskNode);
        if (S_FALSE == hr)
        {
            hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        }
        ExitOnRootFailure(hr, "Failed to find URLTask element.");

        hr = XmlGetAttribute(pURLTaskNode, L"Link", &bstrURLPath);
        if (S_FALSE == hr)
        {
            hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
        }
        ExitOnRootFailure(hr, "failed to get link attribute");

        hr = SubCreateSingleShortcut(gisInstallScope, sczGDFBinPath, 
sczInstanceId, bstrTaskName, bstrURLPath, NULL, nTaskID, bSupportTask, false);
    }

LExit:
    ReleaseObject(pURLTaskNode);
    ReleaseObject(pFileTaskNode);
    ReleaseBSTR(bstrURLPath);
    ReleaseBSTR(bstrBaseKnownFolderID);
    ReleaseBSTR(bstrCommandLineArgs);
    ReleaseBSTR(bstrPath);
    ReleaseBSTR(bstrTaskID);
    ReleaseBSTR(bstrTaskName);

    return hr;
}

/******************************************************************
 CreateShorcuts - create shortcuts for game tasks. This is for the
   case of V2 GDF using IGameExplorer
********************************************************************/
extern "C" HRESULT CreateShorcuts(
    __in LPCWSTR sczGDFBinPath,
    __in LPCWSTR sczInstanceId,
    __in LPCWSTR sczGameInstallPath, 
    __in GAME_INSTALL_SCOPE gisInstallScope)
{
    Assert(sczGDFBinPath);
    Assert(sczInstanceId);
    Assert(sczGameInstallPath);

    IXMLDOMNode* pIXMLDOMNode = NULL;
    IXMLDOMNode* pPlayTasksNode = NULL;
    IXMLDOMNode* pPrimaryPlayTaskNode = NULL;
    IXMLDOMNode* pSecondaryPlayTaskNode = NULL;
    IXMLDOMNode* pSupportTasksNode = NULL;
    IXMLDOMNode* pTaskNode = NULL;

    HRESULT hr = ExtractXMLFromGDFBinary(sczGDFBinPath, &pIXMLDOMNode);
    ExitOnFailure(hr, "failed to ExtractXMLFromGDFBinary");

    hr = XmlSelectSingleNode(pIXMLDOMNode, 
L"//GameDefinitionFile/GameDefinition/ExtendedProperties/GameTasks/Play", 
&pPlayTasksNode);
    if (S_FALSE == hr)
    {
        hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
    }
    ExitOnRootFailure(hr, "Failed to find play task node element.");

    // Primary play tasks
    hr = XmlSelectSingleNode(pPlayTasksNode, L"Primary", &pPrimaryPlayTaskNode);
    if (S_FALSE == hr)
    {
        hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
    }
    ExitOnRootFailure(hr, "Failed to find the primary play task element.");

    hr = CreateSingleShortcut(pPrimaryPlayTaskNode, sczGDFBinPath, 
sczInstanceId, sczGameInstallPath, gisInstallScope, true, false);
    ExitOnFailure(hr, "Failed to CreateSingleTask for primary play task");

    // Secondary play tasks
    hr = pPrimaryPlayTaskNode->get_nextSibling(&pSecondaryPlayTaskNode);
    ExitOnRootFailure(hr, "Failed to get_nextSibling node");
    while (NULL != pSecondaryPlayTaskNode)
    {
        hr = CreateSingleShortcut(pSecondaryPlayTaskNode, sczGDFBinPath, 
sczInstanceId, sczGameInstallPath, gisInstallScope, false, false);
        ExitOnFailure(hr, "Failed to CreateSingleShortcut for secondary play 
task");
        hr = pSecondaryPlayTaskNode->get_nextSibling(&pSecondaryPlayTaskNode);
        ExitOnRootFailure(hr, "Failed to get next sibling node");
    }

    hr = XmlSelectSingleNode(pIXMLDOMNode, 
L"//GameDefinitionFile/GameDefinition/ExtendedProperties/GameTasks/Support", 
&pSupportTasksNode);
    ExitOnRootFailure(hr, "Failed to find SupportTasksNode element.");

    hr = XmlSelectSingleNode(pSupportTasksNode, L"Task", &pTaskNode);
    ExitOnRootFailure(hr, "Failed to find support task element.");

    while (NULL != pTaskNode)
    {
        hr = CreateSingleShortcut(pTaskNode, sczGDFBinPath, sczInstanceId, 
sczGameInstallPath, gisInstallScope, false, true);
        ExitOnFailure(hr, "Failed to CreateSingleTask for support tasks");
        hr = pTaskNode->get_nextSibling(&pTaskNode);
        ExitOnRootFailure(hr, "Failed to get next sibling for support tasks");
    }

LExit:
    ReleaseObject(pTaskNode);
    ReleaseObject(pSupportTasksNode);
    ReleaseObject(pSecondaryPlayTaskNode);
    ReleaseObject(pPrimaryPlayTaskNode);
    ReleaseObject(pPlayTasksNode);
    ReleaseObject(pIXMLDOMNode);

    return hr;
}

/******************************************************************
 RemoveShorcuts - delete shortcuts for game tasks during uninstall. This is for 
the
   case of V2 GDF using IGameExplorer
********************************************************************/
extern "C" HRESULT RemoveShorcuts(
    __in LPCWSTR sczInstanceId,
    __in GAME_INSTALL_SCOPE gisInstallScope)
{
    Assert(sczInstanceId);

    HRESULT hr;
    WCHAR wzPath[MAX_PATH] = {0};
    WCHAR wzAppData[MAX_PATH];

    // Get base path based on install scope
    hr = ::SHGetFolderPathW(NULL, GIS_CURRENT_USER == gisInstallScope ? 
CSIDL_LOCAL_APPDATA : CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, 
wzAppData);
    ExitOnFailure(hr, "Failed to SHGetFolderPathW for APP data");

    hr = ::StringCchPrintfW(wzPath, MAX_PATH, 
L"%s\\Microsoft\\Windows\\GameExplorer\\%s", wzAppData, sczInstanceId);
    ExitOnFailure(hr, "Failed to set shortcut path in removing the shortcuts");

    SHFILEOPSTRUCTW fileOp;
    ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCTW));
    fileOp.wFunc = FO_DELETE;
    fileOp.pFrom = wzPath;
    fileOp.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
    ::SHFileOperationW(&fileOp);

LExit:

    return hr;
}

/******************************************************************
 WriteGameExplorerRegistry - write temporary rows to the Registry
   table that an upgrade to Windows Vista looks for to migrate the
   game registration to Game Explorer.
********************************************************************/
extern "C" HRESULT WriteGameExplorerRegistry(
    __in LPCWSTR wzInstanceId,
    __in LPCWSTR wzComponentId,
    __in LPCWSTR wzGdfPath,
    __in LPCWSTR wzInstallDir
    )
{
    LPWSTR wzRegKey = NULL;
    MSIHANDLE hView = NULL;
    MSIHANDLE hColumns = NULL;

    // both strings go under this new key
    HRESULT hr = StrAllocFormatted(&wzRegKey, 
L"Software\\Microsoft\\Windows\\CurrentVersion\\GameUX\\GamesToFindOnWindowsUpgrade\\%S",
 wzInstanceId);
    ExitOnFailure(hr, "failed to allocate GameUX registry key");

    hr = WcaAddTempRecord(&hView, &hColumns, 
        L"Registry",            // the table
        NULL,                   // we don't care about detailed error codes
        1,                      // the column number of the key we want 
"uniquified" (uniqued?)
        6,                      // the number of columns we're adding
        L"WixGameExplorer",     // primary key (uniquified)
        msidbRegistryRootLocalMachine,
        wzRegKey,
        L"GDFBinaryPath",
        wzGdfPath,
        wzComponentId);
    ExitOnFailure(hr, "failed to add temporary registry row for GDFBinaryPath");

    hr = WcaAddTempRecord(&hView, &hColumns, 
        L"Registry",
        NULL,
        1,
        6,
        L"WixGameExplorer",
        msidbRegistryRootLocalMachine,
        wzRegKey,
        L"GameInstallPath",
        wzInstallDir,
        wzComponentId);
    ExitOnFailure(hr, "failed to add temporary registry row for 
GameInstallPath");

LExit:
    ::MsiCloseHandle(hView);
    ::MsiCloseHandle(hColumns);
    ReleaseStr(wzRegKey);

    return hr;
}

/******************************************************************
 SchedGameExplorer - entry point for the Game Explorer Custom Action

********************************************************************/
extern "C" HRESULT __stdcall SchedGameExplorer(
    __in BOOL fInstall
    )
{
    HRESULT hr = S_OK;
    int cGames = 0;

    PMSIHANDLE hView = NULL;
    PMSIHANDLE hRec = NULL;

    IGameExplorer* piGameExplorer = NULL;
    IGameExplorer2* piGameExplorer2 = NULL;
    LPWSTR pwzInstanceId = NULL;
    LPWSTR pwzFileId = NULL;
    LPWSTR pwzComponentId = NULL;
    LPWSTR pwzFormattedFile = NULL;
    LPWSTR pwzGamePath = NULL;
    LPWSTR pwzGameDir = NULL;
    LPWSTR pwzCustomActionData = NULL;

        int iAllUsers = 0;
        int iMsiInstallPerUser = 0;
        GAME_INSTALL_SCOPE gisInstallScope = GIS_ALL_USERS;

    // anything to do?
    if (S_OK != WcaTableExists(L"WixGameExplorer"))
    {
        WcaLog(LOGMSG_STANDARD, "WixGameExplorer table doesn't exist, so there 
are no games to register with Game Explorer");
        goto LExit;
    }

    // try to create an IGameExplorer; if it fails, assume we're on a pre-Vista 
OS
    hr = ::CoInitialize(NULL);
    ExitOnFailure(hr, "failed to initialize COM");
    hr = ::CoCreateInstance(__uuidof(GameExplorer), NULL, CLSCTX_ALL, 
__uuidof(IGameExplorer), (LPVOID*)&piGameExplorer); 
    BOOL fHasGameExplorer = SUCCEEDED(hr);
    WcaLog(LOGMSG_STANDARD, "IGameExplorer %S available", fHasGameExplorer ? 
L"is" : L"is NOT");

    hr = ::CoCreateInstance(__uuidof(GameExplorer), NULL, CLSCTX_ALL, 
__uuidof(IGameExplorer2), (LPVOID*)&piGameExplorer2); 
    BOOL fHasGameExplorer2 = SUCCEEDED(hr);
    WcaLog(LOGMSG_STANDARD, "IGameExplorer2 %S available", fHasGameExplorer2 ? 
L"is" : L"is NOT");

    // query and loop through all the games
    hr = WcaOpenExecuteView(vcsGameuxQuery, &hView);
    ExitOnFailure(hr, "failed to open view on WixGameExplorer table");

        hr = WcaGetIntProperty(L"ALLUSERS", &iAllUsers);
    ExitOnFailure(hr, "failed to get value of ALLUSERS property");

        if (2 == iAllUsers)
        {
                hr = WcaGetIntProperty(L"MSIINSTALLPERUSER", 
&iMsiInstallPerUser);
                ExitOnFailure(hr, "failed to get value of MSIINSTALLPERUSER 
property");

                if (1 == iMsiInstallPerUser)
                        gisInstallScope = GIS_CURRENT_USER;
        }
        else if (1 != iAllUsers)
        {
                gisInstallScope = GIS_CURRENT_USER;
        }

    while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
    {
        ++cGames;

        // start with the instance guid
        hr = WcaGetRecordString(hRec, egqInstanceId, &pwzInstanceId);
        ExitOnFailure(hr, "failed to get game instance id");

        // get file id 
        hr = WcaGetRecordString(hRec, egqFile, &pwzFileId);
        ExitOnFailure(hr, "failed to get game file id");

        // turn that into the path to the target file
        hr = StrAllocFormatted(&pwzFormattedFile, L"[#%s]", pwzFileId);
        ExitOnFailure1(hr, "failed to format file string for file: %S", 
pwzFileId);
        hr = WcaGetFormattedString(pwzFormattedFile, &pwzGamePath);
        ExitOnFailure1(hr, "failed to get formatted string for file: %S", 
pwzFileId);

        // and then get the directory part of the path
        hr = PathGetDirectory(pwzGamePath, &pwzGameDir);
        ExitOnFailure1(hr, "failed to get path for file: %S", pwzGamePath);

        // get component and its install/action states
        hr = WcaGetRecordString(hRec, egqComponent, &pwzComponentId);
        ExitOnFailure(hr, "failed to get game component id");

        // we need to know if the component's being installed, uninstalled, or 
reinstalled
        WCA_TODO todo = WcaGetComponentToDo(pwzComponentId);

        // skip this entry if this is the install CA and we are uninstalling 
the component
        if (fInstall && WCA_TODO_UNINSTALL == todo)
        {
            continue;
        }

        // skip this entry if this is an uninstall CA and we are not 
uninstalling the component
        if (!fInstall && WCA_TODO_UNINSTALL != todo)
        {
            continue;
        }
                
        // if we got a Game Explorer, write the CA data; otherwise,
        // just write the registry values for an XP-to-Vista upgrade
        if (fHasGameExplorer || fHasGameExplorer2)
        {
                        // write custom action data: operation, instance guid, 
path, directory, installation scope
            hr = WcaWriteIntegerToCaData(todo, &pwzCustomActionData);
            ExitOnFailure1(hr, "failed to write Game Explorer operation to 
custom action data for instance id: %S", pwzInstanceId);

            hr = WcaWriteStringToCaData(pwzInstanceId, &pwzCustomActionData);
            ExitOnFailure1(hr, "failed to write custom action data for instance 
id: %S", pwzInstanceId);

            hr = WcaWriteStringToCaData(pwzGamePath, &pwzCustomActionData);
            ExitOnFailure1(hr, "failed to write game path to custom action data 
for instance id: %S", pwzInstanceId);

            hr = WcaWriteStringToCaData(pwzGameDir, &pwzCustomActionData);
            ExitOnFailure1(hr, "failed to write game install directory to 
custom action data for instance id: %S", pwzInstanceId);

                        hr = WcaWriteIntegerToCaData((int)gisInstallScope, 
&pwzCustomActionData);
            ExitOnFailure1(hr, "failed to write game installation scope to 
custom action data for instance id: %S", pwzInstanceId);
        }
        else
        {
            hr = WriteGameExplorerRegistry(pwzInstanceId, pwzComponentId, 
pwzGamePath, pwzGameDir);
            ExitOnFailure1(hr, "failed to write registry rows for game id: %S", 
pwzInstanceId);
        }
    }

    // reaching the end of the list is actually a good thing, not an error
    if (E_NOMOREITEMS == hr)
    {
        hr = S_OK;
    }
    ExitOnFailure(hr, "Failure occured while processing WixGameExplorer table");

    // schedule ExecGameExplorer if there's anything to do
    if (pwzCustomActionData && *pwzCustomActionData)
    {
        WcaLog(LOGMSG_STANDARD, "Scheduling Game Explorer (%S)", 
pwzCustomActionData);
        hr = WcaDoDeferredAction(gisInstallScope == GIS_ALL_USERS ? 
L"WixRollbackGameExplorer" : L"WixRollbackGameExplorerPerUser", 
pwzCustomActionData, cGames * COST_GAMEEXPLORER);
        ExitOnFailure(hr, "Failed to schedule Game Explorer rollback");
        hr = WcaDoDeferredAction(gisInstallScope == GIS_ALL_USERS ? 
L"WixExecGameExplorer" : L"WixExecGameExplorerPerUser", pwzCustomActionData, 
cGames * COST_GAMEEXPLORER);
        ExitOnFailure(hr, "Failed to schedule Game Explorer execution");
    }

LExit:
    ReleaseStr(pwzInstanceId);
    ReleaseStr(pwzFileId);
    ReleaseStr(pwzComponentId);
    ReleaseStr(pwzFormattedFile);
    ReleaseStr(pwzGamePath);
    ReleaseStr(pwzCustomActionData);
    ReleaseObject(piGameExplorer2);
    ReleaseObject(piGameExplorer);

    ::CoUninitialize();

    return hr;
}

/******************************************************************
 SchedGameExplorerUninstall - entry point for the Game Explorer uninstall 
Custom Action

********************************************************************/
extern "C" UINT __stdcall SchedGameExplorerUninstall(
    __in MSIHANDLE hInstall
    )
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;

    // initialize
    hr = WcaInitialize(hInstall, "SchedGameExplorerUninstall");
    ExitOnFailure(hr, "failed to initialize");

    hr = SchedGameExplorer(FALSE);

LExit:
    return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
}

/******************************************************************
 SchedGameExplorer - entry point for the Game Explorer install Custom Action

********************************************************************/
extern "C" UINT __stdcall SchedGameExplorerInstall(
    __in MSIHANDLE hInstall
    )
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;

    // initialize
    hr = WcaInitialize(hInstall, "SchedGameExplorerInstall");
    ExitOnFailure(hr, "failed to initialize");

    hr = SchedGameExplorer(TRUE);

LExit:
    return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
}

/******************************************************************
 ExecGameExplorer - entry point for Game Explorer Custom Action

*******************************************************************/
extern "C" UINT __stdcall ExecGameExplorer(
    __in MSIHANDLE hInstall
    )
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;
    BOOL fHasAccess = FALSE;
    GUID guidInstanceId = {0};

    IGameExplorer* piGameExplorer = NULL;
    IGameExplorer2* piGameExplorer2 = NULL;
    LPWSTR pwzCustomActionData = NULL;
    LPWSTR pwz = NULL;
    int iOperation = 0;
    LPWSTR pwzInstanceId = NULL;
    LPWSTR pwzGamePath = NULL;
    LPWSTR pwzGameDir = NULL;
    BSTR bstrGamePath = NULL;
    BSTR bstrGameDir = NULL;

        GAME_INSTALL_SCOPE gisInstallScope = GIS_ALL_USERS;

    // initialize
    hr = WcaInitialize(hInstall, "ExecGameExplorer");
    ExitOnFailure(hr, "failed to initialize");

    hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
    ExitOnFailure(hr, "failed to get CustomActionData");
    WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %S", pwzCustomActionData);

    // try to create an IGameExplorer
    hr = ::CoInitialize(NULL);
    ExitOnFailure(hr, "failed to initialize COM");
    hr = ::CoCreateInstance(__uuidof(GameExplorer), NULL, CLSCTX_ALL, 
__uuidof(IGameExplorer), (LPVOID*)&piGameExplorer);
    BOOL fHasGameExplorer = SUCCEEDED(hr);
    WcaLog(LOGMSG_STANDARD, "IGameExplorer %S available", fHasGameExplorer ? 
L"is" : L"is NOT");

    hr = ::CoCreateInstance(__uuidof(GameExplorer), NULL, CLSCTX_ALL, 
__uuidof(IGameExplorer2), (LPVOID*)&piGameExplorer2); 
    BOOL fHasGameExplorer2 = SUCCEEDED(hr);
    WcaLog(LOGMSG_STANDARD, "IGameExplorer2 %S available", fHasGameExplorer2 ? 
L"is" : L"is NOT");

    // nothing to do if there's no Game Explorer (though we should have been 
scheduled only if
    // there was a Game Explorer when we started the install).
    if (fHasGameExplorer || fHasGameExplorer2)
    {   
                // loop through all the passed in data
        pwz = pwzCustomActionData;
        while (pwz && *pwz)
        {
            // extract the custom action data
            hr = WcaReadIntegerFromCaData(&pwz, &iOperation);
            ExitOnFailure(hr, "failed to read operation from custom action 
data");
            hr = WcaReadStringFromCaData(&pwz, &pwzInstanceId);
            ExitOnFailure(hr, "failed to read instance ID from custom action 
data");
            hr = WcaReadStringFromCaData(&pwz, &pwzGamePath);
            ExitOnFailure(hr, "failed to read GDF path from custom action 
data");
            hr = WcaReadStringFromCaData(&pwz, &pwzGameDir);
            ExitOnFailure(hr, "failed to read game installation directory from 
custom action data");
                        hr = WcaReadIntegerFromCaData(&pwz, 
(int*)&gisInstallScope);
            ExitOnFailure(hr, "failed to read game installation scope from 
custom action data");

                        WcaLog(LOGMSG_STANDARD, "Game install scope = %d", 
gisInstallScope);

            // convert from LPWSTRs to BSTRs and GUIDs, which are what 
IGameExplorer wants
            hr = ::CLSIDFromString(pwzInstanceId, &guidInstanceId);
            ExitOnFailure1(hr, "couldn't convert invalid GUID string '%S'", 
pwzInstanceId);

            bstrGamePath = ::SysAllocString(pwzGamePath);
            ExitOnNull(bstrGamePath, hr, E_OUTOFMEMORY, "failed SysAllocString 
for bstrGamePath");
            bstrGameDir = ::SysAllocString(pwzGameDir);
            ExitOnNull(bstrGameDir, hr, E_OUTOFMEMORY, "failed SysAllocString 
for bstrGameDir");

            // if rolling back, swap INSTALL and UNINSTALL
            if (::MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK))
            {
                if (WCA_TODO_INSTALL == iOperation)
                {
                    iOperation = WCA_TODO_UNINSTALL;
                }
                else if (WCA_TODO_UNINSTALL == iOperation)
                {
                    iOperation = WCA_TODO_INSTALL;
                }
            }

            BOOL fIsV2GDF = FALSE;
            hr = IsV2GDF(pwzGamePath, &fIsV2GDF);
            ExitOnFailure(hr, "failed to get game GDF version");
            WcaLog(LOGMSG_STANDARD, "The GDF of this game is %S", fIsV2GDF ? 
L"V2" : L"V1");

            if (fIsV2GDF && fHasGameExplorer2)
            {
                switch (iOperation)
                {
                case WCA_TODO_INSTALL:
                case WCA_TODO_REINSTALL:
                    hr = piGameExplorer2->InstallGame(bstrGamePath, 
bstrGameDir, gisInstallScope);
                    ExitOnFailure1(hr, "failed to install game: %S", 
bstrGamePath);
                    break;
                case WCA_TODO_UNINSTALL:
                    hr = piGameExplorer2->UninstallGame(bstrGamePath);
                    ExitOnFailure1(hr, "failed to remove game instance: %S", 
pwzInstanceId);
                    break;
                }
            }
            else if (fHasGameExplorer)
            {
                switch (iOperation)
                {
                case WCA_TODO_INSTALL:
                    hr = piGameExplorer->VerifyAccess(bstrGamePath, 
&fHasAccess);
                    ExitOnFailure1(hr, "failed to verify game access: %S", 
pwzInstanceId);

                    if (SUCCEEDED(hr) && fHasAccess)
                    {
                        WcaLog(LOGMSG_STANDARD, "Adding game: %S, %S", 
bstrGamePath, bstrGameDir);
                                                hr = 
piGameExplorer->AddGame(bstrGamePath, bstrGameDir, gisInstallScope, 
&guidInstanceId);
                    }
                    ExitOnFailure1(hr, "failed to add game instance: %S", 
pwzInstanceId);

                    if (fIsV2GDF)
                    {
                        hr = CreateShorcuts(pwzGamePath, pwzInstanceId, 
pwzGameDir, gisInstallScope);
                        ExitOnFailure(hr, "failed to add shortcuts for game 
tasks");
                    }
                    break;
                case WCA_TODO_REINSTALL:
                    hr = piGameExplorer->UpdateGame(guidInstanceId);
                    ExitOnFailure1(hr, "failed to update game instance: %S", 
pwzInstanceId);
                    break;
                case WCA_TODO_UNINSTALL:
                    hr = piGameExplorer->RemoveGame(guidInstanceId);
                    ExitOnFailure1(hr, "failed to remove game instance: %S", 
pwzInstanceId);
                    if (fIsV2GDF)
                    {
                        hr = RemoveShorcuts(pwzInstanceId, gisInstallScope);
                        ExitOnFailure(hr, "failed to remove shortcuts for game 
tasks");
                    }
                    break;
                }
            }

            // Tick the progress bar along for this game
            hr = WcaProgressMessage(COST_GAMEEXPLORER, FALSE);
            ExitOnFailure1(hr, "failed to tick progress bar for game instance: 
%S", pwzInstanceId);
        }
    }

LExit:
    ReleaseStr(pwzCustomActionData);
    ReleaseStr(pwzInstanceId);
    ReleaseStr(pwzGamePath);
    ReleaseStr(pwzGameDir);
    ReleaseBSTR(bstrGamePath);
    ReleaseBSTR(bstrGameDir);
    ReleaseObject(piGameExplorer2);
    ReleaseObject(piGameExplorer);
    ::CoUninitialize();

    return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
}
------------------------------------------------------------------------------
Forrester Wave Report - Recovery time is now measured in hours and minutes
not days. Key insights are discussed in the 2010 Forrester Wave Report as
part of an in-depth evaluation of disaster recovery service providers.
Forrester found the best-in-class provider in terms of services and vision.
Read this report now!  http://p.sf.net/sfu/ibm-webcastpromo
_______________________________________________
WiX-devs mailing list
WiX-devs@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/wix-devs

Reply via email to