バージョン情報の問題

フォーラムの話ですが、現状のDelphiXE2で作成される実行ファイルのバージョンリソース等が英語リソースになってしまうという話です。

とりあえずの回避策として、自前のリソーススクリプトをプロジェクトに追加すれば日本語リソースを追加することが出来ます。
バージョン情報の自動更新がされなくなりますが気になる方は試してみてください。

やり方としてはプロジェクトに「テキストファイル」を追加して拡張子をrcにします。
ファイルの中身は以下のような感じです。それからファイルフォーマットを「Little Endian UCS-2にして保存します。
プロジェクトオプションの「バージョン情報を含める」のチェックが外せれば良いんですが、外しても次に開いた時にはチェックが入ってるのでこれは外せないのでしょう。
この状態でコンパイルすると、バージョンリソースが英語・日本語の2つ入った状態になりますが、日本語環境なら日本語のリソースが読まれるはずなので問題ないでしょう。

/* ----- VS_VERSION.dwFileFlags ----- */
#define VS_FF_DEBUG             0x00000001L
#define VS_FF_PRERELEASE        0x00000002L
#define VS_FF_PATCHED           0x00000004L
#define VS_FF_PRIVATEBUILD      0x00000008L
#define VS_FF_INFOINFERRED      0x00000010L
#define VS_FF_SPECIALBUILD      0x00000020L

/* ----- VS_VERSION.dwFileOS ----- */
#define VOS_UNKNOWN             0x00000000L
#define VOS_DOS                 0x00010000L
#define VOS_OS216               0x00020000L
#define VOS_OS232               0x00030000L
#define VOS_NT                  0x00040000L
#define VOS_WINCE               0x00050000L

#define VOS__BASE               0x00000000L
#define VOS__WINDOWS16          0x00000001L
#define VOS__PM16               0x00000002L
#define VOS__PM32               0x00000003L
#define VOS__WINDOWS32          0x00000004L

#define VOS_DOS_WINDOWS16       0x00010001L
#define VOS_DOS_WINDOWS32       0x00010004L
#define VOS_OS216_PM16          0x00020002L
#define VOS_OS232_PM32          0x00030003L
#define VOS_NT_WINDOWS32        0x00040004L

/* ----- VS_VERSION.dwFileType ----- */
#define VFT_UNKNOWN             0x00000000L
#define VFT_APP                 0x00000001L
#define VFT_DLL                 0x00000002L
#define VFT_DRV                 0x00000003L
#define VFT_FONT                0x00000004L
#define VFT_VXD                 0x00000005L
#define VFT_STATIC_LIB          0x00000007L

#define LANG_JAPANESE           0x11
#define SUBLANG_DEFAULT         0x01    // user default

LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT

1 VERSIONINFO LOADONCALL MOVEABLE DISCARDABLE IMPURE
FILEVERSION 1, 0, 0, 0
PRODUCTVERSION 1, 0, 0, 0
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
{
 BLOCK "StringFileInfo"
 {
  BLOCK "041104B0"
  {
   VALUE "CompanyName",      "会社名\0"
   VALUE "FileVersion",  "1.0.0.0\0"
   VALUE "ProductVersion",  "1.0.0.b\0"
   VALUE "ProductName",      "製品名\0"
   VALUE "FileDescription",  "説明\0"
   VALUE "InternalName",  "内部名"
   VALUE "LegalCopyright",  "コピーライト\0"
   VALUE "LegalTrademarks",  "トレードマーク\0"
   VALUE "OriginalFilename",  "正式ファイル名\0"
   VALUE "Comments",  "コメント\0"
  }
 }
 BLOCK "VarFileInfo"
 {
  VALUE "Translation", 1041, 1200
 }
}

64bitプログラムからのmdbへのアクセス

64bitプロセスからのmdbへのアクセスが可能なのか気になってたので調べてみました。
まずは、64bit版Office又はMicrosoft Access データベース エンジン 2010 再頒布可能コンポーネントの64bit版をインストールします。後は、OLEDBプロバイダでMicrosoft.ACE.OLEDB.12.0(IDEから指定する場合はMicrosoft Office 12.0 Access Database Engine〜)を指定したら64bitプロセスからもmdbへのアクセスが可能でした。

ただし、Microsoft Access データベース エンジン 2010 再頒布可能コンポーネントが32bitと64bitが共存できないようでどちらか一方しかインストール出来ませんでした。
Office32bit版が入っている環境には、データベースエンジンの64bit版のインストールは出来ません。
なので、IDEからは同プロバイダでは接続出来ません。IDEからの接続では今まで通りJET OLEDBプロバイダを使用するようにして実行時にConnectionStringを指定するようにするとかしないとダメですね。

