2012-06-14 3 views
0

InstallShield включает в себя возможность изменения файлов приложений .config через XML File Changes * ... но что, если вы хотите поделиться настройками конфигурации в нескольких установочных пакетах?Динамическая конфигурация InstallShield из файла XML

В нашей среде мы поставляем приложение WPF и приложение WinForms, где одна представляет собой консоль управления, а другая - приложение для сбора данных (один установщик, в котором пользователи выбирают то, что им нужно). Мы также отправляем установщик для уровня обслуживания, используемого обоими приложениями, так что конечные точки webservice будут разными в каждом месте клиента. (Фактическая среда немного сложнее, но приведенная выше является примером сложности)

Наше направление состоит в том, чтобы создать XML-файл с общими параметрами конфигурации для обоих установщиков, который включает в себя общие настройки, InstallShield считывает эти значения, а затем обновляет файл .config каждого приложения, указывая на те же конечные точки. Кто-нибудь это сделал? Можно ли это сделать без использования InstallScript или пользовательского действия?

  • Импорт файла конфигурации XML в XML-файл Изменения зоны для чтения в каждой строке из конфигурационного файла и создает добавлять запросы через XPath для каждой строки (напр. Добавить [@ ключ = «IsMultiTouch» и @ значение = "Правда"]). Это кажется менее идеальным, поскольку я подозреваю, что он просто воссоздает файл, где я хочу, чтобы он читал текущее состояние файла из команды разработчиков и только изменял значения в настройках приложения во время установки.

ответ

1

[followUp] Выделенные значения из диалогового окна InstallShield с помощью CtrlGetText() передали эти данные в мою DLL для включения в файл конфигурации моих приложений. Результирующая запись файла .config включала большое количество последовательностей «». Попытки очистить их с помощью InstallScript не получились. В конечном счете пришлось очистить их внутри dll, перевернув XmlDocument в строку, выполнив .Replace («», «») в строке, загрузив ее в XmlDocument (чтобы убедиться, что она действительна), а затем сохранена Это. InstallShield! = Awesome. [/ followUp]

XML Изменения в файле не выглядят хорошо, поэтому вместо этого я написал настраиваемое действие, которое запускается после установки функции. Пользовательское действие вызывает управляемую сборку, которая вращается через XML-файл, применяя изменения в файле .config приложений.

Custom Action

#define CO_APPCONFIG_DLL "MyCompany.InstallShield.AppConfig.dll" 
#define CO_APPCONFIG_FQNAME "MyCompany.InstallShield.AppConfig.ConfigMgr" 

#define CO_APPSETTINGS_NEW "MyApp_AppSettings.xml" 
#define CO_APPSETTINGS_CONFIG "MyCompany.IOAnywhere.Dashboard.exe.config" 
#define CO_APPSETTINGS_SECTION "CO_dashboard" 

//--------------------------------------------------------------------------- 
// The Installed event is sent after the feature Dashboard 
// is installed. 
//--------------------------------------------------------------------------- 
export prototype Dashboard_Installed(); 
function Dashboard_Installed() 
BOOL bResult; 
OBJECT oAppConfig; 
begin 

try 
    // Note: the configuration dll is in the support directory 
    set oAppConfig = DotNetCoCreateObject(SUPPORTDIR^CO_APPCONFIG_DLL, CO_APPCONFIG_FQNAME, ""); 
catch 
    MessageBox("Error Loading" + SUPPORTDIR^CO_APPCONFIG_DLL + ": " + Err.Number + " " + Err.Description, INFORMATION); 
    abort; 
endcatch; 

try 
    // Note: the new configuration settings file should be in the same folder as the setup.exe 
    bResult = oAppConfig.ConfigureSettings(CO_APPSETTINGS_NEW, TARGETDIR^CO_APPSETTINGS_CONFIG, CO_APPSETTINGS_SECTION); 
catch 
    MessageBox("Verify that the file " + CO_APPSETTINGS_NEW + " exists in the setup directory. Error calling ConfigureSettings " + SUPPORTDIR^CO_APPCONFIG_DLL + " " + Err.Number + " " + Err.Description, INFORMATION); 
endcatch; 

end; 

Вызывается DLL

using System; 
using System.Xml; 

