2010年8月10日

RAD Studio XE スニークプレビュー その1

FulcrumことRAD Studio XE(!)のsneak previewが始まったようです。

まずはDelphiソリューションプロダクトマネージャのMichael Rozlogさんのところ。
エンバカデロ・テクノロジーズの公式ページ。
Andreano Lanusseさんのところ。
製品マーケティングマネージャのTim Del Chiaroさんのところ。
EDNのリリース。
チーフエバンジェリストのDavid Iさんのところ。
発売はESD版が2010/09/02、パッケージ版は2010/09/中旬の予定の模様です。

XEというネーミングはよく見りゃ5月の時点でNick HodgesさんがRandom Thoughts on the Passing Scene #158の中でRAD Studio will eventually become an “XE Product”って書いていましたね。見落としてました。

これらの記事のポイントを。
  • RAD Studio XEにはDelphi XE、C++Builder XE、Delphi Prism XE、RadPHP XE(Delphi for PHP)が含まれる。
  • Delphi/C++Builder/Delphi PrismではIDEにSubversionサポートが追加される。またその他のSCM(Source Code Management)の統合が可能になるようにOTA(Open Tools API)が用意されている。
  • Beyond Compareなどのサードパーティ製のツールがバンドルされ、IDEか呼び出すことができる。
  • コードエディタの検索、フォーマッタ、ナビゲーションがアップデートされ、フォーマッタのプロファイルを保存して共有することができるようになる。
  • 最新のデータベースへの対応とDatasnapの機能拡張。
  • 生産性を向上させるいろいろな"ちょっとした"改善も含まれる。
  • VCL/RTL、Boost、標準C++ライブラリの改善、正規表現ライブラリ(Regex)の追加、ジェネリクスの更新が含まれる。
  • Delphi/C++Builder/RadPHPのデバッガには新しい機能が追加される。
  • Delphi XEのモデリング機能が強化され、コードからのクラス図、シーケンス図の生成ができる。またモデルからより高品質のコードが生成できるようになる。
  • Delphi for PHPはRadPHPとなってRAD Studioに含まれるようになる。またRadPHPのIDEはDelphi/C++Builderと同様のものになる(同一かどうかは?)。facebookサポートが追加され、Facebookアプリケーションを簡単に作成できる(個人的にはfacebookの何がいいのかさっぱりわかりませんけど)。またjQueryQooxdooを利用したコンポーネントも追加されている。さらにDatasnapコンポーネントでDatasnapクライアントを作成することもできる。
  • Delphi 2010で追加された(Delphi 7のような)クラシックデザイナも継続して使用できる。
  • XEというブランドはheterogeneous(=X)、embarcadero(=E)から連想したもので、2011はXE、その後はXE2、XE3とする予定。

2010/08/11追記: ComponentSourceさんSEShop.comさんで価格も表示されています。それより気になるのは

また、XEシリーズでは、主要な旧バージョンの製品が追加コストなしに利用が可能となります。
XEにて利用可能な旧バージョン製品:

  • Delphi XE・・・Delphi 2010、2009、2007、7
  • C++Builder XE・・・C++Builder 2010、2009、2007、6
  • Delphi Prism XE・・・Delphi Prism 2011、2010、2009
  • RAD Studio XE・・・上記全ての製品


というところです。これで『どのバージョンを買えばいいのでしょう?』という質問にも『XE買っとけ』でいいことになります。別に古いプロダクトを同梱したからってコストが上がるわけでもないでしょうし。

2010/08/17追記: 更新完了。

データを暗号化して保存する(2)

前回CryptProtectDataCryptUnprotectDataでバイナリデータをTStream経由で暗号化、復号化しましたが、通常は単純に文字列を扱いたいことのほうが多いと思います。そこで簡略版として文字列のみで処理を構成してみました。
uses
  Windows, Classes, SysUtils;

function BlobToString(P: PByte; Size: DWORD): String;
begin

  Result := '';

  while Size > 0 do
  begin
    Result := Result + IntToHex(P^,2);
    Inc(P,1);
    Size := Size - 1;
  end;

end;

procedure ProtectString(const Source: String; var Dest: String;
                        const Description: String; const Entropy: String;
                        LocalMachine: Boolean);
var
  DataIn: DATA_BLOB;
  OptionalEntropy: DATA_BLOB;
  POptionalEntropy: PDATA_BLOB;
  DataOut: DATA_BLOB;
{$IFDEF Unicode}
  WDescription: String;
{$ELSE}
  WDescription: WideString;
{$ENDIF}
  Flags: DWORD;
  MemorySource: TMemoryStream;
  MemoryEntropy: TMemoryStream;
begin

  MemorySource := nil;
  MemoryEntropy := nil;
  try
    MemorySource := TMemoryStream.Create;
    MemoryEntropy := TMemoryStream.Create;

    { DataIn }
    with MemorySource do
    begin
      Write(PChar(Source)^,Length(Source) * SizeOf(Char));
      DataIn.cbData := Size;
      DataIn.pbData := Memory;
    end;

    { OptionalEntropy }
    if Entropy = '' then
    begin
      POptionalEntropy := nil;
    end
    else
    begin
      with MemoryEntropy do
      begin
        Write(PChar(Entropy)^,Length(Entropy) * SizeOf(Char));
        OptionalEntropy.cbData := Size;
        OptionalEntropy.pbData := Memory;
      end;
      POptionalEntropy := @OptionalEntropy;
    end;

    { Description }
    WDescription := Description;

    { Flags }
    Flags := 0;
    if LocalMachine = True then
    begin
      Flags := CRYPTPROTECT_LOCAL_MACHINE;
    end;

    { DataOut }
    FillChar(DataOut,SizeOf(DataOut),0);

    { Protect data }
    if CryptProtectData(@DataIn,PWideChar(WDescription),POptionalEntropy,nil,
                        nil,Flags,@DataOut) = False then
    begin
      RaiseLastOSError;
    end;

    { Result }
    Dest := BlobToString(DataOut.pbData,DataOut.cbData);

    { Free allocated memory }
    LocalFree(HLOCAL(DataOut.pbData));

  finally
    MemorySource.Free;
    MemoryEntropy.Free;
  end;

end;
Source、Description、Entropyに文字列を渡すとDestに暗号化されたデータが16進文字列化されて格納されます。
復号化も同様に
function StringToBlob(const S: String; Stream: TStream): Boolean;
var
  Index: Integer;
  Data: Byte;
  L: Integer;
begin

  Index := 1;
  L := Length(S);

  while Index <= L do
  begin
    try
      Data := StrToInt('$' + Copy(S,Index,2));
    except
      Result := False;
      Exit;
    end;

    Stream.Write(Data,SizeOf(Data));

    Index := Index + 2;
  end;

  Result := True;

end;

function  UnprotectString(const Source: String; var Dest: String;
                          var Description: String; const Entropy: String): Boolean;
var
  DataIn: DATA_BLOB;
  OptionalEntropy: DATA_BLOB;
  POptionalEntropy: PDATA_BLOB;
  DataOut: DATA_BLOB;
  PDescription: PWideChar;
  MemorySource: TMemoryStream;
  MemoryEntropy: TMemoryStream;
begin

  MemorySource := nil;
  MemoryEntropy := nil;
  try
    MemorySource := TMemoryStream.Create;
    MemoryEntropy := TMemoryStream.Create;

    { DataIn }
    if StringToBlob(Source,MemorySource) = False then
    begin
      Result := False;
      Exit;
    end;
    with MemorySource do
    begin
      DataIn.cbData := Size;
      DataIn.pbData := Memory;
    end;

    { OptionalEntropy }
    if Entropy = '' then
    begin
      POptionalEntropy := nil;
    end
    else
    begin
      with MemoryEntropy do
      begin
        Write(PChar(Entropy)^,Length(Entropy) * SizeOf(Char));
        OptionalEntropy.cbData := Size;
        OptionalEntropy.pbData := Memory;
      end;
      POptionalEntropy := @OptionalEntropy;
    end;

    { DataOut }
    FillChar(DataOut,SizeOf(DataOut),0);

    { Unprotect data }
    if CryptUnprotectData(@DataIn,PDescription,POptionalEntropy,nil,
                          nil,0,@DataOut) = False then
    begin
      Result := False;
      Exit;
    end;

    { Result }
    SetString(Dest,PChar(DataOut.pbData),DataOut.cbData div SizeOf(Char));

    { Description }
    Description := PDescription;

    { Free allocated memory }
    LocalFree(HLOCAL(DataOut.pbData));
    LocalFree(HLOCAL(PDescription));

    Result := True;

  finally
    MemorySource.Free;
    MemoryEntropy.Free;
  end;

end;
Source、Entropyに文字列を渡すとDest、Descriptionに復号化された文字列が格納されます。

蛇足ですが、自システムのパスワードはSHAなど(MD5はいまさらな気も)何らかの形でハッシュして保存しておき、入力も同様の方法でハッシュして結果の一致を確認する、という手法が一般的です。

2010年8月7日

[書籍]プログラミングの魔導書 ~Programmers' Grimoire~ vol.1

プログラミングの魔導書 ~Programmers' Grimoire~が発売になりました(PDF版がダウンロード可能になったという意味で)。

プログラミングの魔導書 ~Programmers’ Grimoire~ vol.1/ロングゲート/ISBN 978-4-9905296-0-4(書籍版)、ISBN 978-4-9905296-1-1(PDF版)/1,500円(書籍版)、1,000円(PDF版)

書籍版は完全予約制のため、発売時点以降ではPDF版のみが購入可能です。また書籍版については予約期間終了後に印刷を発注するため、書籍の発送は、8/20以降となる予定です。とのことです。

予告では次号は"Evolution of Programming Languages"と題してHaskell 2.0、D 3.0、Java 7、Scala 2.8、C++0xを扱うようです。またインタビューはDave Abrahamsさん(C++標準化委員会のメンバでBoostの偉い人)だそうです。

2010/08/25追記: 書籍版が08/23に届きました。やはりPDFとは印象が違いますね。できれば光沢のない紙のほうがいいのですが。

2011/10/05追記: リンクをVol.1のサポートページのものに置き換えました。

2010年8月6日

データを暗号化して保存する(1)

他システムやデータベース、FTPサーバなどのパスワードをファイルやレジストリに保存する必要がある場合、何らかの形で暗号化して保存し、使用するときに復号化する、ということが必要になります。このようなときにCryptProtectDataCryptUnprotectDataを使用する方法が考えられます。もちろんこれで万全かといわれると難しいところですが、少なくとも生のまま保存するよりはまし、といったところでしょうか。
CryptProtectDataで暗号化するときに第6パラメータ(dwFlags)にCRYPTPROTECT_LOCAL_MACHINEを含めると同一PC上の全てのユーザで復号化できてしまいますが、含めなければ同一PC上の同一のユーザでのみ復号化できます。また第3パラメータ(pOptionalEntropy)でエントロピを指定して暗号化しておけばCryptUnprotectDataで同じエントロピを指定しないと復号化できないようにすることができます。なおWindows 2000では第2パラメータのszDataDescrを省略する(NULLにする)ことができないので注意が必要です。
まず使用するCryptAPIと構造体を定義します。
uses
  Windows;

type
  _CRYPTOAPI_BLOB = record
    cbData: DWORD;
    pbData: PBYTE;
  end;
  {$EXTERNALSYM _CRYPTOAPI_BLOB}

  DATA_BLOB = _CRYPTOAPI_BLOB;
  {$EXTERNALSYM DATA_BLOB}
  PDATA_BLOB = ^DATA_BLOB;
  {$EXTERNALSYM PDATA_BLOB}

  PCRYPTPROTECT_PROMPTSTRUCT = ^CRYPTPROTECT_PROMPTSTRUCT;
  {$EXTERNALSYM PCRYPTPROTECT_PROMPTSTRUCT}
  _CRYPTPROTECT_PROMPTSTRUCT = record
    cbSize: DWORD;
    dwPromptFlags: DWORD;
    hwndApp: HWND;
    szPrompt: LPCWSTR;
  end;
  {$EXTERNALSYM _CRYPTPROTECT_PROMPTSTRUCT}
  CRYPTPROTECT_PROMPTSTRUCT = _CRYPTPROTECT_PROMPTSTRUCT;
  {$EXTERNALSYM CRYPTPROTECT_PROMPTSTRUCT}

const
  CRYPTPROTECT_LOCAL_MACHINE = $4;
  {$EXTERNALSYM CRYPTPROTECT_LOCAL_MACHINE}

