2008年12月28日日曜日

DataContractJsonSerializerでDictionaryを表現

DataContractJsonSerializerはまじめなので、.NETとJSONとの双方向で型が復元されることを前提にしています。そのため、Dictionary<TKey,TValue>を素直にobject表現してくれません。(TKeyがstringに限定され、intなどが復元できないため。TValueの型が不明なため。)
具体的にどうなるかというと

new Dictionary<string,object>{ { "abc", 1 }, { "def", "ghi" } };
[{"Key":"abc","Value":1},{"Key":"def","Value":"ghi"}]
になります。
一般的にJSONを扱う人は
{"abc":1,"def":"ghi"}
を期待していることでしょう。

どうにかしたいと思い、必死に対策を考え、遂に実現しました。
class Surrogate: IDataContractSurrogate {
[Serializable]
class JsonDictionary: ISerializable {
Dictionary<string, object> Dictionary { get; set; }
public JsonDictionary( Dictionary<string, object> dictionary ) {
Dictionary = dictionary;
}
public void GetObjectData( SerializationInfo info, StreamingContext context ) {
foreach( var pair in Dictionary )
info.AddValue( pair.Key, pair.Value );
}
}

public Type GetDataContractType( Type type ) {
return type == typeof( Dictionary<string, object> ) ? typeof( JsonDictionary ) : type;
}
public object GetObjectToSerialize( object obj, Type targetType ) {
var dictionary = obj as Dictionary<string, object>;
if( obj == null )
throw new NotImplementedException();
return new JsonDictionary( dictionary );
}

#region NotImplemented
object IDataContractSurrogate.GetCustomDataToExport( Type clrType, Type dataContractType ) {
throw new NotImplementedException();
}
object IDataContractSurrogate.GetCustomDataToExport( MemberInfo memberInfo, Type dataContractType ) {
throw new NotImplementedException();
}
object IDataContractSurrogate.GetDeserializedObject( object obj, Type targetType ) {
throw new NotImplementedException();
}
void IDataContractSurrogate.GetKnownCustomDataTypes( Collection<Type> customDataTypes ) {
throw new NotImplementedException();
}
Type IDataContractSurrogate.GetReferencedTypeOnImport( string typeName, string typeNamespace, object customData ) {
throw new NotImplementedException();
}
CodeTypeDeclaration IDataContractSurrogate.ProcessImportedType( CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit ) {
throw new NotImplementedException();
}
#endregion
}

あとはDataContractJsonSerializerコンストラクタのIDataContractSurrogate引数にSurrogateインスタンスを渡せば、変換してくれます。
もちろん型保証が外れるため、deserializeはできません。

2008年12月16日火曜日

グループ定義の均等化 Balancing group definition

グループ定義の均等化と言われても、何のことかさっぱり想像がつきませんが、.NET Frameworkの正規表現に不思議な機能があります。実際、役立つシーンがあります。
ネストされる()のペアを対応関係を維持しつつマッチさせたい、というときに使えます。具体的にはCの関数呼び出しやキャストなど。

(?:(?'Open'<)[^<>]*)+(?:(?'Close-Open'>)[^<>]*)+
丸括弧だと正規表現エスケープで読みづらくなるため山括弧の一致にしていますが、もちろん丸括弧でも対応できます。

べき乗 pow() xy

ふとべき乗計算を考えてみた。

double pow( double x, int y ){
return y > 0 ? pow( x, y - 1 ) * x : 1;
}
整数の時は簡単。

実数になると
__declspec(noinline) double pow( double x, double y ){
__asm{
fld y
fld x
fyl2x
fst ST(1)
frndint
fxch ST(1)
fsub ST(0), ST(1)
f2xm1
fld1
faddp ST(1), ST(0)
fscale
}
}
なんかinline assemblerを使ってしまった。うん、何やってるのかさっぱりです。命令とそのときのスタック状態を表にすると
命令ST(0)ST(1)ST(2)
fld yy
fld xxy
fyl2xy×log2x
fst ST(1)y×log2xy×log2x
frndint⌊y×log2x⌋y×log2x
fxch ST(1)y×log2x⌊y×log2x⌋
fsub ST(0), ST(1)y×log2x-⌊y×log2x⌋⌊y×log2x⌋
f2xm12y×log2x-⌊y×log2x⌋-1⌊y×log2x⌋
fld112y×log2x-⌊y×log2x⌋-1⌊y×log2x⌋
faddp ST(1), ST(0)2y×log2x-⌊y×log2x⌋⌊y×log2x⌋
fscale2y×log2x-⌊y×log2x⌋×2⌊y×log2x⌋

ここで2y×log2x-⌊y×log2x⌋×2⌊y×log2x⌋ = 2y×log2x-⌊y×log2x⌋+⌊y×log2x⌋ = 2y×log2x = xyです。
はい、さっぱりわかりませんでした。

