2015年2月1日日曜日

TextTemplate で C# のリソースを読み込む

C# のリソース文字列周り。デフォルトの Properties.Resources ではなく、独自の方法で取得する必要ができたのであります。
個々の文字を取得して返す部分はどうにかなるものの、大量のリソースを逐次やるのは困るので、T4で自動生成できないかと探したら、以下のサンプルを見つけました。
T4 Template でお手軽ローカリゼーション
http://www.xamlplayground.org/post/2010/11/25/Simplify-localization-with-a-T4-template.aspx

こちらを真似て、コード生成なしのresource ファイル用にしたものが以下の .tt コードになります。
実際に使う場合は、同名のResxファイル+TextTemplateファイルを用意、TextTemplate ファイルの内容へ以下のコードをコピぺ。
// MainWindow.tt
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" encoding="utf-8" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.Linq" #>
//------------------------------------------------------------------------------
// 
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// 
//------------------------------------------------------------------------------
 
<#
string appName = ".resources Generator Template";
string version = "1.0.0.0";
string ns = (string)System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint");
string resxFileName = Path.ChangeExtension(Host.TemplateFile, ".resx");
string resxClassName = Path.GetFileNameWithoutExtension(Host.TemplateFile);
string proxyClassName = string.Format("{0}ResourceProxy", resxClassName);
XDocument document = XDocument.Parse(File.ReadAllText(resxFileName));
#>
namespace <#=ns#>
{
    using System.Globalization;
    using System.Windows.Markup;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
 
    /// 
    /// Represent a proxy class for "<#= resxClassName #>" resources
    /// 
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCode("<#= appName #>", "<#= version #>")]
    public class <#= proxyClassName #> : ResourceProxyBase
    {
        private global::System.ComponentModel.ComponentResourceManager _resource;
        /// 
        /// Initializes the "<#= proxyClassName #>" class
        /// 
        public <#= proxyClassName #>()
        {
           var asm = System.Reflection.Assembly.GetExecutingAssembly();

           _resource = new System.ComponentModel.ComponentResourceManager(typeof(<#= resxClassName #>));
        }
    
<# foreach(var item in document.Element("root").Elements("data")) 
   { 
        string name = EscapeName(item);
    
        if (item.Attributes("type").Count() == 0)
        {
#>
 
        /// 
<# if (item.Elements("comment").Count() == 1) { #>
        /// <#= item.Element("comment").Value #>
<# } #>
        /// Gets the "<#= name #>" Property
        /// 
        [System.CodeDom.Compiler.GeneratedCode("<#= appName #>", "<#= version #>")]
        public string <#= name #> 
        { 
           get{return _resource.GetString("<#= name #>", ResourceCulture);}
        }
<# 
        }
}
#>
    }
}<#+
public string EscapeName(XElement item)
{
    string name = item.Attribute("name").Value;
    return Regex.Replace(name, "[^a-zA-Z0-9_]{1,1}", "_");
}
#>
これに食べさせるための resx ファイルは下図のような感じ。
食べさせた結果以下のようなファイルが作られる。
 // MainWindow.cs
//------------------------------------------------------------------------------
// 
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// 
//------------------------------------------------------------------------------
 
namespace Schwarzer
{
    using System.Globalization;
    using System.Windows.Markup;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
 
    /// 
    /// Represent a proxy class for "MainWindow" resources
    /// 
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCode(".resources Generator Template", "1.0.0.0")]
    public class MainWindowResourceProxy : ResourceProxyBase
    {
        private global::System.ComponentModel.ComponentResourceManager _resource;
        /// 
        /// Initializes the "MainWindowResourceProxy" class
        /// 
        public MainWindowResourceProxy()
        {
           var asm = System.Reflection.Assembly.GetExecutingAssembly();

           _resource = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow));
        }
    
 
        /// 
        /// Gets the "Text1" Property
        /// 
        [System.CodeDom.Compiler.GeneratedCode(".resources Generator Template", "1.0.0.0")]
        public string Text1 
        { 
           get{return _resource.GetString("Text1", ResourceCulture);}
        }
 
        /// 
        /// Gets the "Text2" Property
        /// 
        [System.CodeDom.Compiler.GeneratedCode(".resources Generator Template", "1.0.0.0")]
        public string Text2 
        { 
           get{return _resource.GetString("Text2", ResourceCulture);}
        }
 
        /// 
        /// Gets the "Text3" Property
        /// 
        [System.CodeDom.Compiler.GeneratedCode(".resources Generator Template", "1.0.0.0")]
        public string Text3 
        { 
           get{return _resource.GetString("Text3", ResourceCulture);}
        }
    }
}
黙って Resx のpublic/internal クラス生成を使えと言われればそれまでなのだけど。
これを WPF に表示したければ以下のような感じで。
<Window x:Class="Schwarzer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Schwarzer"
        Height="150" Width="200">
    <Window.Resources>
        <!-- 先ほど自動生成したクラス -->
        <local:MainWindowResourceProxy x:Key="ResourceProxy"/>
    </Window.Resources>
    <Window.Title>
        <Binding Mode="OneWay" Path="Resource.Text3" Source="{StaticResource LangResoruce}"/>
    </Window.Title>

    <StackPanel>
        <Button Content="{Binding Text1, Mode=OneWay, Source={StaticResource ResourceProxy}}" />
        <Button Content="{Binding Text2, Mode=OneWay, Source={StaticResource ResourceProxy}}" />
    </StackPanel>
