外部定義DLLの作り方と使い方。 色々なファイルの識別データを作っていると、多くはありませんが 「バイナリモード」「文字列サーチモード」のどちらを使っても、 識別できないようなファイル形式が見つかります。 たとえば・・「ヘッダーを2ヶ所以上調べなければ形式が分からない」 ファイルだったり、「ファイルサイズによってヘッダーが変わる」 ようなものがこれに該当します。 そのような、ファイル形式の識別データを作る場合 「特定の部分のヘッダーだけに対応していく」 ことで、ある程度解決できる部分もあるのですが、 最終的には、識別処理そのものを加えてしまったほうがデータ的にも 分かりやすくなります。 ここでは、動画(AVI)の圧縮プログラム「codec」の種類を識別するための データを例にとって説明します。 みなさんもご存知の通り、インターネット等で取り扱われるファイルは画像 であれば、JPG,PNG,GIFなどでファイルサイズを小さくしています。(圧縮) これと同じことが動画でも行われており、中でも「AVI」形式の場合、同じ ように見えても内部的には、さまざまな方法によってデータサイズを小さく 圧縮しています。 この時に圧縮に利用するプログラムや符号化の種類を「codec」といいます。 また、それぞれの「codec」を利用して圧縮された動画は、それに対応する プレイヤーがなければ再生することが出来ません。なので「codec」の種類が 分かることは、目的の動画を再生出来るプレイヤーを探す目安となり、ファイル の種類同様に識別できるメリットは大きいと言えます。 では、これを識別できるデータセットを作ってみよう! ・・と思っても、やっかいなことがあります。 codecには「DivX」や「cinepak」などさまざまな種類があるのですが、 ものによっては、別々の2箇所にあるヘッダーの両方を調べなければ識別できない ものがあるのです。 加えて、それぞれの形式は ヘッダーの種類が2タイプずつ(もしくはもっと!?)あります・・。(^^; これらを予め用意された識別モードやその繰り返しで解決するのは、かなりの難問ですね。 なので、これらの問題を一気に解決し、一遍に「cinepakだ!」「DivXだ!」と識別できるような 識別モードを自分で作成します。 まず最初に、大前提としてこれは「バイナリモード」「文字列サーチモード」 で行った「ヘッダーのタイプや種類を指定する」というものでありません。 これらは、ダイノロジカル内部に用意された識別処理を間接的に利用している だけにすぎません。 今回は、これとは異なり直接識別アルゴリズムを追加することになるので、 VC++などのコンパイラを用いて識別処理を作成し、DLL形式のファイルとして 識別データセットに追加します。 以下では、VC++6.0 Professionalを用いて説明を行います。 まず、VC++6.0でプロジェクトの新規作成を行う際に「Win32 Dynamic-Link Library」 を選んでください。 ダイノロジカルの拡張DLLを作る場合には、DLLに標準で必要な"DllMain"関数以外に 以下の2つの関数が必要です。 ・BOOL WINAPI MyJudgeDllEntry(HWND hWnd,unsigned _int16 *extdllid,struct IJGDLLINFO *dllinfo); 拡張DLLの情報を記述してください。ダイノロジカルが対象のDLL情報を取得する際に呼び出します。 ・BOOL WINAPI MyJudgeDllCalculate(HWND hWnd,LPCSTR targetfile,struct IJGDLLACCESS *dllace); 拡張DLLで行う識別処理を記述してください。ダイノロジカルが対象のDLLを用いてファイルを識別する 時に呼び出します。 また、これら関数と一緒に "extdldef_m.h"をソースファイルに追加してください。 "extdldef_m.h"はサンプルプログラムのフォルダ(spjudge_m)の中にあります。 実際のプログラムは以下のようになります。 -------------------------------- spjudge_m.cpp ------------------------------------- #include <windows.h> #include "extdldef_m.h" //拡張DLLを作るのに必要なヘッダー #ifndef _def_IJGDLLINFO struct IJGDLLINFO{ char dllname[31]; //DLL名 char author[31]; //作者名 float ver; //バージョン番号。少数点以下2桁までの使用を推奨 char vername[5]; //バージョン名(接尾語 βなど) _int8 sprnamber; //基本展開の数。普通(jobflgを無視する場合)は"1" }; #define _def_IJGDLLINFO #endif #ifndef _def_IJGDLLACCESS struct IJGDLLACCESS{ _int8 jobflg1st; //ジョブフラグ 識別処理を複数作成し、それを分ける場合に使用 _int8 jobflg2nd; //同上。DataCreaterでは現在未対応なため双方とも"0" unsigned _int32 headerid_hi; //DLLのファイル識別結果を値としてこの変数に渡します。 unsigned _int32 headerid_lo; //DLLのファイル識別結果を値としてこの変数に渡します。 }; #define _def_IJGDLLACCESS #endif #define DEBUG FALSE //Dllのエントリポイント。中に何も記述しないでください。 int WINAPI DllMain(HINSTANCE hInst,DWORD fdwReason,PVOID pvReserved) { return TRUE; } //拡張DLLの情報を記述します。extdllidは呼び出しDLLのIDです。ここで設定した値はDataCreaterでも使用します。 BOOL WINAPI MyJudgeDllEntry(HWND hWnd,unsigned _int16 *extdllid,struct IJGDLLINFO *dllinfo) { *extdllid=40001; lstrcpy(dllinfo->author,"smp_make"); lstrcpy(dllinfo->dllname,"codec special"); dllinfo->ver=1; lstrcpy(dllinfo->vername,"β"); dllinfo->sprnamber=1; return TRUE; } HANDLE tfile; //対象ファイルのハンドル DWORD rbyte; char buffer[100]; //ファイル情報の一時格納 //識別アルゴリズムを記述します。今回は「codec」の識別です。 //targetfileに識別元のファイル名が入っていますのでこれに対してファイルの読み込みなどを行い、 //ファイルを識別します。 //識別した結果を数値として、IJGDLLACCESS構造体のheaderid_hiまたは、headerid_loに格納します。 BOOL WINAPI MyJudgeDllCalculate(HWND hWnd,LPCSTR targetfile,struct IJGDLLACCESS *dllace) { /** codecの識別プログラムです **/ /** 拡張DLLに特に関係する部分は、 **/ /** 識別結果を"dllace->headerid_hi"と"dllace->headerid_lo"に格納する所です **/ tfile=CreateFile(targetfile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,NULL); if(tfile==INVALID_HANDLE_VALUE) return FALSE; //「Cinepak」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidscvid"))||(!lstrcmp(buffer,"vidsCVID"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009211; if(DEBUG) MessageBox(hWnd,"cinepak","check",MB_OK); CloseHandle(tfile); return TRUE; } //「Microsoft Video1」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidsmsvc"))||(!lstrcmp(buffer,"vidsMSVC"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009212; if(DEBUG) MessageBox(hWnd,"Microsoft Video1","check",MB_OK); CloseHandle(tfile); return TRUE; } //「MS-MPEG4(v1)」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidsmpg4"))||(!lstrcmp(buffer,"vidsMPG4"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009216; if(DEBUG) MessageBox(hWnd,"MS-MPEG4(v1)","check",MB_OK); CloseHandle(tfile); return TRUE; } //「MS-MPEG4v2」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidsmp42"))||(!lstrcmp(buffer,"vidsMP42"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009217; if(DEBUG) MessageBox(hWnd,"MS-MPEG4v2","check",MB_OK); CloseHandle(tfile); return TRUE; } //「MS-MPEG4v3」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidsmp43"))||(!lstrcmp(buffer,"vidsMP43"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009218; if(DEBUG) MessageBox(hWnd,"MS-MPEG4v3","check",MB_OK); CloseHandle(tfile); return TRUE; } //「DivX Low-Motion」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidsdiv3"))||(!lstrcmp(buffer,"vidsDIV3"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009219; if(DEBUG) MessageBox(hWnd,"DivX Low-Motion","check",MB_OK); CloseHandle(tfile); return TRUE; } //「DivX Fast-Motion」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidsdiv4"))||(!lstrcmp(buffer,"vidsDIV4"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009220; if(DEBUG) MessageBox(hWnd,"DivX Fast-Motion","check",MB_OK); CloseHandle(tfile); return TRUE; } //「DivX4」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidsdivx"))||(!lstrcmp(buffer,"vidsDIVX"))){ SetFilePointer(tfile,188,0,FILE_BEGIN); ReadFile(tfile,buffer,4,&rbyte,NULL); *(buffer+4)='\0'; if((!lstrcmp(buffer,"DIVX"))||(!lstrcmp(buffer,"divx"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009221; if(DEBUG) MessageBox(hWnd,"DivX4","check",MB_OK); CloseHandle(tfile); return TRUE; } } //「DivX5.0」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidsdivx"))||(!lstrcmp(buffer,"vidsDIVX"))){ SetFilePointer(tfile,188,0,FILE_BEGIN); ReadFile(tfile,buffer,4,&rbyte,NULL); *(buffer+4)='\0'; if((!lstrcmp(buffer,"DX50"))||(!lstrcmp(buffer,"dx50"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009222; if(DEBUG) MessageBox(hWnd,"DivX5.0","check",MB_OK); CloseHandle(tfile); return TRUE; } } //「Xvid」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidsxvid"))||(!lstrcmp(buffer,"vidsXVID"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009223; if(DEBUG) MessageBox(hWnd,"Xvid","check",MB_OK); CloseHandle(tfile); return TRUE; } //「Motion Jpeg」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidsmjpg"))||(!lstrcmp(buffer,"vidsMJPG"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009224; if(DEBUG) MessageBox(hWnd,"Motion Jpeg","check",MB_OK); CloseHandle(tfile); return TRUE; } //「Angel Potion MPEG4」形式のチェック SetFilePointer(tfile,108,0,FILE_BEGIN); ReadFile(tfile,buffer,8,&rbyte,NULL); if((!lstrcmp(buffer,"vidsap41"))||(!lstrcmp(buffer,"vidsAP41"))){ dllace->headerid_hi=0; dllace->headerid_lo=0x00009227; if(DEBUG) MessageBox(hWnd,"Angel Potion MPEG4","check",MB_OK); CloseHandle(tfile); return TRUE; } dllace->headerid_hi=0; dllace->headerid_lo=0x001; CloseHandle(tfile); return TRUE; } -------------------------------- ここまで ------------------------------------- です。これで拡張DLL本体の作成はOKです。 コンパイルとビルドを行い、 これを利用したい識別データセットのフォルダにある "add-in"フォルダに移動させておいてください。 次に、これを利用する識別データセットを作ります。 バイナリヘッダー、テキスト識別双方とも ヘッダー定義に関する設定は、「ヘッダー定義作成」で行いましたね。 拡張DLLを用いて識別処理を行いたい場合、この作成の "モード"を「固定バイナリ」でも「文字列サーチ」でもなく、 「自前(DLL呼び出し)にするだけです。 そして、有効になった入力ボックス「呼び出しDLLのID」に 先ほど、ソースファイル"spjudge_m.cpp"で指定したID(extdllid)である 40001を入力し、「作成、更新」ボタンを押してヘッダー定義作成を終了します。 これで、あとは他のモード同様に識別ベースラインに 「今回作成したヘッダー定義」「拡張子のメッセージ」「実際のヘッダー情報」 を関連付けるだけです。 他のモードのように、識別データ中に一切ヘッダー情報などを設定していないので違和感が あるかもしれませんが、ヘッダーは直接プログラムで読み込んでいるので、識別データ側は そのプログラム(DLL)のIDだけをを指定しておけば、問題ありません。 また、自前モードの場合「実際のヘッダー情報」は本物のファイル(ここではそれぞれの codecヘッダーを持つAVIファイル)を用意して取得しなくても、作成したアルゴリズムで指定した 返却値を直接DataCreater側に指定することによって、データを作成することが出来ます。 これは例えば、上のプログラムで「cinepak」のヘッダー情報が見つかった場合のID値は dllace->headerid_hi=0; dllace->headerid_lo=0x00009211; (10進数にすると、37393) としています。 この値、Hi:「0」Lo:「37393」を識別ベースラインのデータ編集の時に登録することによって 実際には、「cinepak」のAVIファイルを読み込まなくとも識別データを作ることが出来ます。 アドインの値を直接登録する場合"IDキーの取得"で「取得」ではなく、「詳細」を選び、 "直接入力する"のチェックボタンを押して、ID値を入力します。これによって、実際のファイルを開く手間を省くことが出来ます。 (応用すれば、ファイル実態のない識別・・たとえばおみくじや起動時間によって変わるメッセージ を表示するデータセットなどを作ることも出来ます) また、データ編集で「取得」を行った場合は、この画面に「取得」の際に指定したファイルに対する アドインからの返却値(結果値)が自動的に入るので、それによってアドインが正しく動作している かをモニタリングすることも出来ます。