EternalWindows
Cabinet API / キャビネットファイルの情報

展開処理は、Cabinet APIのFDIの関数で行うことになります。 FDIもFCIと同様に、コンテキストやコールバック関数の概念があり、 FCIに比べて処理が複雑でない傾向もあります。 次に示すFDICreateは、FDIコンテキストを作成します。

HFDI FDICreate(
    PFNALLOC pfnalloc,
    PFNFREE  pfnfree,
    PFNOPEN  pfnopen,
    PFNREAD  pfnread,
    PFNWRITE pfnwrite,
    PFNCLOSE pfnclose,
    PFNSEEK  pfnseek,
    int      cpuType,
    PERF     perf
);

pfnallocは、メモリを確保する必要が生じた場合に呼ばれるコールバック関数のアドレスを指定します。 pfnfreeは、メモリを開放する必要が生じた場合に呼ばれるコールバック関数のアドレスを指定します。 pfnopenは、ファイルをオープンする際に呼ばれるコールバック関数のアドレスを指定します。 pfnreadは、ファイルを読み取る際に呼ばれるコールバック関数のアドレスを指定します。 pfnwriteは、ファイルに書き込む際に呼ばれるコールバック関数のアドレスを指定します。 pfncloseは、ファイルを閉じる際に呼ばれるコールバック関数のアドレスを指定します。 pfnseekは、ファイルをシークする際に呼ばれるコールバック関数のアドレスを指定します。 cpuTypeは、16ビットアプリケーションの作成時に意味を持つものであり、 現在ではこの引数は意味を持ちません。 perfは、ERF構造体のアドレスを指定します。 エラーが発生した場合、この構造体のメンバを調べることでエラーの原因を確認できます。 戻り値のHFDIがFDIコンテキストのハンドルとなります。

作成したFDIコンテキストは、FDIDestroyで破棄することになります。

BOOL FDIDestroy(HFDI hfdi);

hfdiは、FDIコンテキストのハンドルを指定します。

FDIコンテキストを取得すれば展開処理を行うためのFDICopyを呼び出すことができますが、 その前にFDIIsCabinetという関数について説明します。 この関数は、指定したファイルがキャビネットファイルであるかを調べると共に、 そのキャビネットファイルの情報を返すことができます。

BOOL FDIIsCabinet(
    HFDI            hfdi,
    INT_PTR         hf,
    PFDICABINETINFO pfdici
);

hfdiは、FDIコンテキストのハンドルを指定します。 hfは、ファイルハンドルを指定します。 pfdiciは、FDICABINETINFO構造体のアドレスを指定します。

typedef struct {
    long        cbCabinet;
    USHORT      cFolders;
    USHORT      cFiles;
    USHORT      setID;
    USHORT      iCabinet;
    BOOL        fReserve;
    BOOL        hasprev;
    BOOL        hasnext;
} FDICABINETINFO;
typedef FDICABINETINFO FAR *PFDICABINETINFO;

cbCabinetは、キャビネットファイルのサイズがバイト単位で格納されます。 この構造体のサイズではないことに注意してください。 cFoldersは、キャビネットファイルに含まれるフォルダ(CFFOLDER構造体)の数です。 cFilesは、キャビネットに含まれるファイルの数が格納されます。 setIDは、FCIで利用したCCAB構造体のsetIDで指定した値が格納されます。 iCabinetは、複数のキャビネットファイルを作成している場合に、 そのファイルが何番目に作成されたのかを示すインデックスが格納されます。 単一のキャビネットを作成した場合は、常に0となります。 fReserveは、予約されています。 hasprevは、前のキャビネットファイルの名前などの情報を持っているかどうかです。 hasnextは、次のキャビネットファイルの名前などの情報を持っているかどうかです。

今回のプログラムは、FDIコンテキストを作成すると共に、 FDIIsCabinetでキャビネットファイルの情報を取得します。 FDIを利用するので、fdi.hをインクルードします。

#include <windows.h>
#include <fdi.h>

#pragma comment (lib, "cabinet.lib")