Microsoft.ACE.OLEDB.12.0を使うように指定したソースから作成されたバイナリは、64bit版のバイナリなら64bit版ドライバ。32bit版のバイナリなら32bit版ドライバがインストールされていればそのまま動作します。32bitと64bitでソースを変更する必要はありませんでした。

System.Zip を使ってみる

Rad Studio XE2が出ましたね。さっそくいろいろ触ってみたのですが、新たに追加されたZipサポートのSystem.Zipユニットヘルプが全くありません。
特にヘルプ無くても困らなそうですが、軽く触ってみたのでメモ。

使うにはusesにSystem.Zipを追加します。使用出来る圧縮方法は標準だと0(store)と8(deflate)のみ。その他の圧縮方法のサポートについては、クラスメソッドTZipFile.RegisterCompressionHandlerを使用して登録する必要があるようです。

使い方は、指定のファイルまたはフォルダをまとめて圧縮する場合は

  TZipFile.ZipDirectoryContents(圧縮ファイル名, 圧縮するパス, 圧縮方法(zcDeflate));

これだけです。実際のファイルパスと圧縮ファイル内のパスを変えるとかファイルを1個ずつ指定する場合は

var
  Zip: TZipFile;
begin
  Zip := TZipFile.Create;
  // 新規作成又は既存のファイルがあっても上書きする場合はOpenModeにzmWriteを指定
  // 既存のファイルにファイルを追加する場合はOpenModeにzmReadWriteを指定する。
  Zip.Open('TEST.ZIP', zmWrite);

  //  Zip.Add(圧縮するファイルパス, 圧縮ファイル内のパス, 圧縮方法) を指定
  //  Zip.Addには他に圧縮するデータをTStreamやTBytesで渡すメソッドも定義されています。
  Zip.Add('Unit1.dcu', 'TEST\Unit1.dcu');
  Zip.Add('Unit1.dcu', 'TEST2\Unit1.dcu');
  Zip.Close;

  FreeAndNil(Zip);
end;

展開する場合は、圧縮ファイル内のファイルを全部展開する場合

  TZipFile.ExtractZipFile(圧縮ファイル名, 展開するパス);

で展開できます。圧縮ファイル内のファイルを選んで展開する場合は以下のような感じで展開できます。

var
  Zip: TZipFile;
begin
  Zip := TZipFile.Create;
  Zip.Open('TEST.ZIP', zmRead);

  // Extractメソッドでファイルを指定して展開できます。
  // Extract(インデックス 又は 圧縮ファイル内でのファイル名, 展開先のパス, フォルダを作成するかどうか)
  Zip.Extract(0);
  Zip.Close;

  FreeAndNil(Zip);
end;

cxGrid でマウス下のセルを取得する

TcxCustomGridViewまたは、TcxCustomGridViewInfoのGetHitTestメソッドを使用します。
例として、TableViewのOnMouseMoveでマウス下のセルの情報を取得してみます。

procedure TForm1.cxGrid1TableView1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  AHitTest: TcxCustomGridHitTest;
  AGRCHitTest: TcxGridRecordCellHitTest;
begin
  AHitTest := TcxGridSite(Sender).ViewInfo.GetHitTest(X, Y);

  if AHitTest.HitTestCode = htCell then
  begin
    AGRCHitTest := AHitTest As TcxGridRecordCellHitTest;
    Memo1.Lines.Add(AGRCHitTest.ViewInfo.Text);
  end;
end;

TcxGridSite(Sender).ViewInfo.GetHitTest(X, Y)で、マウス下の要素の情報を取得し。AHitTest.HitTestCodeで要素が何であるかを調べます。
サンプルではセルの時のみ処理をするのでhtCellと比較しています。
要素が何であるか判ったら、AHitTestをその要素毎のHitTestClassにキャストします。サンプルではセルなのでTcxGridRecordCellHitTestにキャストしています。
TcxGridRecordCellHitTestは、Itemプロパティが列の情報、GridRecordプロパティが行というかレコードの情報にアクセスできます。
サンプルではViewInfoプロパティから表示テキストを取得しています。

ヘルプのキーワード "Understanding HitTests" および、この項目からリンクされているTable View/Banded Table View/Card View/Chart Viewにより詳細な情報(各View毎に取りうるHitCodeや、取得できるHitTestClass)が載っています。

Virtual Listview とWindows7/Vista