2008年12月3日水曜日

ファイルの高速コピー

Webサイトのコンテンツを更新するとき、ローカルディスクからWebサーバにアップロード(ファイルのコピー)をしていますが、ファイル数が多くてExplorerじゃとてもとても。
調べたところFastCopyとかコピーツールがありましたが、ふとC#で書いてみたらどうなるんだろう、と。

static void Copy( FileInfo source, FileInfo destination ) {
var src = new FileStream( source.FullName, FileMode.Open, FileAccess.Read, FileShare.None, 4096, true );
var buffer = new byte[ source.Length ];
src.BeginRead( buffer, 0, buffer.Length, rar => {
src.EndRead( rar );
src.Close();
if( destination.Exists )
destination.Attributes &= ~FileAttributes.ReadOnly;
var dst = new FileStream( destination.FullName, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true );
dst.BeginWrite( buffer, 0, buffer.Length, war => {
  dst.EndWrite( war );
dst.Close();
}, null );
}, null );
}
static void Copy( DirectoryInfo source, DirectoryInfo destination ) {
if( !destination.Exists )
destination.Create();
foreach( var src in source.GetDirectories() )
Copy( src, new DirectoryInfo( Path.Combine( destination.FullName, src.Name ) ) );
var srcfiles = source.GetFiles();
foreach( var dst in destination.GetFiles() )
if( !srcfiles.Any( src => String.Equals( src.Name, dst.Name, StringComparison.OrdinalIgnoreCase ) ) )
dst.Delete();
foreach( var src in srcfiles )
Copy( src, new FileInfo( Path.Combine( destination.FullName, src.Name ) ) );
}
static void Main( string[] args ) {
Copy( new DirectoryInfo( args[ 0 ] ), new DirectoryInfo( args[ 1 ] ) );
}

まずはお試しで、属性やタイムスタンプなどは保存しませんが。
で、コピー時間ですがFastCopyが740秒なのに対して、C#は200秒…。
何も考えずに並列でファイルオープンしているためハンドル数やバッファサイズなど問題点はいろいろあるとは思いますが、この所要時間の差はいったい…?!
ちなみにこのコード、途中に1回だけ30秒間ストールが発生しています。この問題を解決すれば更に30秒短くなるはず…。

2008年11月29日土曜日

PtrToStructureArray(): 配列のマーシャリング

Marshal.PtrToStructure()を使うと、メモリの内容を元に構造体などを復元することができます。これでC言語などとデータをやりとりできるようになります。
構造体の配列に関してもMarshalAs属性を使用すれば扱えますが、この場合、コンパイル時にサイズを決定した固定長配列に限られます。
そこで、実行時にサイズを決定できるPtrToStructureArray()を作ってみました。どれほど使い道があるやら。

public class Marshal{
static ModuleBuilder moduleBuilder;
static ModuleBuilder ModuleBuilder {
get {
if( moduleBuilder == null ) {
var name = new AssemblyName( "Sayuri.Dynamic" );
var ab = AppDomain.CurrentDomain.DefineDynamicAssembly( name, AssemblyBuilderAccess.Run );
moduleBuilder = ab.DefineDynamicModule( name.Name );
}
return moduleBuilder;
}
}
public static T[] PtrToStructureArray<T>( IntPtr ptr, int size ) {
var type = typeof(T);
var typeName = string.Format( "{0}Array{1}", type.Name, size );
var arrayType = ModuleBuilder.GetType( typeName );
if( arrayType == null ){
var tb = ModuleBuilder.DefineType( typeName, TypeAttributes.SequentialLayout );
var fb = tb.DefineField( "array", type.MakeArrayType(), FieldAttributes.Public|FieldAttributes.HasFieldMarshal );
var ctype = typeof( MarshalAsAttribute );
var cb = new CustomAttributeBuilder( ctype.GetConstructor( new[] { typeof( UnmanagedType ) } ),
new object[] { UnmanagedType.ByValArray }, new[] { ctype.GetField( "SizeConst" ) }, new object[] { size } );
fb.SetCustomAttribute( cb );
arrayType = tb.CreateType();
}
var obj = SystemMarshal.PtrToStructure( ptr, arrayType );
var fi = arrayType.GetField( "array" );
return (T[])fi.GetValue( obj );
}
}
やってることはT型とサイズ20が与えられた場合に
[StructLayout(LayoutKind.Sequential)]
class TArray20{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=20)]
public T[] array;
}
というクラスをその場で生成して使っています。

2008年11月28日金曜日

IComparable<T>とその派生

以前、Dictionary<TKey, TValue>とIEquatable<T>で実行時エラーもなくハマる問題を書きましたが、今度は類似で実行時エラーでわかる問題がありました。

