約10年ぶりの更新

pass忘れ等により、長いこと放置状態でしたが、先日奇跡的にログインに成功!
もはやお仕事で直接コードを書くことはなく、"管理ときどき技術"な立ち位置な日々でゴザイマス。
今は技術系のちょっとしたメモもののサイトがごろごろ充実しているから、
もはや存在意義が自分専用でしかないかもしれませんが、最近の備忘録。

Windowsのテーマ変更を知る

現在のテーマの調べ方

現在のテーマが何に設定されているかどうかは以下のレジストリを確認すればOK。
レジストリパスはWindows XPの場合も Vistaの場合も同じ。


[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\ThemeManager]

  • ColorNameが存在しない(ThemaActive? = 0でも同じ)
    • クラシックもしくはスタンダード(Vistaのみ)もしくはハイコントラスト(Vistaのみ)
  • ColorNameが存在する
    • 値が「NormalColor?」ならXPの場合XP Blue、Vistaの場合Vistaベーシック
    • 値が「HomeStead?」ならXP Green
    • 値が「Metallic」ならXP Silver
    • 上記以外の値ならば知らないテーマってことなんでしょう…

ここらへんは参考コードがCode Projectに載ってたりする。
http://www.codeproject.com/KB/cs/xptheme.aspx

テーマ変更検出

ウィンドウメッセージ「WM_WININICHANGE」を拾うことで検出可能。
WININICHANGEは、ウィンドウズの設定変更時に色々飛んでくるので注意。
WININICHANGEを拾って、現在のテーマを調べ、変更があるかどうかを判定すればよさそう。

というわけで、以下がサンプルコード。


protected override void WndProc(ref Message m) {
const int WM_WININICHANGE = 0x001A;
base.WndProc(ref m);
if (m.Msg == WM_WININICHANGE) {
Console.WriteLine(m.LParam.ToString() + "\tTheme : " + getCurrentTheme());
}
}

public Theme getCurrentTheme() {
Theme currentTheme = Theme.Classic;
String regPath = @"Software\Microsoft\Windows\CurrentVersion\ThemeManager";
Microsoft.Win32.RegistryKey regkey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(regPath);
if (regkey != null) {
if ((string)regkey.GetValue("ThemeActive") == "1") {
String colorName = (string)regkey.GetValue("ColorName");
if (colorName.Equals("NormalColor")) {
return Theme.Default;
} else if (colorName.Equals("HomeStead")) {
return Theme.XP_Green;
} else if (colorName.Equals("Metallic")) {
return Theme.XP_Silver;
} else {
return Theme.Unknown;
}
}
}
return currentTheme;
}

public enum Theme {
Classic,
Default,
XP_Green,
XP_Silver,
Unknown
}

実行例は以下のとおり(Windows Vista上で、デフォルトからクラシックに変更した場合)

0 Theme : Default
0 Theme : Classic
3076484 Theme : Classic
3076484 Theme : Classic
3076484 Theme : Classic
3076488 Theme : Classic
3076484 Theme : Classic
3076476 Theme : Classic
3076488 Theme : Classic
3076476 Theme : Classic
3076464 Theme : Classic

Windowsのテーマ変更を検出する

仕事納めの日が、午後出張で直帰(そのまま忘年会へ…)だったので、細々とした事が残ってたままに。

別に年明けからでも大丈夫なんだけど、なんとなく気分が悪いので、年の瀬休日出社して片付けることに。

XMLシリアライズ時のInvalidOperationException

オブジェクトをシリアライズする際には,引数なしのコンストラクタが必要.(コンストラクタを一つも定義していなければデフォルトコンストラクタ(引数なし)がoverrideされるから問題ない).
もし,引数ありのコンストラクタのみを定義し,引数なしのコンストラクタを定義していない場合,シリアライズ時にInvalidOperationExceptionが発生する。

このInvalidOperationException,コンストラクタに問題があるオブジェクトが親か子かでメッセージの文面が異なってくるからいやらしい.

例えば前記事の例だと,Companyオブジェクトに引数なしコンストラクタが存在しない場合は

System.InvalidOperationException はハンドルされませんでした。
Message="Sample.Company にはパラメータを持たないコンストラクタが含まれていないため、これをシリアル化することはできません。"

ところが,Companyオブジェクトには問題なくて,Personオブジェクトに引数なしコンストラクタが存在しない場合は

System.InvalidOperationException はハンドルされませんでした。
Message="型 'Sample.Company' を反映中にエラーが発生しました。"

となり手がかりが少なくなる.

配列のシリアライズで、配列そのものにタグを与えたくない

System.Xml.Serialization.XmlSerializer を使用してプロパティに配列を持つオブジェクトを普通にXMLシリアライズする

と、配列にタグがついて、さらに配列の要素一つ一つにもタグがつく.
たとえば,


class Person {
int id;
String name;

// ... setter, getterは省略 ...
}

class Company {
Person[] persons;
String companyName;

public String CompanyName {
get { return this.companyName; }
set { this.companyName = value; }
}

public Person[] Persons {
get { return this.persons; }
set { this.persons = value; }
}
}

シリアライズすると




1001
Taro


1002
Jiro

・・・


となり,Personsタグが余計な感じとなる.

この場合,CompanyクラスのPersonsプロパティに [XmlElement(Type = typeof(クラス名))]という定義を付けると,Personsタグを省略し,Personタグの繰り返しだけにすることができる.

例:


[XmlElement(Type = typeof(Person))]
public Person[] Person {
get { return this.persons }
set { this.persons = value }
}

この場合のシリアライズ後のXMLは以下の通り.



1001
Taro


1002
Jiro

・・・

ptとemSize

StringFormatを使って、装飾付きのテキストを描こうとしたときの話。

フォントの大きさの指定が、

  • StringFormatではemSize単位
  • Labelとかでは普通 ポイント(pt)単位

というわけで両者の変換が必要になった。

調べたところ、変換式は以下の通りらしい。

pt数×DPI数÷72 = emSize

DPI数はWindowsデフォルト*1では96なので

pt数×96÷72 = emSize

となる模様。

72ってのは 1pt = 1/72インチ の 72らしい。

*1:確認方法は、画面のプロパティ→設定タブ→詳細設定ボタン→全般タブのDPI設定