【C2】Delphiテクニカルセッション「業務アプリ開発で活かせる
Delphiプログラミングテクニック」
富永 英明 (a.k.a. DEKO )本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

1
2

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

はじめに

今回は？
• いつになく、ゆるい話です。
• 「あー、あるある (w」 という話です。
• アナタにとって 「あー、あるあ…ねぇよ！」 とか、「！？ヤベぇ！」 と冷や汗が出るような話ができればいいなぁ。
• タイトルとは関係のない話が含まれているかもしれません。
• サブタイトルとも関係のない話をするかもしれません、
そして内容は斜め上の話かもしれません。

3

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

2
4

自分が持ってない環境(で｜を) テストする

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Windows XP で動作する
アプリケーションのデバッグは？
•Windows XP は 2014/04/08 でサポートが切れました。

•「それでも現実問題として XP 上でアプリケーションを動作させなくてはならない事がある」•

XP ベースの Windows Embedded はまだサポートが切れていない。
(2019/01/08 まで)•

でも、⾃分の開発環境は Windows 7 / Windows 8.x で、XP 固有の問題を再現できない。

•

実機を⽤意しようとしても、最近の PC はドライバがなかったりして XP がマトモに動作しない事がある。

•

さて…どうましょう？
…どうしましょ5

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

XP モード？VMware？VirtualBox?
• OS の仮想化を考えますよね？
• XP モードは Windows 7 Professional 以上でしか使えません。
• 当然、動作中は作業 PC が重くなります。
• PC を別に用意すれば重くなりませんが…OS どうしましょ？
Linux でやりますか？
• 先生。イチイチ別 PC で作業するのはメンドイです。

…どうしろと？
6

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Hyper-V Server 2012 を使えばいいじゃない？
• Hyper-V Server 2012 を使いましょう。
• 無償で使えるサーバ OS です。
• 64bit 対応 CPU (必須)

VPC の VHD がそのまま使えるヨ• VT (Intel: VT-x、AMD: AMD-V) 対応 (必須)
• DEP (Intel: XD、AMD: NX) 対応 (必須)
• SLAT (Intel: EPT / VT-x2、AMD: NPT / RVI) 対応 (推奨…でも必須)• メモリは積めるだけ積みましょう。

• 安い PC で一台デッチ上げましょう。
PC が壊れても OS イメージさえ無事なら復旧は容易です。
7

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Hyper-V Server 2012 ってこんなの (1)Hyper-V Server 2012 のスクリーンショットです。
GUI が使えないので地味な印象です。

Hyper-V の管理コンソールです。
クライアント PC にインストールして使います。このコンソールから
ゲスト OS を操作できますし、
リモートデスクトップからも接続できます。

8

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Hyper-V Server 2012 ってこんなの (2)Hyper-V Server 2012 の
ゲスト OS (XP) 上で
Delphi 7 を動作させています。

Windows 8 にAndroid ⽤ RDP クライアントで接続している所です。

9

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

それでもこんな時は困る。

10

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

リモートデバッガを使いましょう。
お客さんトコで LAN 接続させてもらって、
リモートデバッガでデバッグしましょう。
Delphi のアクティベーション残数を減らすことなくデバッグできます。
Delphi 7 Professional 以降 (それ以前は上位版に付属) 、2005 以外には
リモートデバッガが付属しています。

リモートデバッガをインストール

実稼動 PC

11

Delphi
インストール済
リモートデバッグ本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

持ち込み PC

先生！リモートデバッガの使い方がわかりません！！
• 「この余白はそれを書くには狭すぎる」

投げやがった…

• Delphi 7 ～ XE のリモートデバッガの使い方
http://ht-deko.minim.ne.jp/tech031.html• XE2 ～ のリモートデバッガ (PAServer) の使い方
http://ht-deko.minim.ne.jp/tech071.html• リモートデバッガインストーラ

http://ht-deko.minim.ne.jp/tech053.html#RMDINST

逃げやがった…

12

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

3
13

使える武器は多いに越したことはない

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Windows Vista (1)• Unicode (UTF-16) のサロゲートペア - 2009
• メイリオフォント (英語圏だと Segoe フォント)• DWM (Desktop Window Manager)• Direct 2D - XE2 (FireMonkey)• Direct Write - XE2 (FireMonkey)• TSF (Text Services Framework) /CUAS (Cicero Unaware Application Support)
Unicode 関連は以前やったよネ？
14

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Windows Vista (2)• PNG アイコン - XE
• タスクダイアログ - 2007 (できれば XE2 以降で)• コマンドリンクボタン - 2009
• コンボボックスのテキストヒント - 2009
• Ink Edit (RichEdit のスーパーセット)• フリック (WM_TABLET_FLICK) - 2010
• Microsoft Speech Platform (⾳声認識・合成)• タスクスケジューラ 2.0
コレやってないわ。
15

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

タスクダイアログと InkEdit
左のタスクダイアログは OS
ネイティブのものではなく、SynTaskDialog という互換ダイアログで Windows 2000
でも使えます。
InkEdit は⾒ての通り⼿書き認識ができる RichEdit です。
(但し 32bit 専⽤)

16

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Windows 7• タスクバーボタン - XE6• ジャンプリスト

ジャンプリストは過去のデブキャンで細川さんがやってたヨ• センサー API - XE3• ロケーション API -XE3
• ジェスチャ (WM_GESTURE) - 2010
• タッチ (WM_TOUCH) - 2010
センサー＆ロケーションは VCL でも使える。
タッチとジェスチャの話は"スレート PC プログラミング" で。
http://ht-deko.minim.ne.jp/tw317/#05
17

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

4
18

管理者の権限が必要だ！

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

管理者の権限が必要だ！
• BDE はインストールするのにも管理者権限が必要。
(インストーラ側で対処していなければ)• BDE Administrator は管理者権限で実行する必要がある。
コントロールパネルのアプレットからは実行できない。
• マニフェストの存在しないアプリケーションはバーチャルストアの影響を受けるため、ファイルやレジストリが別の場所へリダイレクトされてしまう。
• UAC そのものについては過去のデブキャン
資料を参照の事。

おぉコワイコワイ

19

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

UAC と requestedExecutionLevel の関係• マニフェストなし
バーチャルストアの影響を受ける。標準ユーザ権限で動作する。

• asInvoker
標準ユーザ権限で動作する。Delphi のマニフェストのデフォルトはコレ。

• highestAvailable
アカウントが管理者であれば管理者権限への昇格を促す。
標準ユーザだとそのまま実⾏される。

• requireAdministrator
アカウントが管理者であれば管理者権限への昇格を促す。
標準ユーザだと管理者アカウントの PWを要求される。
20

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

何はともあれ• マニフェストファイルを追加しましょう。
追加しないと先述の通り、面倒な事になります。
• Manifest Ex コンポーネントをインストールすると楽ですよ 。
(Delphi 6 ～)http://ht-deko.minim.ne.jp/delphiforum/?vasthtmlaction=viewtopic&t=1099

⽬的に応じたコンポーネントを貼るだけです。
(VCL / FMX 両対応)

21

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

カスタムマニフェスト• XE2 以降だとプロジェクトオプションでカスタムマニフェストを指定する事ができます。

22

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

カスタムマニフェストといえば…
• XML に以下の compatibility 要素がない場合、
アプリケーションは Vista 互換モードで動作します。
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"><application><!--Windows 8.1--><supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" /><!--Windows 8--><supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" /><!--Windows 7-->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" /><!--Windows Vista--><supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" /></application></compatibility>23

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

カスタムマニフェストのデフォルト• IDE が生成するアプリケーションマニフェストのデフォルト設定を変更するには、$(BDS)¥bin にある default_app.manifest を
書き換えます。
• デフォルトのマニフェストのままだと、Windows 7 / 8.x でアプリケーションを実行しても Vista 互換モードになります。
• XE2 以降でのみ使える手法です。
Vista 互換モードだと何がイカンの？
プログラム互換性アシスタントの影響を受けるんだってさ。
24

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

プログラム互換性アシスタント (PCA)• ざっくり言うと 「このプログラムは正しくインストールされなかった可能性があります」 とか出るアレです。
• Windows 7 だと自動でエアロをオフにしてくれちゃうアレです。
• コレのお世話になるという事は、IE で言うなら互換表示を使っているようなものです。
• PCA が介入する以上、速度低下のペナルティがある可能性は否定できません。
• PCA で動作しても、問題を先送りにしているに過ぎないので、ネイティブモードのマニフェストを組み込んで、問題点を顕在化させるべきだと思います。
25

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

アヤしいアプリは管理者権限で実行！
• …すればいいってもんじゃありません。
ユーザー インターフェイス特権の分離 (UIPI)(前略) より具体的に⾔うと、より⾼い権限が必要なアプリケーションがChangeWindowMessageFilter() を呼び出してメッセージを明⽰的に許可しない限り、権限モードが低いアプリケーションは、⼀般的に、より⾼い権限が必要なアプリ
ケーションにメッセージを送信することはできません。同様に、必要な権限がより低いアプリケーションは、必要な権限がより⾼いアプリケーションが所有する HWND
を読むことはできますが、変更はできません。
http://msdn.microsoft.com/ja-jp/library/aa480152.aspx#EKFAG

ワケワカラン26

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

論より証拠• 通常のマニフェストと管理者権限マニフェストを組み込んだテストアプリケーションを作成 (Delphi 7)27

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

機能は見ての通り• 普通のマニフェストを組み込んだもの (UIPI_Normal.exe)※ NativeMan 使⽤• 管理者権限マニフェスト を組み込んだもの(UIPI_Admin.exe)※ NativeAdminMan 使⽤昇格を要求される
28

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Explorer からファイルをドロップすると？
• 普通のマニフェストを組み込んだもの (UIPI_Normal.exe)
普通に表⽰される• 管理者権限マニフェスト を組み込んだもの(UIPI_Admin.exe)
何も表⽰されず、エラーにもならない！

Explorer が標準ユーザ権限、アプリケーションが管理者権限で実⾏されているためにこのような現象が起こります。
29

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

ChangeWindowMessageFilter() API の使い方
procedure TForm2.FormCreate(Sender: TObject);
const
MSGFLT_ADD= 1;
MSGFLT_REMOVE = 2;
WM_COPYGLOBALDATA = $0049;
var
pa: TFarProc;
DLLWnd: THandle;
ChangeWindowMessageFilter: function (message: UINT; dwFlag: DWORD): BOOL; stdcall;begin// UIPI 対策
この API は Vista 以降でif CheckWin32Version(6, 0) then
実装されているので、
begin
DLLWnd := LoadLibrary('user32.dll');
LoadLibrary で利⽤する。
if DLLWnd > 0 thenbegin
pa := GetProcAddress(DLLWnd, 'ChangeWindowMessageFilter');
if pa <> nil then
begin
@ChangeWindowMessageFilter := pa;
ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);
ChangeWindowMessageFilter(WM_COPYDATA, MSGFLT_ADD);
ChangeWindowMessageFilter(WM_COPYGLOBALDATA, MSGFLT_ADD);
end;
end;
end;
// ドロップ受け⼊れ
DragAcceptFiles(Handle, True);end;
30

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