class Base: IComparable<Base>{
}
class Derived: Base{
}
とした場合、DerivedはIComparable<Base>を実装するため、通常の比較は行えます。しかしIComparable<Derived>は実装していないため、List<Derived>.Sort()などは行えません。

class Base<T>: IComparable<T> where T: Base{
}
class Derived: Base<Derived>{
}
とかやり出すか、素直にIComparableに切り替えるか。

2008年11月4日火曜日

swprintf()とwsprintf()

Cランタイムライブラリにsprintf()があります。同様の機能を持つwsprintf()がUSER32.dllにもあります。実はsprintf()はntdll.dllにもあります。

これらの関係を整理すると

宣言DLLTCHAR.HのルーチンANSI版Unicode版
tchar.hMSVCRT90.dll_stprintf()sprintf()swprintf()
windows.hUSER32.dllwsprintf()wsprintfA()wsprintfW()
-ntdll.dll-sprintf()swprintf()
となります。

ちなみに注意点がいくつか。
swprintf()の第2引数はバッファサイズで書式指定文字列は第3引数にずれています。他のものは第2引数が書式指定文字列です。代わりに他のものと互換のある_swprintf()も第2引数が書式指定文字列になっています。
wsprintf()は指定できる書式に制限があります。例えば浮動小数(%f)がサポートされていません。
ntdll.dllに含まれるsprintf()は使用方法が公開されてなく、サポートされている機能も公開されていません。ただし、全てのプロセスにロードされるDLLなので何かの役に立つかも。

2008年11月3日月曜日

CallNextHookEx()のHHOOK引数

SetWindowsHookEx( WH_CBT, CBTProc, ... )でDLL injectionしています。このCBTProc()のドキュメントによると、正確な型宣言は

LRESULT CALLBACK CBTProc( int nCode, WPARAM wParam, LPARAM lParam );
です。そしてこのhookプロシージャの中ではnCodeが負のときはCallNextHookEx()を呼び更にその戻り値を返すこととあります。

さてCallNextHookEx()のドキュメントによると、正確な型宣言は
LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam );
です。ちなみに記述が矛盾していて、CallNextHookEx()の呼び出しはoptionalだが、呼び出すことを強く推奨するそうです。CBTProc()側では負の時呼べばいいのに、こう書かれてしまうと0または正の時にも呼ばざるを得なくなります。仕方がないので呼ぶことにしましょう。

問題はここからです。CallNextHookEx()の第一引数hhk。hookプロシージャは進入した各プロセスで実行されます。にもかかわらずCBTProc()には存在しないパラメータが要求されています。とても面倒くさいですが、SetWindowsHookEx()の戻り値を共有メモリに格納しておき、それを読み出して使っていました。

ところが今日ふと気がついたら、
Windows NT/XP/2003: Ignored.
などと書かれているではありませんか。うーん…今まで共有メモリにわざわざ準備していたhhkは無駄でしたか…。

2008年11月1日土曜日

ウィンドウプロシージャとメンバ関数その2

ウィンドウプロシージャとメンバ関数を書いてみたものの、1つのthreadに複数のWindow Procedureが混在する場合、うまく振り分けできませんね。
どうすればいいんだろう…? まぁ、複数Windowを扱ったことがないからいいけど…。

2008年10月26日日曜日

ウィンドウプロシージャとメンバ関数

コールバック関数の1つウィンドウプロシージャWindowProc()をクラスメンバ関数にしたいという話。
調べたところ、ATLはlinked listでクラスインスタンスを管理しておき、atlbase.inlファイルのAtlWinModuleExtractCreateWndData()関数でthread idをキーにして検索をしていました。これウィンドウメッセージが届くたびにやってる気がします。
読んで勉強になったのは、CreateWindowを呼び出したスレッドにウィンドウメッセージが届くのね。だからthread idがキーとして使える、と。

なら1歩進めてthread local storageに情報を置いてしまえばいいのでは?

class Window{
LRESULT WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam,LPARAM lParam ){
// ここが本体
...;
}
static __declspec(thread) Window* window;
static LRESULT CALLBACK StaticWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ){
return window->WindowProc( hwnd, uMsg, wParam, lParam );
}
void CreateWindowOnThisThread(){
Window::window = this;
CreateWindowEx( .../* 適切に */ );
}
};
Window* Window::window = NULL;

こうすれば無難な関数に仕上がりました。ちなみにStaticWindowProc()はかなり負担の少ない実装になっていました。
mov eax, DWORD PTR _lParam$[esp-4]
mov ecx, DWORD PTR _wParam$[esp-4]
mov edx, DWORD PTR fs:__tls_array
push ebx
mov ebx, DWORD PTR _hwnd$[esp]
push eax
mov eax, DWORD PTR _uMsg$[esp+4]
push ecx
mov ecx, DWORD PTR [edx]
mov ecx, DWORD PTR Window::window[ecx]
call Window::WindowProc
pop ebx
ret 16
; 一部構文に矛盾がありますが、読みやすさのためのデマングルですので…
呼び出し規約がCALLBACKから__thiscallに変わるので、無意味なスタックの積み直しが行われてますが、まぁ仕方がありませんね。

