пятница, 21 октября 2011 г.

Обновление манифеста приложения с командной строки

Одна из проблем использования самоустанавливающегося архива, создаваемого программой IExpress, под Windows Vista или Windows 7 с включенным UAC – это обилие вопросов о безопасности, которые задаются пользователю в процессе установки. Эти вопросы возникают из за того, что в манифесте получаемого исполняемого файла указан требуемый уровень доступа как asInvoker, в то время как запускаемые в виде дочерних процессов программы установки требуют более высокого уровня доступа. Проблему можно решить, изменив манифест созданного IExpress приложения, указав более высокие требования.
Для того чтобы сделать это, я сделал маленькую программку, которая позволяет заменить манифест существующего приложения с командной строки:
UpdateManifest.exe MyInstaller.EXE manifest-for-web-setup.xml
Здесь файл манифеста – это слегка модифицированный манифест исходного приложения. Исходный манифест был выгружен из приложения с помощью редактора ресурсов Visual Studio.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (c) Microsoft Corporation -->
<!--
  Copyright (c) Microsoft Corporation.  All rights reserved.
   Authors:
       GaryY
   Module name:
       wextract.manifest
   Abstract:
       Manifest to support IExpress WExtract.exe.
-->
 
  <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="x86"
    name="wextract"
    type="win32"
  />
 
  <description>IExpress extraction tool</description>
 
  <dependency>
    <dependentAssembly>
       <assemblyIdentity
           type="win32"
           name="Microsoft.Windows.Common-Controls"
           version="6.0.0.0"
           processorArchitecture="x86"
           publicKeyToken="6595b64144ccf1df"
           language="*"
       />
    </dependentAssembly>
  </dependency>
 
  <!-- Identify the application security requirements. -->
 
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="requireAdministrator"
          uiAccess="false"/>
        </requestedPrivileges>
       </security>
  </trustInfo>
 
</assembly>
 

Программа получилась очень простая, но полезная. Привожу исходный текст, может быть кому-нибудь понадобится.


void ErrorHandler( LPCSTR errorMsg ) 
{
    throw exception(errorMsg);
}
 
void UpdateApplicationManifest( _TCHAR* applicationPath, _TCHAR* manifestPath ) 
{
    BOOL result = FALSE;
 
    
    HANDLE hmanifestfile = CreateFile(manifestPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (hmanifestfile == INVALID_HANDLE_VALUE)
    {
        ErrorHandler("Cannot open manifest file");
        return;
    }
    DWORD dwFileSize = GetFileSize(hmanifestfile, NULL);
    if (dwFileSize == INVALID_FILE_SIZE)
    {
        ErrorHandler("Cannot read manifest file length");
        return;
    }
    byte * pManifestBuf = new byte[dwFileSize];
    DWORD readBytes;
    if (!ReadFile(hmanifestfile, pManifestBuf, dwFileSize, &readBytes, NULL))
    {
        ErrorHandler("Cannot read manifest file content");
        return;
    }
 
 
 
    // Open the file to which you want to add the dialog box resource.
    HANDLE hUpdateRes = BeginUpdateResource(applicationPath, FALSE);
    if (hUpdateRes == NULL)
    {
        ErrorHandler("Could not open file for writing.");
        return;
    }
 
    // Add the dialog box resource to the update list.
    result = UpdateResource(hUpdateRes,    // update resource handle
        RT_MANIFEST,                         // change manifest resource
        MAKEINTRESOURCE(1),         // manifest id
        MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),  // english language
        pManifestBuf,                         // ptr to resource info
        dwFileSize);       // size of resource info
 
    if (result == FALSE)
    {
        ErrorHandler("Could not add or update resource.");
        return;
    }
 
    // Write changes to FOOT.EXE and then close it.
    if (!EndUpdateResource(hUpdateRes, FALSE))
    {
        ErrorHandler("Could not write changes to file.");
        return;
    }
 
    //clean up
    CloseHandle(hmanifestfile);
 
    delete [] pManifestBuf;
 
 
}
 
void DisplayInfo() 
{
    cout << "usage : UpdateMainfest.exe [application (EXE)] [manifestfile (XML)]\n";
}
 
int _tmain(int argc, _TCHAR* argv[])
{
    if (argc <= 1)
    {
        DisplayInfo();
    }
    else if (argc != 3)
    {
        cout << "Invalid parameters count\n";
    } 
    else 
    {
 
        try
        {
            UpdateApplicationManifest(argv[1], argv[2]);
            cout << "resource update completed ok\n";
        }
        catch (exception & e)
        {
            cout << "failed: " << e.what() << "\n";
        }
    }
 
    return 0;
}

Собранная программа, применяемая мной в сценарии сборки на момент написания статьи доступна для скачивания.

Комментариев нет:

Отправить комментарий