</Window>
上記を書いたソリューションはこちら。

2015年1月27日火曜日

vcでスクリプトファイルみたいなものを、OutDirにコピーするときのカスタムビルドツール

いつも忘れるからメモ。
vc で lua とか hlsl とかを、出力フォルダにコピーするだけのカスタムビルドツールのコマンド。
  1. 該当ファイルのプロパティから、項目の種類で「カスタムビルドツール」を選んで適用。
  2. カスタムビルドツールの設定。
  • コマンドライン
    xcopy "%(FullPath)" "$(OutDir)" /D /Y
  • 出力ファイル
    $(OutDir)%(Filename)%(Extension)
  • リンクオブジェクト
    いいえ
説明 も修正すると分りやすいはず。サブフォルダにファイルを置いてると(%Identity)が想定していたのと微妙に違うのが出てきて困った。

3/15/15 修正:
出力ファイルを
%(Filename)%(Extension)
にした状態で、ファイルを$(ProjectDir)にそのまま配置すると、循環依存していると警告される。そのままリビルドすると消される。
これを書いたときは、サブフォルダに配置してやっていたので、警告されなかった。
例: $(ProjectDir)scripts\myscript.lua


2014年11月25日火曜日

Winアプリで最大化したとき、端がはみ出て困っていた調べ物ノート


Win8の非クライアント領域サイズの調べ物
Aero のユーザーエクスペリエンスのために、 SM_CXPADDEDBORDER というものが増えたらしい。
…なぜ、SM_CXBORDER 系のままじゃダメだったのだろうか。

2014年11月9日日曜日

csharp の xxxx.resources ファイルの読み書きができないか調べたノート

csharp のリソースを編集する必要が出てきて、あれこれ調べたときのノート。
ResourceReader は、未知のリソースを全て列挙するような使い方らしい。
ResourceManager は、既知のリソースの該当データを読み込む使い方らしい。

普通は使わないですわねぇ。csc とか ResourceManager が裏で上記のようなことをしているっぽい。
もうちょい調べもの。

2014年9月19日金曜日

variadic template と COM の QueryInterface

今のお仕事が、ATL/COM と格闘なのだけど、ATLを使いこなせない雑魚はのアカウントはこちらです。
それはともかくQueryInterface をコツコツ記述していたら、 variadic template で QueryInterface が楽に実装できないかと思ったメモ。
void にマッチしたときのコードが、MSVCでしか動かないコードな気がするけれども、__uuidof の時点で専用コードなので諦めた次第。
本当は、継承をたどってほしいのだけれども、やり方が思いつかない。
IID 比較の if 文が並ぶのを防げるだけでもまあいいのかなぁ。
黙ってATLを調べればいいのだろうけど、資料が見当たらなさが厳しすぎるのです。
BOOST.PP の方が建設的な気もしたけど、Libを追加する気力もなかったので。 Waveの方がいいのかな?

2014年8月25日月曜日

nuget と dshow-baseclasses でごちゃごちゃと。

dshow でおなじみの baseclasses 用の nuget のための autopkg(CoApp, c++版の nuget パッケージ用設定ファイル) を書いたノート。

  1. windows sdk 辺りから dshow/baseclasses フォルダを作業フォルダにコピー。
  2. vs2013 で開いて、設定の微調整。
    1. SolutionDir と 中間Dir を普通にプロジェクトを作成した通りに修正。
      • Win32, 出力Directory: $(SolutionDir)$(Configuration)\
      • Win32, 中間Directory: $(Configuration)\
      • x64, 出力Directory: :$(SolutionDir)$(Platform)\$(Configuration)\
      • x64, 中間Directory: $(Platform)\$(Configuration)\
      • 出力 ファイル(lib) を、自分の趣味で、$(OutDir)$(TargetName)$(TargetExt) に変更。
      • PDB を自分の趣味で、$(OutDir)\$(ProjectName).pdb に変更。
    2. TimeKillSynchronousFlagAvailable 関数で、 エラーでてるから、IsWindowsXPOrGreater 関数に直す。
      このとき, mbcs は消した方が混乱しない気がする。
  3. win32/x64 でビルド。
  4. 下のファイル(BaseClasses.autopkg)を作業フォルダにコピー。
  5. PowerShell 起動して、上記作業フォルダに移動。
  6. Write-NuGetPackage .\BaseClasses.autopkg
    を実行すると、警告が出るけど、パッケージが作れる。
自分のところのローカル nuget サーバーに出来上がったnupkgファイルを置けばいいはず。

2014年5月12日月曜日

Visual Studio のデバッグ用環境変数の設定の仕方メモ

visual studio で、デバッグ時だけ特定の環境変数を設定したいとき。
例えば以下のように書く。$(XXXX) だとダメらしい。

* "PCL_ROOT"という環境変数があり、それをPATHにデバッグ時だけ追加したい。
以下の文字を、プロジェクトプロパティの、「デバッグ」→「環境」に追加する。

PATH=%PATH%;%PCL_ROOT%\bin\;%PCL_ROOT%\3rdParty\FLANN\bin\;%PCL_ROOT%\3rdParty\VTK\bin\

このダイアログに以下の用に設定
その気になれば、環境変数をなんでも設定できるみたい。

 追記:2019/07/10
 久しぶりにこれを試したら設定されず。。。@vs2019
「親またはプロジェクトの既定値から継承」を有効にすると、なんか %PATH%の値がその既定値で上書きされているらしい。
 なので、設定するときはこのチェックを必ず外すように。