none
Win32 API CHOOSEFONTについて RRS feed

  • 質問

  •  標準モデュールにてパラメーターを設定し、ChooseFontを実行してもダイアログは表示されずにデバッグは次行に移行し予告なしでエクセルが終了してしまうのですが…

       hMem = GlobalAlloc(GHND, Len(LF))      'メモリブロックを確保してそのハンドルを取得
        Address = GlobalLock(hMem)      'グローバルヒープに確保されたメモリブロックをロックする
       If Address = 0 Then Exit Sub
        CopyMemory Address, LF, Len(LF)     'メモリの領域をコピーする
         With CF     'CHoosefont_B構造体の設定(Choosefont構造体の名前だけ変えたもの)
           .lStructSize = Len(CF)                 '構造体のサイズを設定
           .hwndOwner = Application.hWND 'ウインドウハンドルを設定
           .lpLogFont = Address                      'フォント情報の初期設定
           .flags = CF_INITTOLOGFONTSTRUCT Or _
                        CF_SCREENFONTS Or _
                            CF_LIMITSIZE Or _
                                CF_EFFECTS                       'フラグを設定
           .rgbColors = aFontColor                    'フォントのカラーを設定
           .nSizeMin = FONT_SIZE_MIN                             '最小フォントサイズを設定(変更可能)
           .nSizeMax = FONT_SIZE_MAX     '最大フォントサイズを設定(変更可能)
            End With
         Ret = CHOOSEFONT(CF)   'フォント選定のコモンダイアログボックスを表示する
         CopyMemory LF, ByVal Address, Len(LF)            'メモリの領域をコピーする(選択した情報)

        LF(LOGFONT)の設定が、悪いのでしょうか???

    2022年11月4日 14:01

回答

  • そうですね。LOGFONT のユーザー定義型が正しく宣言されているなら、ローカル変数 LF に対する「.lpLogFont = VarPtr(LF)」を指定しようと、API で確保したアドレスを「.lpLogFont = Address」で指定しようと、どちらでも良いと思います。

    重要なのは、指定したポインターが読み書き可能なメモリを指しており、かつ、そのポインターが指し示す領域に十分なサイズを確保してあるどうかかと。

    .

    一番最初の質問時点では、CHOOSEFONT の nFontType 宣言漏れ問題もあって、.lStructSize = 90 によって「パラメーターが間違っています。」になったわけですよね(32bitなら「60」、64bitなら「104」とする必要があるはず)

    そこへ、さらにパディングの問題が加わっています。

    Win64 環境においては「hMem = GlobalAlloc(GHND, Len(LF))」の時点で、90 バイトの領域しか確保されていなかったのでしょう(nFontType ありだったのなら 92 バイト)

    この時、64bit 版のユーザー定義型は 8 バイトのアライメントなので、実際には 104 バイトを必要としていたはずです(nFontType の有無によらず、 LenB(LF) は 104  を返す)

    LenB(LF) ではなく Len(LF) を用いていたことから、「Address = GlobalLock(hMem)」「CopyMemory LF, ByVal Address, Len(LF)」では、先頭から 90 バイトまでの 0x00~0x59 の範囲のみしか転写されていません。0x5A~0x67 のエリア(nSizeMin/nSizeMax)は漏れていたことでしょう。

    0x00-0x03:lStructSize
     (中略)
    0x50-0x57:lpszStyle
    0x58-0x59:本来は nFontType の位置だが、宣言漏れ時は MISSING_ALIGNMENT
    修正版では、明示的なパディングを入れるか、Long を LongPtr 化することで、Len(LF) でも 104 が返されるようにしたみたいですけれどね。 

    • 回答としてマーク my_think 2022年11月12日 12:50
    2022年11月10日 20:35