2008年10月22日水曜日

DateTimeと時差情報

DateTime構造体は2.0からKind情報を持つようになり、UTCとローカル時間を区別できるようになりました。これによってどんなことが起きるかというと…

DateTime now = DateTime.Now;
DateTime utcNow = now.ToUniversalTime();
Console.WriteLine( "now {0} utcNow", now == utcNow ? "==" : "!=" );
// => now != utcNow
ダメじゃん。epoch秒に慣れてる人は確実にはまりますね。
MSDNを読むと…あー書いてある書いてある
DateTime オブジェクトの計算および比較では、対象となる複数のオブジェクトが同じタイム ゾーンの時刻を表している場合にのみ、意味のある結果を得ることができます。
裏返せば、タイムゾーンが違ったら意味がない…。

この場合どうすればいいかというと、DateTimeOffset構造体を使うそうです。ただし、こちらは2.0SP1から。

2008年10月21日火曜日

Data Entity FrameworkとSQL Server Compact 3.5 SP1

Visual Studio 2008 SP1の新機能Data Entity Frameworkを使ってみたくなりました。ついでにずっと気になっていたSQL Server Compact 3.5 SP1と組み合わせて試してみることにしました。
SQL Server Compactはプロセス内で動作する軽量なSQLエンジンです。SQLiteみたいなものと言えばわかりやすいでしょうか。

Wizardに従って作っていきいざ実行すると…

ハンドルされていない例外: System.ArgumentException: 指定されたストア プロバイダが構成内に見つからないか、無効です。
---> System.ArgumentException: 要求された .Net Framework データ プロバイダが見つかりません。これは、インストールされていない可能性があります。
何を言っているのかわからない例外になります。
「指定されたストア プロバイダ」とか言われてもWizardの生成したデフォルトそのままだし。「データ プロバイダが見つかりません」と言われてもサーバエクスプローラでもWizardでも表示できてるし…。

いろいろググりましたが見つかりません。ならばまずSQL Server Compact 3.5 SP1を単体で動かしてみましょう…
ハンドルされていない例外: System.DllNotFoundException: DLL 'sqlceme35.dll' を読み込めません: 指定されたモジュールが見つかりません。 (HRESULT からの例外: 0x8007007E)
見つからないってどういうこと?

ならばプライベート ファイル ベースの配置をしてみましょう…
ハンドルされていない例外: System.BadImageFormatException: 間違ったフォーマットのプログラムを読み込もうとしました。 (HRESULT からの例外: 0x8007000B)
…やっとっわかった気がする。

Microsoft SQL Server Compact 3.5 ReadmeSQL Server Compact 3.5 と Visual Studioに色々書いてありました。
  • SQL Server Compact 3.5 SP1 の 64 ビット リリースは、Microsoft ダウンロード センターからダウンロードできます (Web からの提供のみ)。
  • Visual Studio と SQL Server Compact 3.5 で 64 ビット開発を行う場合は、[コンパイラの詳細設定][ターゲット CPU] オプションを明示的に [x86] に設定する必要があります。

ええ、私はVista x64で開発してますよ…。Visual Studio自身が32bitで動作していることを忘れてましたよ。普通、開発環境なんだから64bit版もインストール済みと思うよっ!

2008年10月20日月曜日

ArraySegment classと拡張メソッド

C#で部分配列を扱いたくなったとき、unsafe fixedしてpointerを使うこともできますが、ArraySegment classでがんばってみようと思いました。ところがArraySegmentにはインデクサなどのアクセッサが用意されてなく扱いづらいです。

var array = new int[10];
var segment = new ArraySegment<int>( array, 5, 5 );
segment.Array[ segment.Offset + 3 ]; // array[8] 相当


ここで拡張メソッドを思い出しました。といっても拡張インデクサは実装できません。気を取り直してArray.GetValue()やArray.SetValue()の真似をしてみました。
public static class ArraySegmentUtility{
public static T GetValue<T>( this ArraySegment<T> segment, int index ){
return segment.Array[ segment.Offset + index ];
}
public static void SetValue<T>( this ArraySegment<T> segment, T value, int index ){
segment.Array[ segment.Offset + index ] = value;
}
}
こうすれば最初の例は
segment.GetValue( 3 );
おおいい感じ。
早速使ってみよう…
segment.Array[ segment.Offset + i ] ^= x;
ぽか~ん。アクセッサが用意されてない理由がわかった気がする…。

