在InnoSetup脚本中使用Project / Properties / Settings.settings的值?

时间:2011-11-24 08:52:46

标签: c# .net inno-setup msxml settings.settings

我希望能够在C#.NET 4.0项目中的某个位置更改值。为此,我使用内置的Properties/Settings.settings文件,该文件本质上是一个XML文件。

由于我们正在使用InnoSetup(这是很常见的)我们的软件,并且settings.settings文件是存储C#.NET应用程序值的默认方式,我想知道是否有 InnoSetup脚本从设置文件中提取值的方法,或者如果您可以指向一个可以执行此操作的脚本来设置安装脚本的变量。

修改

我运行了XML示例,但现在我无法使用XPath查询来获取正确的节点。这是我的剧本:

[Code]
{--- MSXML ---}

const
  XMLFileName = '..\CCFinderWPF\Properties\Settings.settings';
  XMLFileName2 = '..\CCFinderWPF\Properties\Settings.xml';

function Settings(Default: String): String;
var
  XMLDoc, SeekedTopNode, SeekedNode, iNode, Sel: Variant;
  Path, XPath: String;
begin
  { Load the XML File }
  try
    XMLDoc := CreateOleObject('MSXML2.DOMDocument.4.0');
  except
    RaiseException('Please install MSXML first.'#13#13'(Error ''' + GetExceptionMessage + ''' occurred)');
  end;
  XMLDoc.async := False;    
  XMLDoc.resolveExternals := false;
  XMLDoc.preserveWhiteSpace := true;
  XMLDoc.setProperty('SelectionLanguage', 'XPath');
  XMLDoc.load(XMLFileName);
  if XMLDoc.parseError.errorCode <> 0 then
    RaiseException('Error on line ' + IntToStr(XMLDoc.parseError.line) + ', position ' + IntToStr(XMLDoc.parseError.linepos) + ': ' + XMLDoc.parseError.reason);

  MsgBox('XML-File: ' + XMLFileName, mbInformation, MB_OK);
  { Modify the XML document }
  iNode   := XMLDoc.documentElement;
  iNode := iNode.selectSingleNode('Setting[@Name="' + Default + '"]');
// **selectSingleNode returns null, seems that selectSingleNode with XPath doesn't work?**
  MsgBox('The Node is: ' + iNode.nodeName, mbInformation, MB_OK);

  SeekedNode := iNode.firstChild;
  Result := SeekedNode.lastChild.text;
  MsgBox('The XPath is: ' + XPath, mbInformation, MB_OK);
end;

我使用Inno Setup预编译器调用此函数,如下所示:

#define ABAppName "{code:Settings|AppName}"

XML文件如下所示:

<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="CCFinder.Properties" GeneratedClassName="Settings">
  <Profiles />
  <Settings>
    <Setting Name="AppName" Type="System.String" Scope="Application">
      <Value Profile="(Default)">CCFinder</Value>
    </Setting>
    ...

所有这一切的目标是我可以在我的应用程序中设置我的C#项目中的Settings.settings文件中的值,让我的hudson实例检查我的代码并更改此XML文件以用于不同版本的应用程序,例如更改我在设计时不必知道的一些价值观。我希望能够从这个XML文件中提取变量。我刚刚在这里使用MSXML2。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

没人知道解决方案,所以我用while循环解决了这个问题。请注意,这是我的第一个Delphi代码,因此它可能不优雅和/或100%正确。这就是我定义所需常量的方法:

#define ABAppName "{code:Settings|AppName}"
#define ABAppVersion "{code:Settings|AppVersion}"
#define ABCompany "{code:Settings|CompanyName}"
#define ABAppTitle "{code:Settings|ApplicationTitle}"
#define ABAppTitlePro "{code:Settings|ApplicationTitle)}"+" "+"{code:Settings|ProVersionAppender}"
#define ABCompanyUrl "{code:Settings|CompanyUrl_de}"
#define ABContactEmailDe "{code:Settings|ContactEmail_de}"
#define ABYear "{code:Settings|Year}"

这些是在constants.iss文件中作为纯文本,我在开始时使用#include "constants.iss"包含在我的安装脚本中。这是代码调用调用的delphi代码:

const
  XMLFileName = '..\CCFinder\Properties\Settings.settings';

function Settings(Default: String): String;
var
  XMLDoc, iNode: Variant;
  Path: String;
  i : Integer;
  Loop : Boolean;
begin
  { Load the XML File }
  try
    XMLDoc := CreateOleObject('MSXML2.DOMDocument.6.0');
  except
    RaiseException('Please install MSXML first.'#13#13'(Error ''' + GetExceptionMessage + ''' occurred)');
  end;
  try
  XMLDoc.async := False;
  XMLDoc.resolveExternals := false;
  XMLDoc.load(XMLFileName);
  if XMLDoc.parseError.errorCode <> 0 then
    RaiseException('Error on line ' + IntToStr(XMLDoc.parseError.line) + ', position ' + IntToStr(XMLDoc.parseError.linepos) + ': ' + XMLDoc.parseError.reason);

  iNode := XMLDoc.DocumentElement;
  iNode := iNode.LastChild;
  Loop := True;
  i := 0;
  while Loop do
  begin
    Try
        if iNode.ChildNodes[i].attributes[0].nodeValue = Default
        then
            begin
                Result := iNode.ChildNodes[i].text;
//              MsgBox('Result for Default: ' + Result + Default, mbInformation, MB_OK);
                i := i+1;
                Loop := False;
                Break;
            end
        else
            begin
                i := i+1;
                if i = 100 then Loop := false;
            end;
    except
        Result := '';
    end;
  end;
  except
  end;
end;
  • 循环受到100次运行的限制,因为我太笨拙无法计算Delphi中的XML节点。
  • 需要安装InnoSetup预处理器。
  • Inno似乎不接受路径名中的常量,因此无法从Properties / Settings.settings文件中提取某些值。
  • 我不知道预处理器实际上并没有使用Delphi代码来获取正确的值以将它们包含在脚本中,而是在所有使用常量的地方都包含完整的delphi代码调用。当然,这不是我想要做的,因为当代码嵌入到路径中时它甚至不起作用。因此,我将安装脚本更改为仅包含一个需要通过查找和替换替换的常量,例如,用于路径名和可执行文件名。
  • 对于其他人,我编写了一个.NET命令行应用程序,它与delphi代码基本相同,在Hudson上编译安装程序之前在Hudson中执行它。这将代码调用替换为所需的实际值(仅限于此,但您应该得到这一点):

    {
    XmlDocument xmldoc = new XmlDocument();
    xmldoc.Load(pathToSettingsFile);
    
    XmlNodeList list = xmldoc.GetElementsByTagName("Setting");
    foreach (XmlNode node in list)
    {
         string keystring = node.Attributes["Name"].InnerText;
         string valuestring =  node.FirstChild.InnerText;
         settingValues.Add(keystring,valuestring);
    }
    
    var t = File.OpenText(constantsfilename);
    string text;
    using (t)
    {
        text = t.ReadToEnd();
    }
    string sample = "{code:Settings|";
    char endsign = '}';
    while (text.Contains(sample))
    {
        int startindex = text.IndexOf(sample);
        int endindex = text.IndexOf(endsign, startindex);
        int variableIndex = startindex + sample.Length;
        string variable = text.Substring(variableIndex, endindex - variableIndex);
        text = text.Replace(sample + variable + endsign, newVal(variable));
    }
    
    var w = new StreamWriter(constantsfilename);
    using (w)
    {
        w.Write(text);
        w.Flush();
    }
    }
    
    public static string newVal(string variable)
    {
        if (settingValues.ContainsKey(variable))
            return settingValues[variable];
        else
        {
            return "";
        }
    }
    

这意味着我现在可以在我的Settings-File中设置值,可以使用hudson中的另一个命令行应用程序更改特殊构建,并自动写入安装脚本。

编辑:刚才意识到Visual Studio Designer会将设置从settings-file手动传输到app.config,在构建时会在输出中更改为ProgramName.exe.config文件。因此,您需要更改其中的值以使其具有效果,如果您为文件指定正确的路径,则coe应该起作用。

EDIT2:这个app.config在编译到YourAppName.exe.config期间被重命名...当你没有把它放在设置中时,值没有正确初始化。我扩展了我的代码,以便使用XML中的值调整Settings.designer.cs文件的设计器代码,并猜测这样做。一个配置文件来统治它们: - )