念の為に…
• メッセージハンドラ部分...
public{ Public 宣⾔ }procedure WMDropFiles(var msg: TWMDropFiles); message WM_DROPFILES;
end;
procedure TForm2.WMDropFiles(var msg: TWMDropFiles);var
FileName: array [0..32768] of Char;
begin
えらくデカいバッファだな
Label2.Caption := '';try// 単⼀ファイルしか受け⼊れない⼿抜きコードなので注意
DragQueryFile(msg.Drop, 0, FileName, SizeOf(FileName));Label2.Caption := StrPas(FileName);
finally
DragFinish(msg.Drop);
MAX_PATH 定数をend;
知らないんじゃ？
end;
31

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

管理者権限へ昇格すればいいってもんじゃない。
管理者権限へ昇格させるのはいいけど、⾯倒な処理を余計に書かなきゃいけない事があるヨ。

※安易に管理者権限へ昇格させちゃダメ。
32

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

5
33

大量のコントロールを貼るのが憂鬱で仕方ない

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

コンポーネントは連続貼り付けできる• コンポーネントパレットあるいはコンポーネントツールバーのコンポーネントを Shift を押しながらクリックし、
フォームデザイナの上でマウスのボタンを連打！！
• 連続貼り付けモードを解除するには、
コンポーネントパレットあるいはコンポーネントツールバーのマウスカーソルの形をしたボタンをクリックしてボタンが引っ込んだ状態にする。