2008年10月19日日曜日

演算子の優先順位

自分でも呆れるくらいのミスを…
演算子の優先順位を勘違いしてました。
シフト演算<<や>>よりも先に二項演算子+や-が結合されるんですね。

2008年9月30日火曜日

XmlWriter.Flush()

XmlWriterを使っているとよくXMLが崩れます。ってXmlWriterが悪いような書き方ですが、単に使い方が間違っていました。
普段、auto flushに慣れているわけですが、残念ながらXmlWriterはauto flushモードがなく、定期的にFlush()メソッドを呼び出す必要があります。
といっても、元々buffer fullになればFlush()されるわけですから本当に必要なのは、最後の1回だけです。
デストラクタが自動的にFlush() / Close()してくれるんじゃ…と思いましたがなんとデストラクタがありません。
となると手動でFlush()かClose()かDispose()を呼ぶ必要があるわけですが、Dispose()がpublicではありません。この場合のFlush()は意味が違いますし、Close()で変なステートメントを増やしたくないですし…

結局、using構文が一番しっくりきます。

2008年8月31日日曜日

IEでのinherit

ググったけど話題になっていなかったので。CSSには親要素から値を継承するinherit値がありますが、実はIEではこれがあまり動作しません。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>test</title>
<style type="text/css">
body{ font-weight: normal; }
span{ font-weight: bold; }
span{ font-weight: inherit; }
</style>
</head>
<body>
<span>normalが継承される</span>
</body>
</html>
spanはbold指定を打ち消し、bodyからnormalを継承しなければなりませんが、IE7ではinherit行自体が指定されていなかったものと見なし、boldで表示されてしまいます。

ちなみにIE8 beta2で確認したところ、こちらは正しくnormalで表示されていました。

2008年8月11日月曜日

.NET Framework 3.5 SP1とSQL Server 2008

.NET Framework 3.5 SP1 betaとVistal Studio 2008 SP1 betaを試していましたが、.NET Framework 3.5 SP1側がこっそりリリース?されていました。
正確にはダウンロード可能になっていませんし、再配布可能ものジュールも提供されていないのでまだまだ使えませんが、SQL Server 2008にさりげなく入っていました。

2008年7月21日月曜日

TextureCoordinates

WPFを使って3Dを扱っていたのだが、TextureCoordinatesという概念がよくわからなかった。MSDNの記述もいい加減で

テクスチャ座標に関する追加の情報は、後続のトピックまたは Managed Direct3D SDK を参照してください。
と逃げているし…Managed Direct3Dってもう終わったんじゃ?
ググっていたらDaniel Lehenbauer's Blog : 3D for the Rest of Us: Texture Coordinatesを見つけた。これはわかりやすいかも?

…何の情報もない投稿だなぁ

2008年5月30日金曜日

FizzBuzz Questions

思いつきでFizzBuzz Questionsをやってみる。お題はこちら:

Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz".


普通に解くと
#include <iostream>
using namespace std;

void FizzBuzz( int n ){
if( n % 15 )
if( n % 5 )
if( n % 3 )
cout << n;
else
cout << "Fizz";
else
cout << "Buzz";
else
cout << "FizzBuzz";
cout << endl;
}

int main(){
for( int i = 1; i < 101; i++ )
FizzBuzz( i );
}
こんな感じ?

当たり前ですが実行時に分岐しています。でもTMP(Template MetaProgramming)を使えばコンパイル時に分岐できます。
#include <iostream>
using namespace std;

template<int n, int mod3 = n % 3, int mod5 = n % 5>
struct FizzBuzz{
static ostream& print(){
return FizzBuzz<n-1>::print() << n << endl;
}
};

template<int n, int mod5>
struct FizzBuzz<n, 0, mod5>{
static ostream& print(){
return FizzBuzz<n-1>::print() << "Fizz" << endl;
}
};

template<int n, int mod3>
struct FizzBuzz<n, mod3, 0>{
static ostream& print(){
return FizzBuzz<n-1>::print() << "Buzz" < endl;
}
};

template<int n>
struct FizzBuzz<n, 0, 0>{
static ostream& print(){
return FizzBuzz<n-1>::print() << "FizzBuzz" << endl;
}
};

template<> ostream& FizzBuzz<0>::print(){
return cout;
}

int main(){
FizzBuzz<100>::print();
}
本当はループ展開とかもTMPでできるかも…でもやり方がよくわかりませんでした。

しかし更にすごい方法が…
#include <iostream>
#include <boost/preprocessor/arithmetic/mod.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
using namespace std;

#define FizzBuzz( z, n, data ) \
BOOST_PP_IF( BOOST_PP_MOD( n, 15 ), \
BOOST_PP_IF( BOOST_PP_MOD( n, 5 ), \
BOOST_PP_IF( BOOST_PP_MOD( n, 3 ), \
#n, \
"Fizz" ), \
"Buzz" ), \
"FizzBuzz" ) \
"\n"