すべての返信

  • 下記サイトを参考に、アプリケーション クラッシュ時にプロセス ダンプを生成する WER レジストリ設定を行い、採取したダンプ ファイルを解析してみては?
    ---------------------------
    WER を使って Dump を採取する
    https://jpdscore.github.io/blog/debugging/using-WER/
    ---------------------------
    2022年11月5日 7:17
  • そういう挙動をするということは、ヒープ破壊やスタック破壊なので、構造体の定義とか、Declare Function の定義を見直すところかと思います。
    2022年11月5日 13:21
  • CHOOSEFONTの次の行でGetLastError をとってみると、「パラメーターが間違っています。」とでました。
    CHOOSEFONT構造体のどれかがまちがっているということですね。
    (実は、VBAでCHOOSEFONTは使えないのだという答えを期待していたのですが。。)

    それと、たとえ、エラーによりダイアログが開かなかったとしても、Copymemory LF, ByVal Address, Len(LF)自体は正常に実行されると思うのですが、ここでクラッシュしてしまうのは、よくわからない。

    2022年11月5日 15:30
  • ちなみに CHOOSEFONTの値は下記です

        : flags : 8513 : Long

        : hdc : 0^ : LongLong

        : hInstance : 0^ : LongLong

        : hwndOwner : 330058^ : LongLong

        : iPointSize : 0 : Long

        : lCustData : 0^ : LongLong

        : lpfnHook : 0^ : LongLong

        : lpLogFont : 21^ : LongLong

        : lpszStyle : "" : String

        : lpTemplateName : "" : String

        : lStructSize : 90 : Long

        : MISSING_ALIGNMENT : 0 : Integer

        : nSizeMax : 24 : Long

        : nSizeMin : 6 : Long

        : rgbColors : 0 : Long

    2022年11月5日 15:37
  • それと、たとえ、エラーによりダイアログが開かなかったとしても、Copymemory LF, ByVal Address, Len(LF)自体は正常に実行されると思うのですが、ここでクラッシュしてしまうのは、よくわからない。

    クラッシュに遭遇する箇所が悪いとは限りません。

    スタック破壊やヒープ破壊の怖いところは、原因(コードの誤り)がある行はなんとなく実行された後、離れたタイミングで落ちる・不正動作を招き、原因の特定が難しくなることです。
    たとえば、ChooseFont の戻り値を誤った型にしていた場合は、スタックズレが発生し、関数終了時に不正なアドレスを実行しようとして落ちると思います。

    2022年11月5日 15:41
  • ご返答いただき誠にありがとうございます。

    続報ですが、ローカルウィンドウにて、データを追っかけてみたところ、下記「>>」部分のCopyMemory

    を実行した段階で、ローカル変数のLFのデータ内容が変わってしまうことに気がつきました。

    この関数はロックされたメモリーにLFの内容をコピーしているだけだと思うのですが、LFが変わってしまうのは理解できません。

    なぜでしょうか? 

    以上 よろしくおねがいします。

    Option Explicit
        'CONST
    Public Const GMEM_MOVEABLE = &H2                                        '利用可能なメモリを確保
    Public Const GMEM_ZEROINIT = &H40                                           '新しく確保するメモリブロックの内容を0で初期化
    Public Const GHND = (GMEM_MOVEABLE Or GMEM_ZEROINIT)
    Public Const FONT_SIZE_MIN = 6                                                  '最小フォントサイズを設定(変更可能)
    Public Const FONT_SIZE_MAX = 24                                                 '最大フォントサイズを設定(変更可能)
    Public Const ONE_CHAR = 1
    Public Const POINTER_ZERO = 0
    Private Const POINTS_IN_AN_INCH = 72
        'API
    Private Declare PtrSafe Function GetLastError _
        Lib "kernel32" () As Long
    Private Declare PtrSafe Function GlobalSize _
        Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
    Private Declare PtrSafe Function CHOOSEFONT _
        Lib "comdlg32.dll" Alias "ChooseFontA" (pChoosefont As CHOOSEFONT) As Long
    Private Declare PtrSafe Function GlobalAlloc _
        Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As LongPtr) As LongPtr
    Private Declare PtrSafe Function GlobalFree _
        Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
    Private Declare PtrSafe Function GlobalLock _
        Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
    Private Declare PtrSafe Function GlobalUnlock _
        Lib "kernel32" (ByVal hMem As LongPtr) As Long
    Private Declare PtrSafe Sub CopyMemory _
        Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
    Private Declare PtrSafe Function GetDC Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
    Private Declare PtrSafe Function ReleaseDC Lib "user32" (ByVal hwnd As LongPtr, ByVal hdc As LongPtr) As Long
    Private Declare PtrSafe Function GetDeviceCaps Lib "gdi32" (ByVal hdc As LongPtr, ByVal nIndex As Long) As Long
        '使用する変数
    Public nFontName As String           'フォントの名前
    Public nFontSize As Long             'フォントのサイズ
    Public nFontBold As Boolean          'フォントの太字
    Public nFontItalic As Boolean        'フォントの斜体
    Public nFontStrikethrough As Boolean 'フォントの取り消し線
    Public nFontUnderline As Boolean     'フォント の下線
    Public nColor As Long                'フォントのカラー値
        '■関数名    ShowFontDialog
        '■用途   フォント選定のコモンダイアログボックスを表示して選択した各種情報を取得する
        '■引数
        '   nHwnd               :ウインドウのハンドル
        '   FontName            :フォント名
        '   FontColor           :フォントのカラー値
        '   FontSize            :フォントのサイズ
        '   FontBold            :フォントに太字が付加されているか
        '   FontItalic          :フォントに斜体が付加されているのか
        '   FontStrikethrough   :フォントに取り消し線が付加されているか
        '   FontUnderline       :フォントに下線が付加されているか
        '■戻り値  OKボタンを押した時0,キャンセルボタンを押した時1
    Public Function ShowFontDialog(ahWnd As LongPtr, _
                                                    aFontName As String, _
                                                        aFontColor As Long, _
                                                            aFontSize As Long, _
                                                                aFontBold As Boolean, _
                                                                    aFontItalic As Boolean, _
                                                                        aFontStrikethrough As Boolean, _
                                                                            aFontUnderline As Boolean _
                                                                                ) As Long

        Dim CF As CHOOSEFONT 'CHOOSEFONT構造体
        Dim LF As LOGFONT        'LOGFONT構造体

        Dim Address As LongPtr     'ポインタ
        Dim hMem As LongPtr       '戻り値(メモリブロックのハンドル)

        Dim mRet As LongPtr        '戻り値(GlobalUnlock、GlobalFreeで使用)
        Dim Ret As Long              '戻り値(CHOOSEFONTで使用)
        Dim Tmp() As Byte
        Dim HeightPix As Long
        Dim DPI As Long
        Dim hdc As LongPtr
        Dim i As Long

        hdc = GetDC(ahWnd)
        DPI = GetDeviceCaps(hdc, DC_LOGPIXELSY)  '画面のDPIを取得
        ReleaseDC ahWnd, hdc

        Tmp = StrConv(aFontName, vbFromUnicode) 'フォント名UnicodeからShift_Jisコードに変更

        With LF
            For i = LBound(Tmp) To UBound(Tmp)
                .lfFaceName(i + 1) = Tmp(i)
                Next                                            'フォント名を「Shift_Jis」コードで設定
            .lfHeight = aFontSize / POINTS_IN_AN_INCH * DPI      'フォントのサイズを設定
            .lfItalic = aFontItalic                     'フォントの斜体情報を設定
            .lfUnderline = aFontUnderline          'フォントの下線情報を設定
            .lfStrikeOut = aFontStrikethrough      'フォントの取り消し線情報を設定
            .lfWeight = aFontBold                  'フォントの幅情報を設定
            End With

        hMem = GlobalAlloc(GHND, Len(LF))      'メモリブロックを確保してそのハンドルを取得
        Address = GlobalLock(hMem)      'グローバルヒープに確保されたメモリブロックをロックする

        If Address = 0 Then Exit Sub

    >>    Call CopyMemory(ByVal Address, LF, Len(LF))

        With CF
            .lStructSize = Len(CF)                 '構造体のサイズを設定
            .hwndOwner = ahWnd 'ウインドウハンドルを設定
            .lpLogFont = Address                      'フォント情報の初期設定
            .lpLogFont = VarPtr(LF) 'フォント情報の初期設定
            .flags = CF_INITTOLOGFONTSTRUCT Or _
                        CF_SCREENFONTS Or _
                            CF_LIMITSIZE Or _
                                CF_EFFECTS                       'フラグを設定
            .rgbColors = aFontColor                    'フォントのカラーを設定
            .nSizeMin = FONT_SIZE_MIN      '最小フォントサイズを設定
            .nSizeMax = FONT_SIZE_MAX     '最大フォントサイズを設定
            .iPointSize = 8
            End With

        Ret = CHOOSEFONT(CF)   'フォント選定のコモンダイアログボックスを表示する

        CopyMemory LF, ByVal Address, Len(LF)            'メモリの領域をコピーする(選択した情報)
        mRet = GlobalUnlock(hMem)        'メモリブロックのロックを解除する
        mRet = GlobalFree(hMem)        'メモリブロックのロックを解放する

         If Ret <> 0 Then
            With LF
                nFontName = StrConv(.lfFaceName, vbUnicode)            'フォントの名前のみ取得
                nFontBold = IIf(.lfWeight = FW_NORMAL, False, True) 'フォントの幅情報を取得(lfWeightの値はTrue,Falseではないので注意)
                nFontItalic = .lfItalic            'フォントの斜体情報を取得
                nFontStrikethrough = .lfStrikeOut            'フォントの取り消し線情報を取得
                nFontUnderline = .lfUnderline            'フォントの下線情報を取得
                nFontSize = .lfHeight / DPI * POINTS_IN_AN_INCH            'フォントのサイズ情報を取得
                End With
                'フォントのカラー情報を取得
                nColor = CF.rgbColors
            Else
                ShowFontDialog = 1
            End If
        End Function

                                                             
    2022年11月7日 9:27
  • ちなみに LOGFONT は Win32API_PtrSafe.TXTの定義を使っております。

    (lfFaceName as string*LF_FACESIZE とする場合もあるようですが

    Public Type LOGFONT
        lfHeight         As Long                            '8
        lfWidth          As Long                            '8
        lfEscapement     As Long                        '8
        lfOrientation    As Long                            '8
        lfWeight         As Long                            '8
        lfItalic         As Byte                                '1
        lfUnderline      As Byte                            '1
        lfStrikeOut      As Byte                            '1
        lfCharSet        As Byte                             '1
        lfOutPrecision   As Byte                            '1
        lfClipPrecision  As Byte                            '1
        lfQuality         As Byte                               '1
        lfPitchAndFamily As Byte                        '1
        lfFaceName(1 To LF_FACESIZE) As Byte    '32
        End Type

    2022年11月7日 10:15
  • 隅々までは読んでいません。

    CHOOSEFONTA 構造体を C++ 環境でサイズを測ったところ、x64 で 104 バイト、x86 で 60 バイトですので、lStructureSize が 90 になるのはおかしいと思います。


    現状の CopyMemory の定義でデータが壊れるとなると、VBA の実行環境がどうなっているかを気にしたくなります。
    実行環境の Office は 64bit なのですか? 32bit なのですか?
    (32bit だったとすると壊れて当然ですが…)
    2022年11月7日 12:43
  • ちなみに CHOOSEFONTの値は下記です

       : lStructSize : 90 : Long

    90 バイトですか…? Win32 API の CHOOSEFONT 構造体を、VBA の CHOOSEFONT ユーザー定義型として再定義する際に、どのように宣言していますか?

    <削除>

    この構造体は 32bit 環境ではシングルバイトアライメントですが、64bit 環境では 4 バイト境界に配置される仕様です。

    そのため VBA の場合、このユーザー定義型は 32bit 版と 64bit 版で宣言が変化します。その結果、32bit 環境では 60 、64bit 環境では 104 というサイズになります。

    </削除>
    <訂正>

    CHOOSEFONTA/CHOOSEFONTW 構造体は、32bit ではシングルバイトアライメント(1 バイト単位のパッキング)で定義されています。64bit では既定のアライメント(8 バイト単位のパッキング)ですので、この構造体のサイズは 32bit では 60 バイト、64bit では 104 バイトとなるはずです。

    それに対し、32bit 版の VBA におけるユーザー定義型は各メンバーが常に 32bit 境界に配置される仕様(4 バイト単位のパッキング)です。64bit 版 VBA のユーザー定義型であれば、64bit 境界に配置されます(8 バイト単位のパッキング)。

    そのため VBA でユーザー定義型としてそのまま定義した場合(Win32API_PtrSafe.txt における Type CHOOSEFONT 定義をそのまま用いた場合)、32bit 環境では Len(CF)=LenB(CF)=60、64bit 環境では Len(CF)=92, LenB(CF)=104 というサイズになります。

    いずれにしても「90 バイト」になっているのはおかしいです。

    </訂正>

    64bit 版の場合、CHOOSEFONT の各メンバーはこのように配置されることになるはず。

    0x00-0x03 : DWORD lStructSize; 0x04-0x07 : 《4 バイトのパディング》 0x08-0x0F : HWND hwndOwner; 0x10-0x17 : HDC hDC; 0x18-0x1F : LPLOGFONT lpLogFont; 0x20-0x23 : INT iPointSize; 0x24-0x27 : DWORD Flags; 0x28-0x2B : DWORD rgbColors; 0x2C-0x2F : 《4 バイトのパディング》 0x30-0x37 : LPARAM lCustData; 0x38-0x3F : LPCFHOOKPROC lpfnHook; 0x40-0x47 : LPTCSTR lpTemplateName; 0x48-0x4F : HINSTANCE hInstance; 0x50-0x57 : LPTSTR lpszStyle; 0x58-0x59 : WORD nFontType; 0x5A-0x5B : WORD ___MISSING_ALIGNMENT__; 0x5C-0x5F : INT nSizeMin; 0x60-0x63 : INT nSizeMax; 0x64-0x67 : 《4 バイトのパディング》

    なお、CHOOSEFONT 構造体は、commdlg.h 上で下記のように定義されています。

    // --- 略 ---
    
    #include <prsht.h>
    
    #if !defined(_WIN64)
    #include <pshpack1.h>         /* Assume byte packing throughout */
    #endif
    
    // --- 略 ---
    
    typedef struct tagCHOOSEFONTA {
       DWORD           lStructSize;
       HWND            hwndOwner;          // caller's window handle
       HDC             hDC;                // printer DC/IC or NULL
       LPLOGFONTA      lpLogFont;          // ptr. to a LOGFONT struct
       INT             iPointSize;         // 10 * size in points of selected font
       DWORD           Flags;              // enum. type flags
       COLORREF        rgbColors;          // returned text color
       LPARAM          lCustData;          // data passed to hook fn.
       LPCFHOOKPROC    lpfnHook;           // ptr. to hook function
       LPCSTR          lpTemplateName;     // custom template name
       HINSTANCE       hInstance;          // instance handle of.EXE that
                                           //   contains cust. dlg. template
       LPSTR           lpszStyle;          // return the style field here
                                           // must be LF_FACESIZE or bigger
       WORD            nFontType;          // same value reported to the EnumFonts
                                           //   call back with the extra FONTTYPE_
                                           //   bits added
       WORD            ___MISSING_ALIGNMENT__;
       INT             nSizeMin;           // minimum pt size allowed &
       INT             nSizeMax;           // max pt size allowed if
                                           //   CF_LIMITSIZE is used
    } CHOOSEFONTA;
    typedef struct tagCHOOSEFONTW {
       DWORD           lStructSize;
       HWND            hwndOwner;          // caller's window handle
       HDC             hDC;                // printer DC/IC or NULL
       LPLOGFONTW      lpLogFont;          // ptr. to a LOGFONT struct
       INT             iPointSize;         // 10 * size in points of selected font
       DWORD           Flags;              // enum. type flags
       COLORREF        rgbColors;          // returned text color
       LPARAM          lCustData;          // data passed to hook fn.
       LPCFHOOKPROC    lpfnHook;           // ptr. to hook function
       LPCWSTR         lpTemplateName;     // custom template name
       HINSTANCE       hInstance;          // instance handle of.EXE that
                                           //   contains cust. dlg. template
       LPWSTR          lpszStyle;          // return the style field here
                                           // must be LF_FACESIZE or bigger
       WORD            nFontType;          // same value reported to the EnumFonts
                                           //   call back with the extra FONTTYPE_
                                           //   bits added
       WORD            ___MISSING_ALIGNMENT__;
       INT             nSizeMin;           // minimum pt size allowed &
       INT             nSizeMax;           // max pt size allowed if
                                           //   CF_LIMITSIZE is used
    } CHOOSEFONTW;
    
    // --- 略 ---
    
    #if !defined(_WIN64)
    #include <poppack.h>
    #endif
    
    // --- 略 ---

    2022年11月7日 13:26
  • Win32API_PtrSafe.txtに書いてあるCHOOSEFONTA構造体であるなら、(64bitでの)アラインメントに伴うパディングが入っていないのかな。(いやでも、64bitOfficeのVBAでLen(CF)が90になるなら、ちゃんとアラインメントそろえないOfficeVBAのバグなのでは、、)
    とりあえずは下記のようにLongPtrにしてしまえば動くとは思いますケド。
    Type CHOOSEFONT
            lStructSize As Long → LongPtr
            hwndOwner As LongPtr       '  caller's window handle
            hdc As LongPtr             '  printer DC/IC or NULL
            lpLogFont As LongPtr       '  ptr. to a LOGFONT struct
            iPointSize As Long         '  10 * size in points of selected font
            flags As Long              '  enum. type flags
            rgbColors As Long → LongPtr         '  returned text color
            lCustData As LongPtr       '  data passed to hook fn.
            lpfnHook As LongPtr        '  ptr. to hook function
            lpTemplateName As String   '  custom template name
            hInstance As LongPtr       '  instance handle of.EXE that
                                       '    contains cust. dlg. template
            lpszStyle As String        '  return the style field here
                                       '  must be LF_FACESIZE or bigger
            nFontType As Integer       '  same value reported to the EnumFonts
                                       '    call back with the extra FONTTYPE_
                                       '    bits added
            MISSING_ALIGNMENT As Integer
            nSizeMin As Long           '  minimum pt size allowed &
            nSizeMax As Long→LongPtr        '  max pt size allowed if
                                       '    CF_LIMITSIZE is used
    End Type

    jzkey


    • 編集済み jzkey 2022年11月7日 14:35 Officeのせい?
    2022年11月7日 14:22
  • パディングの 12 バイト分を考慮し忘れていたとしても、Win64 環境なら
    Len= 92 , LenB= 104  という結果が得られても良さそうなのですよね。
    どうにも 2 バイト分、計算が合いません。

    もしかして、「WORD nFontType;」なメンバーの宣言を忘れていたりしませんか?

    「CHOOSEFONT について」の質問なのに、肝心のその宣言部が提示されていないので、どこに間違いがあるのかを指摘しにくいですね。

    2022年11月7日 16:42
  • たびたび御指摘、返答いただき誠にありがとうございます。

    続報となります。
    ①実行環境について

    OSはWindows11で「システムの種類」 64 ビット オペレーティング システム、x64 ベース プロセッサ
    ②エクセルのバージョン
    Microsoft EXCEL For Microsoft 365 MSO 64ビット

    ⓷ユーザー定義型Choosefontのサイズ変更
     ChooseFontの値を投稿した時に実はミスっておりまして、nFontTypeメンバーが漏れていまして90ではなく92でした。
    ご指摘いただいたとうり、lStructSize,rgbColors,nSizeMaxの3つのメンバーを、LongからLongLongに変更し
    3*4=12バイト増 データサイズ92+12=104バイトとしました
    ④実行
     フォントダイアログが表示されました。値も取得できました、
    ⑤感想
    フォーラムに投稿しなかったら、自分で解決することはできなかったと感じています。
    どうもありがとうございました。
    API関数のスタックを破壊しているなんて、考える余地もありませんでした。
    パディングというコトバも今回初めて知りました
    Win32API_PtrSafe.TXTの定義のバグといえるのでしょうか?
    また、LFのlfHeight値が‐21となっているのが、少し気がかりです。

    Public Type CHOOSEFONT
        lStructSize As LongLong         
        hwndOwner As LongPtr            
        hdc As LongPtr                  
        lpLogFont As LongPtr            
        iPointSize As Long              
        flags As Long                   
        rgbColors As LongLong           
        lCustData As LongPtr            
        lpfnHook As LongPtr             
        lpTemplateName As String        
        hInstance As LongPtr            
        lpszStyle As String  
      nFontType As Integer            
        MISSING_ALIGNMENT As Integer
        nSizeMin As Long                        
        nSizeMax As LongLong                    
        End Type
    ちなみに、CFの値は
        : flags : 8513 : Long
        : hdc : 0^ : LongLong
        : hInstance : 0^ : LongLong
        : hwndOwner : 657072^ : LongLong
        : iPointSize : 80 : Long
        : lCustData : 0^ : LongLong
        : lpfnHook : 0^ : LongLong
        : lpLogFont : 2723845560480^ : LongLong  
       : lpszStyle : "" : String
        : lpTemplateName : "" : String
        : lStructSize : 104^ : LongLong
        : MISSING_ALIGNMENT : 0 : Integer
        : nFontType : -7932 : Integer
        : nSizeMax : 24^ : LongLong
        : nSizeMin : 6 : Long
    また LFの値は
        : lfCharSet : 134 : Byte
        : lfClipPrecision : 2 : Byte
        : lfEscapement : 0 : Long
          : lfFaceName(1) : 83 : Byte
          : lfFaceName(2) : 84 : Byte
          : lfFaceName(3) : 72 : Byte
          : lfFaceName(4) : 117 : Byte
          : lfFaceName(5) : 112 : Byte
          : lfFaceName(6) : 111 : Byte
          : lfFaceName(7) : 0 : Byte
          : lfFaceName(8) : 109 : Byte
          : lfFaceName(9) : 97 : Byte
          : lfFaceName(10) : 108 : Byte
          : lfFaceName(11) : 108 : Byte
          : lfFaceName(12) : 0 : Byte
          : lfFaceName(13) : 100 : Byte
          : lfFaceName(14) : 105 : Byte
          : lfFaceName(15) : 110 : Byte
          : lfFaceName(16) : 103 : Byte
          : lfFaceName(17) : 0 : Byte
          : lfFaceName(18) : 32 : Byte
          : lfFaceName(19) : 68 : Byte
          : lfFaceName(20) : 105 : Byte
          : lfFaceName(21) : 115 : Byte
          : lfFaceName(22) : 112 : Byte
          : lfFaceName(23) : 108 : Byte
          : lfFaceName(24) : 97 : Byte
          : lfFaceName(25) : 121 : Byte
          : lfFaceName(26) : 0 : Byte
          : lfFaceName(27) : 0 : Byte
          : lfFaceName(28) : 0 : Byte
          : lfFaceName(29) : 0 : Byte
          : lfFaceName(30) : 0 : Byte
          : lfFaceName(31) : 0 : Byte
          : lfFaceName(32) : 0 : Byte
        : lfHeight : -21 : Long
        : lfItalic : 0 : Byte
        : lfOrientation : 0 : Long
        : lfOutPrecision : 3 : Byte
        : lfPitchAndFamily : 2 : Byte
        : lfQuality : 1 : Byte
        : lfStrikeOut : 0 : Byte
        : lfUnderline : 0 : Byte
        : lfWeight : 700 : Long
        : lfWidth : 0 : Long
    2022年11月8日 6:04
  • Win32API_PtrSafe.TXTの定義のバグといえるのでしょうか?

    Win32API.TXT や Win32API_PtrSafe.TXT で用意されているユーザー定義型では、アライメントについてはあまり考慮されていないと思います。Len と LenB が異なるサイズのユーザー定義型を扱う場合は、特に注意してください。(各メンバーが 32bit 境界に並ばない API の場合に、API 側がどのような配置を期待しているのかは、ヘッダーファイルを読み解くか、あるいは実際に C/C++ で sizeof するなどして確認する必要があります)

    他の有名どころとしては、SHFileOperation API で用いられる SHFILEOPSTRUCT 構造体が挙げられます。

    この構造体、Win32API_PtrSafe.TXT のユーザー定義型定義では
    「32bit では Len = 30、LenB = 32」
    「64bit では Len = 50、LenB = 56」
    となるのですが、本来の正しいサイズは
    「32bit では 30 バイト」
    「64bit では 56 バイト」
    というブレがあります。(ANSI 版 / WIDE 版共に同じ)

    32bit 環境の場合、2 バイトの「FILEOP_FLAGS fFlags;」の後ろに 2 バイトの余白が生じます。しかし、API 側は余白無しで並んでいる物として動作するため、その後に続く「BOOL fAnyOperationsAborted;」への書き込みや「PC(W)STR lpszProgressTitle;」の読み込み位置がずれることになり、キャンセル判定に失敗したり、タイトルの文字化けやクラッシュを引き起こす要因になりえます。

    また、LFのlfHeight値が‐21となっているのが、少し気がかりです。

    LF というのは LOGFONT 構造体のことですね。

    • >0 な正数を指定した場合は、文字セルの高さとして解釈されます。
    • =0 を指定すると、既定の高さと解釈されます。
    • <0 な負数を指定した場合は、その絶対値が文字の高さとして解釈されます。これは、文字セルの高さから internal leading (内部レディング…アクセント記号などのためのスペースのこと) の高さを引いたものです。
    2022年11月8日 9:25
  • 返信ありがとうございます。

    さらに疑問が浮かんだので、投稿させていただきます。

        ちなみにlStructSize, rgbColorsnSizeMaxの次に任意のLongのメンバーを入れても

    動くということでしょうか?また、あえて私はLongLongLongとしましたが

    やはりご指摘のとうり、Lonptr にしておけば、32ビット環境でも動くということですか?

    Type CHOOSEFONT

                    lStructSize As Long

          lPadding1 as long

                    hwndOwner As LongPtr     

          hdc As LongPtr         

          lpLogFont As LongPtr  

                    iPointSize As Long

          flags As Long

                    rgbColors As Long

          lPadding2 as long

    lCustData As LongPtr

          lpfnHook As LongPtr

          lpTemplateName As String

          hInstance As LongPt

          lpszStyle As String

          nFontType As Integer

            MISSING_ALIGNMENT As Integer

            nSizeMin As Long 

            nSizeMax As Long

                    lPadding3 as long

    End Type

        C++の構造体宣言で64ビット環境ではパディングは自動的に挿入される仕組みになっているのですか?

    2022年11月8日 11:34
  • ①ちなみにlStructSize, rgbColors、nSizeMaxの次に任意のLongのメンバーを入れても
    動くということでしょうか?

    試してはいませんが、おそらく動くと思います。こうしたダミーの余白は、元の構造体定義にある「WORD ___MISSING_ALIGNMENT__;」というメンバーに通ずるものがありますね。

    ただし、32bit 環境と 64bit 環境ではアライメントが異なるため、その書き方をする場合は、可能であれば「#If Win64 Then~#Else」を使うなどして、32bit / 64bit で分けて両対応にした方が良いでしょう。

    あるいは実行環境が固定的であり、常にいずれか一方でしか使わない状況に限っては、条件付きコンパイルでの切り分けをあえて省略するという選択肢もあります。

    ただし、そもそもの原因は「.lStructSize に LenB(CF) ではなく Len(CF) を渡してしまっていたこと」と「nFontType 宣言を忘れていたこと」にあるとみています。そこを間違えなければ、Len=LenB となるような明示的なパディング調整を行う必要は無いように思えます。

    やはりご指摘のとうり、Lonptr にしておけば、32ビット環境でも動くということですか?

    × Lonptr
    ○ LongPtr

    LongPtr のうち、Long の範囲しか扱わないという条件を守るのであれば、それも一つの方法です。

    ただし、LongPtr を使えないバージョンも想定する場合は、「#If VBA7 Then」の併用が必要となります。これは Declare 宣言の PtrSafe に対しても言えることですね。

    ② C++の構造体宣言で64ビット環境ではパディングは自動的に挿入される仕組みになっているのですか?

    32bit プロセスや 64bit プロセスといった違いで決まるわけではありません。DLL がコンパイルされる際のオプションによって決まります。

    そしてその動作は、Win16 であれ Win32 であれ Win64 であれ、アライメントのサイズが異なるだけであり、事情は一緒です。

    32bit 向けの DLL と 64bit 向けの DLL は別々のものとしてコンパイルされていますが、既定のアライメントサイズは、コンパイル時の指定によって決まります。

    上記オプションにて、1 / 2 / 4 / 8 / 16 バイトの境界のいずれかを固定的に指定してコンパイルできることがわかります。

    とはいえ、構造体によって別のアライメントに変更したい場合は、『#pragma pack(n)』であるとか、あるいは先の投稿で紹介した『#include <pshpack1.h>』によって個別に変更されるケースもあります。

    具体例でいえば、winnt.h というヘッダーファイルの中では、同じファイル内でも、pshpack1.h、pshpack2.h、pshpack4.h、pshpack8.h という 4 種類が使われていることを確認できます。

    多くの構造体は、パッキングサイズの違いに影響を受けないよう、アライメント境界を跨いだり、無駄なパディングが発生しないような設計になっていることが多いのですが、今回のように一部の構造体はそうではありませんので、注意が必要であるというわけですね。

    .NET であれば、構造体宣言時にアライメントを明示できます(0、1、2、4、8、16、32、64、128)が、VBA にはそのようなものが無いので、実際のメモリー配置を考慮した上で、適宜調整する必要が生じます。

    今回の CHOOSEFONT 構造体の場合、Len指定に合わせて「64bit では追加の余白を設けて、メンバー位置を後ろにずらす必要がある」という対処を取りましたが、先ほど紹介した SHFILEOPSTRUCT の場合はその逆で、「32bit では、VBA によって自動的に差し込まれる 2 バイトのパディング分を除去するため、メンバーを前に詰めなければならない」といったように。

    2022年11月8日 13:56
  • iいつも、丁寧かつ詳細なご回答をいただきまして、ありがとうございます。

    また、ふと思ったことを、投稿します。

    API関数 CHOOSEFONTが 求める、CHOOSEFONT構造体のアラインメントが、下記であるというのは

    どうやって知ることができたのですか? 実行状態で ChooseFontのサイズを取得して、宣言バイト数の総計との比較で、パディングの位置を推察するということですか?

    64bit 版の場合、CHOOSEFONT の各メンバーはこのように配置されることになるはず。

    0x00-0x03 : DWORD lStructSize; 0x04-0x07 : 4 バイトのパディング》 0x08-0x0F : HWND hwndOwner; 0x10-0x17 : HDC hDC; 0x18-0x1F : LPLOGFONT lpLogFont; 0x20-0x23 : INT iPointSize; 0x24-0x27 : DWORD Flags; 0x28-0x2B : DWORD rgbColors; 0x2C-0x2F : 4 バイトのパディング》 0x30-0x37 : LPARAM lCustData; 0x38-0x3F : LPCFHOOKPROC lpfnHook; 0x40-0x47 : LPTCSTR lpTemplateName; 0x48-0x4F : HINSTANCE hInstance; 0x50-0x57 : LPTSTR lpszStyle; 0x58-0x59 : WORD nFontType; 0x5A-0x5B : WORD ___MISSING_ALIGNMENT__; 0x5C-0x5F : INT nSizeMin; 0x60-0x63 : INT nSizeMax; 0x64-0x67 : 4 バイトのパディング》

    2022年11月9日 5:29
  • API関数 CHOOSEFONTが 求める、CHOOSEFONT構造体のアラインメントが、下記であるというのは
    どうやって知ることができたのですか?

    先の投稿で『CHOOSEFONT 構造体は、commdlg.h 上で下記のように定義されています。』と書いたかと思います。

    そのヘッダーファイルの内容を読み解くことで、_WIN64 では既定サイズのパッキング、それ以外では 1 バイト単位のパッキングであることを推察できました。

    .

    構造体のサイズについては、実際に C++ で sizeof してみるのが確実でしょう。自分も sizeof によって『32bit 環境では 60 、64bit 環境では 104 というサイズ』であることを確認しました。ポインタの中身を読み解けば、そのメモリ配置も見えてきますね。

    .

    一方、VBA 側のユーザー定義型のパディングに関しては、LSet ステートメント を使って確認できます。

    Option Explicit
    
    Type UDT_BIN
        Binary(0 To 300) As Byte  '十分な長さを確保しておく
    End Type
    
    Type UDT_CHOOSEFONT
        lStructSize As Long
        hwndOwner As LongPtr
        '以下略
    End Type
    
    Sub Test()
        Dim BIN As UDT_BIN
        Dim CF As UDT_CHOOSEFONT
        
        CF.lStructSize = &H11111111    '4バイトなので8桁
        #If Win64 Then
            CF.hwndOwner = &H2222222222222222^  '8 バイトなので 16 桁
        #Else
            CF.hwndOwner = &H22222222  '4 バイトなので 8 桁
        #End If
    '上記以外のメンバーも同様に、バイト数一杯までの分かりやすい値で埋めておく 'メモリ配置確認のため、Byte配列に変換 LSet BIN = CF 'あとは BIN.Binary の中身を確認すれば OK。 ' 'ウォッチウィンドウで確認したり、 'For ループで Hex 変換していっても良いけど、 'ここでは bin.hex を使って確認してみます。 With CreateObject("Microsoft.XMLDOM").createElement("h") .DataType = "bin.hex" .NodeTypedValue = BIN.Binary Debug.Print .Text End With End Sub

    32bit 環境であれば、"1111111122222222…" な出力となり
    64bit 環境であれば、"111111110000000022222222…" な出力になるかと思います。
    (手元に Win64 な VBA 環境が無いので未確認)

    2022年11月9日 6:33
  • たびたび恐縮です。

    話題がちょっとずれてしまいますけど、質問です。

    ChooseFont API関数に渡す ChooseFontユーザー定義型の lpLogfont メンバーを初期化するのに

    GlobalAlloc、GlobalLockを使っていますが、Varptr(LF)で初期化しても、プログラムは動きました。

    同一プロシージャ―内の変数なので、これでもいいのかと思うのですが、推奨されないことなのでしょうか?


    • 編集済み my_think 2022年11月10日 6:27
    2022年11月10日 6:26
  • そうですね。LOGFONT のユーザー定義型が正しく宣言されているなら、ローカル変数 LF に対する「.lpLogFont = VarPtr(LF)」を指定しようと、API で確保したアドレスを「.lpLogFont = Address」で指定しようと、どちらでも良いと思います。

    重要なのは、指定したポインターが読み書き可能なメモリを指しており、かつ、そのポインターが指し示す領域に十分なサイズを確保してあるどうかかと。

    .

    一番最初の質問時点では、CHOOSEFONT の nFontType 宣言漏れ問題もあって、.lStructSize = 90 によって「パラメーターが間違っています。」になったわけですよね(32bitなら「60」、64bitなら「104」とする必要があるはず)

    そこへ、さらにパディングの問題が加わっています。

    Win64 環境においては「hMem = GlobalAlloc(GHND, Len(LF))」の時点で、90 バイトの領域しか確保されていなかったのでしょう(nFontType ありだったのなら 92 バイト)

    この時、64bit 版のユーザー定義型は 8 バイトのアライメントなので、実際には 104 バイトを必要としていたはずです(nFontType の有無によらず、 LenB(LF) は 104  を返す)

    LenB(LF) ではなく Len(LF) を用いていたことから、「Address = GlobalLock(hMem)」「CopyMemory LF, ByVal Address, Len(LF)」では、先頭から 90 バイトまでの 0x00~0x59 の範囲のみしか転写されていません。0x5A~0x67 のエリア(nSizeMin/nSizeMax)は漏れていたことでしょう。

    0x00-0x03:lStructSize
     (中略)
    0x50-0x57:lpszStyle
    0x58-0x59:本来は nFontType の位置だが、宣言漏れ時は MISSING_ALIGNMENT
    修正版では、明示的なパディングを入れるか、Long を LongPtr 化することで、Len(LF) でも 104 が返されるようにしたみたいですけれどね。 

    • 回答としてマーク my_think 2022年11月12日 12:50
    2022年11月10日 20:35
  • 末尾にパディングを含めるかどうか、ということですね。結論から言えば、LOGFONT においては不要です。

    LOGFONT は条件によらず常に 4 バイトのパッキングであるためです。

    ...

    API 側は構造体によってアライメントが異なります。

    実際に C++ で確認する場合は、alignof() からアライメントを、sizeof() からサイズを得られます。なお、sizeof で返されるのは通常、末尾パディングを含んだ値になります。

    それぞれの構造体のアライメントとサイズは下記の通り。

    alignof(LOGFONTA)⇒{Win32: 4 バイト, Win64: 4バイト}
    alignof(LOGFONTW)⇒{Win32: 4 バイト, Win64: 4バイト}
    alignof(CHOOSEFONTA)⇒{Win32: 1 バイト, Win64: 4バイト}
    alignof(CHOOSEFONTW)⇒{Win32: 1 バイト, Win64: 4バイト}

    sizeof(LOGFONTA)⇒{Win32: 60バイト, Win64: 60 バイト}
    sizeof(LOGFONTW)⇒{Win32: 92バイト, Win64: 92 バイト}
    sizeof(CHOOSEFONTA)⇒{Win32: 60 バイト, Win64: 104 バイト}
    sizeof(CHOOSEFONTW)⇒{Win32: 60 バイト, Win64: 104 バイト}

    ..

    一方、VBA 側のユーザー定義型においてはアライメントは常に一定であり、Win32 なら 4 バイト境界、Win64 ならば 8 バイト境界です。

    ユーザー定義型のサイズは、Len() はパディングを含めない論理サイズ、LenB() はパディングを含めた物理サイズを返します。

    Win32API_PtrSafe.TXT の宣言をそのまま使う場合、Type LOGFONT の定義は、Win32/Win64 のいずれにおいても、 Len と LenB が共に 60 という同じ値を返します。

    つまり、API 側もパディング無しの 60 バイトを要求しているわけですから、メモリ配置としては、VBA 側と API 側との間での乖離がありません。そのため LOGFONT においてはパディングを考慮する必要が無い、ということになります。


    2022年11月12日 9:50
  • たびたび、ご回答、誠にありがとございます。

    Win64環境下で、C,C++でAPIのDLLをコンパイルする際に、CHOOSEFONTは8バイトパッキングなので、末尾パディングを含み、LOGFONTAは4バイトパッキングなので、は含まないということでしょうか?

     とりあえず、VBA上でCHOOSEFONTの宣言を、Win32_PtrSafe.txtに戻し、lStrructSize=LenB(CF)とすれば、フォントダイアログは表示されることhが、確認できました。C,C++にあまり詳しくないので、投稿していただいた内容をすべて把握することはできていないのですが、いろいろと、ご教示いただきありがとうございました。


    • 編集済み my_think 2022年11月12日 13:54
    2022年11月12日 13:52
  • ごめんなさい。

    先の私の回答には幾つか誤りがありました。ここでもう一度改めて説明しなおします。

    Win64環境下で、C,C++でAPIのDLLをコンパイルする際に、CHOOSEFONTは8バイトパッキングなので、末尾パディングを含み、LOGFONTAは4バイトパッキングなので、は含まないということでしょうか?

    アラインされることによって発生するパディングの有無およびその位置は、アライメントのサイズと各要素のメンバー定義によって決まります。(より詳しいルールを知りたい場合は、こちらを参照してみてください)

    そしてその配置は通常、「既定のアライメント」に従ってアラインされることになります。ただし構造体によっては、「意図的に異なるアライメントでコンパイル」されているものも存在します。

    既定のアライメントですが、これは構造体(あるいはユーザー定義型)に含まれる各メンバーの型のアライメントの中で、最大のアライメントに一致します。

    .

    LOGFONTA のメンバーでは、LONG, BYTE, CHAR という 3 つの型が使われていますが、このうち LONG 型が最大のアライメントを持っています。そのため、LOGFONTA 構造体の既定のアライメントは「4 バイト」です。そして 4 バイト単位でメンバーをパッキングしていった場合、隙間なく詰め込むことができるので、この構造体にはパディングが発生しません。

    .

    CHOOSEFONTA のメンバーでは、最大のアライメントとなるのがハンドル系(HWND, HDC, HINSTANCE)や LP何某系です。そのため本来であれば Win32 では 4 バイト、Win64 では 8 バイトが既定のアライメントにあたります。しかしながら、Windows SDK のヘッダーファイルにおいて

    #if !defined(_WIN64)
    #include <pshpack1.h>         /* Assume byte packing throughout */
    #endif

    が指定されているため、Win32 においては、アライメントが「1 バイト」に変更されています。

    Win32 では 1 バイトアライメントに変更されているため、CHOOSEFONTA にはパディングが発生しません。とはいえ、既定の 4 バイトのままであったとしても、「WORD nFontType;」の後に、わざわざ「WORD ___MISSING_ALIGNMENT__」を設けてあることもあり、追加のパディングは発生しません。そのため、VBA (32bit 版)から呼び出すときも、結果的にメンバーの位置調整は不要です。

    一方 Win64 では既定の 8 バイトアライメントなので、先頭の「DWORD lStructSize;」とその次の「HWND hwndOwner;」の間に、4 バイトのパディングを設けてアラインされます。同様に、「LPARAM lCustData;」の直前に 4 バイトと、末尾「INT nSizeMax;」の直後に 4 バイトの、計 12 バイトのパディングが生まれます(これは C++ だけでなく VBA の場合にも当てはまります)。

    ちなみに VBA のユーザー定義型は、メモリ上には「既定のアライメント」で配置される仕様ですが、ファイル入出力の場合は常に 1 バイトのアライメントとして扱われる仕様です(具体的には、Put #/Get # ステートメントでユーザー定義型を読み書きする場合です)。

    .

    注意が必要なのは、先述の SHFILEOPSTRUCT 構造体のケースのように、既定のアライメントだとパディングが発生するような定義になっていおり、かつ、既定のものよりも小さなアライメントが割り当てられているケースです。VBA では既定のアライメントしか扱えないというのに、このケースでは、パディングのズレ以降にポインタ(LPVOID と PCSTR) があるため、このズレを自前で調整して呼び出さないと、クラッシュを誘発させる要因となってしまうのです。

    .

    とはいえ API で利用される構造体は、そもそも無駄なパディングが生まれないようなメンバー配置になっているものが多いはずです。なので、普段はパディングを意識する必要はありません。

    一方、あえて異なるアライメントに調整されている構造体は、その多くが「既定のアライメントだとパディングが発生してしまう構造体」です。VBA 側へ移植する際に、『Len と LenB が異なる結果となるユーザー定義型』を見つけた場合は、注意が必要であるという事ですね。

    なお、対象の構造体が求めるアライメントサイズを確認する場合、そのサイズがドキュメントに明記してあるわけではありませんので、C++ などで実際に呼び出して alignof や sizeof で確認してみないと分からないと思います。

    .

    最後に、VBA 側の 既定のアライメントを確認するために、いくつかの例を載せておきます。

    Type U1 'これは 1 バイトのアライメントになる
        A As Byte 'アライメント 1 バイト、サイズ 1 バイト
    End Type
    
    Type U2 'これも 1 バイトのアライメント
        A As Byte 'サイズ 1 バイト、アライメント 1 バイト
        B As Byte 'サイズ 1 バイト、アライメント 1 バイト
    End Type
    
    Type U3 'これも 1 バイトのアライメント
        A As Byte 'サイズ 1 バイト、アライメント 1 バイト
        B As Byte 'サイズ 1 バイト、アライメント 1 バイト
        C As Byte 'サイズ 1 バイト、アライメント 1 バイト
    End Type
    
    Type U4 'これは 2 バイトのアライメント
        A As Byte 'サイズ 1 バイト、アライメント 1 バイト
        '(ここに 1 バイトのパディングが入る)
        B As Integer 'サイズ 2 バイト、アライメント 2 バイト
    End Type
    
    Type U5 'これも 2 バイトのアライメント
        A As Byte 'サイズ 1 バイト、アライメント 1 バイト
        '(ここに 1 バイトのパディングが入る)
        B As Integer 'サイズ 2 バイト、アライメント 2 バイト
        C As Byte 'サイズ 1 バイト、アライメント 1 バイト
        '(ここに 1 バイトのパディングが入る)
    End Type
    
    Type U6 'これは 4 バイトのアライメント
        A As Byte 'アライメント 1 バイト、サイズ 1 バイト
        '(ここに 3 バイトのパディングが入る)
        B As Long 'サイズ 4 バイト、アライメント 4 バイト
    End Type
    
    Type U7 'これも 4 バイトのアライメント
        A As Byte 'サイズ 1 バイト、アライメント 1 バイト
        B As Byte 'サイズ 1 バイト、アライメント 1 バイト
        '(ここに 2 バイトのパディングが入る)
        C As Long 'サイズ 4 バイト、アライメント 4 バイト
    End Type
    
    Type U8 'これも 4 バイトのアライメント
        A As Byte 'サイズ 1 バイト、アライメント 1 バイト
        '(ここに 3 バイトのパディングが入る)
        B As Long 'サイズ 4 バイト、アライメント 4 バイト
        C As Byte 'サイズ 1 バイト、アライメント 1 バイト
        '(ここに 3 バイトのパディングが入る)
    End Type
    
    Type U9 'これは Win32 では 4 バイト、Win64 では 8 バイトのアライメント
        A As Byte 'サイズ 1 バイト、アライメント 1 バイト
        '(ここに、Win32 なら 3 バイト、Win64 なら 7 バイトのパディングが入る)
        B As Currency 'サイズ 8 バイト、アライメント Win32=4 バイト/Win64=8バイト
    End Type
    
    Type U0 'これも Win32 では 4 バイト、Win64 では 8 バイトのアライメント
        A As Currency 'サイズ 8 バイト、アライメント Win32=4 バイト/Win64=8バイト
        B As Byte 'サイズ 1 バイト、アライメント 1 バイト
        '(ここに、Win32 なら 3 バイト、Win64 なら 7 バイトのパディングが入る)
    End Type

    ユーザー
    定義型
    Win32 Win64
    アライ
    メント
    Len LenB アライ
    メント
    Len LenB
    U1 1 1 1 1 1 1
    U2 1 2 2 1 2 2
    U3 1 3 3 1 3 3
    U4 2 3 4 2 3 4
    U5 2 4 6 2 4 6
    U6 4 5 8 4 5 8
    U7 4 6 8 4 6 8
    U8 4 6 12 4 6 12
    U9 8 9 12 8 9 16
    U0 8 9 12 8 9 16


    2022年11月12日 21:40