コンポーネントパレット
34

コンポーネントツールバー

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

コンポーネントの整列がメンドイ• ツールバーを右クリックし、「配置、間隔、位置合わせ」のツールバーを表示させましょう。

35

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

そして…
左寄せ

• 全選択して…

この機能は Delphi 2005 からあります。

垂直等間隔配置

垂直スペースインクリメント

36

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

均等間隔で
広がったり
狭まったり
します。

幅とかの設定は…
• 「さっきのって XE 4 以前の FMX フォームではできないよね？」(XE5 以降または AppMethod では可能です)• 「幅はどうするの？」• まず "Value Calc プロパティエディタ" をインストールします。

http://ht-deko.minim.ne.jp/delphiforum/?vasthtmlaction=viewtopic&t=1349(Delphi 6 以降。フル機能は 2010 以降)

なんぞそれ？
37

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

コントロールの幅を一気に設定する (1)• MaxLength を設定し、コントロールを選択します。
Edit のフォントは MS ゴシック 10pt とします。

38

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

コントロールの幅を一気に設定する (2)• オブジェクトインスペクタの Width プロパティにMaxLength * 7.1 + 8と入力し、Enter を押します。

39

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

コントロールの幅を一気に設定する (3)• すると、こうなります (ドヤァ)• 先程やった位置合わせも計算で行う事ができます。

大体合ってます40

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

