知って得する！今日から使える
Delphi実践テクニック
第34回 エンバカデロ・デベロッパーキャンプ

株式会社ミガロ．
RAD事業部 技術支援課

吉原 泰介

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

はじめに今回は既存プログラムに組み込んですぐに使えるプログラミングテクニックをテーマにしています。そのため、若干VCL寄りの内容です。

 Delphi/400：
 導入実績

:DelphiをIBM i (AS/400)に完全に対応させたミドルウェア
国内約700社、全世界約6,000社

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

アジェンダ アプリケーションテザリングでモバイル端末を活用 マルチスレッドで処理待ちの体感を改善
 DLL形式でアプリケーションを分割

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

3

1.アプリケーションテザリングでモバイル端末を活用

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

4

アプリケーションテザリングでモバイル端末を活用 アップリケーションテザリングとは？

同じネットワークやBluetooth上のアプリケーション間でデータや処理を共有して連携することができるテザリング機能（以降アップテザリング）
VCLとFireMonkey間でも連携できるので、VCL&モバイルの連携が可能！
モバイルアプリケーション

PCアプリケーション
同一ネットワーク（遠隔でも連携可能）▲●

×

アップテザリングで連動●
Bluetooth 近距離通信（ネットワーク外でも連携可能）

アップテザリングの前提

同じネットワークに両機器がつながっているか、または両機器がBluetoothで通信できる必要があります。

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

5

アプリケーションテザリングでモバイル端末を活用 アップテザリング連携例

これまで写真撮影やバーコード読取を行う業務では専用機器を用意して連携することが多かった。
商品写真を登録する

アップテザリングで代用！

デジタルカメラで撮影して、SDカードをPCにアップして、ようやくサーバへ登録・更新

バーコードを読み取って登録する
専用のバーコードリーダーやPOSを用意して、PCのアプリケーションから登録・更新

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

写真撮影やバーコード読取結果をスマートフォンアプリからPCアプリに送信して登録・更新（アプリで自動化が可能）

6

アプリケーションテザリングでモバイル端末を活用 アップテザリング用のコンポーネント

アップテザリングを使用する場合には、通信をする両方のアプリケーションにTTetheringMangerとTTetheringAppProfileコンポーネントを配置

TTetheringManagerコンポーネント
ネットワーク上でテザリング
するための接続等の管理

TTetheringAppProfileコンポーネント
テザリングで接続した
アプリケーション間で共有するリソースの制御共有するリソースをアイテムとして作成

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

NetworkやBluetoothなど
通信方法を指定共有するグループ名を指定

7

アプリケーションテザリングでモバイル端末を活用 アップテザリング用のコンポーネントの使い方
TTetheringMangerで接続を行い、TTetheringAppProfileで共有リソースを送受信

画像やバーコードで読み取ったデータなどを共有（Action共有も可能）

TTetheringAppProfileで定義した共有リソース

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

8

アプリケーションテザリングでモバイル端末を活用 PCとスマートフォンのアプリケーション連携例
拡張するアプリケーション

PCアプリケーション

モバイルアプリケーション
読取バーコード
送信

撮影写真
送信

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

9

アプリケーションテザリングでモバイル端末を活用 PCとスマートフォンのアプリケーション連携例
iPhoneアプリ側画面設計

TTetheringManger（接続管理用）TTetheringAppProfile（リソース共有用）

TImage（撮影写真表示用）

TActionList（写真撮影用）TMSFMXZBarReader（バーコード読取用）
TMSのフリーコンポーネント

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

TButton × 2 下に配置
（撮影＆バーコード読取用）

10アプリケーションテザリングでモバイル端末を活用 バーコード読み取り機能の実装に便利なコンポーネント
TMSSoftWare社のバーコード読み取りコンポーネント（無償）【ZBarSDK】 ※iOS専用
http://www.tmssoftware.com/site/freetools.asp#TTMSFMXZBarReader

ただしZBarSDKコンポーネントはiOS専用です。
Androidで使用する場合は、これをカスタマイズした
フリーソースとして公開されているTKRBarCodeSannerコンポーネントが便利です。
http://www.file-upload.net/download-8601754/TKRBarCodeSanner.zip.html

使い方はZbarSDKとほぼ同じです。
XE5当時に作られているものなのでXE7以降ではソースの修正が必要です。

（修正例）
Berlinまでしか対応書いてませんがTokyoでも動きました

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

11