int main(){
cout << BOOST_PP_REPEAT_FROM_TO( 1, 101, FizzBuzz, );
}
コンパイル前にすべての処理を終わらせてしまい、preprocessorだけで単一の文字列を作り上げてしまうことも。いやはや。

2008年5月16日金曜日

SslStream

Boost.Asioを書いたけど、.NET Frameworkにも同様にSslStreamクラスが用意されている。
使い方もほぼ同じで

var c = new TcpClient( "ホスト名", 443 );
var s = new SslStream( c.GetStream() );

s.AuthenticateAsClient( "ホスト名" );

s.Write( Encoding.Default.GetBytes( "GET / HTTP/1.0\r\n\r\n" ) );

var r = new StreamReader( s, Encoding.Default );
Console.WriteLine( r.ReadToEnd() );
こんな感じ。違いは
  • Windows環境ではgetservbyname()が信用できないので直接443と書いている点
  • .NET FrameworkはByteとCharを区別しているので明示的な変換が必要になる点
  • Byte→Char変換を中断するのは面倒くさいのでReadToEnd()ですべて読み込んでしまっている点
でしょうか。

Boost.Asio

BoostというC++ライブラリが気になっていたのですが、1.35がリリースされ、Boost.Asioという非同期I/Oライブラリが追加されていることを知りました。このライブラリはWindows、Linux、各種Unix上で動作し、各種コンパイラをサポートしています。さらによく見たらOpenSSLによるSSLまでサポートしていました。

まずは試しに同期I/Oでhttps clientを書いてみました。

io_service i;
ssl::context c( i, ssl::context::sslv3_client );
ssl::stream s( i, c );
s.lowest_layer().connect( *ip::tcp::resolver(i)
.resolve( ip::tcp::resolver::query( "ホスト名", "https" ) ) );

s.handshake( ssl::stream_base::client );

write( s, buffer( "GET / HTTP/1.0\r\n\r\n" ) );

asio::streambuf res;
error_code e;
while( read( s, res, transfer_at_least(1), e ) )
cout << &res;
うーん、これだけで動いちゃいました。

2008年4月26日土曜日

Windows XP SP3

Windows XP SP3が公開されたようです。Release To Manufacturing(RTM)後、1週間ほどでRelease To Web(RTW)されるそうです。
SP2と互換は高いという噂なので特に言及することもなさそうでしたが…1点、すでにWindows Vistaがリリースされていることもあり、Windows XP with SP3は作られないとかなんとか。というわけで後からSP3を追加する形のみになるようです。…ちなみにSP3のインストール条件にSP1が入っていました。つまり素のXPには一旦SP1aかSP2を入れた後にSP3を入れることになるようです。

2008年3月29日土曜日

インサイドWindows第4版下