余談ですが…
• プロパティでの計算は VCL / FMX フォームいずれも可能。
三角関数を使えば以下のような配置を設計時に行えます。
関数

41

引数

戻り値の型

Abs
ArcCos
ArcCoshArcCot
ArcCotH
ArcCscArcCscH
ArcSecArcSecH
ArcSin
ArcSinh
ArcTanArcTanh
Cos
Cosh
Cot
Cotan
CotH
Csc
CscH
CycleToDeg
CycleToGrad
CycleToRad
DegToCycle
DegToGrad
DegToRad
Float
Exp
GradToCycle
GradToDeg
GradToRad
Int
Ln
LnXP1
Log10
Log2
Pi
Sec
Secant
SecH

Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Integer
Float
Float
Float
Float
Float
Float
Float
Float
Float
(なし)
Float
Float
Float

Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float
Float

引数に与えられた実数を絶対値で返しますアークコサインハイパボリックアークコサイン
アークコタンジェント
ハイパボリックアークコタンジェント
アークコセカント
ハイパボリックアークコセカント
アークセカント
ハイパボリックアークセカント
アークサインハイパボリックアークサイン
アークタンジェント
ハイパボリックアークタンジェント
コサイン
ハイパボリックコサイン
コタンジェント
コタンジェント
ハイパボリックコタンジェント
コセカント
ハイパボリックコセカント
周から角度に変換します
周からグラードに変換します
周からラジアンに変換します
角度から周に変換します
角度からグラードに変換します
角度からラジアンに変換します
引数に与えられた整数を実数にして返します
e の <引数> 乗を返します
グラードから周に変換します
グラードから角度に変換します
グラードからラジアンに変換します
引数に与えられた実数の整数部分を返します<引数>の自然対数を返します<引数>+1 の自然対数を返します
10 を底とする<引数>の対数を返します
2 を底とする<引数>の対数を返します
円周率。3.14... を返します
セカント
セカント
ハイパボリックセカント

SelectOrder

(なし)

Float

コンポーネントの選択順序を 0 ベースの実数で返します

Sin
Sinh
Sqr
Sqrt
Tan
Tanh

Float
Float
Float
Float
Float
Float

Float
Float
Float
Float
Float
Float

サイン
ハイパボリックサイン<引数>の 2 乗を返します<引数>の平方根を返します
タンジェントハイパボリックタンジェント

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

説明貼ったのはいいけど変数への出し入れが…
• 趣旨が違っているような…まぁいいです。
• Delphi XE3 以降にはプリミティブ型に対するヘルパーがありますので、それを使えば簡単です。
var
Value: Integer;
begin
Value := 100;
Edit1.Text := Value.ToString;
end;

42

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

それじゃ、逆方向は無理だよね？
• …であれば、TCustomEdit 用のクラスヘルパーを作ればいいのではないでしょうか？
type{ TCustomEditExtention }TCustomEditExtention =class helper for TCustomEdit
private...
published// Properties
property AsBoolean: Boolean read GetBoolean write SetBoolean;
property AsCurrency: Currency read GetCurrency write SetCurrency;
property AsDate: TDate read GetDate write SetDate;property AsDateTime: TDateTime read GetDateTime write SetDateTime;
property AsFloat: Double read GetFloat write SetFloat;
property AsInt64: Int64 read GetInt64 write SetInt64;
property AsInteger: Integer read GetInteger write SetInteger;
property AsString: string read GetString write SetString;property AsTime: TTime read GetTime write SetTime;
end;
43

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

そうすると…
• こんな感じで使えます。
何度も何度も型変換関数を通さなくて済みますね。
procedure TForm1.Button1Click(Sender: TObject);var
Value: Integer;
begin
Value := 100;
Edit1.AsInteger := Value;
end;
procedure TForm1.Button2Click(Sender: TObject);var
Value: Integer;
begin
Value := Edit1.AsInteger;
ShowMessage(Value.ToString);
end;

44

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

645

MDI と SDI

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

MDI は非推奨• MDI = Multi Document Interface• Microsoft は MDI アプリケーション非推奨だと言っています。

MDI
46

SDI

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

でも、Microsoft さん？

Excel はMDI デスヨネ？

47

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Excel 2010 まではね。
• Excel 2013 からは SDI アプリケーションになっています。

48

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Excel 2013 の注意点
• ワークブックを削除するという事はアプリケーションを一つ終了させるという事。つまり、次のフォーカスが Excel だとは限らない。
• ワークブックを操作する時にはアクティブか否かを明示的に指定しなければならない。
• Excel 2013 起動時の初期シートはひとつ (従来は３つ)。
最初にシートが３つある前提で書かれたコードはエラーを拝む。
ちゃんとシート数をコードで確認する事。

