2011年5月25日

シリアルポートコンポーネント

シリアルポート(COM/RS-232C)はレガシーではありますが、依然として使用する機会の多いインタフェースです。そこで一般的にお勧めできそうなシリアルポートコンポーネントを探してみました(対応するDelphi/C++Builderのバージョンはあくまで目安です)。

ComPort Library
作者はDejan Crnilaさん、現在のメンテナはLars B. Dybdahlさん、Paul Dolandさん、Brian Gochnauerさんです。Delphi 3/4/5/6/7/8/2005/2006/2007/2009/2010/XE、C++Builder 3/4/5/6に対応しています。最新バージョンは4.11a(2011/05/22)、ライセンスはPublic Domainです。

TurboPower Async Professional
もともとはTurboPower Softwareの商用製品で、オープンソース化されたTurbo Powerプロジェクトの多機能通信コンポーネント群。現在のメンテナはSean B. Durkinさん、tpsfadmin(TurboPower)ことNick Hodgesさんです。Delphi 7/2005/2007/2010に対応しています。最新バージョンは5.0(2010/03/10)、ライセンスはMPL 1.1です。関連: Installing AsyncPro into Delphi XE and C++Builder XE

SynaSer serial library
Ararat s.r.o.SYNAPSE libraryに含まれるシリアル通信コンポーネント。Delphi/C++Builder 3/4/5/6/7/2005/2006に対応しています。最新バージョンはRelease 16/Version 7.2.0(2007/12/21)、ライセンスは修正BSDスタイルです。

TksComPort
Sergey Kasandrovさんによるkstoolsに含まれるシリアル通信コンポーネント。Delphi 2007/2009に対応しています。最新バージョンは0.50(2010/04/30)、ライセンスはMIT Licenseです。

ComPort component
Winsoftによる商用製品(35.00-70.0USD)。Delphi/C++Builder 5/6/7/2005/2006/2007/2009/2010/XEに対応しています。最新バージョンは3.5(2010/12/01)。

nrComm Lib
DeepSoftwareによる商用製品(69.00-599.00EUR)。Delphi/C++Builder 3/4/5/6/7/2005/2006/2007/2009/2010/XEに対応しています。最新バージョンは9.17(2011/05/11)。

お勧めはComPort LibraryかTurboPower Async Professionalでしょうか。シンプルなものがよければ前者、多機能なものがよければ後者、という感じで。

元ねたはTeam Japan » Delphi XE で COMポート(RS-232C)を操作するなど。

2011年5月24日

よりよいQCレポートの書き方

エンバカデロのQualityCentralのSysopの一人であるUwe SchusterさんHow to improve your QC reports?(QCレポートを改善する方法)というアーティクルを書いています。興味深い。

てきとうな要約: まず、最大の誤りは問題をレポートしないことです。なぜならあなたがその問題は修正されると思っていない、ということだからです。
  • Build No
    ここにはRAD Studioのフルバージョン番号を記入する(XE Update 1なら15.0.3953.35171)。これはどのUpdate/Hotfixを適用した状態なのかを知るために重要。
  • Language/Nationality
    ローカライズとは関係のない問題では"US"。表示の欠けや誤訳などのローカライズに関係する問題ではその言語を選択。不必要に言語を指定するとまずローカライゼーションチームに回されるため修正まで時間が掛かるかも。
  • Type and Severity
    この2つの項目は全てのユーザの視点で指定するべきで、高すぎる評価はSysopやQAによって修正されることがある。またコンパイラによる内部エラー("Fxxxx Internal Error")はTypeを(Crash/Data loss/Total failure)とする。
  • Steps
    Stepsには明白であっても予想される動作と実際の動作を記入する。レポートをOpenにするためには再現可能でなければならない。テストケースのないコンパイラの内部エラーは既存のレポートと重複しているものとして扱われ、修正されない可能性が高い(原因が異なっていても同じエラーになるので)。
  • Automated Reports
    IDEがJCLスタックトレース機能を使って生成するものでAIRレポートとも呼ばれ、QCのDelphi.NETに分類される。これは過去の経緯によるものなので、気にせずそのまま(Delphi/C++Builderプロジェクトに変更したりせず)送信する。
  • Voting
    新しいvoteシステムは2007年から使用されており、それぞれのレポートに+/-10の範囲でvote(投票)することができる。webクライアントは新しいvoteシステムに対応しておらずvote値を指定できないので、Win32クライアント(かQC Plus)を使用するのが望ましい。
  • Questions
    QCレポートの扱いについて質問がある場合はForum: QualityCentralへ。


