リストHOME  リストOpen Source

第14回VBAプログラム豆知識

概要 ~ 構造体の隙間 ~

 皆さん、いきなりですが、以下の2つの構造体、それぞれサイズは幾つか分かりますか?

サンプルのダウンロード



'構造体1
Public Type Structure1
    a As Byte           '1バイト
    b As Integer        '2バイト

End Type

'構造体2
Public Type Structure2
    a As Integer        '2バイト
    b As Long           '4バイト

End Type


 構造体1は3バイト(a:1、b:2)、構造体2は6バイト(a:2、b:4)って、思ったりしませんか?
これ、間違いなんです。正しくは、構造体1が、4バイト、構造体2が8バイトです。


 『なっ、なんで?』って思いますよね。


 プログラムには、実はバイトアライメント(境界)というものがあり、このアライメントに沿って、変数は配置されるようになっています。ですので、以下の図のように、メンバ変数とメンバ変数との間に、隙間が出来てしまう訳です。

構造体の隙間


 『じゃあ、このアライメントってどうなってんの?』ってことになると思うのですが…
基本的には、変数は4バイトのアライメントに沿って配置されるのですが、4バイトより小さいデータ型と、4バイト以上のデータ型で、配置のされ方が異なってきます。



 例えば、1つのByte型の変数の後に、16バイトのVariant型の変数を配置すると、Variant型のアライメントは4ですので、3バイトの隙間が出来ます。また、3つのByte型の変数の後に、Variant型の変数を配置すると、1バイトの隙間が出来ます。ちなみに、Byte型を4つ並べた後であれば、隙間は出来ません。


 ということで、構造体のメンバ変数とメンバ変数の間には、隙間がある場合があります


 このことが、C言語のソースコードを参考にして、VBAでプログラミングするときに、問題が発生する場合があるのです。これを私が、犯したミスを例にして説明したいと思います。


 それは、BMP画像のサイズを取得するプログラムをVBAで実装していた時のことです。BMP画像のサイズは、BMPファイルの先頭のヘッダ情報に記述されているのですが、そのヘッダ情報の構造体をMSDNで調べると以下のようになっていました。ちなみに、画像のサイズはこのファイルヘッダ情報の下のビットマップ情報に存在しています(BMPファイルはヘッダ情報が、ファイル情報とBMP情報の2つ並んで存在)。



typedef struct tagBITMAPFILEHEADER {
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

 上記を真似て、この構造体を読み込むVBAコードを実装したのですが、サイズがうまく取得できませんでした。コードを見直しても、間違っている気がせず、確認のため、上記の構造体をC言語のヘッダファイルで検索してみました。すると、なんと以下のように記述されているのです。


#include 

#pragma region Desktop Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)

typedef struct tagBITMAPFILEHEADER {
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */
#pragma endregion

#include 

 最初の行の#include <pshpack2.h>で、アライメントのサイズを2に変更して最後の行の#include <poppack.h>で、アライメントのサイズを元に戻すように、記述されているのです。


 C言語では、このように、コンパイル時に、パッキングと、よばれるアライメントサイズを変更するすべが存在するのです。これにより、構造体の隙間を詰めることが、C言語では可能となっています。


 ですので、BMPファイルのヘッダ情報を構造体で、読み出した場合、C言語では、問題ないのですが、VBAでは、構造体に隙間がありますので、思った結果が得られないということになります。


 それでは、VBAではどうすれば良いかというと、構造体のメンバ変数を1つずつ読み出すような実装にしなければなりません。


 C言語を参考にして、VBAプログラミングするときは、このように構造体がパッキングされている可能性がありますので、十分注意しましょう。必ず、C言語のヘッダファイルを確認することが重要です。



第13回  <  >  第15回



ダウンロード

 以下から、今回の豆知識で紹介した内容のエクセルファイルがダウンロードできます。

サンプルのダウンロード



所感

 VBAのスキルを一歩踏み込んで習得しようとすると、やはり、C言語の知識が不可欠となってきます。このことが、『VBAプログラミング豆知識』を公開としようと思った大きな理由だったのですが、今回のお話がこのことの、典型的な例の一つかと思います。一般の方がC言語に触れる機会は、ほとんどないと思いますので、これを機に、バイトアライメントの概念をしっかりと理解しましょう。



連絡先

 ご意見・ご要望等ありましたら、画面最下部のメールアドレスまでご連絡ください。




エクセルバックアップ・ページのフッター
管理者のメールアドレス