49

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Excel 2013 の互換性確認• Microsoft は開発者向けに Office 2013 の評価版を用意しているので、Excel 2013 を持っていなければこれで確認する。
http://technet.microsoft.com/ja-jp/evalcenter/jj192782.aspx• Delphi からオートメーションで操作している場合には、
ホワイトペーパー 「Office 2013 マクロ互換性について」 に目を通しておく事。
http://technet.microsoft.com/ja-jp/office/jj149945.aspx

ほー50

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

751

茶色の狐はノロマな犬を何回飛び越えられるのか？

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

大容量ハードディスク• HDD も大容量になりましたね。
• その分、ブッ壊れると復旧は大変デスヨネー。
• UEFI じゃないと大容量ハードディスクから起動できません
よねー。
• 古い PC で大容量 HDD を使う場合には 2.2TB 以下じゃないと起動ディスクにできませんよねー。データディスクとしては使えるけれど…。
• そんな話ではありません。
52

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

NAS やファイルサーバは…
• HDD の大容量化に伴い…
• どんどんドキュメントが蓄積されていきます。
• フォルダは増え、階層も深くなります。
• 名前がカブるのでファイル名も長くなります。
話が⾒えてこないんだが…

53

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

何の話がしたいの？
The quick brown fox jumps over the lazy dog
Confuse!

54

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

(System).SysUtils.ForceDirectories()• 一気にサブフォルダまで掘ってくれる便利な関数。
procedure TForm1.Button1Click(Sender: TObject);const
DIR_STR = 'TheQuickBrownFoxJumpsOverTheLazyDog'; // 35 chars
var
i: Integer;
Dir: string;
begin
Dir := 'Z:';for i:=1 to 10 do
Dir := SysUtils.IncludeTrailingPathDelimiter(Dir) + DIR_STR;
SysUtils.ForceDirectories(Dir);end;• 上記コードで '10階層' 一気に掘れます。

55

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

あれ？
• 6回しか飛び越せんかった...orz

56

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

MAX_PATH• SysUtils.ForceDirectories() は、そのままでは MAX_PATH の範囲でしかフォルダを掘れません。
• MAX_PATH = 260 です。
これはバイト数ではなく文字数 (UTF-16 換算) です。
• さっきの例だと 'TheQuickFox～' は 35文字ですから、10階層にしてドライブ文字列とパス区切り文字を含めると362 文字のパスになってしまいます。
• 「嫌な予感がしてきましたか？」…すっごく。
57

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

拡張プレフィックスパス• 深い階層を表すには、
拡張プレフィクスをパスに付加すればいいのです。
• ローカルパスの場合
C:¥Foo¥¥?¥ C:¥Foo

びっくり
させんなよー• UNC パス (ネットワークパス) の場合¥¥Server¥Share¥Bar¥¥?¥ UNC¥ Server¥Share¥Bar

58

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

再度 (System).SysUtils.ForceDirectories()• 今度はどうでしょう？
procedure TForm1.Button1Click(Sender: TObject);const
DIR_STR = 'TheQuickBrownFoxJumpsOverTheLazyDog'; // 35 chars
var
i: Integer;
Dir: string;
begin
Dir := '¥¥?¥Z:';
for i:=1 to 10 do
Dir := SysUtils.IncludeTrailingPathDelimiter(Dir) + DIR_STR;
SysUtils.ForceDirectories(Dir);end;'¥¥?¥Z:';59

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

今度は！
• 10回飛び越せた ＼(^o^)／60

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

ちなみに…
• A系 API は拡張プレフィクスパスに対応していない…ハズなのですが、実際には通るものが多いです。
(全部は確認していませんけれど…)• Delphi 2007 以前の ANSI 版 Delphi では内部でA系 API が使われています。
• 実際問題として、この件は ANSI 版 Delphi でも対応できない事はありませんが、できれば Unicode 版 Delphiを使った方がいいように思います。

61

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

A系 API と W系 API の MAX_PATH• MSDN にはこう書いてあるのですけどね…
この関数の ANSI 版では、名前は最⼤ MAX_PATH ⽂字に制限されています。この制限をほぼ 32,000 ワイド⽂字へ拡張するには、この関数の Unicode 版を呼び出し、パスの前に "¥¥?¥" という接頭辞を追加してください。詳細については、MSDN ライブラリの「」（ファイル名の規則）を参照してください。
※「」は脱字ではありません。

62

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

MAX_PATH を Char 型のバッファで使う• Unicode 版 Delphi で A 系 API を呼んだり、逆に ANSI 版
Delphi で W 系 API を呼ばないのならこうなります。
const{$IFDEF UNICODE}
MAX_PATH_LEN = MAX_PATH;{$ELSE}
MAX_PATH_LEN = MAX_PATH * 2;{$ENDIF}
var
Buf: array [0..MAX_PATH_LEN + 1] of Char;• Unicode の場合はそのまま。ANSI (Shift_JIS) の場合は最大でも 1文字 = 2Bytes なので、2 倍にすればいい。
(Shift_JIS は Unicode の BMP の範囲に収まるため)
63

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

