バージョン情報の問題 その2

前回は自前のリソーススクリプトをプロジェクトに追加して問題を回避する方法をとりましたが、その後根本的に対策するにはMSBuildのカスタムタスクを作成してタスク「CreateRcFile」を置き換えてしまうのが一番かなと言う事を考えてまして一応形になったぽいので公開します。初めてのC#と格闘してる間にDEKOさんが別の方法を公開されました。

作成したバイナリとソースをここ(2011/9/29更新しました)に置いておきます。
なにぶんC#使ったのが初めてなので、色々変なことやってる可能性もありますが指摘して頂ければ幸いです。
使い方としては、

  1. RCCreateTask.dllをどこか適当な場所に置きます。
  2. XE2インストールフォルダ\binにあるCodeGear.Common.Targetsファイルをバックアップします。
  3. CodeGear.Common.Targetsを開いて28行目辺りにある「」を「」に変更して保存します。

以上でvrcファイルを作成するタスクが純正のものからRCCreateTask.dllのものに変わります。
Win32、Win64では確認しましたが、ターゲットがMACの時に関してはよく判らんので純正の処理を実行するようにしてあります。
やってる内容としては、言語コードを出力してリソースデータが指定の言語リソースになるようにしてある事と、ファイルバージョン、製品バージョンに数字以外が指定されてる場合はFILEVERSION、PRODUCTVERSIONに出力する時に0を出力してエラーが出ないようにしてある事くらいのはずです。

カスタムタスク作ってみて判ったんですが、オプションのバージョン情報で設定してる「モジュールのバージョン番号」って渡って来て無いんですよね。
なので、下のグリッドで設定してるファイルバージョンとか製品バージョンとかを使ってます。これは多分純正の処理でも同じ。
バージョン情報の辺りは色々と問題が潜んでそうな感じです。

using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using Borland.Build.Tasks.Shared;

namespace RCCreateTask
{
    public class CreateRcFile : Task
    {
        // IDEから情報を受け取るプロパティ
        public ITaskItem OutputFile { get; set; }
        public bool IncludeVerInfo { get; set; }
        public bool AutoGenVersion { get; set; }
        public int MajorVer { get; set; }
        public int MinorVer { get; set; }
        public int Release { get; set; }
        public int Build { get; set; }
        public bool Debug { get; set; }
        public bool PreRelease { get; set; }
        public bool Special { get; set; }
        public bool Private { get; set; }
        public bool DLL { get; set; }
        public int Locale { get; set; }
        public string KeysString { get; set; }
        public string ManifestFile { get; set; }            
        public string VCLCustomStyles { get; set; }
        public string Icon { get; set; }
        public string ResourcePlatform { get; set; }
        public string OSXOutputDir { get; set; }
        public int TargetedPlatforms { get; set; }
        public string ProjectName { get; set; }