function CryptProtectData(pDataIn: PDATA_BLOB; szDataDescr: PWideChar;
                          pOptionalEntropy: PDATA_BLOB; pvReserved: Pointer;
                          pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT;
                          dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall;
  external 'crypt32.dll' name 'CryptProtectData';
{$EXTERNALSYM CryptProtectData}

function CryptUnprotectData(pDataIn: PDATA_BLOB; var ppszDataDescr: PWideChar;
                            pOptionalEntropy: PDATA_BLOB; pvReserved: Pointer;
                            pPromptStruct: PCRYPTPROTECT_PROMPTSTRUCT;
                            dwFlags: DWORD; pDataOut: PDATA_BLOB): BOOL; stdcall;
  external 'crypt32.dll' name 'CryptUnprotectData';
{$EXTERNALSYM CryptUnprotectData}
CryptProtectData/CryptUnprotectDataで扱うデータは基本的にバイナリなので、まずはTStreamを暗号化してみます。
uses
  Windows, Classes, SysUtils;

procedure ProtectStream(Source: TStream; Dest: TStream;
{$IFDEF Unicode}
                        const Description: String;
{$ELSE}
                        const Description: WideString;
{$ENDIF}
                        Entropy: TStream; LocalMachine: Boolean);
var
  DataIn: DATA_BLOB;
  OptionalEntropy: DATA_BLOB;
  POptionalEntropy: PDATA_BLOB;
  DataOut: DATA_BLOB;
  Flags: DWORD;
  MemorySource: TMemoryStream;
  MemoryEntropy: TMemoryStream;
begin

  MemorySource := nil;
  MemoryEntropy := nil;
  try
    MemorySource := TMemoryStream.Create;
    MemoryEntropy := TMemoryStream.Create;

    { DataIn }
    with MemorySource do
    begin
      Source.Position := 0;
      LoadFromStream(Source);
      DataIn.cbData := Size;
      DataIn.pbData := Memory;
    end;

    { OptionalEntropy }
    if Entropy = nil then
    begin
      POptionalEntropy := nil;
    end
    else
    begin
      with MemoryEntropy do
      begin
        Entropy.Position := 0;
        LoadFromStream(Entropy);
        OptionalEntropy.cbData := Size;
        OptionalEntropy.pbData := Memory;
      end;
      POptionalEntropy := @OptionalEntropy;
    end;

    { Flags }
    Flags := 0;
    if LocalMachine = True then
    begin
      Flags := CRYPTPROTECT_LOCAL_MACHINE;
    end;

    { DataOut }
    FillChar(DataOut,SizeOf(DataOut),0);

    { Protect data }
    if CryptProtectData(@DataIn,PWideChar(Description),POptionalEntropy,nil,
                        nil,Flags,@DataOut) = False then
    begin
      RaiseLastOSError;
    end;

    { Result }
    Dest.Write(DataOut.pbData^,DataOut.cbData);

    { Free allocated memory }
    LocalFree(HLOCAL(DataOut.pbData));

  finally
    MemorySource.Free;
    MemoryEntropy.Free;
  end;

end;
対象データと(指定されていれば)エントロピをTMemoryStreamに格納してそのMemory/SizeプロパティをDATA_BLOB構造体(=CRYPT_INTEGER_BLOB構造体)のpbData/cbDataにセットし、CryptProtectDataを呼び出します。DataOutには暗号化されたデータが格納されていますので、これを取り出し、最後に忘れずにLocalFreeでDataOutの内容を解放します。
復号化も同様に
function UnprotectStream(Source: TStream; Dest: TStream;
{$IFDEF Unicode}
                         var Description: String;
{$ELSE}
                         var Description: WideString;
{$ENDIF}
                         Entropy: TStream): Boolean;
var
  DataIn: DATA_BLOB;
  OptionalEntropy: DATA_BLOB;
  POptionalEntropy: PDATA_BLOB;
  DataOut: DATA_BLOB;
  PDescription: PWideChar;
  MemorySource: TMemoryStream;
  MemoryEntropy: TMemoryStream;
begin

  MemorySource := nil;
  MemoryEntropy := nil;
  try
    MemorySource := TMemoryStream.Create;
    MemoryEntropy := TMemoryStream.Create;

    { DataIn }
    with MemorySource do
    begin
      Source.Position := 0;
      LoadFromStream(Source);
      DataIn.cbData := Size;
      DataIn.pbData := Memory;
    end;

    { OptionalEntropy }
    if Entropy = nil then
    begin
      POptionalEntropy := nil;
    end
    else
    begin
      with MemoryEntropy do
      begin
        Entropy.Position := 0;
        LoadFromStream(Entropy);
        OptionalEntropy.cbData := Size;
        OptionalEntropy.pbData := Memory;
      end;
      POptionalEntropy := @OptionalEntropy;
    end;

    { DataOut }
    FillChar(DataOut,SizeOf(DataOut),0);

    { Unprotect data }
    if CryptUnprotectData(@DataIn,PDescription,POptionalEntropy,nil,
                          nil,0,@DataOut) = False then
    begin
      Result := False;
      Exit;
    end;

    { Result }
    Dest.Write(DataOut.pbData^,DataOut.cbData);

    { Description }
    Description := PDescription;

    { Free allocated memory }
    LocalFree(HLOCAL(DataOut.pbData));
    LocalFree(HLOCAL(PDescription));

    Result := True;

  finally
    MemorySource.Free;
    MemoryEntropy.Free;
  end;

end;
対象データと(指定されていれば)エントロピをTMemoryStreamに格納してそのMemory/SizeプロパティをDATA_BLOB構造体のpbData/cbDataにセットし、CryptUnprotectDataを呼び出します。DataOutとPDescriptionには復号化されたデータと暗号化のときに指定したDescriptionが格納されていますので、これらを取り出し、最後に忘れずにLocalFreeでDataOutとPDescriptionの内容を解放します。もし復号化に失敗した場合は戻値がFalseになります。

長くなったので続きます。

元ねたはNyaRuRuさんローカルストレージに保存するデータの暗号化 ― Windows の場合 - NyaRuRuの日記EternalWindowsさんDPAPIによる暗号化

2010年8月5日

ユニット間の依存関係を解析する

それなりの規模のプロジェクトでユニット間の依存関係を調べたい、あるいは必要のないusesを削除したい、ということが時々あります。そのようなときに使えそうなフリーのツール3種類です。

Peganza - ICARUS
解析結果がレポート形式で表示されます。日本語を含むソースの場合は[Options]→[Project Properties...]→[Parser]の"Allow multi-byte characters"をチェックオンする必要があります。usesの必要のないユニットやinterface部ではなくimplementation部にusesすれば十分なユニットがマークされるので便利です。

ModelMaker Tools :: Unit Dependency Analyzer ; unit uses / used by relations and cyclic dependencies.
解析結果はツリー形式で表示されます。usesしているユニット、usesされているユニット、循環参照がわかります。またツリー部分で選択しているuses/used byのユニットを[Ctrl]+[Enter]でたどっていくこともできます。

またCnPack Open Source ProjectsのCnWizardsには"Uses Unit Cleaner Wizard"という不要なusesを自動的にクリーンアップする機能があるようです。

元ねたはStack OverflowDelphi - Reverse Lookup ' who includes this unit'How to "automatically" remove unused units from uses clause?

2010年8月3日

Microsoft OOB Update 2010/08

Microsoftの定例外のセキュリティアップデートがリリースされています。
MS10-046

Windows 2000 Service Pack 4は延長サポートフェーズが終了しており更新プログラムが提供されていないため、サードパーティによる対策ツールを使用するのが望ましいです…。

G Data Software AGさん「アイコン表示ウイルス」対策無償ツール
ソフォスさんWindows Shortcut Exploit Protection Tool

どちらもWindows 2000で動作します。

2010年7月30日

Firebird 2.5 Release Candidate 3リリース

Firebird 2.5 Release Candidate 3が部分的にリリースされました。Windows x64版は現在QA中で、まもなくリリースされる予定です。

Firebird File Repositories (download)
Firebird 2.5 Release Notes

2010/08/02追記: Windows x64版もQAが完了してリリースされています。現時点でMacOSX 32bit版のみ未リリースとなっています。

2010/08/03追記: MaxOSX 32bit版もリリースされています。

2010年7月29日

ヘルプインサイトの利用方法

Delphi 2007以降の新機能であるヘルプインサイトの利用方法に関する記事。興味深い。とりあえずメモ。

Help insight - Delphi Programming
In Praise of Help Insight Support
Delphi Help Insight Customization
Delphi Help Insight XSL

2010年7月28日

ウェブセミナー資料ダウンロード

過去のウェブセミナーのプレゼンテーション資料がダウンロードできるようになっています(要登録)。

エンバカデロ Webセミナー プレゼンテーション資料ダウンロード

2010/07/28時点で以下のプレゼンテーション資料がダウンロードできます。

Webセミナー「データベースパフォーマンスチューニングの概要と評価」(2010/06/02)
Webセミナー「システム品質を高めるデータベースツールからのアプローチ」(2010/05/19,2010/06/09,2010/07/14)
Webセミナー「異種データベース利用者必見!管理負荷を軽減するベストプラクティス」(2010/06/16,2010/07/28)
Webセミナー「新規プロジェクト開発に必須、データベースの情報共有インフラの活用」(2010/06/23,2010/07/21)
Webセミナー「チュートリアル:すぐに始められるデータベース設計とSQL文の作成」(2010/06/30)
Webセミナー「データベース設計ツールER/Studioの概要と効果的活用法」(2010/07/07)

開発系(RAD Studio)のものはどうなんでしょう…。

2010年7月27日

keyed events

以前クリティカルセクション仕様変更の影響について触れましたが、この件に関連して、CriticalSectionに代わる非公開機能の"keyed events"に関する興味深い記事を見つけました。

Windows keyed events, critical sections, and new Vista synchronization features(Joe Duffy's Weblog)

とってもいいかげんな要約: クリティカルセクションはWindows 2000以前の実装ではSMP上でのパフォーマンスのボトルネックになってしまっていた。これを改善するためにWindows 2000で仕様を変更したが、今度は信頼性を損なうことになってしまった。これを解決するためにWindows XPで新たに非公開の"keyed events"が作られ、Windows Vistaではその実装が改善された。ハンドルとノンページメモリを圧迫しない新しい同期機能としてユーザモードのコードからも利用可能になることを期待している。

もう少し詳しい話がWindows Internals, Fifth Edition (amazon)にあるそうですが、日本語訳はいつ出るんでしょうか…。

元ねたは江添さん本の虫: keyed-eventsNyaRuRuさんTwitter上の2010/07/24-25あたりの発言

2010年7月26日

InterBase SMP 2009 Hotfix Update 4リリース

InterBase SMP 2009 Hotfix Update 4がリリースされています。Hotfix Update 4を適用するには予めHotfix Update 3が適用されている必要があります。またHotfix Update 4は英語版のものを日本語版にも適用可能です。

27729 InterBase SMP 2009 Hotfix Update 4 (9.0.4.443) for Windows
InterBase 2009 Hotfix Update 4 (Windows版) リリースノート (version 9.0.4.443)

Delphiのエディタのキーバインドを作成する

OTA(Open tools API)を使用してDelphiのエディタで新しくキーバインドを作成する方法を説明した記事。興味深い。とりあえずメモ。

Cary Jensen "Let's Get Technical": Creating Editor Key Bindings in Delphi

2010年7月23日

Windowsのシステムキーコンビネーションを無効にする

全画面で動作するようなアプリケーションなどでは、[Win]、[Ctrl]+[ESC]、[ALT]+[TAB]、[ALT]+[ESC]、[Ctrl]+[Shift]+[ESC]、[ALT]+[F4]のようなWindowsのシステムキーコンビネーションを無効にしたいような状況があります。そのような場合はキーボードを低レベルフックします。フックの設定はSetWindowsHookEx(ja)で、解除はUnhookWindowsHookEx(ja)で行い、フック関数はLowLevelKeyboardProc(ja)に従って作成します。フック関数が呼び出されるときにlParamにはKBDLLHOOKSTRUCT構造体へのポインタが格納されており、この中のvkCodeで押されたキーの仮想キーコードを、flagsのLLKHF_ALTDOWNビット(bit5)で[ALT]キーが押されているかどうかを、それぞれ知ることができます。ここで無効化したいキーだった場合は戻値を1としてそのままリターンします。一方でそのままにしたいキーだった場合はCallNextHookEx(ja)で次のフックチェーンに処理を委ねる必要があります。また[ALT]以外のキー([Ctrl]や[Shift]など)とのコンビネーションを知りたい場合はGetAsyncKeyState(ja)で取得します(bit15が現在そのキーが押されているかどうかを示しています)。
まずキーボードの低レベルフックに必要な定義を行います。
uses
  Windows;

type
  LPKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT;
  {$EXTERNALSYM LPKBDLLHOOKSTRUCT}
  tagKBDLLHOOKSTRUCT = record
    vkCode: DWORD;
    scanCode: DWORD;
    flags: DWORD;
    time: DWORD;
    dwExtraInfo: Pointer;
  end;
  {$EXTERNALSYM tagKBDLLHOOKSTRUCT}
  KBDLLHOOKSTRUCT = tagKBDLLHOOKSTRUCT;
  {$EXTERNALSYM KBDLLHOOKSTRUCT}
  TKbDllHookStruct = KBDLLHOOKSTRUCT;
  PKbDllHookStruct = LPKBDLLHOOKSTRUCT;

const
  WH_KEYBOARD_LL = 13;
  {$EXTERNALSYM WH_KEYBOARD_LL}

  LLKHF_EXTENDED = KF_EXTENDED shr 8;
  {$EXTERNALSYM LLKHF_EXTENDED}
  LLKHF_INJECTED = $00000010;
  {$EXTERNALSYM LLKHF_INJECTED}
  LLKHF_ALTDOWN  = KF_ALTDOWN shr 8;
  {$EXTERNALSYM LLKHF_ALTDOWN}
  LLKHF_UP       = KF_UP shr 8;
  {$EXTERNALSYM LLKHF_UP}
次にフック関数を用意します。
type
  TSystemKeyCombination  = (skLWin,          // [WIN] (Left)             - Open Start menu
  skRWin,          // [WIN] (Right)            - Open Start menu
  skCtrlEsc,       // [CTRL] + [ESC]           - Open Start menu
  skAltTab,        // [ALT] + [TAB]            - Switch programs
  skAltEsc,        // [ALT] + [ESC]            - Next program
  skCtrlShiftEsc,  // [CTRL] + [SHIFT] + [ESC] - Open task manager
  skAltF4);        // [ALT] + [F4]             - Quit program
  TSystemKeyCombinations = set of TSystemKeyCombination;

var
  hh: HHOOK;
  InvalidCombinations: TSystemKeyCombinations;

function LowLevelKeyboardProc(nCode: Integer;
                              wP: WPARAM; lP: LParam): LRESULT; stdcall; export;
var
  pkbhs: PKbDllHookStruct;
  CtrlKey: Boolean;
  ShiftKey: Boolean;
begin

  pkbhs := PKbDllHookStruct(lP);

  case nCode of
    HC_ACTION:
    begin
      case pkbhs^.vkCode of
        VK_LWIN:
        begin
          if skLWin in InvalidCombinations then
          begin
            { LWIN }
            Result := 1;
            Exit;
          end;
        end;

        VK_RWIN:
        begin
          if skRWin in InvalidCombinations then
          begin
            { RWIN }
            Result := 1;
            Exit;
          end;
        end;

        VK_TAB:
        begin
          if ((pkbhs^.flags and LLKHF_ALTDOWN) <> 0) and
             (skAltTab in InvalidCombinations) then
          begin
            { ALT + TAB }
            Result := 1;
            Exit;
          end;
        end;

        VK_ESCAPE:
        begin
          CtrlKey  := ((GetAsyncKeyState(VK_CONTROL) and $8000) <> 0);
          ShiftKey := ((GetAsyncKeyState(VK_SHIFT)   and $8000) <> 0);

          if ((pkbhs^.flags and LLKHF_ALTDOWN) <> 0) and
             (skAltEsc in InvalidCombinations) then
          begin
            { ALT + ESC }
            Result := 1;
            Exit;
          end;

          if ShiftKey = False then
          begin
            if (CtrlKey = True) and (skCtrlEsc in InvalidCombinations) then
            begin
              { CTRL + ESC }
              Result := 1;
              Exit;
            end;
          end
          else
          begin
            if (CtrlKey = True) and (skCtrlShiftEsc in InvalidCombinations) then
            begin
              { CTRL + SHIFT + ESC }
              Result := 1;
              Exit;
            end;
          end;
        end;

        VK_F4:
        begin
          if ((pkbhs^.flags and LLKHF_ALTDOWN) <> 0) and
             (skAltF4 in InvalidCombinations) then
          begin
            { ALT + F4 }
            Result := 1;
            Exit;
          end;
        end;
      end;
    end;
  end;

  Result := CallNextHookEx(hh,nCode,wP,lP);

end;
ここでは集合型のInvalidCombinationsに含まれるキーコンビネーションであればResultを1にしてそのままリターン、そうでなければCallNextHookExでフックチェーンの呼び出しを行うようにしています。
最後にキーボードの低レベルフックを設定、解除する関数は
procedure HookKeyboardLL;
begin

  if hh <> 0 then
  begin
    Exit;
  end;

  hh := SetWindowsHookEx(WH_KEYBOARD_LL,LowLevelKeyboardProc,HInstance,0);

end;

procedure UnhookKeyboardLL;
begin

  if hh = 0 then
  begin
    Exit;
  end;

  UnhookWindowsHookEx(hh);
  hh := 0;

end;
と単にSetWindowsHookExおよびUnhookWindowsHookExを呼び出すだけです。
なお[Ctrl]+[ALT]+[Del]のキーストロークだけはキーボードの低レベルフックでも捕捉できません(GINAの置き換えが必要になります)。

2010年7月22日

サービスに関する情報を列挙する

インストールされているサービスを取得するにはまずOpenSCManager(ja)でサービスコントロールマネージャをオープンし、EnumServicesStatus(ja)でサービスの情報を取得し、最後にCloseServiceHandle(ja)でサービスコントロールマネージャをクローズします。EnumServicesStatusはサイズを0として呼び出すことで必要な領域の大きさを取得し、領域を確保後に改めてEnumServicesStatusを呼び出します。取得した情報はENUM_SERVICE_STATUS構造体の配列と、そのメンバであるSERVICE_STATUS構造体に格納されます。
ところがこれらの情報の中にはサービスを起動したときのコマンドラインなどが含まれていません。そこでEnumServicesStatusで取得したそれぞれのサービス名についてOpenService(ja)でサービスをオープンし、QueryServiceConfig(ja)でサービスの構成パラメータを取得し、最後にCloseServiceHandleでサービスをクローズします。QueryServiceConfigもまずサイズを0として呼び出して必要な領域のサイズを取得し、領域を確保後に改めてQueryServiceConfigを呼び出します。取得した情報はQUERY_SERVICE_CONFIG構造体に格納されます。
必要な定義は概ねWinSvcユニットに存在しますが、EnumServicesStatusで取得するTEnumServiceStatus(=ENUM_SERVICE_STATUS構造体)の配列へのポインタの型はそのままでは扱いにくいので、
uses
  Windows, SysUtils, WinSvc;

type
  TEnumServiceStatusArray = array [0..0] of TEnumServiceStatus;
  PEnumServiceStatusArray = ^TEnumServiceStatusArray;

{ Alias }
function EnumServicesStatus(hSCManager: SC_HANDLE; dwServiceType: DWORD;
                            dwServiceState: DWORD;
                            lpServices: PEnumServiceStatusArray; cbBufSize: DWORD;
                            var pcbBytesNeeded: DWORD;
                            var lpServicesReturned: DWORD;
                            var lpResumeHandle: DWORD): BOOL; stdcall;
  external advapi32 name
{$IFDEF Unicode}
    'EnumServicesStatusW';
{$ELSE}
    'EnumServicesStatusA';
{$ENDIF}
  {$EXTERNALSYM EnumServicesStatus}
と再定義しておきます。そして
type
  TEnumServicesFunc = function (const ServiceName: String;
                                const DisplayName: String;
                                ServiceStatus: TServiceStatus;
                                const CommandLine: String;
                                Data: Pointer): Boolean;

procedure EnumServices(Func: TEnumServicesFunc; Data: Pointer);
var
  hSCManager: SC_HANDLE;
  RetVal: Boolean;
  BytesNeeded: DWORD;
  ServicesReturned: DWORD;
  ResumeHandle: DWORD;
  PServiceStatus: PEnumServiceStatusArray;
  hService: SC_HANDLE;
  PServiceConfig: Pointer;
  Index: Integer;
  ServiceName: String;
  DisplayName: String;
  ServiceStatus: TServiceStatus;
  CommandLine: String;
begin

  { Open service manager }
  hSCManager := OpenSCManager(nil,nil,SC_MANAGER_ENUMERATE_SERVICE);
  if hSCManager = 0 then
  begin
    RaiseLastOSError;
  end;

  PServiceStatus := nil;
  try
    { Get buffer size }
    BytesNeeded := 0;
    ServicesReturned := 0;
    ResumeHandle := 0;
    RetVal := EnumServicesStatus(hSCManager,SERVICE_WIN32,
                                 SERVICE_STATE_ALL,nil,0,
                                 BytesNeeded,ServicesReturned,ResumeHandle);
    if RetVal = False then
    begin
      { Allocate buffer for EnumServicesStatus }
      PServiceStatus := AllocMem(BytesNeeded);

      { Enumerate service status }
      ResumeHandle := 0;
      RetVal := EnumServicesStatus(hSCManager,SERVICE_WIN32,
                                   SERVICE_STATE_ALL,PServiceStatus,BytesNeeded,
                                   BytesNeeded,ServicesReturned,ResumeHandle);
    end;

    if RetVal = False then
    begin
      RaiseLastOSError;
    end;

    for Index := 0 to ServicesReturned - 1 do
    begin
      ServiceName   := PServiceStatus^[Index].lpServiceName;
      DisplayName   := PServiceStatus^[Index].lpDisplayName;
      ServiceStatus := PServiceStatus^[Index].ServiceStatus;
      CommandLine   := '';

      { Open service for QueryServiceConfig }
      hService := OpenService(hSCManager,PChar(ServiceName),SERVICE_QUERY_CONFIG);
      if hService <> 0 then
      begin
        PServiceConfig := nil;
        try
          { Get buffer size }
          QueryServiceConfig(hService,nil,0,BytesNeeded);

          { Allocate buffer for QueryServiceConfig }
          PServiceConfig := AllocMem(BytesNeeded);

          { Query service configuration }
          if QueryServiceConfig(hService,PServiceConfig,
                                BytesNeeded,BytesNeeded) = True then
          begin
            { Binary pathname }
            CommandLine := TQueryServiceConfig(PServiceConfig^).lpBinaryPathName;
          end;

        finally
          { Close service }
          CloseServiceHandle(hService);

          { Free buffer }
          if PServiceConfig <> nil then
          begin
            FreeMem(PServiceConfig);
          end;
        end;
      end;

      { Callback }
      if Assigned(Func) then
      begin
        if Func(ServiceName,DisplayName,
                ServiceStatus,CommandLine,Data) = False then
        begin
          Break;
        end;
      end;
    end;

  finally
    { Close service manager handle }
    CloseServiceHandle(hSCManager);

    { Free buffer }
    if PServiceStatus <> nil then
    begin
      FreeMem(PServiceStatus);
    end;
  end;

end;
とします。例によってDelphi 2009以降の無名メソッド(anonymous method)を使用する場合は
type
  TEnumServicesFunc = reference to
    function (const ServiceName: String;
              const DisplayName: String;
              ServiceStatus: TServiceStatus;
              const CommandLine: String): Boolean;

procedure EnumServices(Func: TEnumServicesFunc);
var
  hSCManager: SC_HANDLE;
  RetVal: Boolean;
  BytesNeeded: DWORD;
  ServicesReturned: DWORD;
  ResumeHandle: DWORD;
  PServiceStatus: PEnumServiceStatusArray;
  hService: SC_HANDLE;
  PServiceConfig: Pointer;
  Index: Integer;
  ServiceName: String;
  DisplayName: String;
  ServiceStatus: TServiceStatus;
  CommandLine: String;
begin

  { Open service manager }
  hSCManager := OpenSCManager(nil,nil,SC_MANAGER_ENUMERATE_SERVICE);
  if hSCManager = 0 then
  begin
    RaiseLastOSError;
  end;

  PServiceStatus := nil;
  try
    { Get buffer size }
    BytesNeeded := 0;
    ServicesReturned := 0;
    ResumeHandle := 0;
    RetVal := EnumServicesStatus(hSCManager,SERVICE_WIN32,
                                 SERVICE_STATE_ALL,nil,0,
                                 BytesNeeded,ServicesReturned,ResumeHandle);
    if RetVal = False then
    begin
      { Allocate buffer for EnumServicesStatus }
      PServiceStatus := AllocMem(BytesNeeded);

      { Enumerate service status }
      ResumeHandle := 0;
      RetVal := EnumServicesStatus(hSCManager,SERVICE_WIN32,
                                   SERVICE_STATE_ALL,PServiceStatus,BytesNeeded,
                                   BytesNeeded,ServicesReturned,ResumeHandle);
    end;

    if RetVal = False then
    begin
      RaiseLastOSError;
    end;

    for Index := 0 to ServicesReturned - 1 do
    begin
      ServiceName   := PServiceStatus^[Index].lpServiceName;
      DisplayName   := PServiceStatus^[Index].lpDisplayName;
      ServiceStatus := PServiceStatus^[Index].ServiceStatus;
      CommandLine   := '';

      { Open service for QueryServiceConfig }
      hService := OpenService(hSCManager,PChar(ServiceName),SERVICE_QUERY_CONFIG);
      if hService <> 0 then
      begin
        PServiceConfig := nil;
        try
          { Get buffer size }
          QueryServiceConfig(hService,nil,0,BytesNeeded);

          { Allocate buffer for QueryServiceConfig }
          PServiceConfig := AllocMem(BytesNeeded);

          { Query service configuration }
          if QueryServiceConfig(hService,PServiceConfig,
                                BytesNeeded,BytesNeeded) = True then
          begin
            { Binary pathname }
            CommandLine := TQueryServiceConfig(PServiceConfig^).lpBinaryPathName;
          end;

        finally
          { Close service }
          CloseServiceHandle(hService);

          { Free buffer }
          if PServiceConfig <> nil then
          begin
            FreeMem(PServiceConfig);
          end;
        end;
      end;

      { Callback }
      if Assigned(Func) then
      begin
        if Func(ServiceName,DisplayName,
                ServiceStatus,CommandLine) = False then
        begin
          Break;
        end;
      end;
    end;

  finally
    { Close service manager handle }
    CloseServiceHandle(hSCManager);

    { Free buffer }
    if PServiceStatus <> nil then
    begin
      FreeMem(PServiceStatus);
    end;
  end;

end;
こんな感じになります。なお取得したコマンドラインの中にはパス名に空白文字を含むにもかかわらずダブルクォーテーションで括られていないものもあるため、コマンドラインから実行ファイルのフルパス名を取り出すには
uses
  StrUtils;

function CommandlineToPathname(const CommandLine: String): String;
var
  Start: Integer;
  Position: Integer;
begin

  Result := CommandLine;

  if Result = '' then
  begin
    Exit;
  end;

  if Result[1] = '"' then
  begin
    { Quoted }
    Delete(Result,1,1);  // Delete first double quote
{$IFDEF Unicode}
    Position := Pos('"',Result);
{$ELSE}
    Position := AnsiPos('"',Result);
{$ENDIF}
    if Position > 0 then
    begin
      Delete(Result,Position,Length(Result));
    end;
  end
  else
  begin
    { Not quoted }
    Start := 1;
    while True do
    begin
      Position := PosEx(' ',Result,Start);
      if Position = 0 then
      begin
        Break;
      end;

      Start := Position + 1;
{$IFDEF Unicode}
      if CharInSet(Result[Start],['-','/']) then
{$ELSE}
      if Result[Start] in ['-','/'] then
{$ENDIF}
      begin
        Delete(Result,Position,Length(Result));
        Break;
      end;
    end;
  end;

end;
このように一捻り必要です。

元ねたは旧Delphi-MLの90131以下のスレッド(特に90133のKHE00221さんの回答)。

2010年7月21日

ネットワークアダプタの情報を取得する(IPv4)

ネットワークアダプタの情報を取得するにはGetAdaptersInfoを使用します。取得した情報はIP_ADAPTER_INFO構造体に格納されますが、複数のネットワークアダプタが存在する場合は戻値がERROR_BUFFER_OVERFLOWになり、第2パラメータのpOutBufLenに必要なサイズがバイト単位で返されるので、領域を再確保して再びGetAdaptersInfoを呼び出す必要があります。この場合はIP_ADAPTER_INFO構造体のNextがNULLになるまでたどっていくことになります。
まずこれらのIPヘルパAPIや構造体を定義します。
uses
  Windows, SysUtils;

const
  MAX_ADAPTER_DESCRIPTION_LENGTH = 128;
  {$EXTERNALSYM MAX_ADAPTER_DESCRIPTION_LENGTH}
  MAX_ADAPTER_NAME_LENGTH        = 256;
  {$EXTERNALSYM MAX_ADAPTER_NAME_LENGTH}
  MAX_ADAPTER_ADDRESS_LENGTH     = 8;
  {$EXTERNALSYM MAX_ADAPTER_ADDRESS_LENGTH}

  MIB_IF_TYPE_OTHER     = 1;
  {$EXTERNALSYM MIB_IF_TYPE_OTHER}
  MIB_IF_TYPE_ETHERNET  = 6;
  {$EXTERNALSYM MIB_IF_TYPE_ETHERNET}
  MIB_IF_TYPE_TOKENRING = 9;
  {$EXTERNALSYM MIB_IF_TYPE_TOKENRING}
  MIB_IF_TYPE_FDDI      = 15;
  {$EXTERNALSYM MIB_IF_TYPE_FDDI}
  MIB_IF_TYPE_PPP       = 23;
  {$EXTERNALSYM MIB_IF_TYPE_PPP}
  MIB_IF_TYPE_LOOPBACK  = 24;
  {$EXTERNALSYM MIB_IF_TYPE_LOOPBACK}
  MIB_IF_TYPE_SLIP      = 28;
  {$EXTERNALSYM MIB_IF_TYPE_SLIP}

type
  time_t = Longint;
  {$EXTERNALSYM time_t}

  PIP_MASK_STRING = ^IP_MASK_STRING;
  {$EXTERNALSYM PIP_MASK_STRING}
  IP_ADDRESS_STRING = record
    S: array [0..15] of AnsiChar;
  end;
  {$EXTERNALSYM IP_ADDRESS_STRING}
  PIP_ADDRESS_STRING = ^IP_ADDRESS_STRING;
  {$EXTERNALSYM PIP_ADDRESS_STRING}
  IP_MASK_STRING = IP_ADDRESS_STRING;
  {$EXTERNALSYM IP_MASK_STRING}

  PIP_ADDR_STRING = ^IP_ADDR_STRING;
  {$EXTERNALSYM PIP_ADDR_STRING}
  _IP_ADDR_STRING = record
    Next: PIP_ADDR_STRING;
    IpAddress: IP_ADDRESS_STRING;
    IpMask: IP_MASK_STRING;
    Context: DWORD;
  end;
  {$EXTERNALSYM _IP_ADDR_STRING}
  IP_ADDR_STRING = _IP_ADDR_STRING;
  {$EXTERNALSYM IP_ADDR_STRING}

  PIP_ADAPTER_INFO = ^IP_ADAPTER_INFO;
  {$EXTERNALSYM PIP_ADAPTER_INFO}
  _IP_ADAPTER_INFO = record
    Next: PIP_ADAPTER_INFO;
    ComboIndex: DWORD;
    AdapterName: array [0..MAX_ADAPTER_NAME_LENGTH + 3] of AnsiChar;
    Description: array [0..MAX_ADAPTER_DESCRIPTION_LENGTH + 3] of AnsiChar;
    AddressLength: UINT;
    Address: array [0..MAX_ADAPTER_ADDRESS_LENGTH - 1] of BYTE;
    Index: DWORD;
    Type_: UINT;
    DhcpEnabled: UINT;
    CurrentIpAddress: PIP_ADDR_STRING;
    IpAddressList: IP_ADDR_STRING;
    GatewayList: IP_ADDR_STRING;
    DhcpServer: IP_ADDR_STRING;
    HaveWins: BOOL;
    PrimaryWinsServer: IP_ADDR_STRING;
    SecondaryWinsServer: IP_ADDR_STRING;
    LeaseObtained: time_t;
    LeaseExpires: time_t;
  end;
  {$EXTERNALSYM _IP_ADAPTER_INFO}
  IP_ADAPTER_INFO = _IP_ADAPTER_INFO;
  {$EXTERNALSYM IP_ADAPTER_INFO}

function GetAdaptersInfo(pAdapterInfo: PIP_ADAPTER_INFO;
                         var pOutBufLen: ULONG): DWORD; stdcall;
  external 'iphlpapi.dll' Name 'GetAdaptersInfo';
  {$EXTERNALSYM GetAdaptersInfo}
そして
type
  TAdapterType = (atUnknown,
                  atOther,
                  atEthernet,
                  atTokenRing,
                  atFDDI,
                  atPPP,
                  atLoopback,
                  atSLIP,
                  atWLAN);

  TEnumAdapterInfoFunc = function (const AdapterName: String;
                                   const Description: String;
                                   const HardwareAddress: String;
                                   AdapterIndex: Integer;
                                   AdapterType: TAdapterType;
                                   DhcpEnabled: Boolean;
                                   const IPAddress: String;
                                   const Mask: String;
                                   const Gateway: String;
                                   const DHCPServer: String;
                                   Data: Pointer): Boolean;

procedure EnumAdapterInfo(Func: TEnumAdapterInfoFunc; Data: Pointer);

  function FormatHardwareAddress(Ptr: PIP_ADAPTER_INFO): String;
  var
    Index: Integer;
  begin

    Result := '';

    for Index := 0 to Ptr^.AddressLength - 1 do
    begin
      Result := Result + Format('%02.2X-',[Ptr^.Address[Index]]);
    end;

    if Result <> '' then
    begin
      Delete(Result,Length(Result),1);
    end;

  end;

var
  pAdapterInfo: PIP_ADAPTER_INFO;
  Ptr: PIP_ADAPTER_INFO;
  pOutBufLen: ULONG;
  RetVal: DWORD;
  AdapterIndex: Integer;
  AdapterName: String;
  Description: String;
  HardwareAddress: String;
  AdapterType: TAdapterType;
  IPAddress: String;
  NetMask: String;
  Gateway: String;
  DHCPServer: String;
begin

  pOutBufLen := SizeOf(IP_ADAPTER_INFO);
  pAdapterInfo := AllocMem(pOutBufLen);
  try
    RetVal := GetAdaptersInfo(pAdapterInfo,pOutBufLen);
    if RetVal = ERROR_BUFFER_OVERFLOW then
    begin
      FreeMem(pAdapterInfo);
      pAdapterInfo := AllocMem(pOutBufLen);
      RetVal := GetAdaptersInfo(pAdapterInfo,pOutBufLen);
    end;

    if RetVal <> NO_ERROR then
    begin
      Exit;
    end;

    Ptr := pAdapterInfo;
    while Ptr <> nil do
    begin
      AdapterName := Trim(String(Ptr^.AdapterName));
      Description := Trim(String(Ptr^.Description));
      HardwareAddress := FormatHardwareAddress(Ptr);
      AdapterIndex := Ptr^.Index;
      case Ptr^.Type_ of
        MIB_IF_TYPE_OTHER:
        begin
          AdapterType := atOther;
        end;

        MIB_IF_TYPE_ETHERNET:
        begin
          AdapterType := atEthernet;
        end;

        MIB_IF_TYPE_TOKENRING:
        begin
          AdapterType := atTokenRing;
        end;

        MIB_IF_TYPE_FDDI:
        begin
          AdapterType := atFDDI;
        end;

        MIB_IF_TYPE_PPP:
        begin
          AdapterType := atPPP;
        end;

        MIB_IF_TYPE_LOOPBACK:
        begin
          AdapterType := atLoopback;
        end;

        MIB_IF_TYPE_SLIP:
        begin
          AdapterType := atSLIP;
        end;

        71:
        begin
          AdapterType := atWLAN;
        end;

        else
        begin
          AdapterType := atUnknown;
        end;
      end;

      IPAddress  := String(Ptr^.IpAddressList.IpAddress.S);
      NetMask    := String(Ptr^.IpAddressList.IpMask.S);
      Gateway    := String(Ptr^.GatewayList.IpAddress.S);
      if Ptr^.DhcpEnabled = 0 then
      begin
        DHCPServer := '';
      end
      else
      begin
        DHCPServer := String(Ptr^.DhcpServer.IpAddress.S);
      end;

      if Assigned(Func) then
      begin
        if Func(AdapterName,Description,HardwareAddress,AdapterIndex,
                AdapterType,Ptr^.DhcpEnabled <> 0,IPAddress,NetMask,
                Gateway,DHCPServer,Data) = False then
        begin
          Break;
        end;
      end;

      Ptr := Ptr^.Next;
    end;

  finally
    FreeMem(pAdapterInfo);
  end;

end;
とします。Delphi 2009以降の無名メソッド(anonymous method)を使用する場合はこんな感じに。
type
  TAdapterType = (atUnknown,
                  atOther,
                  atEthernet,
                  atTokenRing,
                  atFDDI,
                  atPPP,
                  atLoopback,
                  atSLIP,
                  atWLAN);

  TEnumAdapterInfoFunc = reference to
    function (const AdapterName: String;
              const Description: String;
              const HardwareAddress: String;
              AdapterIndex: Integer;
              AdapterType: TAdapterType;
              DhcpEnabled: Boolean;
              const IPAddress: String;
              const Mask: String;
              const Gateway: String;
              const DHCPServer: String): Boolean;

procedure EnumAdapterInfo(Func: TEnumAdapterInfoFunc);

  function FormatHardwareAddress(Ptr: PIP_ADAPTER_INFO): String;
  var
    Index: Integer;
  begin

    Result := '';

    for Index := 0 to Ptr^.AddressLength - 1 do
    begin
      Result := Result + Format('%02.2X-',[Ptr^.Address[Index]]);
    end;

    if Result <> '' then
    begin
      Delete(Result,Length(Result),1);
    end;

  end;

var
  pAdapterInfo: PIP_ADAPTER_INFO;
  Ptr: PIP_ADAPTER_INFO;
  pOutBufLen: ULONG;
  RetVal: DWORD;
  AdapterIndex: Integer;
  AdapterName: String;
  Description: String;
  HardwareAddress: String;
  AdapterType: TAdapterType;
  IPAddress: String;
  NetMask: String;
  Gateway: String;
  DHCPServer: String;
begin

  pOutBufLen := SizeOf(IP_ADAPTER_INFO);
  pAdapterInfo := AllocMem(pOutBufLen);
  try
    RetVal := GetAdaptersInfo(pAdapterInfo,pOutBufLen);
    if RetVal = ERROR_BUFFER_OVERFLOW then
    begin
      FreeMem(pAdapterInfo);
      pAdapterInfo := AllocMem(pOutBufLen);
      RetVal := GetAdaptersInfo(pAdapterInfo,pOutBufLen);
    end;

    if RetVal <> NO_ERROR then
    begin
      Exit;
    end;

    Ptr := pAdapterInfo;
    while Ptr <> nil do
    begin
      AdapterName := Trim(String(Ptr^.AdapterName));
      Description := Trim(String(Ptr^.Description));
      HardwareAddress := FormatHardwareAddress(Ptr);
      AdapterIndex := Ptr^.Index;
      case Ptr^.Type_ of
        MIB_IF_TYPE_OTHER:
        begin
          AdapterType := atOther;
        end;

        MIB_IF_TYPE_ETHERNET:
        begin
          AdapterType := atEthernet;
        end;

        MIB_IF_TYPE_TOKENRING:
        begin
          AdapterType := atTokenRing;
        end;

        MIB_IF_TYPE_FDDI:
        begin
          AdapterType := atFDDI;
        end;

        MIB_IF_TYPE_PPP:
        begin
          AdapterType := atPPP;
        end;

        MIB_IF_TYPE_LOOPBACK:
        begin
          AdapterType := atLoopback;
        end;

        MIB_IF_TYPE_SLIP:
        begin
          AdapterType := atSLIP;
        end;

        71:
        begin
          AdapterType := atWLAN;
        end;

        else
        begin
          AdapterType := atUnknown;
        end;
      end;

      IPAddress  := String(Ptr^.IpAddressList.IpAddress.S);
      NetMask    := String(Ptr^.IpAddressList.IpMask.S);
      Gateway    := String(Ptr^.GatewayList.IpAddress.S);
      if Ptr^.DhcpEnabled = 0 then
      begin
        DHCPServer := '';
      end
      else
      begin
        DHCPServer := String(Ptr^.DhcpServer.IpAddress.S);
      end;

      if Assigned(Func) then
      begin
        if Func(AdapterName,Description,HardwareAddress,AdapterIndex,
                AdapterType,Ptr^.DhcpEnabled <> 0,IPAddress,NetMask,
                Gateway,DHCPServer) = False then
        begin
          Break;
        end;
      end;

      Ptr := Ptr^.Next;
    end;

  finally
    FreeMem(pAdapterInfo);
  end;

end;
なおIP_ADAPTER_INFO構造体のTypeの値として無線LANの場合の定義がMIB_IF_TYPE_...にはありませんが、実際には71が格納されるようです。
またIPv6(Windows XP以降)の場合はGetAdaptersAddressesを使用する必要があります。

2010年7月20日

参加しているドメインまたはワークグループの種類と名前を取得する

前回のNetWkstaGetInfoを使用する方法ではNTドメイン(AD)に参加しているのかワークグループに参加しているのかを知ることができませんでした。そこでNetGetJoinInformation(ja)で参加しているNTドメインまたはワークグループの名前と参加ステータスを取得してみます。ここでもNetWkstaGetInfoで取得したドメインまたはワークグループの名前の領域はNetApiBufferFree(ja)で解放する必要があることに注意が必要です。
まずこれらのネットワーク管理APIと列挙を定義します。
uses
  Windows;

type
  NET_API_STATUS = DWORD;
  {$EXTERNALSYM NET_API_STATUS}

  _NETSETUP_JOIN_STATUS = (NetSetupUnknownStatus = 0,  // The status is unknown.
                           NetSetupUnjoined,           // The computer is not joined.
                           NetSetupWorkgroupName,      // The computer is joined to a workgroup.
                           NetSetupDomainName);        // The computer is joined to a domain.
  {$EXTERNALSYM _NETSETUP_JOIN_STATUS}
  NETSETUP_JOIN_STATUS  = _NETSETUP_JOIN_STATUS;
  {$EXTERNALSYM NETSETUP_JOIN_STATUS}
  PNETSETUP_JOIN_STATUS = ^NETSETUP_JOIN_STATUS;
  {$EXTERNALSYM PNETSETUP_JOIN_STATUS}
  TNetsetupJoinStatus   = NETSETUP_JOIN_STATUS;

const
  NERR_Success = 0;
  {$EXTERNALSYM NERR_Success}

function NetGetJoinInformation(lpServer: PWideChar;
                               var lpNameBuffer: PWideChar;
                               BufferType: PNETSETUP_JOIN_STATUS): NET_API_STATUS; stdcall;
  external 'netapi32.dll' Name 'NetGetJoinInformation';
  {$EXTERNALSYM NetGetJoinInformation}

function NetApiBufferFree(Buffer: Pointer): NET_API_STATUS; stdcall;
  external 'netapi32.dll' Name 'NetApiBufferFree';
  {$EXTERNALSYM NetApiBufferFree}
あとはこれを呼び出すだけです。
function GetNetworkJoinInformation(var NetworkName: String;
                                   var Status: TNetsetupJoinStatus): Boolean;
var
  Buffer: PWideChar;
begin

  if NetGetJoinInformation(nil,Buffer,@Status) <> NERR_Success then
  begin
    Result := False;
    Exit;
  end;

  NetworkName := Buffer;

  NetApiBufferFree(Buffer);

  Result := True;

end;

2010年7月16日

プログラミングの魔導書 予約受付開始

プログラミングの魔導書 ~Programmers' Grimoire~ Vol.1の予約受付が始まっています。

株式会社ロングゲート - 製品案内

ただいま予約受付中
予約期間は 2010年8月6日(金) までとなります。
完全受注生産のため、書籍版については予約でのみご購入いただけます。
PDF版につきましては、予約終了後でも購入可能です。

書籍版 1,500円(税込・送料別)
PDF版 1,000円(税込)
書籍+PDFセット 2,000円(税込・送料別)

書籍版は2010/08/06までに予約しないと入手不能になる(それ以降はPDFのみになる)とのことです。

元ねたは江添さん本の虫: 新しいプログラミング雑誌の発行アキラさん[Grimoire]「プログラミングの魔導書」予約開始! - Faith and Brave - C++で遊ぼう

参加しているドメインまたはワークグループの名前を取得する

現在参加しているNTドメインまたはワークグループの名前を取得するにはNetWkstaGetInfo(ja)を使用します。NetWkstaGetInfoで取得した構造体(WKSTA_INFO_100WKSTA_INFO_101WKSTA_INFO_102)はNetApiBufferFree(ja)で解放する必要があることに注意が必要です。
まずこれらのネットワーク管理APIや構造体を定義します。
uses
  Windows;

type
  NET_API_STATUS = DWORD;
  {$EXTERNALSYM NET_API_STATUS}

  PWKSTA_INFO_100 = ^WKSTA_INFO_100;
  {$EXTERNALSYM PWKSTA_INFO_100}
  _WKSTA_INFO_100 = record
    wki100_platform_id: DWORD;
    wki100_computername: PWideChar;
    wki100_langroup: PWideChar;
    wki100_ver_major: DWORD;
    wki100_ver_minor: DWORD;
  end;
  {$EXTERNALSYM _WKSTA_INFO_100}
  WKSTA_INFO_100 = _WKSTA_INFO_100;
  {$EXTERNALSYM WKSTA_INFO_100}

  PWKSTA_INFO_101 = ^WKSTA_INFO_101;
  {$EXTERNALSYM PWKSTA_INFO_101}
  _WKSTA_INFO_101 = record
    wki101_platform_id: DWORD;
    wki101_computername: PWideChar;
    wki101_langroup: PWideChar;
    wki101_ver_major: DWORD;
    wki101_ver_minor: DWORD;
    wki101_lanroot: PWideChar;
  end;
  {$EXTERNALSYM _WKSTA_INFO_101}
  WKSTA_INFO_101 = _WKSTA_INFO_101;
  {$EXTERNALSYM WKSTA_INFO_101}

  PWKSTA_INFO_102 = ^WKSTA_INFO_102;
  {$EXTERNALSYM PWKSTA_INFO_102}
  _WKSTA_INFO_102 = record
    wki102_platform_id: DWORD;
    wki102_computername: PWideChar;
    wki102_langroup: PWideChar;
    wki102_ver_major: DWORD;
    wki102_ver_minor: DWORD;
    wki102_lanroot: PWideChar;
    wki102_logged_on_users: DWORD;
  end;
  {$EXTERNALSYM _WKSTA_INFO_102}
  WKSTA_INFO_102 = _WKSTA_INFO_102;
  {$EXTERNALSYM WKSTA_INFO_102}

const
  NERR_Success = 0;
  {$EXTERNALSYM NERR_Success}

function NetWkstaGetInfo(servername: PWideChar; level: DWORD;
                         var bufptr: PByte): NET_API_STATUS; stdcall;
  external 'netapi32.dll' Name 'NetWkstaGetInfo';
  {$EXTERNALSYM NetWkstaGetInfo}

function NetApiBufferFree(Buffer: Pointer): NET_API_STATUS; stdcall;
  external 'netapi32.dll' Name 'NetApiBufferFree';
  {$EXTERNALSYM NetApiBufferFree}
今回はローカルコンピュータのNTドメイン/ワークグループ名があれば十分なので、servernameにNULLを、levelに100を指定してWKSTA_INFO_100構造体を取得し、wki100_langroupに格納されている文字列を取り出します。
function GetNTDomainName: String;
var
  Ptr: PWKSTA_INFO_100;
begin

  Result := '';
  if NetWkstaGetInfo(nil,100,PByte(Ptr)) <> NERR_Success then
  begin
    Exit;
  end;

  Result := Ptr^.wki100_langroup;
  NetApiBufferFree(Ptr);

end;
ただしこの方法ではNTドメインに参加しているのかワークグループに参加しているのかを知ることができないので、NetGetJoinInformationを使用する方法をお勧めします。

元ねたはDelphiDabbler.comGet the network computer and domain names

Nick HodgesがEmbarcaderoを退職

RAD StudioのR&DマネージャだったNick Hodgesさんがエンバカデロ・テクノロジーズを退職したというニュースが飛び込んできました。

Good luck Nick « Delphi Haven
Embarcadero Discussion Forums: Nick Hodges blog doesn't exist in ...

ForumのRobert Loveさんの発言によれば、I was notified today that Nick no longer works at Embarcardero.とのことです。

ForumにEmbarcadero Discussion Forums: Changes for Nick ...というスレッドが立っています。There have been some rumors floating around, and I wanted to set things straight.
I was let go by Embarcadero on Monday. This wasn't my decision.
ということだそうです。

2010/07/19追記: Nick Hodges | A man's got to know his limitationsができています。"Nick, thank you for your all works, and good luck!"

2010年7月15日

第17回エンバカデロ・デベロッパーキャンプ開催決定

第17回エンバカデロ・デベロッパーキャンプは2010年09月15日に開催されます。時期的にRAD Studio 2011の話が出てくるのでしょうか。今回はエンバカデロ・テクノロジーズ 製品担当副社長("Senior Vice President of Marketing and Product Management")のMichael Swindellさんがいらっしゃるようです。

2010/09/07追記: 一部のセッションがUstreamで配信されるそうです。詳細は当日(2010/09/15)にデベロッパーキャンプのページで確認してくださいとのこと。

2010/09/09再追記: http://www.embarcadero.com/jp/landing-pages/developer-camp-liveでストリーミングが配信されるとのことです。

2010/09/10再々追記: Team Japanに情報が追加されています。

Team Japan » 第17回 エンバカデロ・デベロッパーキャンプ情報(その1) - 会場へのアクセス
Team Japan » 第17回 エンバカデロ・デベロッパーキャンプ情報(その2) - ライブ配信

2010年7月14日

Microsoft Monthly Update 2010/07

今日はMicrosoftのセキュリティアップデートの日です。
MS10-042
MS10-043
MS10-044
MS10-045

実行環境のアーキテクチャを取得する

実行環境のアーキテクチャはGetSystemInfo(ja)で取得したSYSTEM_INFO構造体のwProcessorArchitectureで知ることができるはずです。ところが実際には32bitのアプリケーションからは64bit版WindowsでもwProcessorArchitectureがPROCESSOR_ARCHITECTURE_INTEL(0)となってしまいます。GetSystemInfoの説明をよく見るとTo retrieve accurate information for an application running on WOW64, call the GetNativeSystemInfo function.ということでGetNativeSystemInfoを使用する必要があるようです。GetNativeSystemInfoはWindows XP/Server 2003以降でしか使えませんので、それ以前の環境ではGetSystemInfoを使用します。
uses
  Windows;

const
  PROCESSOR_ARCHITECTURE_AMD64   =     9;   // x64 (AMD or Intel)
  PROCESSOR_ARCHITECTURE_IA64    =     6;   // Intel Itanium Processor Family (IPF)
  PROCESSOR_ARCHITECTURE_INTEL   =     0;   // x86
  PROCESSOR_ARCHITECTURE_UNKNOWN = $FFFF;   // Unknown architecture

type
  TGetSystemInfoFunc = procedure (var lpSystemInfo: TSystemInfo); stdcall;

function GetProcessorArchitecture: Word;
var
  SI: TSystemInfo;
  GetSystemInfoFunc: TGetSystemInfoFunc;
begin

  FillChar(SI,SizeOf(SI),0);

  { Call GetNativeSystemInfo or GetSystemInfo }
  @GetSystemInfoFunc := GetProcAddress(GetModuleHandle(kernel32),
                                       'GetNativeSystemInfo');
  if Assigned(GetSystemInfoFunc) = True then
  begin
    GetSystemInfoFunc(SI);
  end
  else
  begin
    GetSystemInfo(SI);
  end;

  Result := SI.wProcessorArchitecture;

end;

2012/07/07追記: Delphi XE2以降であればSystem.SysUtilsユニットのTOSVersion.Architectureで実行環境のアーキテクチャを知ることができます。ねた元はZarko GajicさんのIs Your 32bit Delphi Applications Running On x86 (Win 32) OR x64 (Win 64)?

2010年7月13日

コンピュータ名を取得する

一時ファイル名の一部として使うなど、コンピュータ名を取得したい状況というのはそれなりにあると思います。そこでまずGetComputerName(ja)でNetBIOS名を取得してみます。
uses
  Windows, SysUtils;

function GetComputerName: String;
var
  Size: DWORD;
begin

  { Adjust buffer size }
  Size := MAX_COMPUTERNAME_LENGTH + 1;
  SetLength(Result,Size);

  { Get computer name }
  if Windows.GetComputerName(PChar(Result),Size) = False then
  begin
    RaiseLastOSError;
  end;
  SetLength(Result,Size);

end;
次にGetComputerNameEx(ja)(Windows 2000以降であれば使用可能)でDNSホスト名や完全修飾DNS名を取得してみます。まずはWindows.pasにGetComputerNameEx(W)と第1パラメータのCOMPUTER_NAME_FORMATの定義がWindows.pasに存在しない(Delphi 2010にはあるのかな?)ので用意します。
type
{$Z4}
  _COMPUTER_NAME_FORMAT = (ComputerNameNetBIOS,
                           ComputerNameDnsHostname,
                           ComputerNameDnsDomain,
                           ComputerNameDnsFullyQualified,
                           ComputerNamePhysicalNetBIOS,
                           ComputerNamePhysicalDnsHostname,
                           ComputerNamePhysicalDnsDomain,
                           ComputerNamePhysicalDnsFullyQualified,
                           ComputerNameMax);
  {$EXTERNALSYM _COMPUTER_NAME_FORMAT}
  COMPUTER_NAME_FORMAT  = _COMPUTER_NAME_FORMAT;
  {$EXTERNALSYM COMPUTER_NAME_FORMAT}
  TComputerNameFormat   = COMPUTER_NAME_FORMAT;

function GetComputerNameExW(NameType: COMPUTER_NAME_FORMAT; lpBuffer: PWideChar;
                            var nSize: DWORD): BOOL; stdcall;
  external kernel32 name 'GetComputerNameExW';
  {$EXTERNALSYM GetComputerNameExW}
そしてこれを
uses
  Windows, SysUtils;

function GetComputerNameEx(NameType: TComputerNameFormat): String;
var
{$IFDEF Unicode}
  Buffer: String;
{$ELSE}
  Buffer: WideString;
{$ENDIF}
  Size: DWORD;
begin

  { Adjust buffer size }
  Size := 0;
  GetComputerNameExW(NameType,nil,Size);
  SetLength(Buffer,Size);

  { Get computer name }
  if GetComputerNameExW(NameType,PWideChar(Buffer),Size) = False then
  begin
    RaiseLastOSError;
  end;
  SetLength(Buffer,Size);
  Result := Buffer;

end;
という形で呼び出します。1回目のGetComputerNameExではlpBufferをNULLに、nSizeを0にすることでバッファに必要なサイズ(終端のNUL文字を含む)を取得し、2回目のGetComputerNameExで実際のコンピュータ名を取得するのですが、GetComputerNameExA(ANSI版)ではlpBufferをNULLにしても正常にバッファサイズを取得できないため、GetComputerNameExW(Unicode版)を使用しています。

2010年7月10日

[書籍]アプレンティスシップ・パターン

有隣堂 ヨドバシAKIBA店

アプレンティスシップ・パターン (amazon)/Dave H. Hoover、Adewale Oshiney著/柴田 芳樹訳/オライリージャパン/ISBN 978-4-87311-460-6/2,310円

を購入。

2010年7月8日

2nd anniversary

さすがに今年は覚えてました。とはいえここのところ内容がいまいちですね。この1年間のエントリは155件(2009/07/07-2010/07/02)。そろそろDelphi 2011の話が出始めると思われるので、今年も追いかけていきたいと思っています。

2010年7月2日

David Iが日本にきているらしい

David IことDavid Intersimoneさんが日本に来ていて懇談会をやっているようです(プレス関係かな?)。詳しくはTwitterのハッシュタグ#davidiembarcadero_jpCYonezawaあたりで。どうせならustreamあたりで生中継でもどうでしょう?

2010/07/04追記: builder by ZDNetの2010/06/18の開発環境の整備とクロスプラットフォームに注力--エンバカデロが新たな製品戦略 - builder by ZDNet Japanという記事にMalcolm Grovesさんの発言として7月には、CodeGear製品のチーフエバンジェリストを務めるDavid Intersimone(David I)氏が来日し、開発ツールに関する詳細な説明を行うことも明らかにした。あるので、週明けにはまた発表かインタビューがあるのではないでしょうかありますが、藤井さんDavid I プライベートで日本へによれば今回はほぼプライベートだったようです。今月中にまた来るってことでしょうか。

2010/07/05追記: 藤井さんの記事に従って2010/07/04追記分の記述を修正しました。アメリカ合衆国ではこの週末は独立記念日でお休みなんですね。

2010年6月18日

QC Windowsクライアント

EmbarcaderoのQC(QualityCentral)にアクセスするには基本的にwebからか専用クライアントを使用することになります。Embarcadero純正のWindows GUIクライアントはQCのメインページから最新版(2010/06/18現在でVer1.0.1.41、2009/10/17が最新)がダウンロードできますが、この他にJED SoftwareのJeremy NorthさんによるQC webサービスを使用した高機能なQC Plus(最新版は1.0.5.656)というクライアントがあります。複数のレポートを同時に開くことができたり色々便利そうです。

元ねたはJohn KasterさんBehind the Screen » EDN Tip #3: Keeping your QualityCentral windows client current

2010/06/26追記: QC Plus1.1.4.121に更新されています。

2010/09/23追記: QC Plusが1.1.6.145に更新されています。

2010年6月9日

2010年6月2日

プログラミングの魔導書 創刊

以前から一部で話題になっていたC++のプログラミング雑誌がついに創刊されるようです。

株式会社ロングゲート - 製品案内

ユーザーの力によって際限なく進化するプログラミング言語C++。
2010年のいま、C++はどこにいるのか
『プログラミングの魔導書 ~Programmers' Grimoire~
Vol.1 「Construct the World, C++」 』
書籍版は一般の流通ルートには乗らないということなので通信販売で申し込む必要があるようです(環境に優しくないかもしれないけどPDF版は体に合わないので)。

元ねたはA7Mさん新雑誌:プログラミングの魔導書 - C++Builder好きの秘密基地江添さん本の虫: プログラミング雑誌、もうすぐ販売

2010年5月18日

[書籍]リコールを起こさないソフトウェアのつくり方

啓文堂書店の渋谷店

組込みプレスSelectionシリーズ リコールを起こさないソフトウェアのつくり方 (amazon)/酒井由夫著/技術評論社/ISBN 978-4-7741-4216-6 (ISBN 4774142166)/2,814円

を購入。ISO/IEC/JIS T 62304絡み(Appendixだけだけど)で買ったけど、内容はなかなか充実していると思う。タイトルがよければもっと売れるのではないかな?一般論としてタイトルは編集サイドで決めるらしいけど。

2010年5月12日

第19回ソフトウェア開発環境展(SODEC)

第19回ソフトウェア開発環境展(SODEC)にいってきました。もちろんエンバカデロさんのブースにもいきました(サクラ?)。
しかし会社を空けると仕事の連絡が来るの法則により、くわしく書いている暇がありません。そのうち何か加筆するかもしれません…。
SODECやESECなど今回の一連の展示会にいかれる方は混雑度が全然違いますので午前中からにすることをお勧めします(12:10-12:20のスペシャルセッションでは米澤さんを拝見することもできますし(って自分は某社のデモを見ていて見逃したんですけど))。

Microsoft Monthly Update 2010/05

今日はMicrosoftのセキュリティアップデートの日です。
MS10-030
MS10-031

2010年5月11日

RAD Studio Roadmap

RAD Studio/Delphi/C++Builderのロードマップが更新されています。

RAD Studio, Delphi and C++Builder Roadmap
RAD Studio, Delphi, C++Builder, Delphi Prism のロードマップについて

注目ポイントは、Project "Fulcrum"はWindows/MacOS Xのクロスプラットフォームで、Linuxサポートが別途Project "Wheelhouse"(操舵室)に分離された(=2011には当初含まれない可能性もある?)こと、64bit版コマンドラインコンパイラのプレビュー版が2011/1H(2011年01月から06月)にリリースされること、Project "Commodore"およびProject "Chromium"については従来の方針が維持されている(="次の次"扱いのまま)こと、あたりでしょうか。

2010年5月10日

Fulcrum(次期Delphi/C++Builder)フィールドテスト

Fulcrum(次期Delphi/C++Builder)のフィールドテスト参加者の募集が始まっています。

Delphi "Fulcrum" フィールドテストプログラムのご案内

さらに今回は、マルチプラットフォームサポートを重要なフォーカスとしているため、複数バージョンの Windowsプラットフォームのほかに、Mac OS X、Linuxの主要なディストリビューションをテスト環境として利用できる方を募集します。
ということで、Delphi/C++Builder 2011はWindows/MacOS X/Linuxのクロスプラットフォームになるようです。

興味はあるものの、まだ手元にUnicode Readyじゃないライブラリが残っていたり、MacOS X/Linuxには縁がない、などの理由でたぶん参加しないような気がします。

2010年5月6日

エンバカデロがSODECに出展

2010/05/12-14に東京ビッグサイトで行われるSODEC(第19回ソフトウェア開発環境展)にエンバカデロが出展するとのことです。

5/12~5/14 第19回ソフトウェア開発環境展(SODEC)

ミニシアターセッション(10分間)では『AjaxフレームワークでカンタンWeb開発』、『開発環境のTCOを46%削減!ツールクラウドとは?』、『あなたのDBは本当に大丈夫? ― 誰もが知りたいその信頼度』が10分の休憩を置いてローテーションして、スペシャルセッションとして『SPECIAL! DBモデリング ― ボトムアップからのアプローチ』(12:10-12:20)と『SPECIAL! DBパフォーマンスチューニングの概要と評価』(16:00-16:10)が間に挟まるようです。SODECもずいぶん(5年くらい?)ご無沙汰してたけど、久しぶりに行こうかな…。

エンバカデロの新しいロゴ

Embarcadero Technologiesのコーポレートロゴが新しいものに変更されるそうです。

Team Japan » エンバカデロのロゴが変わります。

既にTopページのロゴも置き換わっています。まぁ前のロゴよりは新しいロゴほうがスマート(というかシンプル?)だとは思います。

2010年4月26日

[書籍]アルゴリズムクイックリファレンス

ジュンク堂書店の新宿店で

アルゴリズムクイックリファレンス (amazon)/George T. Heineman、Gary Pollice、Stanley Selkow著/黒川利明、黒川洋訳/オライリージャパン/ISBN 978-4-87311-428-6 (ISBN 4873114284)/3,360円

を購入。

2010/05/06追記: コードリポジトリはここ、ErrataはAlgorithms in a Nutshell Confirmed Errata | O'Reilly Media

2010年4月14日

[書籍]正規表現クックブック

ジュンク堂書店の新宿店で

正規表現クックブック (amazon)/Jan Goyvaerts、Steven Levithan著/長尾高弘訳/オライリージャパン/ISBN 978-4-87311-450-7 (ISBN 4873114500)/4,410円

を購入。

Microsoft Monthly Update 2010/04

今日はMicrosoftのセキュリティアップデートの日です。
MS10-019
MS10-020
MS10-021
MS10-022
MS10-023
MS10-024
MS10-025
MS10-026
MS10-027
MS10-028
MS10-029

2010年4月13日

C++0xのlambda

C++0xで導入予定のlambda(ラムダ、無名関数)についての江添さん(=hitoさん)による解説。

本の虫: lambda 完全解説
本の虫: シンタックスシュガーとしてのlambdaの解説
本の虫: named lambdaが必要な理由

lambdaとは名前のない(名前を付ける必要がない)データ(参照またはコピーでキャプチャされたもの)と関数の組み合わせ、つまりその場で定義されて呼び出される関数オブジェクトのこと…という理解でいいのかな?Delphiの無名メソッドreference toはC++0xのstd::functionの代替表現と考えればいいのでしょうかね。

RAD Studio/Delphi/C++Builder 2010 Hotfix 2

RAD Studio/Delphi/C++Builder 2010のHotfix 2がリリースされています。
  • Delphiオプションのタイプライブラリで[SafeCall呼び出しの割り当て]および[ソースの更新]オプションが表示されない。
  • CPP32.EXE 存在するドライブのボリューム名が9文字を超えている場合に、プリプロセッサで処理されたファイルに出力されるC++ソースファイルが常に小文字になる。
  • フィルタの使用時に発生するDataSnap通信の問題。
の3点を修正するものです。

RAD Studio 2010 Hotfix 2 リリースノート
27671 Hotfix 2 for RAD Studio 2010 (Delphi and C++Builder 2010)

2010年4月9日

Firebird 2.0.6 Release Candidate 1リリース

Firebird 2.0.6 Release Candidate 1がリリースされました。2.1/2.5の開発で発見された不具合がバックポートされたようです。

Firebird File Repositories (download)
Firebird 2.0.6 Release Candidate 1 Release Notes

2010年3月30日

Delphiのメモリマネージャ

Delphiのメモリマネージャには大きく分けて
  • Delphi 2.0-2005の標準メモリマネージャ
  • Delphi 2006以降の標準メモリマネージャ
  • カスタムメモリマネージャ
があります(Win16は気にしないってことで)。Delphi 2.0-2005の標準メモリマネージャはWin32初期の(あまりリソースが潤沢でなかった)環境に合わせて作られており、小さいメモリブロックの割当と解放を繰り返すと空きメモリの断片化が生じたり、パフォーマンスがあまりよくないなどの欠点が指摘されてきました。一方でDelphiのRTLではメモリマネージャの置き換え(カスタムメモリマネージャ)が当初(Delphi 2.0)から考慮されており、このメカニズムを利用してこれらの問題点を改善しようというオープンソースプロジェクトがFastMMです。Delphi 2006(C++Builder 2010)ではこの成果を取り入れて新しい(FastMMベースの)メモリマネージャが採用されました。Delphi 2006(C++Builder 2010)以降でも依然としてFastMMは使用可能で、標準の新しいメモリマネージャでは省略されている詳細レポート機能などを使用することでメモリリークの発生状況をより詳細に報告させることができます。またカスタムメモリマネージャとしてはFastMMの他に中村さんのNkMM(DHGLに付属)などもあります。NkMMの詳細については

Delphi Graphic Secrets Know-how & Libraries (amazon)/中村拓男著/ソフトバンククリエイティブ(ソフトバンク パブリッシング)/ISBM 978-4797319224 (ISBN 4-7973-1922-4)/3,129円

のp.202-204のNOTE"Delphiのメモリ管理の大きな問題点"を参照してください(Delphi Graphic Secretsは復刊ドットコムで2010/03/30現在41票となっています)。

FastMMやDelphi 2006以降の標準メモリマネージャについては以下のURLが参考になります。

Borland Developer Studio 2006の新しいメモリマネージャ - by Pierre le Riche
Delphiアプリケーションのメモリリーク検出法
某所 - 新しいメモリマネージャの仕組み
FastMMの導入

またDelphi 2.0の標準メモリマネージャについては

Inside Delphi (amazon)/Ray Lischner著/光田秀訳/アスキー/ISBN 978-4756119513 (ISBN 4-7561-1951-4)/9,240円

の"第19章 ヒープの秘密"に詳細な解説があります。

2010年3月18日

IDE Fix Pack 3.01 for Delphi 2010リリース

Andreas HausladenさんIDE Fix Pack for Delphi/C++Builder 2010がアップデートされてVersion 3.01になっています。Delphi 2010版のみの更新で、リリース時の手違いによるものとのことです。

Andy’s Blog and Tools » IDE Fix Pack 3.01 for RAD Studio 2010

2010年3月17日

IDE Fix Pack 3.0リリース

Andreas HausladenさんのIDE Fix Pack (for Delphi/C++Builder 2007/2009/2010)がアップデートされてVersion 3.0になっています。

Andy’s Blog and Tools » IDE Fix Pack 3.0 released

2010年3月16日

[書籍]Delphi 2010 Handbook

2010/03/06の発売時に注文したMarco CantuさんDelphi 2010 Handbook書籍版が今日(2010/03/16)配送されてきました。さていつ読み終わることやら。
ちなみに先日のデベロッパーキャンプで藤井さんはDelphi 2010 Handbookはたぶん翻訳されないようなことを言ってました。まぁこんなご時世なので致し方ないかとは思います。

2010年3月12日

第16回 エンバカデロ・デベロッパーキャンプ 2日目

b-mobileでもLive Meetingの専用クライアントなら辛うじて動作する模様(音声なしで画像のみですけど)。
  • [2A]Delphiテクニカルセッション「Delphi 2010 言語の新機能」
    Davidさんのセッションスタート。会場でも音声状態がよくないですね(通訳さんのものを含めて)。まぁスライドを見てればいいって話ですか。英語ですけど。

  • [2B]テクニカルセッション「ソフトウェア・アーキオロジー- もしあなたが100万行のコードの引き継ぎをすることになったらどうしますか?」
    Michaelさんのセッションでもやはり音声状態がよろしくないですね。archeology(archaeology)は考古学、という言葉です。引き継いだコードは発掘した遺物のように全く未知の状態から順番に解明していく必要がある、というような比喩でしょうか。

  • Live Meetingは接続できてもイメージが来ない…b-mobileだめか。接続し直して復帰。会場内の音声も多少は改善されたかな。

  • [2C]Delphi/C++テクニカルセッション「正しいGUIの作り方 - 重い処理でGUIが固まるのを防ぐ」
    高橋さんのセッションでスレッドの内部変数のアクセスは何らかの排他処理が必要、という部分はクリティカルセクション、System.TMonitor(ja)(Delphi 2010 2009 or later)、InterlockedExchangeAddのようなInterlocked系APIなど。クリティカルセクションについては以下の問題に注意。ってQC73347は"Cannot Fix"でClosedになってますね。It is not Delphi's issue. って話じゃないと思うんだけど。
    Team Japan » InitializeCriticalSectionEx
    早くもサンプルコードが公開されています(15:56)。
    Team Japan » サンプルコードを用意しました: 【2C】 Delphi/C++テクニカルセッション

  • 昼食時間が…。

  • [2D]スペシャルスポンサーセッション「Delphi, UC-win/Road SDKによるVRアプリケーション開発事例」
    宮本さんのセッションは3Dの絵がぬるぬる動く感じが出なかったのが残念。パッケージを使ったシステムの拡張という面で非常に興味深い。書籍も出すようです。

  • [2E]Delphiコミュニティセッション「インストーラ対決一本勝負!」
    インストーラ対決で、山本さんがInstallAwareを扱っていますが、具体的なところをもっと知りたい人はとりあえずここを読んでおくべき。ちなみに奇特なってのは山本さんの表現ですから。
    ちっぷす
    インストーラでシリアル番号の認証をしたりするのであればInstall Shield Express(113,400JPY)かInstallAware Developer(1,499USD=135,900JPY)が必要です。またInno Setupでは[Code] セクションでイベント関数 CheckSerial を記述している場合には、シリアル番号入力欄が表示される。ということで可能なようです。
    Inno Setup 日本語 デベロッパーズガイド: ウィザードページ(ダイアログ)について

  • [2F]Delphiテクニカルセッション「Delphiでキカイを制御する - アプリケーションの設計とテクニック」
    高木さんのセッションは機器不調のためディレイ中(15:11)。始まりました(15:17)。説明で使用しているソースコード(ライブラリ)は後日イマジオムさんのwebページで公開されるとのこと。TCommandQueueやSynchronizeは無名関数やジェネリクスを応用したら更にいろんなことができるかも。って早っ。25分も巻いて終わりました。

  • [2G]クロージング~抽選会
    クロージングセッションには藤井さんとJasonさんが登場。Tシャツありがとうございます。QueryPerformanceCounterとか呼んでますね、このコード。ん?TThreadPoolってどこのもんでしょう?これ(Allen Bauerさんの解説)かな?どうもマルチスレッドで素数を計算するようなプログラムのような気がする…。どっちかというと.NETなのかな?

無事終了。みなさまお疲れさまでした。会場を提供していただいたフォーラムエイトさま、コンセントがあって助かりました。

2010/03/17追記: プレゼンテーション資料がダウンロードできるようになっています。

第16回エンバカデロ・デベロッパーキャンプ - 資料ダウンロード

2010/03/18追記: ビデオもダウンロード/リプレイできるようになっています(要登録)。またアンケートも実施されています。そういえばアンケートやらなかったな、とは思っていました。

第16回 エンバカデロ・デベロッパーキャンプ ビデオ視聴/ダウンロード
第16回 エンバカデロ・デベロッパーキャンプ アンケート

2010年3月11日

第16回 エンバカデロ・デベロッパーキャンプ 1日目

会場は20人弱、中継は同時に最大140人くらいですかね(藤井さんによれば合計で200人くらいとのこと)。b-mobileでLive Meetingに参加するのはちょっと難しいみたいです。Javaのクライアント(Web Access)は動作しないし、Office Live Meetingクライアントはダウンロードできないし。期限が切れたらauかDoCoMoにするか?(仕事の都合上僻地で使えないWiMaxは選択肢に入らない)
  • [1A]イントロダクションセッション「デベロッパーキャンプを2倍楽しむ方法・新企画のご紹介」
    堀江さん頑張れ。Delphi and C++Builder Application Showcaseに日本発のものがないということでなにやら企画中とのことです。でもうちみたいに委託だとお客さんに無断で、というわけにはいかないわけでねぇ。

  • [1B]C++チュートリアルセッション「C++Builder 5/6ユーザと初心者のためのC++Builder 2010入門」
    個人的にはPadding/Marginsプロパティの意味がわかったのが収穫(C++Builder使いじゃないもので)。

  • [1C]Delphiチュートリアルセッション「Delphi 7ユーザと初心者のためのDelphi 2010入門」
    細川さんのセッションは音声トラブルのため遅延中…(14:40)。直ったのかな?リスタート(14:44)。

  • 一応各セッションは5分前終了のような雰囲気だけど、会場にいると明示的な休憩がなくてちょっと不便かも。

  • [1D]グランドオープニングセッション「エンバカデロ製品戦略 ソフト開発者・DB管理者のための真に自由なIT環境」
    Jasonさんの"What's Next?"ではToolCloud、Commodore、Fulcrum、InterBaseに言及あり。

  • [1E]Delphiテクニカルセッション「Delphiでの文字コードのハンドリングについて 」
    富永さんのセッションで出てくるWindowsのCodePageの一覧。
    Code Page Identifiers (Windows)
    新しいIPAexフォントではJIS0213:2004制定時に字体変更された文字の旧字体を異体字として収容とのこと。
    IPAフォントのダウンロード || OSS iPedia
    IPAフォントリリースノート || OSS iPedia

  • [1F]C++テクニカルセッション「C++言語のパワーとRAD/ビジュアルアプリケーション開発の活用」
    JasonさんのC++BuilderのセッションはC++0x draft準拠機能、Boostの正規表現、新しいTDirectoryクラス、デバッガのビジュアライザ、スレッドのデバッグ機能などのデモ。


1日目は無事終了。[1B][1F]を見ていて思ったのは、C++BuilderはVSユーザのなかでもC++を使いたいんだけどC++/CLIはありえない、かといってMFCはいまさら、という人にフォーカスしてアピールするといいのでは、ということ。そのためにはC++0xなどの標準準拠度の向上やBoostのようなライブラリへの積極的な対応が重要なのではないでしょうか。

2010年3月10日

Microsoft Monthly Update 2010/03

今日はMicrosoftのセキュリティアップデートの日です。
MS10-016
MS10-017

2010年3月6日

Delphi 2010 Handbook (書籍版)出版

Marco CantuさんDelphi 2010 Handbookの書籍版がAmazon(US)から発売されました。全318ページで、定価は43.50USD(3,930JPY)ですが、現時点(2010/03/06)では34.25USD(3,095JPY)のディスカウントが設定されています。またDelphi/C++Builder/RAD Studio 2010の登録ユーザはEDNのCodeCentralからPDF形式のものをダウンロードできるようになっています。

元ねたはDelphi 2010 Handbook on Amazon, Developer Network

2010/03/17追記: Delphi/C++Builder/RAD Studio 2010の登録ユーザ向のDelphi 2010 HandbookとTMS Smooth Controls Packの無償ダウンロードについての記事がEDNに追加されています。

Delphi 2010, C++Builder 2010 および RAD Studio 2010 の登録ユーザーの方へ、 2つの無償ダウンロードの提供

2010/03/27追記: Amazon(JP)での取り扱いを確認しました。4,956円で配送料無料ですし、日本語で注文できるので、こちらのほうがいいかもしれません。

Amazon.co.jp: Delphi 2010 Handbook: A Guide to the New Features of Delphi 2010; Upgrading from Delphi 2009: Marco Cantu: 洋書

元ねたはDelphi 2010 Handbook in Europe

2010年3月1日

[書籍]プログラマのための文字コード技術入門

ジュンク堂書店の新宿店で

WEB+DB PRESS plusシリーズ プログラマのための文字コード技術入門/矢野啓介著/技術評論社/ISBN 978-4-7741-4164-0/2,709円

を見かけたので購入。

2010年2月23日

Delphi 2010 Handbook (PDF版)出版

Marco CantuさんDelphi 2010 Handbookが完成してPDF版(28.00USD/2,474JPY)が発売されました。書籍版(43.50USD/約3,970JPY)は来週くらいにCreateSpaceから、その後数週間でAmazonなどでも入手可能になるようです。
またPDF版をDelphi 2010登録ユーザ向にEmbarcaderoから無償でダウンロードできるように交渉中とのことです。

元ねたはDelphi 2010 Handbook Released

2010/03/01追記: Delphi 2010 Handbook In Printによると書籍版(print on demand)がCreateSpaceで発売になっています。

2010年2月17日

第16回エンバカデロ・デベロッパーキャンプ開催決定

第16回 エンバカデロ・デベロッパーキャンプ前回予告されたとおりにウェブセミナー形式(Mircosoft Live Meeting)で2010年03月11日-12日の2日間で開催されます。今回は中継が可能になったということでDavid Intershimoneさん(エンバカデロ・テクノロジーズ チーフエヴァンジェリスト)、Jason Vokesさん(エンバカデロ・テクノロジーズ テクノロジー&インターナショナルマーケティング シニアディレクタ)、富永英明さん(有限会社エイブル)といった豪華布陣です(あっいつものみなさんが豪華じゃない、という意味ではありません)。
再放送が可能になれば時間や日付が合わない人も見られるようになっていいと思います。

2010/03/05追記: ライブ会場に行きます。仕事はいいのか…?

2010年2月10日

Microsoft Monthly Update 2010/02

今日はMicrosoftのセキュリティアップデートの日です。
MS10-003
MS10-004
MS10-005
MS10-006
MS10-007
MS10-008
MS10-009
MS10-010
MS10-011
MS10-012
MS10-013
MS10-014
MS10-015

Delphi 2010におけるREST

Marco CantuさんによるDelphi 2010を使用したRESTの扱いに関するホワイトペーパが翻訳、公開されています。また解説ビデオ(英語)も公開されています。

Rad Studio 2010 in Action JP Marco
「Delphi 2010におけるREST」ホワイトペーパーダウンロード

元ねたはMarco CantuさんDelphi REST White Paper in Japanese

2010年2月9日

Firebird 2.5 Release Candidate 2リリース

firebirdsql.orgのサーバクラッシュの影響で遅れていたFirebird 2.5のRelease Candidate 2ですが、やっとリリースされました。

Firebird File Repositories (download)
Firebird 2.5 Release Notes

2010年2月8日

一定時間ユーザ入力がないことを検知する

一定時間ユーザがPCの操作が行わなかったことを検知するにはGetLastInputInfo(ja)で最後に操作したタイミング(LASTINPUTINFO構造体のdwTime)と現在のGetTickCount(ja)の値(いずれもmsec単位)を比較します。注意点としてはGetLastInputInfoはシステム全体ではなく現在のセッションのユーザ入力のみを扱うというところでしょうか。
このサンプルではボタンをクリックしてポーリングを開始した後でユーザ入力(キーボード、マウス操作)が10秒間発生しなかったときにメッセージダイアログを表示します。またポーリング毎に前回からユーザ入力が発生していない場合はLabel1に表示しているGetTickCountの表示をclRedに変更しています。
procedure TForm1.Button1Click(Sender: TObject);
begin

  Timer1.Enabled := True;
  Button1.Enabled := False;

  FLastTick := 0;
  Timer1Timer(Sender);

end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  LII: TLastInputInfo;
begin

  FillChar(LII,SizeOf(LII),0);
  LII.cbSize := SizeOf(LII);
  if GetLastInputInfo(LII) = False then
  begin
    Exit;
  end;

  Label1.Caption := 'Last user input: ' + IntToStr(LII.dwTime);
  if FLastTick <> LII.dwTime then
  begin
    Label1.Font.Color := clWindowText;
    FLastTick := LII.dwTime;
  end
  else
  begin
    Label1.Font.Color := clRed;
  end;

  if (GetTickCount - LII.dwTime) > (10 * MSecsPerSec) then
  begin
    Timer1.Enabled := False;
    Button1.Enabled := True;
    MessageDlg('Detect for input idle (10 seconds).',
               mtInformation,[mbOk],0);
    Label1.Caption := '';
  end;

end;
別解としてSetWindowsHookEx(ja)にWH_KEYBOARD_LL(ja)/WH_MOUSE_LL(ja)を指定して低レベルフックをかけ、フック関数内でGetTickCountの値を取得、というものも考えられますが、
  1. DelphiのIDEから実行するとデバッガでブレークしたときにやたらに時間がかかる。
  2. Windows XPのVisual Style(Luna)とマウスの低レベルフックの相性が悪く(どちらかというとLunaの不具合)、フォームの最小化、最大化ボタンをクリックしたときに極端に反応が悪くなる。
という問題があるため、こちらはあまりお勧めできません。

2010/03開催のウェブセミナー

2010/03/03 18:00-19:00(JST) 3/3 - Webセミナー「DB Optimizerによるデータベース パフォーマンスチューニングの概要と評価方法」
2010/03/24 18:00-19:00(JST) 3/24 - Webセミナー「DBArtisan チュートリアル」

2010年1月28日

RAD Studio/C++Builder 2010 Hotfix 1

RAD Studio/C++Builder 2010のHotfix 1がリリースされています。QC79954のCコマンドラインプリプロセッサの不具合を修正するものです。

RAD Studio 2010 (C++Builder 2010) 用 Hotfix1
27567 Hotfix 1 for RAD Studio 2010 (C++Builder 2010)

2010/02/02追記: EDNの日本語の記事が出たのでリンクを差し替えました。

2010年1月27日

Fulcrum

Allen BauerさんThe Oracle at Delphi » Requiem for the {$STRINGCHECKS xx} directive…によれば、従来"Project X"と呼ばれていた(現在開発中のMac/Linuxクロス対応)次期RAD Studioのコードネームは"Fulcrum"だそうです(さっそく"Fulcrumだって?Mig-29かよ"というツッコミが入っていますが)。それにしてもCommodoreはいずこへ…。
ちなみに本題である$STRINGCHECKSコンパイラディレクティブ(日本語ページはない)というのは、Delphi 2009/2010でUnicodeStringがデフォルトになった一方でC++Builderでは(Stringではなく)明示的にAnsiStringが使用されてきたため、この間を取り持つ変換処理を挿入するかどうかを指定するためもの(プロジェクトオプションのコンパイラオプションでデフォルトを指定可能)。Fulcrumではこれが廃止される(無視されるようになる)、というお話みたいです。

2010年1月22日

WindowsのサービスからGUIを持つプロセスを起動

Windows Vista/7ではセッション0の分離が行われたため、単純にサービスからGUIプロセスを起動できなくなりました。そこで
  1. Win32APIのWtsGetActiveConsoleSessionID関数で現在ログオンしているユーザのセッショントークンを取得。
  2. サービス(SYSTEMアカウント上で動作している)のプロセスのユーザトークンを取得。
  3. ユーザトークンを複製。
  4. ログオンユーザのセッションを複製したトークンに設定。
  5. 複製したトークンを使用してWin32APIのCreateProcessAsUser(ja)関数でプロセスを起動。
という手順を踏み、サービスからGUIを持つプロセスを起動する方法を説明した記事。興味深い。とりあえずメモ。

From Zero To One » Blog Archive » How to start a GUI process from service, under Windows Vista/7

Microsoft OOB Update 2010/01

Microsoftの定例外のセキュリティアップデートがリリースされています。
MS10-002

2010年1月21日

RAD Studio/Delphi/C++Builder 2010 Update 4/5適用後のTTimerの振る舞いについて

Team Japanの高橋さんの

Team Japan » TTimerコンポーネント

によれば、TTimerのSetEnabled(True)の実装がRAD Studio/Delphi/C++Builder 2010 Update 4/5適用で変更され、これに伴なってメインスレッド以外のスレッドからTTimerをスタートしようとするとうまくいかないという報告があるようです。

QC81256: Changing TTimer.Enabled from thread does not work anymore since Update 4 for Delphi 2010
QC80376: RAD Studio 2010 IdTCPServer1Execute Error (After latest updates)

2010年1月19日

RAD Studio/Delphi/C++Builder 2009 Hotfix 3

RAD Studio/Delphi/C++Builder 2009のHotfix 3がリリースされています。IntraWebでUTF-8のWebRequestの内容を解析するTUTF8ContentParserの中国語(繁字体)マルチバイト文字の処理の問題を解決するようです(高橋さんによれば従来サポートでのみ配布していたものが正式公開になったようです)。IntraWebを10.0.21に更新した上でUTF8ContentParser.pasをプロジェクトフォルダに配置してリンク対象にし、かつ実行時パッケージを使用しないようにする、という変則的なものです。

Delphi/C++Builder/RAD Studio 2009用 Hotfix 3
27563 Hotfix 3 for Delphi 2009 and C++Builder 2009
Team Japan » Delphi/C++Builder 2009向けHotfix 3 for IntraWeb 10

なおこの問題は最新のRAD Studio/Delphi/C++Builder 2010では発生しない(The problem does not occur in the latest 2010 version.)とのことです。

2010/01/20追記: IntraWeb 10.0.21についてはTeam Japanのこちらの記事も参考になります。

Team Japan » IntraWeb Ver10.0.21 がリリースされました

2010/01/25追記: EDNの日本語の記事が出たのでリンクを差し替えました。

2010年1月13日

Microsoft Monthly Update 2010/01

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

2010年1月7日

2010/01開催のウェブセミナー

2010/01/14 18:00-19:00(JST) 1/14 - Webセミナー「詳細!DataSnap」
2010/01/21 17:00-18:00(JST) 1/21 - Webセミナー「エンバカデロ・テクノロジーズによるツール・クラウド All-Access」Replay download
2010/01/27 18:00-19:00(JST) 1/27 - Webセミナー「DB Optimizerによるデータベース パフォーマンスチューニングの概要と評価方法」

時間があれば…。

2010/01/14追記: 「詳細!DataSnap」は気がついたら終わっていた。だめじゃん…。

2010/01/26追記: うぅむ、時間がない…。

2010/02/25追記: 2010/01/21分のリプレイのリンクを追加しました。

2009年12月25日

TTouchKeyboardのハッキング

Chris BensenさんによるDelphi 2010の新機能の一つ、タッチキーボードのhackについての解説です。

Hacking the TTouchKeyboard Part Iではまずキーボードレイアウトの名称の一覧を取得し、この名称からキーボードレイアウトをロードします。

Hacking the TTouchKeyboard Part IIでは取得したキーボードレイアウトをXMLで読み書きする、というサンプルプログラムが紹介されています(タッチキーボードとは直接関係ない部分だからか説明省略です)。

Hacking the TTouchKeyboard Part IIIでは逆にXML表現のキーボードレイアウトをコンパイルしてリソースとしてリンクすることができる形式に変換するキーボードレイアウトコンパイラが紹介されています。

Hacking the TTouchKeyboard Part IVではXML表現のキーボードレイアウトファイルの各要素が説明されています。

(以下追記予定)

2009年12月24日

2009年12月23日

IDE Fix Pack 2.9リリース

Andreas HausladenさんのIDE Fix Pack (for Delphi/C++Builder 2007/2009/2010)がアップデートされてVersion 2.9になっています。

Andy’s Blog and Tools » New IDE Fix Pack 2.9