(10/12/01~)

10/12/03

Windows Slate/Windows 7/マルチタッチ対応アプリケーション開発者会議
 参加者の情報によると、評価用に貰えたスレート PC は オンキヨー TW217A5 だった模様。スレート PC ネタがない訳じゃなかったのだけれど、ここ最近忙しくて東京まで足を運ぶのは無理だった (そもそも、招待が会議の1週間前ではスケジュールを押さえるのもままならない)。残念。

第18回 エンバカデロ・デベロッパーキャンプ
 開催が近づいて来ました。私は LiveMeeting で聴講しようと思っております。何かしらのサプライズがある事を期待して...。


10/12/07

第18回 エンバカデロ・デベロッパーキャンプ
 徹夜明けの寝不足だったもんで、【T4】Q&A セッション から聴講しました。相変わらず LiveMeeting の音量問題は解決しておらず、USTREAM と併用する事にしました。細川さんのセッションの最後の辺りで PC がフリーズしてしまい、再起動を余儀なくされたのが非常に残念でした (急いで再起動したのですが間に合いませんでした)。

 いきなり自分の名前が出てくると心臓に悪いのですが、今日もマイグレーションの話が出てましたね。マイグレーション話はあちこちでした上に、デブキャンでも私を含め何人もの方がセッションされましたので、「もうこのテの質問はないだろう」と思っていたのですが、まだ出てきますね。

 よくよく考えてみると、Delphi 購入時期というのは会社/個人問わずタイミングがありますので、2009 だったり 2010 だったり XE だったり "これから" だったり、とマイグレーション話の出るタイミングもバラけてしまうのは仕方のない事なのでしょう。

 ただ...。例えば「俺、北海道出身だよ」と言った途端に「木彫りの熊って実家にあるの?」と言われるのと同様、「(キミは面白いつもりで言ったのかもしれないが、それは既に何百人からも言われた事なのだよな...(´ー`)y-~~ )」状態であるのは確かです (w

 聴き逃したセッションに関しては後に配布されるであろう資料を含めて後日チェックしたいと思っております。セッションスピーカの方々お疲れ様でした。

RAD Studioマイグレーションセンター
 マイグレーションに関してはエンバカさんのこのページを押さえておけば間違いないと思います。余裕があれば 英語のページ もチェックするといいでしょう (日本語ページとは内容が一部異なっています)。見出しが "Delphi、C++Builder、RAD Studio XE - バージョンアップ情報" なので一見するとただの製品情報ページのようにも思えますが、紛う事なきマイグレーション情報の蓄積所です。


10/12/10

TComboGrid
 マイグレーションと言えば...

 なんだかんだで、Delphi 2 の頃のこのコンポーネントをマイグレーションし続けて使っています。これは "Delphi 2.0J コンポーネントセット 服部誠著 (ISBN: 4756110576)" に付属の TComboGrid というコンポーネントです。この Grid の特徴は...

 ...等々です。これを改修して使っています。  ...等々です。テーマに対応しているので、Delphi 2009 とかでものっぺりとしたグリッドにはなりません。"TComboGrid EJ改" 程度には手を入れていると思います。

 ちょっとした事にはとても便利で重宝するコンポーネントなのですが、入手難なのが残念ですね。


10/12/13

Embarcadero のアップグレード優待
 関連して、BDS 2006 / Turbo 2006 ユーザは来年になると XE のバージョンアップ版を購入する事ができなくなります。"次の製品リリース" という意味の来年ではなく、2011/01/01 というカレンダー通りの来年です。ご注意ください。

自動灯油給油ポンプ (その1)
 「なんか調子悪い」奥さんはそう言った。何が調子悪いのかわからずに給油したら灯油が溢れた。オートストップ機構が壊れているとは...orz 頭では解っているのだが、あわわわわと給油口を上に向けてしまった。スイッチ切らないと止まらないよね...。こぼれた灯油には粉洗剤をふりかけて吸い取らせ、そのまま掃いてゴミ袋へ。

自動灯油給油ポンプ (その2)
 「なんか調子悪い」会社で使ってる奴だ。全然吸わないので、電池を交換したのだがモータ作動音すらしない。こりゃ壊れてるな。

自動灯油給油ポンプ (その3)
 「なんか調子悪い」からと "その2" とは別に会社に捨ててあった灯油給油ポンプをいじってみた。モータ作動音はする...が、止まる。よく見てみると電池ボックスに液漏れした跡がある。分解してみたら電池ボックスの端子が錆びて基板から剥がれていた。これは修理だな。

  1. 端子を "KURE CRC サビ取りクリーナー" で錆落とし。古くなった歯ブラシでこすりながらだと効果的。
  2. 取り外した端子を水洗いして乾燥。
  3. 取り外した基板もを水洗いして乾燥。
  4. 半田付け。
  5. 端子を "KURE CRC 5-56" で保護。
 これで何事もなかったかのように動作した。

給油時間を短縮する
 リフィル時間を短縮するには、ストーブ/ファンヒータのタンクよりも上方にポリタンクを設置し給油するといい (ポリタンク持ち上げてもいいけど)。知ってる人には当たり前な "サイフォンの原理" だ。高速に給油できる分、電池の持ちがいい。ただ機種によっては...また灯油が少なく給油管に空気が混じる場合には効果がない事があるので注意。

手動式灯油給油ポンプの使い方
 念のため。

  1. ストーブ/ファンヒータのタンクよりもポリタンクを上方に配置する。
  2. ポンプをセットする。
  3. ポンプの上部のつまみを閉める。
  4. ポンプを数回握る。
  5. "サイフォンの原理" により、勝手に給油される。
  6. ストーブ/ファンヒータのタンクのゲージを注視する。
  7. ゲージが上限に来る前に余裕をもってポンプの上部つまみをゆるめる。
  8. 完了
 ポンプ内部の分があるので、ゲージ上限になってからポンプの上部つまみをゆるめると大抵溢れてしまう事に注意。

嫌がらせ?
 Delphi で新規プロジェクトを作ってボタンを 3 つ貼ってみるとこうなりますね。

 ソースコードには自動生成された TButton が並んでいると思います。

 実は、最近の Delphi では TButton をカンマで区切って並べる事ができます。

 コンパイルはちゃんと通って、実行する事もできます。Delphi の文法的には間違っていないという事です。フォームに貼り付けたコントロールが大量にある場合にはこれを使うと便利そうです..."が"。Button2 を削除してみましょう。

 結果はご覧の通りです。Button3 まで消えてしまいます。

 カンマ区切りで整形したソースを配布しないで下さいね、ただの嫌がらせです (^^;A なお、Delphi 7 等の古い Delphi では文法エラー (?) となります。


10/12/14

Ctrl+Shift+0 のコンビネーション
 アプリケーションに実装されている Ctrl+Shift+0 (Shift+Ctrl+0) のコンビネーションは Vista 以降の OS では動作しない。このショートカットは IME 切り替えのショートカットで使われているからだ。これを解除するには以下の操作を行う。

  1. コントロールパネルを開く
  2. "地域と言語" を開く
  3. [キーボードと言語] タブの [キーボードの変更] ボタンを押下
  4. [詳細なキー設定]タブの "入力言語を切り替える" を選択
  5. [キーシーケンスの変更] ボタンを押下
  6. "キーボードレイアウトの切り替え" で "割り当てなし" を選択
  7. [OK] ボタンを押下し、ダイアログを閉じる
 KB967893 に詳細がある。ちなみにこのコンビネーションは Delphi のコードエディタだと "しおり 0" に相当する。


10/12/15

第18回 エンバカデロ・デベロッパーキャンプ - セッション資料ダウンロード
 先に開催されたデベロッパーキャンプの資料がダウンロードできるようになったようだ。本サイトの資料も更新しておいた。

 ライトニングトークの資料がないので、タイムアップになってしまったセッションスピーカの方の話が中途半端でモヤモヤする感は否めない。【T4】セッションと併せて、動画の再配信があると嬉しいかも。


10/12/16

Delphi 2.0J コンポーネントセット (ISBN: 4756110576)
 ちょっと書き忘れていましたが、この本はコンポーネント作成の基礎を学ぶにはいいと思います。コンポーネントを作る気はなくとも、コンポーネントを修正したい場合に必要な知識を得る事ができるでしょう。Delphi 5~6 で変更になった dsgnintf の事は別に考えなくてはいけませんが...。

第18回 エンバカデロ・デベロッパーキャンプ - ビデオ公開
 デスヨネー。【T4】/【T6】がアップされています。観れなかった【T4】の前半が観れて満足満足。

 【T4】「開発者の疑問に答える!徹底Q&A」

 【T6】ライトニングトーク 「共有!みんなの開発事例、開発経験、テクニック 」


10/12/20

数値 <-> n 進数変換
 Delphi で 10 進数を 16 進数に変換するには、

 等を使いますが、2 進数や 8 進数へ変換するにはどうすればいいのでしょうか?

unit RadixConv;

interface

uses
  SysUtils, Math;

type
  TRadixRange  = 2..256;
  TDigitsRange = 0..255;

const
  FRadixTable = '0123456789ABCDEF';

// 数値 -> n 進数文字列
function IntToStrEx(const Value: Int64; const Radix: TRadixRange = 10const Digits: TDigitsRange = 0const RadixTable: string = ''): string;
// n 進数文字列 -> 数値
function StrToIntEx(const s: stringconst Radix: TRadixRange = 10const RadixTable: string = ''): Int64;

implementation

function IntToStrEx(const Value: Int64; const Radix: TRadixRange = 10const Digits: TDigitsRange = 0const RadixTable: string = ''): string;
// -----------------------------------------------------------------------------
// 数値 -> n 進数文字列
//
// 引数
//   Value     : 変換する数値
//   Radix     : 基数 (Radix 進数) Radix >= 2
//   Digits    : 表示桁 (0 で自動)
//   RadixTable: 桁文字テーブル
//
// 戻り値
//   Radix 進数文字列
// -----------------------------------------------------------------------------
var
  i: Integer;
  dRadixTable: String;
  dValue: UInt64;
begin
  result := '';
  // Radix Table
  if RadixTable = '' then
    dRadixTable := FRadixTable
  else
    dRadixTable := RadixTable;
  // Error
  if Length(dRadixTable) < Radix then
    Exit;
  // Calc
  dValue := Value;
  for i:=1 to 64 do
    begin
      result := dRadixTable[(dValue mod Radix) + 1] + result;
      dValue := dValue div Radix;
      if dValue = 0 then
        Break;
      if (Digits > 0and (i = Digits) then
        Break;
    end;
  // Format
  if Digits > 0 then
    begin
      result := Copy(result, Min(1, Digits - Length(result) + 1), Digits);
      result := StringOfChar(dRadixTable[1], Digits - Length(result)) + result;
    end;
end;

function StrToIntEx(const s: stringconst Radix: TRadixRange = 10const RadixTable: string = ''): Int64;
// -----------------------------------------------------------------------------
// n 進数文字列 -> 数値
//
// 引数
//   s         : Radix 進数文字列
//   Radix     : 基数 (Radix 進数) Radix >= 2
//   RadixTable: 桁文字テーブル
//
// 戻り値
//   数値
// -----------------------------------------------------------------------------
var
  i: Byte;
  dRadixTable: String;
  dValue: Byte;
  Loop: Byte;
  s2: string;
begin
  result := 0;
  // Radix Table
  if RadixTable = '' then
    dRadixTable := FRadixTable
  else
    dRadixTable := RadixTable;
  // Error
  if Length(dRadixTable) < Radix then
    Exit;
  // Loop Counter
  s2 := Trim(s);
  for i:=1 to Length(s2) do
    if s2[i] = dRadixTable[1then
      s2[i] := ' '
    else
      Break;
  s2 := Trim(s2);
  Loop := Length(s2);
  // Calc
  for i:=1 to Loop do
    begin
      result := result * Radix;
      dValue := Pos(s2[i], dRadixTable);
      if (dValue = 0or (dValue > Radix) then
        begin
          result := 0;
          Break;
        end;
      result := result + (dValue - 1);
    end;
end;
end.

 このようになります。

var
  s: string;
  v: Int64;
begin
  Memo1.Lines.Clear;

  Memo1.Lines.Add('[Decimal -> Hex]');
  Memo1.Lines.Add('-10');

  // -10 を Byte (8 bit) の 16 進数表現で表示
  s := IntToStrEx(Byte(-10), 16, SizeOf(Byte) * 2);
  Memo1.Lines.Add('Byte: '     + s);
  // -10 を Word (16 bit) の 16 進数表現で表示
  s := IntToStrEx(Word(-10), 16, SizeOf(Word) * 2);
  Memo1.Lines.Add('Word: '     + s);
  // -10 を Longword (32 bit) の 16 進数表現で表示
  s := IntToStrEx(Longword(-10), 16, SizeOf(Longword) * 2);
  Memo1.Lines.Add('Longword: ' + s);
  // -10 を UInt64 (64 bit) の 16 進数表現で表示
  s := IntToStrEx(-1016, SizeOf(UInt64) * 2);
  Memo1.Lines.Add('UInt64: '   + s);

  Memo1.Lines.Add('');

  Memo1.Lines.Add('[Hex -> Decimal]');

  // 'FF' を 16 進数 -> 10 進値
  s := 'FF';
  Memo1.Lines.Add('0x' + s);
  v := Shortint(StrToIntEx(s, 16));
  Memo1.Lines.Add('Shortint: ' + IntToStr(v));
  v := Byte(StrToIntEx(s, 16));
  Memo1.Lines.Add('Byte: '     + UIntToStr(v));
  v := Smallint(StrToIntEx(s, 16));
  Memo1.Lines.Add('Smallint: ' + IntToStr(v));
  v := Word(StrToIntEx(s, 16));
  Memo1.Lines.Add('Word: '     + UIntToStr(v));
  Memo1.Lines.Add('');

  // 'FFFF' を 16 進数 -> 10 進値
  s := 'FFFF';
  Memo1.Lines.Add('0x' + s);
  v := Smallint(StrToIntEx(s, 16));
  Memo1.Lines.Add('Smallint: ' + IntToStr(v));
  v := Word(StrToIntEx(s, 16));
  Memo1.Lines.Add('Word: '     + UIntToStr(v));
  v := Longint(StrToIntEx(s, 16));
  Memo1.Lines.Add('Longint: '  + IntToStr(v));
  v := Longword(StrToIntEx(s, 16));
  Memo1.Lines.Add('Longword: ' + UIntToStr(v));
  Memo1.Lines.Add('');

  // 'FFFFFFFF' を 16 進数 -> 10 進値
  s := 'FFFFFFFF';
  Memo1.Lines.Add('0x' + s);
  v := Longint(StrToIntEx(s, 16));
  Memo1.Lines.Add('Longint: '  + IntToStr(v));
  v := Longword(StrToIntEx(s, 16));
  Memo1.Lines.Add('Longword: ' + UIntToStr(v));
  v := StrToIntEx(s, 16);
  Memo1.Lines.Add('Int64: '    + IntToStr(v));
  v := UInt64(StrToIntEx(s, 16));
  Memo1.Lines.Add('UInt64: '   + UIntToStr(v));
  Memo1.Lines.Add('');

  // 'FFFFFFFFFFFFFFFF' を 16 進数 -> 10 進値
  s := 'FFFFFFFFFFFFFFFF';
  Memo1.Lines.Add('0x' + s);
  v := StrToIntEx(s, 16);
  Memo1.Lines.Add('Int64: '    + IntToStr(v));
  v := UInt64(StrToIntEx(s, 16));
  Memo1.Lines.Add('UInt64: '   + UIntToStr(v));
end;

 16 進数以上を扱う場合には、基数の桁文字テーブルを自前で定義しなくてはなりません。

var
  dRadixTable: string;
  v: Int64;
  s: string;
begin
  // BASE32
  v := 100;
  dRadixTable :=               'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  dRadixTable := dRadixTable + '234567';
  s := IntToStrEx(v, 320, dRadixTable);
  Memo1.Lines.Add(s);

  // BASE64
  v := 100;
  dRadixTable :=               'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  dRadixTable := dRadixTable + 'abcdefghijklmnopqrstuvwxyz';
  dRadixTable := dRadixTable + '+/';
  s := IntToStrEx(v, 640, dRadixTable);
  Memo1.Lines.Add(s);
end;

 コードを見て 「?!」と思った方は、ヘルプで "単純型" を検索してみて下さい。

突然ですがクイズです。
 さて、上記を踏まえると 'A'..'Z' を用いて 26 進数が実現できる事が理解できると思いますが、では Excel のカラム文字である "A(0)~Z(25), AA(26), AB(27)~ZZ(701), AAA(702), AAB(703)~" を表現するにはどうしたらいいでしょうか?

var
  i: Integer;
  s: string;
  RadixTable: string;
begin  
  // 65535 を 26 進数 に変換
  RadixTable := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  for i:=0 to 999 do
    begin
      s := IntToStrEx(i, 260, RadixTable); 
      Memo1.Lines.Add(s);
    end;  
end;

 これでは駄目ですよ。"A(0)..Z(25)" なのですから、26 は "BA" になりますからね ("A" も "AA" も "AAA" も 0 でしょう?)。投げっぱなしのクイズとさせて頂きます。

Delphi で BASE64 をエンコード / デコード
 Indy 10 を使えば簡単です。

uses
  ..., IdCoderMIME;


procedure TForm1.Button1Click(Sender: TObject);
var
  Base64Encoder: TIdEncoderMIME;
begin
  Base64Encoder := TIdEncoderMIME.Create(Self);
  try
    Memo1.Lines.Text := Base64Encoder.EncodeString('ABCDEFG');
  finally
    FreeAndnil(Base64Encoder);
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Base64Decoder: TIdDecoderMIME;
begin
  Base64Decoder := TIdDecoderMIME.Create(Self);
  try
    Memo1.Lines.Text := Base64Decoder.DecodeString('QUJDREVGRw==');
  finally
    FreeAndnil(Base64Decoder);
  end;
end;

 IntToStrEX() の例で BASE64 を出したものの、あれだけで BASE64 変換できる訳ではないので念のために。


10/12/22

突然ですがクイズです。[解答編]
 私なりの解答を出しておきます。

function IntToExcelHexacos(const Value: Int64): string;
// -----------------------------------------------------------------------------
// 数値 -> Excel 桁文字列
//
// 引数
//   Value : 変換する数値
//
// 戻り値
//   Excel 桁形式 26 進数文字列
// -----------------------------------------------------------------------------
const
  RadixTable = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var
  dValue: UInt64;
  dDigits: TDigitsRange;
begin
  // Calc
  dValue  := 0;
  dDigits := 0;
  repeat
    Inc(dDigits);
    dValue := dValue + Trunc(IntPower(26, dDigits));
  until (Value < dValue);
  dValue := dValue - Trunc(IntPower(26, dDigits));
  result := IntToStrEx(Value - dValue, 26, dDigits, RadixTable);
end;

function ExcelHexacosToInt(const s: string): Int64;
// -----------------------------------------------------------------------------
// Excel 桁文字列 -> 数値
//
// 引数
//   s : Excel 桁形式 26 進数文字列
//
// 戻り値
//   数値
// -----------------------------------------------------------------------------
const
  RadixTable = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var
  i: Integer;
  s2: string;
begin
  s2 := Trim(s);
  // Calc
  result := StrToIntEx(s2, 26, RadixTable);
  for i:=1 to Length(s2)-1 do
    result := result + Trunc(IntPower(26, i));
end;

 これを "出題編" の前で提示した RadixConv.pas に組み込んで使います。"Hexacos" は "26" という意味です。実証コードは以下の通りとなります。

var
  i: Integer;
  v: Int64;
  s: string;
begin
  Memo1.Lines.Clear;
  for i:=0 to 999 do
    begin
      s := IntToExcelHexacos(i);
      v := ExcelHexacosToInt(s);
      Memo1.Lines.Add(IntToStr(i) + ' : ' + s + ' -> ' + IntToStr(v));
    end;
end;

 もっといいアルゴリズムがありましたらご連絡下さい。なお、Excel の 1000 列目 (Index = 999) は "ALL" となります。That's all.

Delphi で BASE64 をエンコード / デコード
 EncdDecd 名前空間には、

 があります。Indy 絡みで使う場合は先に紹介した IdCoderMIME.TIdEncoderMIME / IdCoderMIME.TIdDecoderMIME を使うのがよいでしょう。両者は用途が異なっており日本語を通す場合に差異が出てきます。

 DecodeBase64() / EncodeBase64() に関しては 2009/05/24 の雑談 を参考にして下さい。Unicode 版 Delphi で ISO-2022-JP を処理する場合には DecodeString() / EncodeString() を安易に使うべきではありません。

Help Wrapper
 先のデブキャン【T4】セッションにインスパイアされて作ってみました。ダウンロードはこちら。新しい Delphi に古い Delphi のヘルプを組み込む場合に使います。whwrap.exe は WinHelp (*.hlp) 用、hhwrap.exe は HtmlHelp (*.chm) 用です。

 例えば、ガリレオ IDE の Delphi に Delphi 7 のヘルプを組み込むには、[ツール | ツールの構成] で [追加] を押し、

 このように指定すると 状況感知ヘルプ になります...つまり、カーソル位置の文字列を古いヘルプで検索してくれます。

program whwrap;
{$RTTI EXPLICIT METHODS([]) FIELDS([]) PROPERTIES([])}

uses
  Windows, SysUtils, Forms;

{$R *.res}

begin
  if ParamCount = 0 then
    Exit;
  if not FileExists(ParamStr(1)) then
    Exit;
  Application.Initialize;
  Application.MainFormOnTaskBar := True;
  if (ParamCount < 2or (Trim(ParamStr(2)) = ''then
    WinHelp(Application.Handle, PChar(ParamStr(1)), HELP_FINDER, 0)
  else
    WinHelp(Application.Handle, PChar(ParamStr(1)), HELP_KEY, DWORD(PChar(ParamStr(2))));
end.

program hhwrap;
{$RTTI EXPLICIT METHODS([]) FIELDS([]) PROPERTIES([])}

uses
  Windows, SysUtils, Forms;

{$R *.res}

var
  HelpWnd: THandle;

begin
  if ParamCount = 0 then
    Exit;
  if not FileExists(ParamStr(1)) then
    Exit;
  Application.Initialize;
  Application.MainFormOnTaskBar := True;
  if (ParamCount < 2or (Trim(ParamStr(2)) = ''then
    HelpWnd := HtmlHelp(Application.Handle, PChar(ParamStr(1)), HH_DISPLAY_TOC, 0)
  else
    HelpWnd := HtmlHelp(Application.Handle, PChar(ParamStr(1)), HH_DISPLAY_INDEX, DWORD(PChar(ParamStr(2))));
  while IsWindow(HelpWnd) do
    Application.ProcessMessages;
end.

 とても小さなプログラムなので、全ソースを晒しておきます (Delphi XE でコンパイルしています)。


10/12/23

C++Builder 1-6 Registered Users Updates
 C++Builder 1~6 迄のアップデータが公開されました。Delphi のも近々公開されるのだろうか?


10/12/24

LightReport2 2.51 Unicode Edition Rel.6
 リリース。TLRImage で TMetaFile / TGIFImage (2007 以降) / TPNGImage (2009 以降) / TWICImage (2010 以降)を扱えるようにした。

Delphi の Tips
 一件追加。

 まだ何か忘れている気が...思い出したら追加しておこう。とうとう Tips が煩悩の数と同じになったな。

TWICImage
 Delphi 2010 以降では TGraphic ラッパーに TWICImage が追加されており、特に何もしなくても WIC 経由で TIFF 画像を読み込む事が可能。

uses
  ..., Jpeg, GIFImg, PNGImage;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
    begin
      Image1.Picture.LoadFromFile(OpenDialog1.FileName);
      ShowMessage(Image1.Picture.Graphic.ClassName);
    end;
end;

 このような検証コードで、"TGraphic ラッパーとして何が使われているのか?" を知る事ができます。TIFF には TWICImage が使われているので、TIFF 画像が読み込めるかどうかは Windows のバージョンに依存します。検証用画像の詰め合わせもついでに置いておきます (test_images.zip)。

Delphi の Tips
 一件追加。

 煩悩の数超えたし。

MessageDlg() に mtConfirmation を指定してもアイコンがクエスチョンマークにならない (Vista 以降 & テーマ有効)
 おそらく Delphi 2007 以降で発生する問題です。

 "Windows Vista対応アプリを作る" も参考になるかと。


10/12/25

MessageDlg() に mtConfirmation を指定してもアイコンがクエスチョンマークにならない (Vista 以降 & テーマ有効)
 えっと。昨日のエントリは冗談で書いたのではありませんで。

 まず、MessageDlg() 内部で使われている TaskDialog() / TaskDialogIndirect() ですが、疑問符アイコンが指定できない訳じゃありません。これはコードで示した通りです。

 リンク先で中村さんが仰っていますが Vista 以降、疑問符アイコンのガイドラインが変更になっています。

 Microsoft の説明 によるとこのようになっています。

 で、何が問題なのかと言うと当然それはユーザの混乱です。「OS のバージョンが変わるとアイコンの意味も変わる」、「メッセージボックスとタスクダイアログは違う」とか言っても納得しない人が多いのではないかと思います (アイコンに関しては ERROR / STOP / HAND のような先例がない訳じゃないです)。

 質問に疑問符アイコンを使うかどうかはあくまでガイドラインです。「質問に疑問符アイコンを使うな!」と言うのであれば、過去のアプリケーションで使われている MessageBox() のアイコンをすべて変更しなくてはなりません。MessageBox() で MB_ICONQUESTION を指定しても疑問符アイコンは疑問符アイコンのままなのですから。OS を判断して疑問符アイコンと情報アイコンを切り替えますか?

 乱暴な言い方をすれば 「Vista 以降であれば TaskDialog() / TaskDialogIndirect() を使え、それ以前では MessageBox() を使え」 という事です。この点において Delphi の MessageDlg() を使えば...なるほど、確かにそのガイドラインに沿う事ができそうです。

 しかし、ここでまた問題が出てきます。Delphi のMessageDlg() ではテーマが有効でない場合には内部で TaskDialog() / TaskDialogIndirect() が呼ばれないのです。このため、OS が同じでも "テーマ有効 / 無効によってアイコンが違う" という問題が起こります。この仕様は Microsoft のガイドライン に沿っていないように思えます。これを回避するには、MessageDlg() ではなく、自前で OS を判断して TaskDialog() / TaskDialogIndirect() を使うようにするしかありません。

 無用な混乱を招かないようにするには、ガイドラインを無視するのが一番簡単です (XP の動作保障をしないのであれば話は別ですが)。

 昨日のエントリで記述した通りですね。古いアプリケーションを Vista 以降の OS で実行すると、結局は質問のダイアログでも疑問符アイコンが出てくるのですから、ガイドラインはあってないようなものです...というか、そもそも "Windows 以外の OS のアイコン" と整合性がなくなっちゃいますしね。

TTaskDialog
 なお、TTaskDialog で疑問符アイコンを使うには、MainICon に "32514" を指定します。但し、TTaskDialog は Vista 以降かつ テーマが有効の時 にしか動作しません (テーマの有無による MessageDlg() の挙動の違いはここからきています)。

疑問符アイコンのガイドライン
 守ろうとするとこういう事になります。

Vista 以前の OS Vista 以降の OS (テーマ無) Vista 以降の OS (テーマ有)
Microsoftのガイドライン MessageBox() / MessageBoxEx() / MessageBoxIndirect() TaskDialog() / TaskDialogIndirect()
Delphi 2007 以前 Application.MessageBox()
Delphi 2007 以降 Application.MessageBox() MessageDlg() / TaskMessageDlg() / TTaskDialog

 いっそすべて MessageBox() で疑問符アイコンを情報アイコンに置換した方が楽な気がします。

Vista 以前の OS Vista 以降の OS (テーマ無) Vista 以降の OS (テーマ有)
MessageBox()
MessageBoxEx()
MessageBoxIndirect() API
疑問符アイコン
(MB_ICONQUESTION)
情報アイコン
(MB_ICONINFORMATION)
Delphi の Application.MessageBox()
TaskDialog() /
TaskDialogIndirect() API
(利用不可) 情報アイコン
(TD_ICON_INFORMATION)
Delphi (2007 以降) の TTaskDialog (利用不可) 情報アイコン
(tdiInformation)
Delphi 2007 以前の MessageDlg() 疑問符アイコン
(mtConfirmation)
(疑問符アイコンになってしまうため利用不可)
mtInformation を指定すればアイコンは変わるがキャプションが "情報" になってしまう
Delphi 2007 以降の MessageDlg() (疑問符アイコンになってしまうため利用不可)
mtInformation を指定すればアイコンは変わるがキャプションが "情報" になってしまう
情報アイコン
(mtConfirmation)
Delphi (2007 以降) の TaskMessageDlg()

 OS の判別が不可避なので、このガイドラインを守るのは結構シンドイと思います。

 さらにややこしいのですが、TTaskDialog はテーマが有効 (OS のテーマ有効 & Delphi でランタイムテーマ有効) でないと利用できませんが、TaskDialog() / TaskDialogIndirect() には本来そのような制限はありません。但し、Delphi 側で "ランタイムテーマ" に対応していなければ、TaskDialog() / TaskDialogIndirect() を使う事もできません。


10/12/26

メッセージ系ダイアログのまとめ。
 本当にややこしい。

 TTaskDialog がテーマに左右されなくなれば、かなり幸せになれるような気がしないでもない。少なくとも MessageDlg() / TaskMessageDlg() の範囲で使う分にはテーマの有無は関係ないのだから。

メッセージ系ダイアログのまとめのまとめ。
 山本隆さんによる画像付の記事です。


10/12/27

LightReport2 2.51 Unicode Edition Rel.7
 リリース。

 Delphi 3 が対象外になっていますが、元々 "Unicode Edition" であって ANSI バージョンはオマケみたいなものですからこれでいいかと。

"赤いエラー" と "緑の確認"
 "MessageDlgの誤動作とHELPの記載間違い?" において、解決していない問題がありましたね。

また、Delphiのテクニックページを調べていたところ
「mtConfirmation 緑色の疑問符が表示されているメッセージボックス」
とかかれているページがいくつか見つかりました。

過去のWindowsバージョンを調べても、
疑問符が緑色で表示されていた時代はなかったりしたと思いますが、
そういう認識であってますでしょうか?

 この件です。さて...どうなのでしょうね?検証するには Delphi で試す のが一番でしょう。

 ..."間違いなく Delphi" でしょ、何か問題でも?間違いなく Windows ですが何か?

  

  

 "結論: 疑問符アイコンは緑色である" ...という事で (^^;A


10/12/28

Enhanced Metafile Picker
 小物アプリをリリース。主に Microsoft Office 製品に貼り付けられたオートシェイプを "図 (EMF) として保存" するのに使います。

 EMF は拡大縮小に強い (ある意味でベクトルデータですから) ので、アプリケーションのデコレーションとして使えます。EMF は他の画像フォーマットに比べファイルサイズが小さいので、EXE にリソースとして埋め込んでも大した事はありません。また、先日リリースした LightReport2 2.51 Unicode Edition の TLRImage にも EMF を貼り付ける事ができますので、面倒な図形とかは Excel で作っちゃいましょう。

 古い Word / Excel にはオートシェイプを "図として保存" する機能が存在しましたが、最近の Word / Excel ではできなくなっているようです。Word / Excel のオートシェイプを図として保存するには、一旦 PowerPoint へ貼り付けてから右クリックで "図として保存" する必要があります。

 小物とはいえ、多少面白い機能を有していますので、一度触ってみて下さい。UI としては "保存" ボタンと "閉じる" ボタンしかないのですが...。

 See Also:


10/12/29

Enhanced Metafile Picker で作られた EMF おかしくね? TImage に読み込むと影とかがゴミになるんだけど?」
 ファイル自体は正しいです。ペイントブラシにファイルを放り込んでみましょう。

 Metafile ってのは、要するに GDI の描画手順を記録しているファイルな訳です。MoveTo() とか LineTo() でね。で、"Metafile を表示する" というのは "GDI コマンドを再生する" 事に他なりません。 で、新しめの Ofice でオートシェイプから EMF を作成すると、EMF には "GDI+ のコマンド" が書かれてしまいます。このような EMF ファイルを "EMF+" と呼びます。TImage では EMF+ (GDI+) の描画ができませんから、"GDI+ のコマンド" 部分は正確に再生されません。これがゴミに見えるという訳です。

 Delphi で GDI+ を使うには、GDI+ 1.1 Library を使います (Delphi 2009 以降)。但し、GDI+ は Windows XP 以降での標準サポートですので、Windows 2000 では動作しない事があります (何らかのアプリと共に GDIPlus.dll が配布されている事がありますが、標準ではインストールされません)。

Windows Animation Manager (Windows 7 以降)
 よくマルチタッチのデモで見かけるアレです。散らばった画像を指でヒョイヒョイ移動してますが、あれのアニメーションにはこれを使っているのだろうと思われます。GDI+ 1.1 Library で思い出したのですが、GDI+ 1.1 Library の作者である Erik van Bilsen 氏が Delphi (2009 以降) から Windows Animation Manager を利用するライブラリを公開しています。

 "Windows Slate/Windows 7/マルチタッチ対応アプリケーション開発者会議" に参加したはいいものの、ネタに困ってしまっている方にはいいかもしれませんね (^^;A


10/12/30

Delphi によく似た開発環境
 先日、Delph 1 の話をしたのでついでに。

 イロイロありますね。


 BACK   古いのを読む   新しいのを読む