QualityCentralのレポートの書き方に関しては以下のアーティクルも参考になります。

Quality Centralでのバグレポートのコツ
Quality Centralの基礎知識 - Delphi 2009 特集
バグレポートの際のコツ - Delphi 2009 特集
QualityCentral の Tips
Quality Central についての基礎知識

2011年5月19日

Windowsの互換モード上でのLCMapStringの不具合

ちょっと前になりますが、au2010さんのところで気になる話を見つけました。

Windows7でDelphi7のプログラムを動かす時の注意点 - au2010の日記

実際にLCMapString (ja)の動作を確認してみると、Windows 7で互換モードを設定(プログラムのショートカットの"互換性"タブで"互換モード"にチェックオン)すると、LCMapStringのcchDestを0にしたときだけ戻値がA版(LCMapStringA)であっても(W版と同様の)文字数で返ってくる、という不具合があります(cchDestが非0ならば戻値は仕様通り)。いろいろ調べてみましたがプログラム側から互換モードの指定状況を知る方法はなく、結果が既知の変換動作を行わせて、その戻値で判定するしかないようです。ということで半角→全角変換全角→半角変換のコードを修正しました。

2011年5月18日

C++0x FDIS(N3290)解説

C++0xの規格化作業は最終段階に差し掛かっていて、現在はN3290FDIS(Final Draft International Standards)として公開されています(もちろん英語で)。

このC++0xのN3290について、2011/05/14に行われたBoost.勉強会 #5 名古屋道化師さんが解説した資料が見られるようになっています。