安心するのは早くないですか？

64

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

SHFileOperation()• 拡張プレフィクスを付けると動作しなくなる。
(戻り値: DE_INVALIDFILES = 0x7C)• UNC 形式の拡張プレフィクスはエラーになる。
• もちろん MAX_PATH の制約を受ける。
• COM インターフェイス IFileOperation で置換するという手もあるが、コピー時にコピー先フォルダがあると失敗するという問題があり、 かつ Vista 以降でしか使えない 。
• SHFileOperation() のファイル操作は実は遅い。
マジですか…
65

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

TPath / TDirectory / TFile• Delphi 2010 から追加された TPath / TDirectory / TFile を使えば SHFileOperation() を代替できる。
• 名前空間は (System.)IOUtils• TDirectory はディレクトリの操作、 TFile はファイルの操作、
TPath はフォルダ名やファイル名の操作を行うためのレコード型 (クラスではないので生成 / 破棄は不要)。
• 拡張プレフィクスに対応している。
• 名前空間で判るようにマルチプラットフォーム対応。

66

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

(System).IOUtils.TDirectory• TDirectory.Copy() はディレクトリごとファイルをコピーする。
• TDirectory. Delete() はディレクトリごとファイルを削除する。
但し、サブフォルダが存在する場合には Recursive を True にしないと削除されない。
• TDirectory.Move() はディレクトリごとファイルを移動する。
但し、移動先にフォルダが存在してはならない。

67

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

(System).IOUtils.TFile• TFile.Copy() は単一のファイルをコピーする。
「Delphi にはファイルコピー関数がない」 というのは過去の話。
ファイルがコピー先に既にあっても上書きするようにするにはパラメータ Overwrite を True にする。
• TFile.Delete() は単一のファイルを削除する。
• TFile.Move() は単一のファイルを移動する。
ファイルがコピー先に既にある場合には移動できない。
• いずれもファイル属性を考慮する必要がある。

68

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

TDirectory + TFile• ワイルドカードでのコピー / 削除 / 移動は TDirectory + TFile
を組み合わせて使います。
var
FileName: string;
begin
for FileName in TDirectory.GetFiles('Z:¥', '*.jpg',
TSearchOption.soAllDirectories) do
TFile.Delete(FileName);end;

Z: ドライブにあるすべての階層の *.jpg ファイルを削除します。

69

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

(System).IOUtils.TPath• パス名、フォルダ名、ファイル名を操作するのに使う。
• 旧来の ExtractFilePath() 等と同様のメソッドが"あらかた" 用意されている。

何か？

• FireMonkey には名前空間違いのTPath (FMX.Objects.TPath) があるけれど
全く違うので勘違い…しないね、うん。
• 欲を言えばもうちょっとだけ機能が欲しい。

70

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

パス名あれこれ• パス区切り文字は '￥' ではなく PathDelim を使うといいです。
• コードが冗長だと思えてもパス終端は
IncludeTrailingPathDelimiter() とExcludeTrailingPathDelimiter() でキッチリ処理しましょう。
• 上記 2 つを守ればマルチプラットフォーム対応の時に困った事になりません。
• SHFileOperation() は C:¥foo¥¥bar みたいな不正なパスも受け付けるのでバグの元です。パスの連結は ”+” を使わずにTPath.Combine() を使うと不正なパスを作り出しにくくなります。
71

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

そういやSHFileOperation() が遅いって…
• 疑り深いなぁ…。
Z:¥Foo (HDD 上)• 35MB 程度のフォルダ• フォルダ数: 208• ファイル数: 462
これを Z:¥Bar にコピーするのを 10 回計測して平均を算出。
結果:
SHFileOperation(): 3744ms
TDirectory.Copy(): 2031ms
72

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