インサイドWindows第4版上に続いてようやくインサイドWindows第4版下を買えました。近くの本屋さんにはいつ見ても上しか置いてなくて…でも注文するのは負けた気がするし。
読んでいて一つ参考になった話題を。大容量のメモリを搭載しているWindows XPでもタスクマネージャのシステムキャッシュを見ると512MBで抑えられている場合があります。このPCは68MBとしょぼしょぼですが(;;
こういう場合、パフォーマンス オプションで「システム キャッシュ」を選択すると512MB以上のメモリをシステムキャッシュに割り当てられるようになります。メモリが余ってる人はどうぞ。
64bit版やWindows Vistaはこのリミッターが最初から解除されています。

HTTP Server API

以前からWindows OSにはIISが含まれていたけど、Windows XP SP2、Windows Server 2003以降にはOS側にWebサーバ機能が組み込まれていて、IISもこれを使う設計らしい。
試しにこのHTTP Server APIを使ってみた。

HTTPAPI_VERSION version = HTTPAPI_VERSION_1;
HttpInitialize( version, HTTP_INITIALIZE_SERVER, NULL );

HANDLE handle;
HttpCreateHttpHandle( &handle, NULL );

HttpAddUrl( handle, L"http://+:80/", NULL );

while( !stop ){
HTTP_REQUEST_ID requestId;
HTTP_SET_NULL_ID( &requestId );
char buffer[ 4096 ] = {};
PHTTP_REQUEST request = reinterpret_cast<PHTTP_REQUEST>( buffer );
ULONG size;
HttpReceiveHttpRequest( handle, requestId, 0, request, sizeof buffer, &size, NULL );

HTTP_RESPONSE response;
memset( &response, 0, sizeof response ); // デフォルトコンストラクタ用意してください(;;)
HTTP_DATA_CHUNK chunk;
HANDLE file = INVALID_HANDLE_VALUE;
if( request->Verb != HttpVerbGET )
response.StatusCode = 503;
else{
// URLパスの先頭の'/'をスキップ
file = CreateFileW( &request->CookedUrl.pAbsPath[1], FILE_READ_DATA, 0, NULL, OPEN_EXISTING, 0, NULL );
if( file == INVALID_HANDLE_VALUE )
response.StatusCode = 404;
else{
chunk.DataChunkType = HttpDataChunkFromFileHandle;
chunk.FromFileHandle.ByteRange.StartingOffset.QuadPart = 0;
chunk.FromFileHandle.ByteRange.Length.QuadPart = HTTP_BYTE_RANGE_TO_EOF;
chunk.FromFileHandle.FileHandle = file;
response.StatusCode = 200;
response.EntityChunkCount = 1;
response.pEntityChunks = &chunk;
}
}
HttpSendHttpResponse( handle, request->RequestId, 0, &response, NULL, &size, NULL, 0, NULL, NULL );
if( file != INVALID_HANDLE_VALUE )
CloseHandle( file );
}
HttpRemoveUrl( handle, L"http://+:80/" );
CloseHandle( handle );
HttpTerminate( HTTP_INITIALIZE_SERVER, NULL );
サンプルなのでエラー処理は省いてます。機能的には、カレントディレクトリのファイルを返します。
stop = trueとしてもすぐには終了しません。HttpReceiveHttpRequest()でブロックされているので、次のリクエストを処理したところでループを抜けます。

2008年3月2日日曜日

fwrite()と改行コード

fwrite()は任意のバイナリデータをstreamに書き込むライブラリ関数だと理解していましたが、今更ながらstreamがテキストモードの場合、任意文字列を書き込むものだと知りました。具体的にはバイナリデータを文字列と見なし、その中からNL(0x0A)をCR-LF(0x0D-0x0A)に置き換えられてはまりました。
テキストとバイナリを混在させたstreamの場合、その都度モードを切り替える必要があるそうです。

FILE* stream;
_setmode( _fileno( stream ), _O_BINARY );

2008年2月27日水曜日

WPADのContent-Type

Web Proxy Auto-Discovery ProtocolいわゆるWPADですが、なんとWindows Update ClientはContent-Typeをちゃんと見ていました。application/x-ns-proxy-autoconfigに設定されていないと、無効と見なされてしまうようです。
これ気づいてる人は少ないようで、proxycfg.exeに頼ってる記述が多々ありました。

2008年2月20日水曜日

RAIDデバイス名の変更

Linuxのsoftware RAID(md)の場合、disk controllerをまたがって好きなようにRAIDを構成できるため重宝します。またhardware RAIDの場合、raid controllerが故障すると読み出せませんが、そのような欠点もありません。
mdはディスク内に自身のデバイス名(md0)などを保持しています。ここで例えばディスク交換のために、/パーティションをmd0からmd1にコピーした場合、新たに作られる/はmd1というデバイス名を持ってしまいます。これを繰り返すとデバイス名がどんどんいびつになっていきます。
これをどうにか整理したかったのですが、どこにも書かれていなかったので自力で解決しました。

まだまだ説明が続きます。多くのドキュメントではmdのcreateについてしか記述されていませんでした。しかし実際にはassembleとstopという行為が重要になってきます。なぜこれらが記述されていないのかというと、Linuxの各ディストリでは起動時やshutdown時のscriptにあらかじめ組み込まれていて、説明する必要がないわけです。そして、試した結果わかったのですが、「mdはディスク内に自身のデバイス名(md0)などを保持しています。」これはcreate時だけでなくassemble時にも更新されるようです。

いよいよデバイス名の変更の仕方です。例えばmd2をmd1に変更するには

# umount md2
# mdadm -S /dev/md2
# mdadm -A /dev/md1 /dev/sd... /dev/sd...
# mount ...
わかってしまえば簡単でした。
このあと/etc/fstabの変更も忘れずに。

ただしこれができるのはumount可能なパーティションだけです。CD bootなどでもいいかもしれません。
CD bootできなくてもがんばれば更新できます。例えば/のデバイス名をmd0に変更する場合kernel optionで
raid=noautodetect md=0,/dev/sd...,/dev/sd... root=/dev/md0
で変更されます。raid=noautodetectを指定しておかないと、autodetectされ、保持されているデバイス名でassembleされてしまい変更できません。またデバイス名が変わるためroot=/dev/md0の指定をしないと起動に失敗します。
このあと/boot/grub/menu.listや/etc/fstabの更新も忘れずに。

2008年2月11日月曜日

ハードディスクの2TBの壁

750GB×4のハードウェアRAID5で、2.25TBになります。このときハードディスクのパーティションテーブルは32bit sectors(4giga sectors)×512bytes/sector=2TBまでしか表現できないという、2TBの壁にぶつかりました。
Debian GNU/Linux(etch)をインストールしたところ、パーティションエディタは2TBの壁を超えるために新しいGPT形式に対応していて、ファイルのインストール自体はできました。ところがブートローダのGRUBがGPTに未対応のバージョンのため、結局ブート不可能なインストール結果となってしまいました。

2008年2月9日土曜日

ハードディスクの問題が検出されました

時々、OSからハードディスクが認識されなくて、先週からは起動時のBIOSからも認識されなくなって、やばいと思っていました。週末ということもあり、バックアップして徹底的にチェック! …まずはバックアップを~こんなダイアログが出ました。Windows VistaってSelf-Monitoring, Analysis and Reporting Technology、いわゆるS.M.A.R.T.に対応してたのね。
ググると遭遇している方が結構いらっしゃるようです。でも…この警告を無視するコメントが多数…う~ん、無理に使ってデータが壊れても自己責任ですよ。

Windows Vista SP1 RTM

2/4にWindows Server 2008とWindows Vista SP1のRelease To Manufacturing、RTMがアナウンスされました。それと同時にMSDNでWindows Server 2008の提供も開始されましたが、Vista SP1に関しては3月中旬に公開されるそうです。
…と、これでは寂しいのでVista SP1のRTM版を入手してインストールしました。普通にインストールできました。ハイ、それだけです。

robocopy

ファイルを多数コピーしたくなりました。Explorerでは遅いからここはxcopyコマンドだと思い、まずはコマンドヘルプを…

C:\Users\sayuri>xcopy /?
ファイルとディレクトリ ツリーをコピーします。

注意: Xcopy は現在推奨されていません。Robocopy を使用してください。

XCOPY 送り側 [受け側] [/A | /M] [/D[:日付]] [/P] [/S [/E]] [/V] [/W]
[/C] [/I] [/Q] [/F] [/L] [/G] [/H] [/R] [/T] [/U]
[/K] [/N] [/O] [/X] [/Y] [/-Y] [/Z] [/B]
[/EXCLUDE:ファイル1[+ファイル2][+ファイル3]...]
…ん?
C:\Users\sayuri>robocopy /?

-------------------------------------------------------------------------------
ROBOCOPY :: Windows の堅牢性の高いファイル コピー

-------------------------------------------------------------------------------

これはバックアップとか便利に使えそうなコマンドのようです。

2008年2月2日土曜日

LoadLibrary

今までLoadLibrary()を使ってましたが、実はLoadLibraryEx()にLOAD_WITH_ALTERED_SEARCH_PATHフラグを付けた方がいいことが判明。
こうすることによって、ロード対象のDLLがさらに依存するDLLを探す際に、DLL自身のdirectoryを検索対象に含めることができるようになる。

2008年1月26日土曜日

Dictionary<TKey, TValue>とIEquatable<T>

ある意味当たり前ですが、気づかずにはまってしまいました。
Dictionary<TKey,TValue>はIEquatable<T>が実装されていればそれを使って比較してくれます。Object.Equals()と違って型保証されているので便利に使っていました。
しかし、Dictionary<TKey,TValue>はTKeyを比較する前にまずハッシュします。この際、IEquatable<T>の有無にかかわらずObject.GetHashCode()を使います。つまり、このハッシュ値が異なるTKeyはIEquatable<T>.Equals()が呼び出されることなく異なる値として扱われてしまいます。
むーん。

2008年1月7日月曜日

ILogicalThreadAffinativeとThread Local Storage

TLSつまりThread Local Storageは便利で、コンテキスト情報を持たせるときに活用しようとずっと考えていました。すでにある例としてはIPrincipalでしょうか。Threadにプリンシパルを持たせることで、実行に必要な権限があるかをいつでもテストできます。
ですがこれ、Thread Localということは当然Threadをまたぐことができません。.NETでは簡単に非同期処理などを利用でき複数Threadにまたがってしまう。そうなったときTLSは使えません。

そこで自分の生成した子スレッドにのみ引き継がれる都合のいいTLSはないかと.NETクラスライブラリを調べていましたがなかなか出合えませんでした。
が、やはり根気よく調べればありました。それがILogicalThreadAffinativeです。このinterfaceはメンバがありません。ただ継承すればそれだけでTLSの内容が子スレッドにも引き継がれるようになります。
日本語圏でググったところ、まだあまり使われていないようですが、これはかなり使えます。

??演算子

C#で??演算子というものを見つけました。調べたところ、C# 2.0、つまりVS2005からあったそうで、ずっと気づいていませんでした。

a = b ?? c;
a = b != null ? b : c;
とほぼ等価です。違うのは、bが1回しか評価されないことです。Perlの||的な動きをすると言えばいいでしょうか。