void * DIAMONDAPI AllocProc(ULONG cb);
void DIAMONDAPI FreeProc(void *memory);
int DIAMONDAPI OpenProc(char *pszFile, int oflag, int pmode);
UINT DIAMONDAPI ReadProc(int hf, void *pv, UINT cb);
UINT DIAMONDAPI WriteProc(int hf, void *pv, UINT cb);
int DIAMONDAPI CloseProc(int hf);
long DIAMONDAPI SeekProc(int hf, long dist, int seektype);
BOOL GetCabinetInfo(HFDI hfdi, LPSTR lpszFileName, PFDICABINETINFO pInfo);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	HFDI           hfdi;
	ERF            erf;
	TCHAR          szBuf[256];
	FDICABINETINFO info;

	hfdi = FDICreate(AllocProc, FreeProc, OpenProc, ReadProc, WriteProc, CloseProc, SeekProc, cpu80386, &erf);
	if (hfdi == NULL) {
		wsprintf(szBuf, TEXT("FDIコンテキストの作成に失敗しました。 ErrorCode %d"), erf.erfOper);
		MessageBox(NULL, szBuf, NULL, MB_ICONWARNING);
		return 0;
	}

	if (GetCabinetInfo(hfdi, "c:\\sample.cab", &info)) {
		wsprintf(szBuf, TEXT("ファイルサイズ : %dKb\nファイル数 : %d\nID : %d"), info.cbCabinet / 1024, info.cFiles, info.setID);
		MessageBox(NULL, szBuf, TEXT("OK"), MB_OK);
	}

	FDIDestroy(hfdi);

	return 0;
}

BOOL GetCabinetInfo(HFDI hfdi, LPSTR lpszFileName, PFDICABINETINFO pInfo)
{
	HANDLE hFile;
	BOOL   bResult;
	
	hFile = CreateFileA(lpszFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE) {
		MessageBox(NULL, TEXT("ファイルのオープンに失敗しました。"), NULL, MB_ICONWARNING);
		return FALSE;
	}

	bResult = FDIIsCabinet(hfdi, (int)hFile, pInfo);
	if (!bResult)
		MessageBox(NULL, TEXT("キャビネットファイルではありません。"), NULL, MB_ICONWARNING);
	
	CloseHandle(hFile);

	return bResult;
}

void * DIAMONDAPI AllocProc(ULONG cb)
{
	return HeapAlloc(GetProcessHeap(), 0, cb);
}

void DIAMONDAPI FreeProc(void *memory)
{
	HeapFree(GetProcessHeap(), 0, memory);
}

int DIAMONDAPI OpenProc(char *pszFile, int oflag, int pmode)
{
	HANDLE hFile;

	hFile = CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	return (int)hFile;
}

UINT DIAMONDAPI ReadProc(int hf, void *pv, UINT cb)
{
	DWORD dwReadByte;

	ReadFile((HANDLE)hf, pv, cb, &dwReadByte, NULL);
	
	return dwReadByte;
}

UINT DIAMONDAPI WriteProc(int hf, void *pv, UINT cb)
{
	DWORD dwWriteByte;

	WriteFile((HANDLE)hf, pv, cb, &dwWriteByte, NULL);

	return dwWriteByte;
}

int DIAMONDAPI CloseProc(int hf)
{
	CloseHandle((HANDLE)hf);

	return 0;
}

long DIAMONDAPI SeekProc(int hf, long dist, int seektype)
{
	return SetFilePointer((HANDLE)hf, dist, 0, seektype);
}

WinMainでは、FDICreateでFDIコンテキストを作成し、 それを自作関数のGetCabinetInfoという関数に渡しています。 FDICreateの呼び出しは次のようになっています。

hfdi = FDICreate(AllocProc, FreeProc, OpenProc, ReadProc, WriteProc, CloseProc, SeekProc, cpu80386, &erf);
if (hfdi == NULL) {
	wsprintf(szBuf, TEXT("FDIコンテキストの作成に失敗しました。 ErrorCode %d"), erf.erfOper);
	MessageBox(NULL, szBuf, NULL, MB_ICONWARNING);
	return 0;
}

各コールバック関数をFCIのときと同じ要領で指定します。 エラー発生時にERF構造体のerfOperメンバを確認するのも同じです。 第8引数にはCPUタイプを指定しますが、これは16ビット時代の名残です。 0を指定しても問題ありませんが、cpu80386という定義を利用しています。

BOOL GetCabinetInfo(HFDI hfdi, LPSTR lpszFileName, PFDICABINETINFO pInfo)
{
	HANDLE hFile;
	BOOL   bResult;
	
	hFile = CreateFileA(lpszFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE) {
		MessageBox(NULL, TEXT("ファイルのオープンに失敗しました。"), NULL, MB_ICONWARNING);
		return FALSE;
	}

	bResult = FDIIsCabinet(hfdi, (int)hFile, pInfo);
	if (!bResult)
		MessageBox(NULL, TEXT("キャビネットファイルではありません。"), NULL, MB_ICONWARNING);
	
	CloseHandle(hFile);

	return bResult;
}

FDIIsCabinetに指定するのは、ファイル名ではなくファイルハンドルであるため、 まずCreateFileAを呼び出してファイルオープンしなければなりません。 ANSI版の関数を利用するのは、FCIと同じくFDIがUNICODEに対応していないためです。 FDIIsCabinetは、内部でReadProcとSeekProcを呼び出し、 ファイルがキャビネットファイルである場合はFDICABINETINFO構造体を初期化します。


戻る