/// <summary> 
/// Called by InstallShield Installer process to apply appSettings to an installed applications .config file 
/// </summary> 
namespace CO.InstallShield.AppConfig 
{ 
    /// <summary> 
    /// ConfigMgr is the class that encapsulates functionality related to modifying a .config file 
    /// </summary> 
    public class ConfigMgr 
    { 
     /// <summary> 
     /// ConfigureSettings applies changes from a common xml file to the applications .config file 
     /// </summary> 
     /// <remarks> 
     /// Ensures required keys for the application are included in the .config file 
     /// Applies common settings to the .config file 
     /// Applies application specific settings to the .config file 
     /// </remarks> 
     /// <param name="configFilePath">Path to the xml file that has the setting that need to be applied</param> 
     /// <param name="targetAppConfigPath">Path to the .config file for the appliction</param> 
     /// <param name="targetAppName">Section in the xml file that has application specific settings</param> 
     /// <returns>True if it was able to configure settings</returns> 
     public bool ConfigureSettings(string configFilePath, string targetAppConfigPath, string targetAppName) 
     { 
      bool completed = false; 

      try 
      { 
       XmlDocument configFileDoc = new XmlDocument(); 
       configFileDoc.Load(configFilePath); 

       XmlDocument targetAppConfigDoc = new XmlDocument(); 
       targetAppConfigDoc.Load(targetAppConfigPath); 

       // ensure the appSettings section exists 
       AddRequiredSections(ref targetAppConfigDoc); 

       // ensure all required keys exist in the target .config file 
       AddRequiredKeys(configFileDoc.SelectSingleNode("configuration/" + targetAppName + "/requiredKeys"), ref targetAppConfigDoc); 

       // loop through each key in the common section of the configuration file 
       AddKeyValues(configFileDoc.SelectSingleNode("configuration/common/appSettings"), ref targetAppConfigDoc); 

       // loop through each key in the app specific section of the configuration file - it will override the standard configuration 
       AddKeyValues(configFileDoc.SelectSingleNode("configuration/" + targetAppName + "/appSettings"), ref targetAppConfigDoc); 

       // save it off 
       targetAppConfigDoc.Save(targetAppConfigPath); 
      } 
      catch (Exception ex) 
      { 
       throw ex; 
      } 
      finally 
      { 
       completed = true; 
      } 
      return completed; 
     } 
     private void AddRequiredSections(ref XmlDocument targeAppConfigDoc) 
     { 
      // Ensure the target .config file has an appSettings section... unusual but evidently possible 
      if (targeAppConfigDoc.SelectSingleNode("configuration/appSettings") == null) 
       targeAppConfigDoc.SelectSingleNode("configuration").AppendChild(targeAppConfigDoc.CreateNode(XmlNodeType.Element, "appSettings", null)); 
     } 
     private void AddKeyValues(XmlNode configAppNodeSet, ref XmlDocument targetAppConfigDoc) 
     { 
      if (configAppNodeSet == null) return; // Nothing to do 

      foreach (XmlNode configNode in configAppNodeSet.SelectNodes("add")) 
      { 
       XmlNode targetNode = targetAppConfigDoc.SelectSingleNode("configuration/appSettings/add[@key='" + configNode.Attributes["key"].Value + "']"); 
       if (targetNode != null) 
       { 
        targetNode.Attributes["value"].Value = configNode.Attributes["value"].Value; 
       } 
      } 
     } 
     private void AddRequiredKeys(XmlNode targetAppNodeSet, ref XmlDocument targetAppConfigDoc) 
     { 
      if (targetAppNodeSet == null) return; // Nothing to do 

      foreach (XmlNode targetNode in targetAppNodeSet.SelectNodes("key")) 
      { 
       // add the key if it doesn't already exist 
       XmlNode appNode = targetAppConfigDoc.SelectSingleNode("configuration/appSettings/add[@key='" + targetNode.Attributes["value"].Value + "']"); 
       if (appNode == null) 
       { 
        appNode = targetAppConfigDoc.SelectSingleNode("configuration/appSettings"); 
        XmlNode newAddNode = targetAppConfigDoc.CreateNode(XmlNodeType.Element, "add", null); 
        XmlAttribute newAddNodeKey = targetAppConfigDoc.CreateAttribute("key"); 
        newAddNodeKey.Value = targetNode.Attributes["value"].Value; 
        XmlAttribute newAddNodeValue = targetAppConfigDoc.CreateAttribute("value"); 
        newAddNodeValue.Value = "NotSet"; 
        newAddNode.Attributes.Append(newAddNodeKey); 
        newAddNode.Attributes.Append(newAddNodeValue); 
        appNode.AppendChild(newAddNode); 
       } 
      } 
     } 
    } 
} 
0

С точки зрения установки, вы можете получить много этого, следуя варианту шаблона запоминания-свойства. Создайте общий компонент, который регистрирует различные данные конфигурации в общем разделе реестра, сохраняя значения свойств, на которые ссылаются ваши изменения файла XML. Используйте системные поиски, чтобы попытаться прочитать эти значения в последующих прогонах той же или связанной с ними установки. Однако этот подход не будет обновлять существующие отдельные файлы конфигурации в связанных проектах.

Чтобы решить эту проблему (хотя вы можете найти отдельные конфигурации, которые могут быть полезны сами по себе), я бы предложил найти способ ссылки на одну общую конфигурацию. Это может быть связано с использованием общего каталога для файла конфигурации. Это может быть связано напрямую с общим разделом реестра и пропуском ранее описанных изменений файла XML.

+0

Размещение данных в реестре ключа только решает несколько проблемы установки на одной машине, где мне нужно, чтобы разделить эти настройки по ряду машин. – roderickprince

+0

Если вам нужны несколько пакетов и несколько * машин *, вам нужно выяснить способ совместного использования данных. Это единственный «первичный» сервер, который все остальные запрашивают для конфигурации? Является ли это настройкой GPO для всех локальных машин? Или вам просто нужно обновить все машины? –

+0

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