SHFileOperation() の実装
FileOperation()function FileOperation(Mode: UINT32; const Src, Dst: string): Integer;
var
FileOp: TSHFileOpStruct;
beginwith FileOp do
begin
Wnd := 0;
wFunc := Mode;fFlags := FOF_NOCONFIRMATION or FOF_SILENT;
if (Mode <> FO_DELETE) thenfFlags := fFlags or FOF_NOCONFIRMMKDIR;
pFrom := PChar(Src + #$00#$00);
pTo := PChar(Dst + #$00#$00);
fAnyOperationsAborted := False;
普通の実装だと思います。
hNameMappings := nil;
ワザと遅くなるような
end;
result := SHFileOperation(FileOp);
⼩細⼯はしてありません (wend;※ SHFileOperation() を使うには (Winapi.)ShellAPI を uses に加える必要がある。
73

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

評価コードSHFileOperation()var
Tick: Int64;
StopWatch: TStopWatch;
begin
StopWatch := TStopWatch.StartNew;
Tick := StopWatch.ElapsedMilliseconds;
FileOperation(FO_COPY, 'Z:¥FOO', 'Z:¥BAR');ShowMessage(IntToStr(StopWatch.ElapsedMilliseconds - Tick));
end;

TDirectory.Copy()var
Tick: Int64;
StopWatch: TStopWatch;
begin
StopWatch := TStopWatch.StartNew;
Tick := StopWatch.ElapsedMilliseconds;
TDirectory.Copy('Z:¥FOO', 'Z:¥BAR');ShowMessage(IntToStr(StopWatch.ElapsedMilliseconds - Tick));end;※ TStopWatch の名前空間は(System.)Diagnostics
74

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

SHFileOperation() を高速に！
• 長いパスはともかく、速度の件だけなら以下を試してください。
• NIC のプロパティで QoS パケットスケジューラ を外します。
• 同じく インターネット プロトコル バージョン6 (TCP/IPv6) も外します。
• コマンドプロンプトで以下を実行します (Windows 7 以降)。
netsh int tcp set global chimney=disabled
netsh int tcp set global rss=disabled
netsh int tcp set global netdma=disabled• これで SHFileOperation() が速くなるハズです (Explorer も)。
• スペックの低い PC で特に有効な手段です。
• …それでも、TFile / TDirectory の方が速いですけどね！
75

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Delphi 2010 よりも前のバージョンでは？
• FindFirst() / FindNext() / FindClose() と再帰を使い、
SysUtils にある従来のフォルダ / ファイルルーチンを組み合わせてどうにかしてください。
• SUBST や net use を使い、深いパスを仮想ドライブ /ネットワークドライブに割り当ててやるのも一つの手です。
• …ただ、SUBST は Windows 起動時に再接続してくれないのでスタートアップに仕込むなりする必要があります。
• 階層をまんべんなくアクセスしなくていいのなら、
シンボリックリンクを使うとか (NTFS 限定)。
76

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

SUBST と net use• SUBST指定されたパスを仮想ドライブとして割り当てる。ネットワークパ
スを割り当てる事はできない。永続的にドライブを割り当てる機
能はない。API で同等なのは DefineDosDevice()。
• net use
指定された UNC パスをネットワークドライブとして割り当てる。
ローカルパスを割り当てる事はできない。
API で同等なのは WNetAddConnection() /WNetAddConnection2() / WNetAddConnection3()。
• ファイル / フォルダ操作のテストは仮想ドライブでやりましょう。
77

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

念の為に確認してみる。
DB のフィールドにFILENAME
VARCHAR(255)とかないよネ？
(フルパス格納)※メモ型あるいは BLOB 型にしときましょう。
78

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

8
79

FireMonkey あれこれ

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

TNumberBox ですよ、 TNumberBox• TNumberBox ってコレです。
数値を⼊⼒できる…のだけど• 格納できる値は Single• DecimalDigits に⼩数点以下の桁を指定しても固定⼩数点にならない。
• デフォルトだと値をマウスで選択しようとすると値がインクリメント/デク
リメントされてしまう。
• デフォルトで左揃え• 桁区切り⽂字は表⽰されない。

ビジネス⽤途には微妙に使えない！！
80

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

ないのなら…
• 作ってしまえ、TNumEdit
数値を⼊⼒できる！
• 格納できる値は Extended• DecimalDigits に
⼩数点以下の桁を指定可能。
• デフォルトで右揃え• 桁区切り⽂字は Thousands
プロパティで ON/OFF 可能。

ビジネス⽤途に使えるかも！
81

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

TEdit は…
• ダブルクリックすると、ボタンが付けられます。
上から順に、• TEditButton• TClearEditButton
TClearingEdit 使ったほうが早い。

•
•
•
•

TPasswordEditButton
TSearchEditButton
TEllipsesEditButton
TDropDownEditButton
TComboEdit 使ったほうが早い。

• FMX の TEdit にはコンポーネントエディタがあります。
82

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

TEditButton の派⽣ボタンは…
• [構造] ペインで確認するとこんな感じ。

ん？
それってつまり…

83

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

TCustomEdit の派生コンポーネントに…
• ぶら下げる事ができます (^o^)/84

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

ダブルクリックしてコンポーネントを追加できるコンポーネント• TEdit• TGrid / TStringGrid• THeader• TListBox / TComboBox
右クリック→[項目の追加] だと、
TListBoxHeader と TSearchBox が別途追加可能。

• TMainMenu / TPopupMenu / TMenuBar• TTabControl• TTreeView

85

とりあえず
ダブルクリックしてみ？

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

986

言い忘れていた VCL のアレ

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

言い忘れてたけど…
Vista 以降だとProgressBar を使うだけで動作が遅くなる事があるヨ！

※検索が遅いんじゃなくて、ProgressBar の描画が遅いだけだったりするヨ。
87

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

uProgressBarFix.pas
unit uProgressbarFix;
interface
uses
Classes, Windows, StdCtrls, ComCtrls, CommCtrl;
type
TProgressBar = class(ComCtrls.TProgressBar)
privateFPosition: Integer;
procedure SetPosition(const Value: Integer);function GetPosition: Integer;
publicconstructor Create(AOwner: TComponent); override;
property Position: Integer read GEtPosition write SetPosition default 0;
end;
implementation
constructor TProgressBar.Create(AOwner: TComponent);
begin
inherited;
FPosition := 0;
end;function TProgressBar.GetPosition: Integer;
begin
if HandleAllocated then
Result := SendMessage(Self.Handle, PBM_GETPOS, 0, 0)
else
Result := FPosition;
end;
procedure TProgressBar.SetPosition(const Value: Integer);begin
if FPosition <> Value thenbegin
FPosition := Value;
if HandleAllocated thenSendMessage(Self.Handle, PBM_SETPOS, Value, 0);
end;
end;
end.

88

実測して
遅かったら
試してみてネ

uProgressBarFix
を uses するだけ
で OK ダヨ。

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

描画を正常にした上で高速化するには？
• SetPosition() を書き換えてください。
procedure TProgressBar.SetPosition(const Value: Integer);var
IncMax: Boolean;
begin
if FPosition = Value thenExit;
if HandleAllocated thenbegin
if FPosition < Value thenbegin
IncMax := (Value = Self.Max);
if IncMax thenSelf.Max := Self.Max + 1;
SendMessage(Self.Handle, PBM_SETPOS, Value + 1, 0);
SendMessage(Self.Handle, PBM_SETPOS, Value, 0);
if IncMax thenSelf.Max := Self.Max - 1;
end
else
SendMessage(Self.Handle, PBM_SETPOS, Value, 0);end;
FPosition := Value;
end;

ProgressBar の
速度はコードの書き⽅によっても左右されるから
⼀概には語れないのヨネー

描画速度は逆に落ちるかもしれないけれど、ゲージは正しく描画されるコードです。
89

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

そもそも…
• ProgressBar.Max は 100 のままにして Position を百分率で指定すれば 最大でも 100 回しか描画されないので高速に動作する…ハズ？
vari, RecCnt: Integer;
実は uProgressBarFix を
begin
RecCnt := 12345;
uses していないと、
for i := 1 to RecCnt do
遅くなるコードの代表例です。
begin
...
// (処理)...
ProgressBar1.Position := i * 100 div RecCnt; // 整数演算で％を出す
Application.ProcessMessages;
end;
end;

ProgressBar の描画にはコストが掛かる事をお忘れなく！
90

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

?
91

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

まとめ？

脱線しまくりでしたネ• XP のメンテが必要なら、仮想化を視野に入れておきましょう。
• 古い Delphi アプリケーションを最近の OS で動かすのなら、最低限の修正は行いましょう (マニフェストなど)。
• 単純作業が苦痛なら、最初に面倒な作業をやっときましょう。
(「急がばまわれ」 です)• Excel 2013 には気をつけましょう。
• パスの扱いには注意しましょう。
• SHFileOperation() はなるべく使わないようにしましょう。
• DB にパスを格納する時も注意しましょう。
92

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

X
93

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

ExtrasMicrosoft Hyper-V Server• Hyper-V Server は無償で利用可能です。
ゲスト OS は XP 以降をサポートしています。
• Microsoft Hyper-V Server 2008 R2

http://www.microsoft.com/ja-jp/download/details.aspx?id=3512※ 比較的古いハードウェアを用いる場合。
※ 2012 でサポートされなくなったゲスト OS のドライバが必要な場合に。

• Microsoft Hyper-V Server 2012

http://www.microsoft.com/ja-jp/server-cloud/hyper-v-server/※ 比較的古いハードウェアを用いる場合。

• Microsoft Hyper-V Server 2012 R2

http://technet.microsoft.com/ja-jp/evalcenter/dn205299.aspx※ 比較的新しいハードウェアを用いる場合。

94

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

セッション中に出てきたイロイロなもの• 評価コード等は後日まとめて閲覧できるようにしておきます。
http://ht-deko.minim.ne.jp

今⽇のうちにやれよ！
ひぃ〜！！
95

本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。