C++0x総復習 (Boost.勉強会 #5 名古屋 - boostjp)

slideshareだとこちらですね。

C++0x総復習

江添さんによる本の虫: post-Madrid mailingの簡易レビューも併せて読むといいと思います。

元ねたはアキラさんBoost.勉強会 #5 名古屋でした - Faith and Brave - C++で遊ぼう

Delphiにも"= delete"(§8.4.3 Deleted definitions)があればこんなことをしなくてもすんだのに…。

2011年5月17日

DelphiでSingletonパターンを実装する(リベンジ)

DelphiでSingletonパターンを実装する(再考)ではTSingletonのインスタンスの解放を防ぐためにデストラクタ内で例外を送出する、という方法を考えてみましたが、2ちゃんねる界隈では不評だったようです。一般論からいえば確かにデストラクタからの例外の送出はいかがなものか、という気もしますが、そもそもこのアプローチは(コンストラクタからの例外の送出による複数インスタンスの生成の防止と同様に)テストレベルで問題コードを検出、修正するためのものであって、リリースコード上で実行されることを想定しているわけではありません。また前回考察したように、一旦呼び出しがなされたコンストラクタ、デストラクタのインスタンスに対する生成、破棄処理を回避するには例外の送出しかないことも確かです。ということで"より望ましい"解決方法を考えてみることにします。

まず前回の"Phoenix Singleton"ですが、デストラクタを呼び出すことでインスタンスが完全に解放されてしまうため、次回のアクセスで新しいインスタンスが生成されてもその内容は初期状態に戻ってしまいます。そこで"予備"のインスタンスを用意しておき、ここに内容を退避して再生成時に復元します。まずinitialization/finalization版から。
unit Unit32;

interface

uses
  SysUtils;

type
  TSingleton = class(TObject)
  private
    FTestValue: Integer;
    constructor CreateInstance;
  public
    constructor Create;
    destructor  Destroy; override;
    procedure   Assign(Source: TSingleton);
    class function GetInstance: TSingleton;
    property    TestValue: Integer
                  read  FTestValue
                  write FTestValue;
  end;

  ECreateSingleton = class(Exception)
  end;


implementation

var
  FSingleton: TSingleton;
  FShadowSingleton: TSingleton;

{ TSingleton }

constructor TSingleton.Create;
begin

  raise ECreateSingleton.Create('TSingleton.Create cannot use.');

end;

constructor TSingleton.CreateInstance;
begin

  inherited Create;

  { Initialize }
  FTestValue := 0;

end;

destructor TSingleton.Destroy;
begin

  { Shadowing }
  if FShadowSingleton = nil then
  begin
    FShadowSingleton := TSingleton.CreateInstance;
  end;
  if FShadowSingleton <> Self then
  begin
    FShadowSingleton.Assign(Self);

    { Delete singleton reference }
    FSingleton := nil;
  end;

  { Finalize }

  inherited;

end;

procedure TSingleton.Assign(Source: TSingleton);
begin

  { Copy from source }
  TestValue := Source.TestValue;

end;

class function TSingleton.GetInstance: TSingleton;
begin

  if FSingleton = nil then
  begin
    FSingleton := TSingleton.CreateInstance;
    if FShadowSingleton <> nil then
    begin
      FSingleton.Assign(FShadowSingleton);
    end;
  end;

  Result := FSingleton;

end;

initialization
  FSingleton := nil;
  FShadowSingleton := nil;

finalization
  FSingleton.Free;
  FShadowSingleton.Free;

end.
次にclass constructor/class destructor版です。
unit Unit34;

interface

uses
  SysUtils;

type
  TSingleton = class(TObject)
  private
    FTestValue: Integer;
    class var
      FSingleton: TSingleton;
      FShadowSingleton: TSingleton;
    constructor CreateInstance;
  public
    class constructor Create;
    class destructor  Destroy;
    constructor Create;
    destructor  Destroy; override;
    procedure   Assign(Source: TSingleton);
    class function GetInstance: TSingleton;
    property    TestValue: Integer
                  read  FTestValue
                  write FTestValue;
  end;

  ECreateSingleton = class(Exception)
  end;


implementation

{ TSingleton }

class constructor TSingleton.Create;
begin

  FSingleton := nil;
  FShadowSingleton := nil;

end;

class destructor TSingleton.Destroy;
begin

  FSingleton.Free;
  FShadowSingleton.Free;

end;

constructor TSingleton.Create;
begin

  raise ECreateSingleton.Create('TSingleton.Create cannot use.');

end;

constructor TSingleton.CreateInstance;
begin

  inherited Create;

  { Initialize }
  FTestValue := 0;

end;

destructor TSingleton.Destroy;
begin

  { Shadowing }
  if FShadowSingleton = nil then
  begin
    FShadowSingleton := TSingleton.CreateInstance;
  end;
  if FShadowSingleton <> Self then
  begin
    FShadowSingleton.Assign(Self);

    { Delete singleton reference }
    FSingleton := nil;
  end;

  { Finalize }

  inherited;

end;

procedure TSingleton.Assign(Source: TSingleton);
begin

  { Copy from source }
  TestValue := Source.TestValue;

end;

class function TSingleton.GetInstance: TSingleton;
begin

  if FSingleton = nil then
  begin
    FSingleton := TSingleton.CreateInstance;
    if FShadowSingleton <> nil then
    begin
      FSingleton.Assign(FShadowSingleton);
    end;
  end;

  Result := FSingleton;

end;

end.
いずれもシングルトンの内容の退避、復帰のためにインスタンスの内容をコピーするprocedure Assignというメソッドを用意し、これをコンストラクタ、デストラクタで使用します。なおシングルトンが別のクラスのインスタンスを所有するような場合、Assignはいわゆる"deep copy"の動作を実装する必要があります。またpublicなconstructor Createについてはとりあえず従来どおりの例外送出のままとしてあります。

次にコンストラクタ、デストラクタでの例外の送出、というアプローチですが、これは前述のとおりテストレベルでの問題コードの検出、修正を目的としています。しかしこの方法には問題コードが実行されない限り意味をもたない(コンパイル時には検出できない)、という欠点もあります。そこで例外の送出ではなく、constructor Create、destructor Destory、そして再定義したprocedure Freeに(少々目的は異なるものの)ヒント指令deprecatedを指定しておき、これらを呼び出しているコードをコンパイル時に警告されるようにする、という解決策を考えてみます。まずinitialization/finalization版から。
unit Unit28;

interface

uses
  SysUtils;

type
  TSingleton = class(TObject)
  private
    FTestValue: Integer;
    constructor CreateInstance;
    destructor  DestroyInstance;
  public
    constructor Create;            deprecated {$IFDEF CONDITIONALEXPRESSIONS}{$IF CompilerVersion >= 20.00} 'Do not use TSingleton.Create.'  {$IFEND}{$ENDIF};
    destructor  Destroy; override; deprecated {$IFDEF CONDITIONALEXPRESSIONS}{$IF CompilerVersion >= 20.00} 'Do not use TSingleton.Destory.' {$IFEND}{$ENDIF};
    procedure   Free;              deprecated {$IFDEF CONDITIONALEXPRESSIONS}{$IF CompilerVersion >= 20.00} 'Do not use TSingleton.Free.'    {$IFEND}{$ENDIF};
    class function GetInstance: TSingleton;
    property    TestValue: Integer
                  read  FTestValue
                  write FTestValue;
  end;


implementation

var
  FSingleton: TSingleton;

{ TSingleton }

constructor TSingleton.Create;
begin

  { Place holder, do not use }

end;

destructor TSingleton.Destroy;
begin

  { Place holder, do not use }

end;

constructor TSingleton.CreateInstance;
begin

  inherited Create;

  { Initialize }
  FTestValue := 0;

end;

destructor TSingleton.DestroyInstance;
begin

  { Finalize }

  inherited Destroy;

end;

procedure TSingleton.Free;
begin

  { Place holder, do not use }

end;

class function TSingleton.GetInstance: TSingleton;
begin

  if FSingleton = nil then
  begin
    FSingleton := TSingleton.CreateInstance;
  end;

  Result := FSingleton;

end;

initialization
  FSingleton := nil;

finalization
  if FSingleton <> nil then
  begin
    FSingleton.DestroyInstance;
  end;

end.
Delphi 2009以降({$IFDEF CONDITIONALEXPRESSIONS}{$IF CompilerVersion >= 20.00}で判定)ではdeprecatedに追加のメッセージを指定しています。

次にclass constructor/class destructor版です。
unit Unit30;

interface

uses
  SysUtils;

type
  TSingleton = class(TObject)
  private
    FTestValue: Integer;
    class var
      FSingleton: TSingleton;
    constructor CreateInstance;
    destructor  DestroyInstance;
  public
    class constructor Create;
    class destructor  Destroy;
    constructor Create;            deprecated 'Do not use TSingleton.Create.';
    destructor  Destroy; override; deprecated 'Do not use TSingleton.Destory.';
    procedure   Free;              deprecated 'Do not use TSingleton.Free.';
    class function GetInstance: TSingleton;
    property    TestValue: Integer
                  read  FTestValue
                  write FTestValue;
  end;


implementation

{ TSingleton }

class constructor TSingleton.Create;
begin

  FSingleton := nil;

end;

class destructor TSingleton.Destroy;
begin

  if FSingleton <> nil then
  begin
    FSingleton.DestroyInstance;
  end;

end;

constructor TSingleton.Create;
begin

  { Place holder, do not use }

end;

destructor TSingleton.Destroy;
begin

  { Place holder, do not use }

end;

constructor TSingleton.CreateInstance;
begin

  inherited Create;

  { Initialize }
  FTestValue := 0;

end;

destructor TSingleton.DestroyInstance;
begin

  { Finalize }

  inherited Destroy;

end;

procedure TSingleton.Free;
begin

  { Place holder, do not use }

end;

class function TSingleton.GetInstance: TSingleton;
begin

  if FSingleton = nil then
  begin
    FSingleton := TSingleton.CreateInstance;
  end;

  Result := FSingleton;

end;

end.
どちらもDelphi 2009以降であれば"プロジェクトオプション"の"Delphiコンパイラ"の"ヒントと警告"で"使用を推奨されていないシンボル"をエラーにするか、シングルトンを使用している側のユニットに
{$WARN SYMBOL_DEPRECATED ERROR}
を指定することでconstructor Create、destructor Destory、procedure Freeの呼び出しをエラーにすることができます(通常は警告)。

2011年5月16日

IDE Fix Pack 4.1/DelphiSpeedUp 3.1リリース

Andreas HausladenさんIDE Fix Pack 2007およびIDE Fix Pack 2009/2010/XEがアップデートされてVersion 4.1に、DelphiSpeedUpもDelphi 7/2007用がアップデートされてVersion 3.1になっています。IDE Fix Pack、DelphiSpeedUpともRCからの変更はないようです。

IDE Fix Pack 4.1, DelphiSpeedUp 3.1 | Andy’s Blog and Tools

2011年5月11日

Microsoft Monthly Update 2011/05

今日はMicrosoftのセキュリティアップデートの日です。
MS11-035
MS11-036

2011年5月9日

コンストラクタとデストラクタについての考察

Delphiのコンストラクタ、デストラクタとは、ヘルプの

コンストラクタ (メソッド)
デストラクタ (メソッド)

にあるように、クラスのインスタンスを生成、破棄するための特別なメソッドです。あまり知られていませんがコンストラクタとデストラクタには

メソッド呼び出しの処理 (プログラムの制御)

にあるように、Pointer型とByte型の2つの隠しパラメータが存在します(デフォルトのregister呼出規約ではEAXレジスタとDLレジスタ)。第1パラメータ(Pointer型)はSelf(C++のthisに相当)です。一方で第2パラメータ(Byte型)は真偽値(0または非0)であり、クラスメソッドとして呼び出される場合、つまり
  Foo := TFoo.Create;
では1(真)が設定され、インスタンスメソッドとして呼び出される場合、つまり
constructor TFoo.Create;
begin
  inherited;
end;
のinherited(継承元コンストラクタの呼び出し)では0(偽)が設定されます。コンストラクタでは先頭にこの第2パラメータをチェックして真の場合はインスタンス領域の確保と初期化が行われるようなコードが生成されます(確保したインスタンス領域のアドレスはEAXに格納され、呼出元に返されます)。またデストラクタでは末尾に第2パラメータのチェックとインスタンス領域の解放が行われるようなコードが生成されます。さらにコンストラクタでは内部で例外が生成されたときに自動的にデストラクタが呼び出され、初期化後に変更されたフィールドに対してリソースの解放を行うことでコンストラクタで例外を送出したときのリソースリークを防ぐような仕組みになっています。

実際のコードで確認してみましょう(例はシングルトンパターンその1のUnit2をDelphi 2007でデバッグビルドしたものです)。

コンストラクタ呼び出し
  FSingleton := TSingleton.Create;


Unit2.pas.30: FSingleton := TSingleton.Create;
00457C35 B201             mov dl,$01
00457C37 A1D47B4500       mov eax,[$00457bd4]
00457C3C E80B000000       call TSingleton.Create
00457C41 A3CC0C4600       mov [$00460ccc],eax

となり、DLレジスタに0x01が設定されてコンストラクタ(TSingleton.Create)を呼び出しています。コンストラクタ本体の
constructor TSingleton.Create;
begin

  inherited;

  { Initialize }
  FTestValue := 0;

end;


Unit2.pas.38: begin
00457C4C 53               push ebx
00457C4D 56               push esi
00457C4E 84D2             test dl,dl
00457C50 7408             jz $00457c5a
00457C52 83C4F0           add esp,-$10
00457C55 E8B2C0FAFF       call @ClassCreate
00457C5A 8BDA             mov ebx,edx
00457C5C 8BF0             mov esi,eax
Unit2.pas.40: inherited;
00457C5E 33D2             xor edx,edx
00457C60 8BC6             mov eax,esi
00457C62 E869BDFAFF       call TObject.Create
Unit2.pas.43: FTestValue := 0;
00457C67 33C0             xor eax,eax
00457C69 894604           mov [esi+$04],eax
Unit2.pas.45: end;
00457C6C 8BC6             mov eax,esi
00457C6E 84DB             test bl,bl
00457C70 740F             jz $00457c81
00457C72 E8EDC0FAFF       call @AfterConstruction
00457C77 648F0500000000   pop dword ptr fs:[$00000000]
00457C7E 83C40C           add esp,$0c
00457C81 8BC6             mov eax,esi
00457C83 5E               pop esi
00457C84 5B               pop ebx
00457C85 C3               ret

と先頭でDLレジスタが非0ならばClassCreateを呼び出し、BLレジスタにDLレジスタの値を保存してからDLレジスタを0に変更して以下の処理を行い、最後にBLレジスタ(=呼び出されたときのDLレジスタ)が非0であればAfterConstructionを呼び出すようになっています。

次にデストラクタを見てみます。といってもサンプルではTObject.Freeで呼び出しが隠蔽されてしまっていますので、ここはDestroyを直接呼び出すようにしてみます。
  FSingleton.Destroy;


Unit2.pas.63: FSingleton.Destroy;
00457CD2 B201             mov dl,$01
00457CD4 A1CC0C4600       mov eax,[$00460ccc]
00457CD9 8B08             mov ecx,[eax]
00457CDB FF51FC           call dword ptr [ecx-$04]

となり、やはりDLレジスタに0x01を格納してからデストラクタを呼び出しています(呼出先が[ecx-$04]というのはVMT経由でデストラクタのアドレスを取得しているためです)。デストラクタ本体の
destructor TSingleton.Destroy;
begin

  { Finalize }

  inherited;

end;


Unit2.pas.48: begin
00457C88 53               push ebx
00457C89 56               push esi
00457C8A E825C1FAFF       call @BeforeDestruction
00457C8F 8BDA             mov ebx,edx
00457C91 8BF0             mov esi,eax
Unit2.pas.52: inherited;
00457C93 8BD3             mov edx,ebx
00457C95 80E2FC           and dl,$fc
00457C98 8BC6             mov eax,esi
00457C9A E851BDFAFF       call TObject.Destroy
Unit2.pas.54: end;
00457C9F 84DB             test bl,bl
00457CA1 7E07             jle $00457caa
00457CA3 8BC6             mov eax,esi
00457CA5 E8B2C0FAFF       call @ClassDestroy
00457CAA 5E               pop esi
00457CAB 5B               pop ebx
00457CAC C3               ret

となり、まずBeforeDestructionが呼び出され、BLレジスタにDLレジスタの値を保存してから0xFCとANDをとって(DLレジスタは0x01 AND 0xFC = 0x00となっている)継承元デストラクタを呼び出し、最後にBLレジスタ(呼び出されたときのDLレジスタ)が非0であればClassDestroyを呼び出すようになっています。またデストラクタ内でExitしても

Unit2.pas.48: begin
00457C88 53               push ebx
00457C89 56               push esi
00457C8A E825C1FAFF       call @BeforeDestruction
00457C8F 8BDA             mov ebx,edx
00457C91 8BF0             mov esi,eax
Unit2.pas.50: Exit;
00457C93 EB0C             jmp $00457ca1
Unit2.pas.54: inherited;
00457C95 8BD3             mov edx,ebx
00457C97 80E2FC           and dl,$fc
00457C9A 8BC6             mov eax,esi
00457C9C E84FBDFAFF       call TObject.Destroy
Unit2.pas.56: end;
00457CA1 84DB             test bl,bl
00457CA3 7E07             jle $00457cac
00457CA5 8BC6             mov eax,esi
00457CA7 E8B0C0FAFF       call @ClassDestroy
00457CAC 5E               pop esi
00457CAD 5B               pop ebx
00457CAE C3               ret

とBLレジスタのチェックにジャンプするようになっており、DLが非0の呼び出しでは途中でExitしても必ずClassDestroyが呼び出されます。

このような事情から、コンストラクタの呼び出しによるクラスインスタンスの生成をキャンセルするためにはコンストラクタ内で例外を送出し、最外側コンストラクタ先頭で確保したインスタンス領域をデストラクタで解放させるしかありません(コンストラクタをクラスメソッドで呼び出すと必ず領域確保が行われるため)。同様にデストラクタの呼び出しによるインスタンスの破棄をキャンセルするには最外側のデストラクタの末尾までのどこかの時点で必ず例外を送出してインスタンス領域の解放を防ぐ必要があります。

元ねたはDelphiクイックリファレンス (amazon)/Ray Lischner著/光田秀、竹田知生訳/オライリー・ジャパン/ISBN4-87311-040-8/4,725円。

2011年5月2日

MS11-025によって発生する問題とその対策

2011/04のセキュリティアップデートのうち、MS11-025には、
  1. Windows 2000 SP4にKB2467175 (Visual C++ 2005 SP1 再頒布可能パッケージ セキュリティ更新プログラム (2011 年 4 月 12 日))およびKB2467174 (Visual C++ 2008 SP1 再頒布可能パッケージ セキュリティ更新プログラム (2011 年 4 月 12 日))を適用するとMFC 8.0/9.0を動的にリンクしているプログラムが起動時にエラーになる。
  2. VC2008でMFCを静的リンクしたプログラムのサイズが(VC2010と同様に)不必要に大きくなる。
  3. MFC 8.0/9.0を動的にリンクしているプログラムがMFCの言語DLLを使用しない(DLLを発見できなくなる)。
  4. Windows XP以降にしか存在しないFindActCtxSectionStringAに静的にリンクしてしまうためリビルドしたプログラムがWindows 2000で動作しなくなる。
といった問題が存在しているようです。

このうちWindows 2000とVC2005/VC2008再頒布可能パッケージ(Redist)の問題についてはKB2467175とKB2467174がWindows 2000の適用範囲外となったため、インストールしないのが正しく、もしインストールしてしまった場合は

New redists break all dynamically linked MFC 2005/2008 apps on Windows 2000 « Ted's Blog

にあるように、
  1. KB2467175とKB2467174をアンインストール
  2. Windows(WINNT)フォルダ内のWinSxSフォルダから4053と4148を検索して(フォルダが存在しなければ代わりにそれぞれ762と21022で検索)、検索結果に含まれるフォルダ内のファイル(ATLxx.DLL, MFCxx*.DLL, MFCMxx*.DLL, MSVCMxx*.DLL, MSVCPxx*.DLL, MSVCRxx*.DLL, VCOMPxx.DLL)全てを一時フォルダ("C:\MFCDLL"など)にコピー
  3. Windowsを再起動
  4. [F8]キーを押してWindows 2000拡張オプションメニューを表示し、セーフモードとコマンドプロンプトでコマンドプロンプトを選択して起動
  5. 一時フォルダ内のファイルをSystem32に
    copy C:\MFCDLL\*.* C:\Windows\System32
    (一時フォルダが"C:\MFCDLL"でWindowsのインストールフォルダが"C:\Windows"の場合)
    としてコピー
  6. EXITで再起動
という手順で回復することができます。

その他の(Visual Studio上での開発に関する)問題とその解決方法については

Problem with FindActCtxSectionString in MFC security updates on all platforms « Ted's Blog
Martin's Blog » BUG: Black Patchday for all OS from XP and later 3. – MFC 8.0 (VC-2005) or MFC 9.0 (VC-2008) linked dynamically to the MFC may not find the MFC language DLLs after installation of the security packs dated April 12th 2011
Static MFC code bloat problem from VC2010 is now in VC2008 SP1+security fix « Ted's Blog
Fixing problems with FindActCtxSectionString in MFC security updates « Ted's Blog

が参考になります。

2011/05/05追記: MicrosoftのVisual C++ Team Blogにもこの件に関する記事があります(考えてみれば当たり前か)。

MS11-025 Visual C++ Update Issue - Visual C++ Team Blog - Site Home - MSDN Blogs

これによると、Windows 2000に関する問題についてはこの時点(2011/04/26)ではMS11-025をアンインストールするしかないようですが、
Our team has identified the cause of these issues and is currently testing the fix. The update will be publicly available once testing is complete, and we will update this blog.

いい加減な訳: 我々VC++チームではこれらの問題の原因を特定し、修正したものを現在テストしているところです。テストが完了したらアップデートは一般に利用可能となり、このブログを更新します。

ということで、近日中に何らかの解決が図られるようです。

2011/06/15追記: MS11-025が更新され、以下の問題が解決したとされています。
  • ローカライズされたアプリケーションをご使用のお客様に MFC アプリケーションのリソースの一部がローカライズされずに表示されます。アプリケーションは正常に機能しますが、リソースが英語で表示されます。
  • MFC のセキュリティの修正は Microsoft Windows 2000 でサポートされていない API を使用します。Microsoft Windows 2000 システムに更新プログラムを適用した場合、インストールによっていくつかのアプリケーションの機能的な問題が引き起こされました。
  • 以前の更新プログラムは x64-based アーキテクチャの Windows 7 SDK を使用しているユーザーにはインストールされませんでした。


2011/06/21追記: Visual C++ Team Blogにも新しい更新プログラムで上記の問題を修正した、という記事が出ています。

Update on Bulletin MS11-025 - Visual C++ Team Blog - Site Home - MSDN Blogs