アプリケーションテザリングでモバイル端末を活用
 iPhoneアプリ側開発手順①
TTetheringAppProfileの設定
リソースを2つ追加

通信グループ名

ダブルクリック

ポイント！
写真用に
Nameプロパティ：Camera
ResTypeプロパティ：Stream

送信側のKindプロパティはShared

バーコード用にNameプロパティ：Barcode
ResTypeプロパティ：Data

ResTypeプロパティはDataかStreamを選択
Data：文字などの送信
Stream：画像などの送信

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

12

アプリケーションテザリングでモバイル端末を活用
 iPhoneアプリ側開発手順②
Actionの設定（写真撮影ボタン）

ButtonのActionプロパティで標準アクションの新規追加

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

TTakePhotoFromCameraActionを選択

13

アプリケーションテザリングでモバイル端末を活用
 iPhoneアプリ側開発手順③
Actionのイベントにプログラムを実装（写真撮影ボタン）

OnDidFinishTakingイベントを作成

OnDidFinishTakingイベント（撮影写真を送信）
procedure TForm1.TakePhotoFromCameraAction1DidFinishTaking(Image: TBitmap);var
FStream: TMemoryStream;
begin
FStream := TMemoryStream.Create; //写真用のStreamを作成
image.SaveToStream(FStream);//撮影写真をStreamに格納
TetheringAppProfile1.Resources.Items[0].Value := FStream; //共有リソースに送信
Image1.Bitmap.Assign(Image);//画面に写真を表示
end;

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

14

アプリケーションテザリングでモバイル端末を活用
 iPhoneアプリ側開発手順④

バーコード撮影用のイベントにプログラムを実装（バーコード撮影ボタン）
OnClickイベント（バーコード撮影起動）
procedure TForm1.Button2Click(Sender: TObject);begin
TMSFMXZBarReader1.Show; //バーコード撮影を起動
end;

バーコード撮影用のイベントにプログラムを実装（TMSFMXZBarReader）
uses節にJSON関連のクラスを使用するためのユニットを追加
OnGetResultイベント（取得バーコード送信）
procedure TForm1.TMSFMXZBarReader1GetResult(Sender: TObject; AResult: string);
Begin//読み取ったバーコード値を共有リソースに送信
TetheringAppProfile1.Resources.Items[1].Value := AResult;end;

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

15

アプリケーションテザリングでモバイル端末を活用
 iPhoneアプリ側開発手順⑤
画面起動時のイベントにプログラムを実装
OnCreateイベント（テザリングで接続）
procedure TForm1.FormCreate(Sender: TObject);begin//起動時にテザリング接続を行う
TetheringManager1.AutoConnect();end;

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

16

アプリケーションテザリングでモバイル端末を活用 PCとスマートフォンのアプリケーション連携例
PCアプリ側画面設計

TTetheringManger（接続管理用）TTetheringAppProfile（リソース共有用）

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

17

アプリケーションテザリングでモバイル端末を活用
ポイント！

 PCアプリ側開発手順①
TTetheringAppProfileの設定（スマートフォン側と設定を合わせる）

通信グループ名

リソースを2つ追加

写真用に
Kindプロパティ：Mirror
Nameプロパティ：Camera
ResTypeプロパティ：Stream

バーコード用にKindプロパティ：Mirror
Nameプロパティ：Barcode
ResTypeプロパティ：Data

受信側のKindプロパティはMirror

ダブルクリック

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

18

アプリケーションテザリングでモバイル端末を活用 PCアプリ側開発手順②

写真撮影リソースのイベントにプログラム実装
OnResourceReceivedイベント（撮影写真を受信）
procedure TForm1.TetheringAppProfile1Resources0ResourceReceived(const Sender: TObject; const AResource: TRemoteResource);
begin
AResource.Value.AsStream.Position := 0;//Streamのポジション
Image1.Bitmap.LoadFromStream(AResource.Value.AsStream); //画面に受信画像を設定
Image1.Repaint;
//再描画
end;

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

19

アプリケーションテザリングでモバイル端末を活用 PCアプリ側開発手順③

バーコードリソースのイベントにプログラム実装
OnResourceReceivedイベント（取得バーコードを受信）
procedure TForm1.TetheringAppProfile1Resources1ResourceReceived(const Sender: TObject; const AResource: TRemoteResource);
begin
Edit1.Text := AResource.Value.AsString; //画面に受信値を設定
end;

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

20

