Top > Programmingとか > VB / VB.NET > GDI+ in VB6

04 GDI+の開始と終了

まず最初に抑えておきたいことは、

GDI+は起動と終了を行い、各機能はその間に行う

といぅことです。

Bitbltに代表されるGDI APIは単発のAPIで、いきなりその命令だけをコールするスタイルでの使用です。
それに対しGDI+ APIは、最初にGDI+(ライブラリ?)を初期化し、各命令をコールし、作成したすべてのオブジェクトをきちんと解放した上でシャットダウンする、という手続きを執ります。

.NETのクラスライブラリでも、この手続きは直接関数として用意されていますので、必要な情報はmsdnからそのまま入手できます。



GDI+の初期化はGdiplusStartup、終了はGdiplusShutdownといぅAPIで行います。
msdnでの説明は以下のよぅな感じです。
相変わらずないい加減な超訳(便利だなこの言葉は)ですので、正確に知りたい方は原本をご参照ください。
GdiplusStartup Function
原本Online
凡例API
Status GdiplusStartup(ULONG_PTR token *token, const GdiplusStartupInput *input, GdiplusStartupOutput *output);
TypeLibrary GdiPlus.GdiPlusExportsのメンバ
Function GdiplusStartup(token As Long, Input As GdiplusStartupInput, [Output]) As GpStatus
説明The GdiplusStartup function initializes Microsoft® Windows® GDI+. Call GdiplusStartup before making any other GDI+ calls, and call GdiplusShutdown when you have finished using GDI+.
GdiplusStartupファンクションはMicrosoft® Windows® GDI+を初期化します。他の何らかのGDI+をコールする前に、GdiplusStartupをコールしてください。そしてGDI+の使用を終了する時にはGdiplusShutdownをコールしてください。
パラメータtoken
[out]
Pointer to a ULONG_PTR that receives a token. Pass the token to GdiplusShutdown when you have finished using GDI+.
トークンを返すULONG_PTR型のポインタです。
GDI+の使用を終了させる時、GdiplusShutdownにこのトークンを渡す必要があります。
input
[in]
Pointer to a GdiplusStartupInput structure that contains the GDI+ version, a pointer to a debug callback function, a Boolean value that specifies whether to suppress the background thread, and a Boolean value that specifies whether to suppress external image codecs.
GdiplusStartupInput構造体のポインタです。
これはGDI+のバージョン、デバッグ用コールバック関数のポインタ、バックグラウンドスレッドでの動作を抑制するかどうかを指定するBoolean値、外部イメージのコーデックを抑制するかどうかを指定するBoolean値で構成されています。
output
[out]
Pointer to a GdiplusStartupOutput structure that receives a pointer to a notification hook function and a pointer to a notification unhook function. If the SuppressBackgroundThread data member of the input parameter is FALSE, then this parameter can be NULL.
GdiplusStartupOutput構造体のポインタです。
通知をフックする関数のポインタとフックしない関数のポインタを含みます。
inputパラメータで「バックグラウンドスレッドでの動作を抑制するかどうかを指定するBoolean値」がFALSEであれば、この値はNULLでかまいません。
戻り値If the function succeeds, it returns Ok, which is an element of the Status enumeration.
成功すれば、Status列挙体の要素「Ok」が戻ります。
If the function fails, it returns one of the other elements of the Status enumeration.
失敗すれば、Status列挙体の他の要素が戻ります。
訳注:TypeLibraryでは、Const Ok = 0(GdiPlus.GpStatusのメンバ)として定数宣言されています。
解説You must call GdiplusStartup before you create any GDI+ objects, and you must delete all of your GDI+ objects (or have them go out of scope) before you call GdiplusShutdown.
何らかのGDI+オブジェクトを作成する前に、GdiplusStartupをコールしなければなりません。
そしてGdiplusShutdownをコールする前にすべてのGDI+オブジェクトを削除(あるいはスコープ外へ追いやる)しておかなければなりません。
You can call GdiplusStartup on one thread and call GdiplusShutdown on another thread as long as you delete all of your GDI+ objects (or have them go out of scope) before you call GdiplusShutdown.
あるスレッドでGdiplusStartupをコールし、すべてのGDI+オブジェクトを削除(あるいはスコープ外へ追いやる)した後であれば、別のスレッドからでもGdiplusShutdownをコールすることができます。
Do not call GdiplusStartup or GdiplusShutdown in DllMain or in any function that is called by DllMain. If you want to create a dynamic-link library (DLL) that uses GDI+, you should use one of the following techniques to initialize GDI+:
DllMainまたはDllMainからコールされるいかなる関数の中からも、GdiplusStartupあるいはGdiplusShutdownをコールしてはいけません。
もしGDI+を使ったDLLを作りたいなら、GDI+の初期化についての補足テクニックのひとつを使うべきです。
Require your clients to call GdiplusStartup before they call the functions in your DLL and to call GdiplusShutdown when they have finished using your DLL.
Export your own startup function that calls GdiplusStartup and your own shutdown function that calls GdiplusShutdown. Require your clients to call your startup function before they call other functions in your DLL and to call your shutdown function when they have finished using your DLL.
Call GdiplusStartup and GdiplusShutdown in each of your functions that make GDI+ calls.