        public override bool Execute()
        {
            if (IncludeVerInfo && OutputFile != null)
            {
                if (ResourcePlatform.Equals("Win32") || ResourcePlatform.Equals("Win64"))
                {
                    using (StreamWriter sw = new StreamWriter(OutputFile.ToString(), false, Encoding.Unicode))
                    {

                        sw.WriteLine("/* ----- VS_VERSION.dwFileFlags ----- */");
                        // DEFINE定義の出力のため割愛
                        sw.WriteLine("");
                        // 言語情報の出力
                        int LangId = Locale & 0xFFFF;
                        sw.WriteLine("LANGUAGE 0x{0:X2}, 0x{1:X2}", LangId & 0x3ff, LangId >> 10);

                        sw.WriteLine("");
                        // グリッドで設定した情報が';'区切りのデータで入ってるのでそれを分割
                        // バージョン情報の名前と値をDictionaryに入れる
                        if (KeysString != null)
                        {
                          Dictionary<string, string> VersionInfos = new Dictionary<string, string>();
                          foreach (string item in versions)
                          {
                              string[] values = item.Split('=');
                              VersionInfos.Add(values[0], values[1]);
                          }
                        }

                        sw.WriteLine("1 VERSIONINFO LOADONCALL MOVEABLE DISCARDABLE IMPURE");
                        // MajorVer等の値が渡されてたらそれを使用してFILEVERSION を出力
                        if ((MajorVer+MinorVer+Build+Release)>0)
                            sw.Write("FILEVERSION {0}, {1}, {2}, {3}", MajorVer, MinorVer, Release, Build);
                        else if (VersionInfos["FileVersion"] != null)
                        {
                            // MajorVer等が0の場合、ファイルバージョンから値を取ってくる
                            // '.'で値を分割して、数字以外が使われてたら削除する
                            Regex re = new Regex("[^0-9]+");
                            string[] vernum = VersionInfos["FileVersion"].Split('.');
                            sw.Write("FILEVERSION {0}", vernum[0]);
                            for (int idx = 1; idx < vernum.Length; idx++)
                            {
                                int i;
                                string s = re.Replace(vernum[idx], "");
                                int.TryParse(s, out i);
                                sw.Write(", {0}", i);
                            }
                            sw.WriteLine("");
                        }
                        else
                            sw.WriteLine("FILEVERSION 0, 0, 0, 0");

                        if (Debug || Special || PreRelease || Private)
                        {
                            int flags = 0;
                            if (Debug)
                                flags |= 0x01;
                            if (Special)
                                flags |= 0x20;
                            if (PreRelease)
                                flags |= 0x02;
                            if (Private)
                                flags |= 0x08;
                            sw.Write("FILEFLAGS {0:X2}", flags);
                        }
                        else
                            sw.WriteLine("FILEFLAGS 0x0L");

                        if ((MajorVer + MinorVer + Build + Release) > 0)
                            sw.Write("PRODUCTVERSION {0}, {1}, {2}, {3}", MajorVer, MinorVer, Release, Build);
                        else if (VersionInfos["ProductVersion"] != null)
                        {
                            Regex re = new Regex("[^0-9]+");
                            string[] vernum = VersionInfos["ProductVersion"].Split('.');
                            sw.Write("PRODUCTVERSION {0}", vernum[0]);
                            for (int idx = 1; idx < vernum.Length; idx++)
                            {
                                int i;
                                string s = re.Replace(vernum[idx], "");
                                int.TryParse(s, out i);
                                sw.Write(", {0}", i);
                            }
                            sw.WriteLine("");
                        }
                        else
                            sw.WriteLine("PRODUCTVERSION 0, 0, 0, 0");

                        sw.WriteLine("FILEOS VOS__WINDOWS32");
                        if (DLL)
                            sw.WriteLine("FILETYPE VFT_DLL");
                        else
                            sw.WriteLine("FILETYPE VFT_APP");
                        sw.WriteLine("{");
                        sw.WriteLine(" BLOCK \"StringFileInfo\"");
                        sw.WriteLine(" {");
                        sw.WriteLine("  BLOCK \"{0:X4}{1:X4}\"", Locale, 1200);
                        sw.WriteLine("  {");
                        foreach (KeyValuePair<string, string> item in VersionInfos)
                        {
                            if (item.Value.Length > 0)
                                sw.WriteLine("   VALUE \"" + item.Key + "\",  \"" + item.Value + "\\0\"");
                        }
                        sw.WriteLine("  }");
                        sw.WriteLine(" }");
                        sw.WriteLine(" BLOCK \"VarFileInfo\"");
                        sw.WriteLine(" {");
                        sw.WriteLine("  VALUE \"Translation\", {0:D}, 1200", Locale);
                        sw.WriteLine(" }");
                        sw.WriteLine("}");
                        sw.WriteLine("");
                        if (Icon != null)
                          sw.WriteLine("MAINICON ICON \"" + Icon.Replace("\\", "\\\\") + "\"");
                        if (ManifestFile != null)
                          sw.WriteLine("1 24 \"" + ManifestFile.Replace("\\", "\\\\") + "\"");

                        if (VCLCustomStyles != null)
                        {
                            string[] Styles = VCLCustomStyles.Split(';');
                            sw.WriteLine("PLATFORMTARGETS RCDATA {{{0}}}", Styles.Length + 1);
                            foreach (string item in Styles)
                            {
                                string[] vals = item.Trim('\"').Split('|');
                                sw.WriteLine("{0} {1} \"{2}\"", vals[0].Replace(" ", ""), vals[1], vals[2].Replace("\\", "\\\\"));
                            }
                        }
                        else
                            sw.WriteLine("PLATFORMTARGETS RCDATA {1}");
                        sw.Close();
                    }
                }
                else
                {
                    Borland.Build.Tasks.Shared.CreateRcFile rc = new Borland.Build.Tasks.Shared.CreateRcFile();
                    rc.AutoGenVersion = AutoGenVersion;
                    rc.Debug = Debug;
                    rc.Icon = Icon;
                    rc.OutputFile = OutputFile;
                    rc.IncludeVerInfo = IncludeVerInfo;
                    rc.MajorVer = MajorVer;
                    rc.MinorVer = MajorVer;
                    rc.Release = Release;
                    rc.Build = Build;
                    rc.PreRelease = PreRelease;
                    rc.Special = Special;
                    rc.Private = Private;
                    rc.DLL = DLL;
                    rc.Locale = Locale;
                    rc.KeysString = KeysString;
                    rc.ManifestFile = ManifestFile;
                    rc.VCLCustomStyles = VCLCustomStyles;
                    rc.ResourcePlatform = ResourcePlatform;
                    rc.OSXOutputDir = OSXOutputDir;
                    rc.TargetedPlatforms = TargetedPlatforms;
                    rc.ProjectName = ProjectName;
                    rc.Execute();
                }
            }
            return true;
        }
    }
}