アプリケーションテザリングでモバイル端末を活用 PCとスマートフォンのアプリケーション連携拡張例

それぞれコンパイルを行い、アプリケーション連携が完成！簡単に拡張が可能です。
PCアプリケーション

モバイルアプリケーション

読取バーコード
送信

撮影写真
送信

こうしたアプリ連携はAndroidはもちろん、スマートデバイス間、PC間のアプリケーションでも活用することができます。
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

21

アプリケーションテザリングでモバイル端末を活用 接続状況を表示する（補足）
TLabel （接続表示用）
TTimer （監視用）

OnTimerイベント（接続表示）
procedure TForm1.Timer1Timer(Sender: TObject);
begin
Label1.Visible := (TetheringManager1.RemoteProfiles.Count > 0);end;

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

接続カウントがあればLabelを表示

22

アプリケーションテザリングでモバイル端末を活用 受信処理が重いとどうなってしまうか？（補足）
OnResourceReceivedイベント（撮影写真を受信）

procedure TForm1.TetheringAppProfile1Resources0ResourceReceived(const Sender: TObject; const AResource: TRemoteResource);
begin
Sleep(10000);
受信処理がやたら重たい場合
end;

受信するとアプリ動作に弊害！

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

23

アプリケーションテザリングでモバイル端末を活用 別スレッドで実行する（補足）SynchronizeEventsがTrueになっているとメインスレッドで実行される

Falseにしておけば別スレッドになり、メインスレッドは止まらずに使えて便利！
10.2 Tokyo以前は、自分でスレッドを考慮する必要がある
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

24

2.マルチスレッドで処理待ちの体感を改善（今回の内容はWindows用です）

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

25