(このへん補足テクニックなんでしょうけれども、VB6からほんとの意味でのDLLを作ることはできません。今回の作業にはなんぼなんでも無関係でしょうし、めんどくさいので訳しません。)

GdiplusShutdown Function
原本CD/DVD(2003.04版) Online
凡例API
void GdiplusShutdown(ULONG_PTR token);
TypeLibrary GdiPlus.GdiPlusExportsのメンバ
Sub GdiplusShutdown(token As Long)
説明The GdiplusShutdown function cleans up resources used by MicrosoftR WindowsR GDI+. Each call to GdiplusStartup should be paired with a call to GdiplusShutdown.
GdiplusShutdownファンクションはMicrosoft® Windows® GDIによるリソースの使用状態をクリーンアップします。
GdiplusStartupをコールしたら、必ずGdiplusShutdownをコールしてください。
(「複数のGdiplusStartupをコールしている場合には、対を成す形でそれぞれGdiplusShutdownをコールしてください」かもしれません。)
パラメータtoken
[in]
Token returned by a previous call to GdiplusStartup.
GdiplusStartupをコールした時に戻されたトークンを指定します。
戻り値No return value.
ありません。
訳注:戻り値がないため、TypeLibraryではSubで宣言されています。
解説You must call GdiplusStartup before you create any GDI+ objects, and you must delete all of your GDI+ objects (or have them go out of scope) before you call GdiplusShutdown.
何らかのGDI+オブジェクトを作成する前に、GdiplusStartupをコールしなければなりません。
そしてGdiplusShutdownをコールする前にすべてのGDI+オブジェクトを削除(あるいはスコープ外へ追いやる)しておかなければなりません。

えー、とりあえずこんなところで。

GdiplusStartupInput/GdiplusStartupOutput構造体についても一応訳してみたんですが、ほとんどバックグラウンドスレッドの取り扱いとコールバック関数のセットに関する記述でした。

VB6はAPIなどを使って明示的にスレッドを起こしでもしない限り、基本的にシングルスレッド(OCX等で独立したスレッドを持つ場合を除きます)アプリケーションしか生成できませんので、スレッドをまたがる場合のGDI+の設定はあまり意味を持ちません。
GDI+側でも「バックグラウンドスレッドを考慮しない」デフォルトとなっていますので、何も設定しない状態でVB6にマッチした設定状態になりますね。

コールバック関数のセットは、主にpaintなどのシステムメッセージに呼応した形での再描画を目的として用意されている機能です。
VC++などでは再描画(他のウィンドウの裏に隠れて一部/全部が見えなくなった状態から再度前面に出て、隠れていた部分の表示が必要になるなど)は基本的にアプリケーション側で行う必要があります。ので、再描画を促すシステムメッセージが流れてきた場合にどの関数を作動させるかをあらかじめ登録しておくのが普通です。

それに対してVBは、基本的には再描画を必要とするような描画を行えるオブジェクトはFormとPictureBoxくらいです。
こかもこれらはAutoRedrawプロパティをTrueにしておくだけで、描画したイメージをメモリ上にバックアップして、再描画時にはそのイメージを再表示するような動作をします。
ので、オーナードローでも行わない限り再描画のための機能をこしらえる必要はほとんどないんですよね。

従って、GdiplusStartupInput構造体はGdiplusVersionメンバを1にセットする(使用するGDI+のバージョンを指定。現在Ver1/1.1しか存在していませんので、必ず「1」を指定することになります)くらいで、他のメンバは0(つまり構造体を定義した時の初期値のまんま)で構いません。
GdiplusStartupOutput構造体に至ってはまったく使いませんし、第一GdiplusStartup関数の中でもオプショナルの扱いになっていますので、引数として用意する必要すらありません。

以上より、GDI+の開始と終了は、以下のよぅな記述となります。

Private Sub Command1_Click()
Dim typGPInput As Gdiplus.GdiplusStartupInput
Dim lngToken As Long
typGPInput.GdiplusVersion = 1
If (GdiplusStartup(lngToken, typGPInput) _
<> GdiPlus.GpStatus.Ok) Then
MsgBox "GDI+の起動に失敗しました", vbCritical
Exit Sub
End If
Call GdiplusShutdown (lngToken)
End Sub
いぇ別に起動と終了だけなので、実行してもなんにならないんですけど。
まずは基本形といぅことで、ここまで。


記述サンプルとして解析に使っているGDIPlusLOGOプロジェクトでは、プロシージャごとに起動-終了を行っているんですよね。
これは途中でバグって強制終了になってしまった場合にGDI+のインスタンスが残りにくい、といぅ開発者側の都合での記述のよぅに思えます。
実際に操作してみると、やはりけっこぅ重めの動作に感じられます。これは、あるいはプロシージャごとの起動-終了にかかるオーバーヘッドのせいなのかなぁといぅ気もしています。
「猫でもわかるプログラミング」の方では、
  1. プログラムの最初にGdiplusStartup関数を実行する。

  2. プログラムが終了する直前にGdiplusShutdown関数を実行する。
といぅ説明になっており、動作効率から考えるとこちらの方が正当な手法のよぅにも思えます。
このへんは、もぅ少し動作するよぅなところまで作り込んでから、動作時間計測でもしてみよぅかと思っています。

トラックバック

このエントリーのトラックバックURL:
http://salv.miscnotes.com/mt/mt-tb.cgi/452

コメントを投稿