結構前のDelphiMLでの話です。
どうも、この辺りを見るとVista以降でVirutal Listviewを使用するとLVM_GETITEMPOSITIONで返ってくる値が正しくない値になってしまうようです。
なので、TListItemのTopとかLeftの値が使えないんですね。LVM_GETITEMRECTで取得できる値は正しいようなのでこちらを使用するようにするしかないですね。
CommCtrlをusesに追加してListView_GetItemRectを利用して取得しましょう。

var
  rctop: TRECT;
begin
  ListView_GetItemRect(ListView1.Handle, ListView1.TopItem.Index, rcTop, LVIR_BOUNDS);
end;

ブロック参照のダブルクリックカスタマイズ

AutoCAD2006でObjectARXでprotocol extensionsを使ってブロック参照をダブルクリックした時に、属性の値で追加したコマンドを呼び分けるという処理をしてました。

この機能をAutoCAD2012に移植したのでメモ。
コード自体には特に変更の必要はなかったのですが、必要なlibファイルがAcDblClkEditPE.libだったかな?からacad.libに変更になってました。
また、ARXの初期化時にAcDbDoubleClickEdit::rxInit();をみたいな感じでrxInit()を呼び出す必要があったのが必要なくなってました。

最後に重要なのが、AutoCADのカスタマイズ機能で要素がダブルクリックされた時に実行するコマンドを指定出来るようになってるんですが、これのブロック参照の設定を削除する必要がありました。

以上で、AutoCAD2006の時と同様にブロック参照がダブルクリックされた時に自前の処理が出来るようになりました。

TStreamReader とShiftJISデータ

結構なサイズのテキストデータ(ShiftJIS)の分割をする必要が出たので何か良い方法は無いかとヘルプを見ててTStreamReaderを見つけました。
早速TStreamReaderを使って分割をするプログラムを作ったんですが、出来上がったファイルを確認すると途中にNULL値があるんでスペースに置き変えますって言われてデータが所々化けてしまってます。
確認してみると、TStreamReaderのFillBufferの中でShiftJISのLeadByteがバッファの最後に読み込まれるとそこでデータが化けます。
QC登録した時に判ったんですが、WindowsXPだと問題が発生しますが、Windows7だと問題ありません。

さらに調べてみてわかったんですが、エンコーディングの返還にMultiByteToWideCharWideCharToMultiByteが使われてるんですがMultiByteToWideCharに対してLeadByteのみで終わるようなデータ、例えば#$82#$A0#$82とかってデータを渡すとWindowsXPだと#$3042#$0000と半端なデータがNULLになるみたいなんですが、Windows7で同じ処理を実行すると#$3042#$30FBと変換されます。

FillBufferの中では、読み込んだバイト数と読み込んだ文字列をUNICODEに変換した後、もう一度変換前のエンコーディングに再変換した場合のバイト数を比較して一致しなければデータを分割してしまってると判断するようになっているようです。MultiByteToWideCharの結果が違うからなのでしょう、このバイト数のチェックがXPだとデータを分割した状態でも一致してしまいチェックをすり抜けてしまいます。データが化けるのはこの辺りが原因ぽいです。

TStreamReaderが悪いと言うよりはWindowsAPIの問題のような気もしますがデータが化けてしまうのは事実で困ってしまうのでとりあえずの範囲で化けないように修正してみました。
TStreamReaderを丸々コピーしてクラス名を適当に変えてFillBufferを修正します。

procedure TStreamReader.FillBuffer(var Encoding: TEncoding);
var
    :
  RawString: RawByteString;
begin
    :
  ByteBufLen := BytesRead - StartIndex;
  if Not TEncoding.IsStandardEncoding(FEncoding) And Not FEncoding.IsSingleByte then
  begin
    // バッファの最後がLeadByteだった場合に追加で1バイト読み込むようにします。
    RawString := PAnsiChar(@LBuffer[0]);
    if StrByteType(PAnsiChar(RawString), ByteBufLen-1) = mbLeadByte then
    begin
      if (StartIndex + ByteBufLen) = Length(LBuffer) then
        SetLength(LBuffer, Length(LBuffer) + BufferPadding);
      BytesRead := FStream.Read(LBuffer[StartIndex + ByteBufLen], 1);
      if BytesRead > 0 then
      begin
        Inc(ByteBufLen, BytesRead);
      end;
    end;
  end;
  LString := FEncoding.GetString(LBuffer, StartIndex, ByteBufLen);
  ByteCount := FEncoding.GetByteCount(LString);
    :
end;

登録したQCは以下です。

Report No: 96375 Status: Open
TStreamReader corrupt Shift-JIS data
http://qc.embarcadero.com/wc/qcmain.aspx?d=96375
QCWIN:Defect_No=96375