マルチスレッドで処理待ちの体感を改善 パフォーマンスが悪いとせっかくのアプリも評価されにくい…
[実行]ボタンを押したとき、画面の応答がなくなると、イライラする。
(一般的にストレスを感じない応答時間は、約3秒！）（１）ボタンを押下したか、していないかが分からない。

（２）処理中に画面を触ろうとすると、反応がなく、（応答なし）と表示される。

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

26

マルチスレッドで処理待ちの体感を改善
 通常のアプリケーションは、シングルスレッド（逐次実行）である。
処理開始

メインスレッド（TForm）

データ読込開始
1件データ処理次レコードへ移動

N

EOF？

Y

繰り返し処理等、時間がかかる処理を実行すると、他の処理が実行できないため、画面が固まってしまう。

処理終了

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

27

マルチスレッドで処理待ちの体感を改善
シングルスレッド プログラム実装例
procedure TForm1.btnGetDataClick(Sender: TObject);var
i, iRow: Integer;
begin
iRow := 0;
SQLQuery1.Active := True;try//繰り返し繰り返し処理while (not SQLQuery1.Eof) do
begin
StringGirdの行数を追加
Inc(iRow); //カウントアップ
StringGrid1.RowCount := iRow + 1;
for i := 0 to SQLQuery1.FieldCount - 1 do
StringGrid1.Cells[i, iRow] := SQLQuery1.Fields[i].Text;
SQLQuery1.Next;
end;
finally
SQLQuery1.Active := False;
各フィールドの値をend;
順番にStringGridに書き出し
end;

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

28

マルチスレッドで処理待ちの体感を改善
 シングルスレッド実行例

実行している間、画面の応答が無くなるため、
Edit1に値を入力したり、「×」ボタンでアプリケーションを終了したり一切不可となる。

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

29

マルチスレッドで処理待ちの体感を改善
 マルチスレッドを使えばレスポンスタイム（応答時間）が向上
時間のかかる処理をサブスレッドとすることで、メインスレッド（画面）は別の処理が実行可能
処理開始

メインスレッド（Form）

サブスレッド

処理開始
データ読込開始

スレッドの開始
1件データ処理
処理終了

次レコードへ移動

N
次の処理
時間のかかる処理は、別スレッドとして処理の呼出しだけを行い、メイン
スレッドはそのまま処理を継続できる
為、UI操作が行えるようになる。
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

EOF？

Y
処理終了

30

マルチスレッドで処理待ちの体感を改善
 TThread クラスを使用して、別スレッドを記述する方法[ファイル]→[新規作成]→[その他]
新規作成ダイアログ：[Delphiファイル]→[スレッドオブジェクト]
メインスレッド
procedure TfrmMain.Buttton1Click(Sender: TObject);begin//登録処理のスレッドを生成する
TDataEntryThread.Create(受け渡しパラメータ);
end;

サブスレッドtype//データ登録用スレッド
TDataEntryThread = class(TThread)
private((スレッド内で使用する変数や手続きを宣言))
protected
procedure Execute; override;
publicconstructor Create(パラメータリスト); virtual;end;

スレッドクラスを別に定義する為、メインスレッド上では、スレッド内でどのような処理が行われているか、一目では分かりづらい。

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

31

マルチスレッドで処理待ちの体感を改善
 CreateAnonymousThread を使ったスレッド処理
procedure TForm1.Button1Click(Sender: TObject);begin

メインスレッド//ボタンクリックの処理…
//スレッド処理
TThread.CreateAnonymousThread(procedure()
begin//重たい処理
Sleep(10000);
Edit1.Text := ‘処理終了';

名前の無いサブルーチン ： 無名メソッド として定義

サブスレッドend).Start;end;

シングルスレッド同様一つのサブルーチンで処理が記述可能！

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

32

マルチスレッドで処理待ちの体感を改善
マルチスレッドプログラム実装例
procedure TForm1.btnThreadGetDataClick(Sender: TObject);
begin
TThread.CreateAnonymousThread(procedure()vari, iRow: Integer;
begin
iRow := 0;
SQLQuery1.Active := True;try//繰り返しスレッドの生成シングルスレッドプログラムと同じコードwhile (not SQLQuery1.Eof) do
begin
Inc(iRow); //カウントアップ
StringGrid1.RowCount := iRow + 1;
for i := 0 to SQLQuery1.FieldCount - 1 do
StringGrid1.Cells[i, iRow] := SQLQuery1.Fields[i].Text;
SQLQuery1.Next;
end;
finally
SQLQuery1.Active := False;
end;end).Start;
スレッドの開始
end;
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

33

マルチスレッドで処理待ちの体感を改善
 マルチスレッド実行例

実行後、画面制御がすぐに戻る為、Edit1への値の入力やStringGridの内容が即座に確認可能！

レスポンスの体感が向上！

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

34

マルチスレッドで処理待ちの体感を改善
 マルチスレッドの注意点【デバッグ実行】【デバッグ実行】
スレッド実行中に、「×」ボタンでアプリケーションを終了。

例外（エラー）が発生。

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

35

マルチスレッドで処理待ちの体感を改善
 マルチスレッドの注意点

ビジュアルコンポーネントを使用するのは、基本メインスレッドのみである。
サブスレッド側でビジュアルコンポーネントを操作したい場合、Synchronizeメソッドを使用して、メイン
スレッド側を一時停止し、サブスレッド側から操作を行えるようにする必要がある。
メインスレッド
メインスレッド

サブスレッドメインスレッド処理１
メインスレッド処理１
一時停止

サブスレッド処理
メインスレッド処理２
メインスレッド処理２
再開

1件データ処理
Synchronize;

メインスレッド処理２
メインスレッド処理３
メインスレッド処理３
メインスレッド処理３

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

（スレッド使用時のその他留意点）
・メインスレッドとサブスレッドでコンポーネントを競合操作しない。
・Synchronize処理に時間がかかる処理をしない。
36

マルチスレッドで処理待ちの体感を改善
 Synchronizeを使用した操作
procedure TForm1.Button1Click(Sender: TObject);begin

メインスレッド//ボタンクリックの処理…
//スレッド処理
TThread.CreateAnonymousThread(procedure()
begin//重たい処理
Sleep(10000);
TThread.Synchronize(TThread.CurrentThread,procedure
begin
Edit1.Text := ‘処理終了';
end);end).Start;

サブスレッドメインスレッドに割り込みして、Edit1(ビジュアルコンポーネント)を操作。

end;

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

37

マルチスレッドで処理待ちの体感を改善
Synchronizeを使用した改良
procedure TForm1.btnThreadGetDataClick(Sender: TObject);
begin
TThread.CreateAnonymousThread(procedure()
処理を書き換えvari, iRow: Integer;
begin
iRow := 0;
SQLQuery1.Active := True;trywhile (not SQLQuery1.Eof) do
begin
Inc(iRow); //カウントアップ
StringGrid1.RowCount := iRow + 1;
for i := 0 to SQLQuery1.FieldCount - 1 do
StringGrid1.Cells[i, iRow] := SQLQuery1.Fields[i].Text;
SQLQuery1.Next;
end;
finally
SQLQuery1.Active := False;
サブスレッドの中で
end;
直接StringGridに対しend).Start;
書き込みを実行end;
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

Synchronizeの開始
while (not SQLQuery1.Eof) do
begin
Inc(iRow); //カウントアップ//ビジュアルコンポーネントを操作
TThread.Synchronize(TThread.CurrentThread,procedurevar
ループ変数はローカルのみ
i: Integer;
begin
StringGrid1.RowCount := iRow + 1;
for i := 0 to SQLQuery1.FieldCount - 1 do
StringGrid1.Cells[i, iRow] :=SQLQuery1.Fields[i].Text;
end);
SQLQuery1.Next;
Synchronizeの終了
end;

38

マルチスレッドで処理待ちの体感を改善
 改良したマルチスレッド実行例【デバッグ実行】
スレッド実行中に、「×」ボタンでアプリケーションを終了
しても、エラーとならない。

Synchronizeを使用することで、安全にスレッドを使用可能！

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

39

3.DLL形式でアプリケーションを分割

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

40

DLL形式でアプリケーションを分割
 DLLとは？
Windowsで使用される技術の一つ。単体では実行せず、他のプログラム(EXE)から呼び出されて機能するプログラム。DLLの中にサブルーチン（手続き・関数）を定義しておき、EXE側からDLLをリンクすると、DLL関数を呼び出して利用できる。
単体実行可能リンク
SampleExe.exe

DLL関数を使用
procedure Button1Click(Sender: TObject);var
C: Integer;
begin
C := CalcAdd(3, 4, 5);
end;

単体実行不可SampleDｌｌ.dll

関数（サブルーチン）を定義
function CalcAdd(A, B, C: Integer): Integer;
begin
Result := A + B + C;
end;

例えばDLL側にBL(ビジネスロジック)、EXE側はUIといった分離開発にも有効！
（修正などのメンテナンスを機能で切り分けて管理できる）
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

41

DLL形式でアプリケーションを分割

DLL

 DLLプロジェクトの新規作成[ファイル]→[新規作成]→[その他] より「ダイナミックリンクライブラリ」を選択

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

42

DLL形式でアプリケーションを分割

DLL

 DLLプロジェクトの作成[プロジェクトに名前を付けて保存]でファイルを保存

dll名が決定

この中に、外部から呼び出される手続き(procedure)や関数(function) を記述。

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

43

DLL形式でアプリケーションを分割

DLL

DLLプログラム 記述例
library SampleDll;…
uses
System.SysUtils,
System.Classes;

実行したい手続き/関数
呼出規約：stdcallを追加（Delphi以外からdllが使用可能）{$R *.res}function CalcAdd(A, B, C: Integer): Integer; stdcall;
begin
Result := A + B + C;
end;
exports
CalcAdd;
begin
end.

外部から呼び出したい
手続き/関数名をexports節に追加

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

44

DLL形式でアプリケーションを分割

EXE

 VCLフォームアプリケーションよりDLL呼出し
edtA: TEdit

edtB: TEdit

edtC: TEdit
DLL側の手続き/関数を宣言
external句 に参照するDLLを指定
edtAns: TEdit//----- Dll関数を宣言
function CalcAdd(A, B, C: Integer): Integer; stdcall; external 'SampleDll.dll';

btnCalc: TButton

procedure TfrmSample.btnCalcClick(Sender: TObject);begin
edtAns.Text := IntToStr(CalcAdd(StrToInt(edtA.Text),
StrToInt(edtB.Text),
StrToInt(edtC.Text)));end;

通常の手続き/関数と同様
EXE側からDLL関数が使用可能
実行

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

45

DLL形式でアプリケーションを分割

DLL

 (補足)DLLプロジェクト デバッグ方法[実行]→[実行時引数] より「ホストアプリケーションを指定」
DLLは、単体では動作しない為、デバッグ実行できない。

呼出し元のEXEを指定することで、DLLのデバッグが可能となる。
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

46

DLL形式でアプリケーションを分割
 一つのプロジェクト(EXE)で、複数フォーム（機能）を構成する場合• グローバル変数等により、画面間の値の受け渡しが容易• EXEファイル一つでシステムが完結する• 画面（機能）数が多くなると、実行ファイルサイズが拡大• 仕様変更の都度、プロジェクト全体のEXE再配布が必要

一つのプロジェクト（EXE）に複数画面を配置。

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

47

DLL形式でアプリケーションを分割
 メニュー用のEXEと各機能ごとにプロジェクト(EXE)を分割する場合• 機能ごとに個別開発、単体テストが行える• 個別機能の仕様変更が発生しても、当該EXEのみ置き換えで良い• 実行されるEXE分だけ、プロセスが生成され、個別データベース接続が行われる• EXE間の値の受け渡し方法が必要 （実行時引数など）
プロセス

メインメニュー
DSM010.exe

顧客マスタ
DSM020.exe

プロセス

プロセス

受注入力
DSM030.exe
プロセス

請求書発行
DSM040.exe
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

48

DLL形式でアプリケーションを分割
 メニュー用のEXEと各機能ごとにプロジェクト(DLL)を分割する場合• 機能ごとに個別開発、単体テストが行える• 個別機能の仕様変更が発生しても、当該DLLのみ置き換えで良い(BLとUIで分けても有効)• 単体EXEプロジェクト同様、実行プロセスやデータベース接続が一つとなる• EXE-DLL間のグローバル変数等の値の受け渡しが可能
メインメニュー
DSM010.exe

顧客マスタ
DSM020.dll
受注入力
DSM030.dll

プロセス

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

請求書発行
DSM040.dll

49

DLL形式でアプリケーションを分割

DLL

 DLLフォームの作成
通常のVCLフォームアプリ同様、フォームを持つDLLも作成可能。
DLLプロジェクト作成後、 [ファイル]→[新規作成]→[VCLフォーム] で作成

画面プログラムは、VCLフォーム
アプリケーションと同様に開発可能。

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

50

DLL形式でアプリケーションを分割

DLL

 DLL呼び出し部分の実装
DLLプロジェクトには、自動生成フォームはないのでフォームを生成して表示するDLL関数を プロジェクトファイルに作成する
library DSM020;…
uses
System.SysUtils,
System.Classes,
Winapi.Windows,
Vcl.Forms,
Vcl.Controls,
DSM020Frm in 'DSM020Frm.pas'{frmDSM020};{$R *.res}

フォーム表示処理ロジックに必要なユニットを追加
Windows, Forms, Controls （XE以前）

function ShowDSM020Form (AppHandle: HWND): TModalResult; stdcall;begin
EXEアプリのウィンドウハンドルが必要
Application.Handle := AppHandle;try
frmDSM020 := TfrmDSM020.Create(Application);try
Result := frmDSM020.ShowModal;
finally
frmDSM020.Release;end;
finally
Application.Handle := 0;
end;
end;
exports
ShowDSM020Form;
begin
end.

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

一般的なモーダルフォームの表示と同様のロジック

処理結果(ModalResult)を呼出し元に返却
51

DLL形式でアプリケーションを分割

EXE

 メインプログラムのEXEより、DLLフォームを起動
btnShowDSM020: TButton//----- Dll関数を宣言
function ShowDSM020Form(AppHandle: HWND): TModalResult; stdcall; external 'DSM020.dll';
procedure TfrmDSM010.btnShowDSM020Click(Sender: TObject);begin//顧客マスター呼出し
ShowDSM020Form(Application.Handle);
end;

アプリケーション
メインフォームのウィンドウハンドルをセット実行

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

52

DLL形式でアプリケーションを分割

EXE

 DLLが増えるごとに、DLL関数の宣言の追加が必要
DLL関数をコード中に宣言しないと呼び出せない。

メニュープログラム//----- Dll関数を宣言
function ShowDSM020Form(AppHandle: HWND): TModalResult; stdcall; external 'DSM020.dll'; //顧客マスタ
function ShowDSM030Form(AppHandle: HWND): TModalResult; stdcall; external 'DSM030.dll'; //受注入力
function ShowDSM040Form(AppHandle: HWND): TModalResult; stdcall; external 'DSM040.dll'; //請求書発行

もし、DSM050.dll を追加しようとすると//----- Dll関数を宣言
function ShowDSM020Form(AppHandle: HWND): TModalResult; stdcall; external 'DSM020.dll'; //顧客マスタ
function ShowDSM030Form(AppHandle: HWND): TModalResult; stdcall; external 'DSM030.dll'; //受注入力
function ShowDSM040Form(AppHandle: HWND): TModalResult; stdcall; external 'DSM040.dll'; //請求書発行
function ShowDSM050Form(AppHandle: HWND): TModalResult; stdcall; external 'DSM050.dll'; //入金照会

新しいDLL用の宣言追加が必要
メニュー

EXEの置き換えが都度発生

追加

DSM010.exe

DSM050.ｄｌｌ

DSM050用の宣言追加が必要、 要プログラム修正。

DLLが増えても、EXEを修正せずそのまま使用する方法はないか？
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

53

DLL形式でアプリケーションを分割

EXE

 LoadLibrary関数で、パラメータ指定したDLLを動的に読み込むことが可能
フォームを生成して表示するDLL関数は、全て同じ関数名とする。
動的なDLLの読み込み実装
function TfrmDSM010.ShowForm(ADllName: String): TModalResult;var
hDll: Integer;
ShowDllForm: function(AppHandle: HWND): TModalResult; stdcall;begin

DLLファイル名
DLL関数を表す変数関数の定義と一致させる//Dllの読み込み
hDll := LoadLibrary(PWideChar(ADllName));DLLファイルの読込
try
if hDll = 0 thenraise Exception.Create(ADllName + ' を読み込むことができません');//Dll関数の読み込み@ShowDllForm := GetProcAddress(hDll, PWideChar('ShowDllForm'));if @ShowDllForm = nil thenraise Exception.Create('ShowDllForm関数を読み込めません');DLLファイル内のDLL関数の読込//Dll関数の実行
Result := ShowDllForm(Application.Handle);
finally

読み込んだDLL関数の実行//Dllの解放
FreeLibrary(hDll);
end;
end;
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

DLLファイルの解放
54

DLL形式でアプリケーションを分割

EXE

 EXE側で、DLL名を指定して実行
DLL関数の宣言なしに、実行時にDLLを読み込むことが可能。
edtDllName: TEdit//------ DLL 宣言不要
procedure TfrmDSM010.btnDllExecClick(Sender: TObject);
var
sDllName: String;
前ページで作成した
begin
サブルーチンを使用
sDllName := edtDllName.Text;（引数：DLL名）
ShowForm(sDllName);
end;

実行

DLL名を入力して実行

例えばメニュー項目をマスター化すれば、メインプログラムは修正不要！
本文書の一部または全部の転載を禁止します。本文書の著作権は、著作者に帰属します。

55

DLL形式でアプリケーションを分割
 EXEとDLLでデータベース接続を共有
EXE側でデータベース接続したものをDLL側でも使用できれば、データベース接続の共有化が可能。

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

56

DLL形式でアプリケーションを分割

EXE FireDACでの共有例
DLL呼出し時にEXE側のFDConnectionのハンドルを渡す
function TfrmDSM010.ShowForm(ADllName: String): TModalResult;var
hDll: Integer;
ShowDllForm: function(AppHandle: HWND; ACliHandle: Pointer): TModalResult; stdcall;
begin//Dllの読み込み
hDll := LoadLibrary(PWideChar(ADllName));try
if hDll = 0 thenraise Exception.Create(ADllName + ' を読み込むことができません');//Dll関数の読み込み@ShowDllForm := GetProcAddress(hDll, PWideChar('ShowDllForm'));if @ShowDllForm = nil thenraise Exception.Create('ShowDllForm関数を読み込めません');

DLL関数にFDConnectionのハンドルを渡すパラメータを追加//Dll関数の実行
Result := ShowDllForm(Application.Handle, FDConnection1.CliHandle);
finally//Dllの解放
FreeLibrary(hDll);
end;
end;

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

FDConnectionのChiハンドルをセット

57

DLL形式でアプリケーションを分割

DLL

 FireDACでの共有例
DLL側で、FDConenctionのハンドルを受け取る

DLL関数にデータモジュール

library DSM020;を渡すパラメータを追加…
function ShowDllForm(AppHandle: HWND; ACliHandle: Pointer): TModalResult; stdcall;begin
Application.Handle := AppHandle;try
frmDSM020 := TfrmDSM020.Create(Application);
frmDSM020.FDConnection1.SharedCliHandle := ACliHandle;//受け取ったFDConnectionのハンドルをセット
try
Result := frmDSM020.ShowModal;
finally
EXE側で生成されたFDConenctionのハンドル
frmDSM020.Free;をDLL側のFDConnectionのSharedChiHandleにセット
end;
finally
Application.Handle := 0;
end;
end;
exports
ShowDllForm;

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

58

THANKS!
www.embarcadero.com/jp
第34回 エンバカデロ・デベロッパーキャンプ

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