1

我希望能够在我的 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 Precompiler 调用这个函数,如下所示:

#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。任何帮助将不胜感激。

4

1 回答 1

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}"

这些在我的 setup-script#include "constants.iss"开头包含的纯文本形式的 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 "";
        }
    }
    

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

编辑:刚刚意识到是 Visual Studio 设计器手动将设置从设置文件传输到 app.config,在构建时将其更改为输出中的 ProgramName.exe.config 文件。因此,您需要更改其中的值以使其生效,如果您为其提供正确的文件路径,则 coe 应该可以工作。

EDIT2:此 app.config 在编译过程中重命名为 YourAppName.exe.config ...当您没有将其放入设置时,这些值未正确初始化。我扩展了我的代码以使用 XML 中的值调整 Settings.designer.cs 文件的设计器代码,并猜测这会做到这一点。一个配置文件来统治它们:-)

于 2011-12-08T06:06:58.937 回答