Windowsでは同一プロセス内に、32ビットプログラムと64ビットプログラムの混在は許されていません。例えば、32ビット動作のプログラムをx86、64ビット動作のプログラムをx64とした場合、

OK:x86のExeとx86のDLL *1、x64のExeとx64のDLL *2 は呼出し可能
NG:x86のExeとx64のDLL、  x64のExeとx86のDLLは呼出し不可

 *1:32ビットOS または Wow64配下で動作
 *2:64ビットOSのみで動作

ですので、ネイティブ動作指向のVisualBasicでは IsWow64Process 関数とは無縁だよなぁ~と思ってしまいましたが、前回、既定値の「AnyCPU」を「x86」に変更した事もあり、使ってみる事に。

マイクロソフトのドキュメントによれば、GetCurrentProcess関数とIsWow64Process関数の構文は、

■ Retrieves a pseudo handle for the current process.

HANDLE GetCurrentProcess();

Return value
The return value is a pseudo handle to the current process.

■ Determines whether the specified process is running under WOW64 or an Intel64 of x64 processor.

BOOL IsWow64Process(HANDLE hProcess, PBOOL Wow64Process);

Return value
If the function succeeds, the return value is a nonzero value.
If the function fails, the return value is zero. To get extended error information, call GetLastError.

なので、戻り値と引数の変数型を Integer と Boolean に置き換えれば、上手くゆく筈?

とは言うものの、一筋縄では行かないのはBOOLでしょうか。

マイクロソフトのドキュメントによれば、

・C/C++では、BOOL は int 型、BOOLEAN は BYTE 型の様です(TRUE または FALSE である必要があります)。

・VBでは、以下様なのだとか。

Boolean 型が数値型に正確に変換されない

Boolean データ型の値は数値として格納されず、格納された値は数値と等価であると見なされません。以前のバージョンとの互換性のために、Visual Basic は変換キーワード (CType 関数、CBool、CInt など) を使用して、Boolean と数値型の間で変換を行います。ただし、その他の言語では、.NET Framework メソッドと同様に、これらの変換が異なる方法で実行されることがあります。

True と False に対して等価の数値に依存するコードを記述することは避けてください。可能な限り、Boolean 変数には、仕様で定められている論理値以外の値を使用しないようにしてください。Boolean 値と数値を混在させる必要がある場合は、選択する変換方法をよく理解してください。

Visual Basic での変換

CType または CBool の変換キーワードを使用して数値データ型を Boolean に変換するとき、0 が False になり、その他のすべての値が True になります。変換キーワードを使用して Boolean 値を数値型に変換するとき、False は 0 になり、True は -1 になります。


<注意点>

・C/C++では、HANDLE は PVOID 型です。(AnyCPU では Integer ではなく IntPtr で)

・VBの Boolean 変数は サイズが2バイトらしいので、偶々、動作している可能性大。

・不測の事態を招かないために、IsWow64Process 関数の戻り値は失敗したかどうか、つまり"0"かどうかをチェックするのが宜しいかと。

・VisualStudio11と.NET4.5でマイクロソフトがAnyCPUを再定義したそうな
 新たなデフォルトは、「AnyCPU32Bit優先」らしい。(何周遅れ? の気付き)
 ⇒これは動作が可能な場合、32Bitで動作する。という事らしい。


お試し環境
  WindowsXP 32bit Edition、Windows7 64bit Edition
  Visual Basic 2008 対象CPUはx86とAnyCPU


/*-------------------------------- お試し結果 ------------------------------*/

・敢えて、x86版を WindowsXP 32bit Edition で動作させてみる

x86 版 WindowsXP 32bit Edition で動作

・x86版を Windows7 64bit Edition で動作

x86 版 Windows7 64bit Edition で動作

・AnyCPU版を Windows7 64bit Edition で動作

AnyCPU 版 Windows7 64bit Edition で動作

/*----------------------------------------------------------------------------*/


/*-------------------------------- お試しソース ----------------------------*/

Public Class Form1

    Declare Auto Function GetCurrentProcess Lib "kernel32" () As Integer
    Declare Auto Function IsWow64Process Lib "kernel32" (ByVal hprcs As Integer, ByRef w64prcs As Boolean) As Boolean

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim hdl As Integer
        Dim res As Boolean
        Dim biw64 As Boolean
        Dim msg As String

        hdl = GetCurrentProcess
        res = IsWow64Process(hdl, biw64)
        If res <> 0 Then
            msg = "return GetCurrentProcess : " & hdl.ToString & vbCrLf & _
                  "return IsWow64Process : " & res & vbCrLf & _
                  "Wow64Process value : " & biw64

            MsgBox(msg, MsgBoxStyle.Information, "result IsRunningUnderWow64")
        Else
            MsgBox(Err.LastDllError, MsgBoxStyle.AbortRetryIgnore, "IsWow64Process Error Occured")
        End If
    End Sub
End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

前回、VBコンパイラが規定値の"任意のCPU"設定だった事もあり、Windows7でもネイティブ(64ビット)動作となってしまいましたので、今一だったなぁ~と。

そこでWindowsXPのVisualBasicは、32ビットでコンパイルする様にIDEの設定を変更してみました。

マイクロソフトのドキュメントによると、

・構成マネージャーを使用して、対象プラットフォームを指定
・プロジェクトのプロパティを使用して、対象CPUを指定

などが有るようですが、構成マージャーには新規作成や編集などの作業が必須の様なので、面倒くさがり屋には不向きです。という事で、プロジェクトのプロパティを使用して、対象CPUを指定してみる事に。

・メニューバーの「プロジェクト」をクリック→プルダウンメニューの「(プロジェクト名)のプロパティ」を選択、または、
ソリューションエクスプローラの「プロジェクト名」を"右"クリック→プルダウンメニューの「プロパティ」を選択して、プロパティのダイアログを表示します。


1.プロジェクトの「プロパティ」画面で、「コンパイル」を選択し、「詳細コンパイルオプション」ボタンをクリック

VBプロジェクトのプロパティ画面


2.「コンパイラの詳細設定」画面で、「ターゲットCPU」の内容を確認

コンパイラの詳細設定画面で内容確認

・32ビットでコンパイル出来る様に、「AnyCPU」を「x86」に変更します


3.「コンパイラの詳細設定」画面で、「ターゲットCPU」の変更内容を確認

コンパイラの詳細設定画面で内容変更


コンパイラの対象CPU指定をx86に変更して Windows7 64Bit 版で動作確認したところ、WOW64配下で動作する事を確認できました。
2つの関数の取得データには、wProcessorArchitecture/lpMaximumApplicationAddress/dwProcessorType に違いがみられました。

・やはりCPU情報を詳しく知りたい場合には、GetNativeSystemInfo 関数の方が優れているようです。


お試し環境
  WindowsXP 32bit Edition、Windows7 64bit Edition
  Visual Basic 2008 対象CPUはx86


/*-------------------------------- お試し結果 ------------------------------*/

・GetSystemInfo 関数の取得データ

WOW64下 GetSystemInfo 関数の取得データ

・GetNativeSystemInfo 関数の取得データ

WOW64下 GetNativeSystemInfo 関数の取得データ

/*----------------------------------------------------------------------------*/


/*-------------------------------- お試しソース ----------------------------*/

前々回の「32ビットOS下のVisualBasicからGetSystemInfo関数を呼び出す」記事のソースと同じです。

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

前回は32ビット環境、今回は64ビット環境でVisualBasicからアンマネージド関数を呼び出してみる事に。

マイクロソフトのGetSystemInfo関数のドキュメントに、

To retrieve accurate information for an application running on WOW64, call the GetNativeSystemInfo function.

とあったので、GetNativeSystemInfo関数を呼び出すだけのサンプルを作ってみました。

★凡そ10年前の Windows7 は当然、現役のバリバリ。

それはさて置き、前回作成の32ビット版だと思っていたプログラムが、WOW64下(?)で思いがけない動作結果に。

取得SYSTEM_INFO_32データ

取得SYSTEM_INFO2_32データ


CPUタイプなど、一部不確かなデータを拾ってきてしまいます。試しに Windows7 64Bit 版でもプログラムを作成・実行してみると、同様の結果に。

調べてみると、初期設定(インストール時)のままのIDEでは、Visual Basic は実行対象OSが 32bit/64bit のどちらのバージョンでも、ネイティブ動作するようにコンパイルするようです。

マイクロソフトドキュメント ⇒ 任意のプラットフォーム上で実行されるように、アセンブリをコンパイルします。アプリケーションは、Windowsの32ビットバージョンでは32ビットアプリケーションとして、Windowsの64ビットバージョンでは64ビットアプリケーションとして実行されます。この anycpu フラグが既定値です。


★例え32ビットのOS上でコンパイルしても、上記の動作をするのには驚きだわ! 各.NET開発言語によって作成される中間言語(MSIL)は、CPUや各Windowsプラットフォームに依存しないコードになっていて、実行される際に順次ネイティブコードにコンパイルされます(=JITコンパイラだそうな)。


・と言う訳で、規定値設定のVisualBasicで作成された実行ファイル(exe)は、WOW64配下には入らず64ビットCLR上で動作します。

・なので、以下の3メンバーを ULong に直してやれば構造体サイズには不満は残るけど、それなりの動作結果に。

LPVOID    lpMinimumApplicationAddress;
LPVOID    lpMaximumApplicationAddress;
DWORD_PTR dwActiveProcessorMask;

・自分的には、ムズイ。意識していないと、多分ど壺に嵌るな、きっと。


<注意点>

・Win64 ではC/C++のポインタサイズは8バイト、ポインタ精度変数のサイズも8バイト。つまり、Long 型です。

・DWORD_PTR も Win64 では8バイトで Long 型。LPVOID は IntPtr の方が好ましい?

・Visual Basic のデフォルトの文字セットは、UNICODE です。


お試し環境
  WindowsXP 32bit Edition、Windows7 64bit Edition
  Visual Basic 2008


/*-------------------------------- お試し結果 ------------------------------*/

・GetNativeSystemInfo 関数の取得データ

取得SYSTEM_INFO_64データ


・GetSystemInfo 関数の取得データ

取得SYSTEM_INFO2_64データ

/*----------------------------------------------------------------------------*/


/*-------------------------------- お試しソース ----------------------------*/

Imports System.Runtime.InteropServices

Public Class Form4

    Const PROCESSOR_ARCHITECTURE_INTEL As Integer = 0 ' x86
    Const PROCESSOR_ARCHITECTURE_ARM As Integer = 5 ' ARM
    Const PROCESSOR_ARCHITECTURE_IA64 As Integer = 6 ' Intel Itanium-based
    Const PROCESSOR_ARCHITECTURE_AMD64 As Integer = 9 ' x64(AMD or Intel)
    Const PROCESSOR_ARCHITECTURE_ARM64 As Integer = 12 ' ARM64
    Const PROCESSOR_ARCHITECTURE_UNKNOWN As Integer = &HFFFF ' Unknown architecture

    Const PROCESSOR_INTEL_386 As Integer = 386
    Const PROCESSOR_INTEL_486 As Integer = 486
    Const PROCESSOR_INTEL_PENTIUM As Integer = 586
    Const PROCESSOR_INTEL_IA64 As Integer = 2200
    Const PROCESSOR_AMD_X8664 As Integer = 8664
    ' Const PROCESSOR_ARM As Integer = Reserved

    Structure SYSTEM_INFO
        Dim wProcessorArchitecture As UShort
        Dim wReserved As UShort
        Dim dwPageSize As UInteger
        Dim lpMinimumApplicationAddress As ULong
        Dim lpMaximumApplicationAddress As ULong
        Dim dwActiveProcessorMask As ULong
        Dim dwNumberOfProcessors As UInteger
        Dim dwProcessorType As UInteger
        Dim dwAllocationGranularity As UInteger
        Dim wProcessorLevel As UShort
        Dim wProcessorRevision As UShort
    End Structure

    Structure SYSTEM_INFO2
        Dim wProcessorArchitecture As UShort
        Dim wReserved As UShort
        Dim dwPageSize As UInteger
        Dim lpMinimumApplicationAddress As IntPtr
        Dim lpMaximumApplicationAddress As IntPtr
        Dim dwActiveProcessorMask As ULong
        Dim dwNumberOfProcessors As UInteger
        Dim dwProcessorType As UInteger
        Dim dwAllocationGranularity As UInteger
        Dim wProcessorLevel As UShort
        Dim wProcessorRevision As UShort
    End Structure

    Declare Auto Sub GNSInfo Lib "kernel32.dll" Alias "GetNativeSystemInfo" (ByRef sysInfo As SYSTEM_INFO)
    Declare Auto Sub GSInfo Lib "kernel32.dll" Alias "GetSystemInfo" (ByRef sysInfo As SYSTEM_INFO2)

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim sInfo As SYSTEM_INFO
        Dim sInfo2 As SYSTEM_INFO2
        Dim msg1, msg2 As String

        GNSInfo(sInfo)
        GSInfo(sInfo2)

        msg1 = "wProcessorArchitecture = " & sInfo.wProcessorArchitecture & vbCrLf & _
               "wReserved = " & sInfo.wReserved & vbCrLf & _
               "dwPageSize = " & sInfo.dwPageSize & vbCrLf & _
               "lpMinimumApplicationAddress = " & sInfo.lpMinimumApplicationAddress.ToString("x") & vbCrLf & _
               "lpMaximumApplicationAddress = " & sInfo.lpMaximumApplicationAddress.ToString("x") & vbCrLf & _
               "dwActiveProcessorMask = " & sInfo.dwActiveProcessorMask & vbCrLf & _
               "dwNumberOfProcessors = " & sInfo.dwNumberOfProcessors & vbCrLf & _
               "dwProcessorType = " & sInfo.dwProcessorType & vbCrLf & _
               "dwAllocationGranularity = " & sInfo.dwAllocationGranularity & vbCrLf & _
               "wProcessorLevel = " & sInfo.wProcessorLevel & vbCrLf & _
               "wProcessorRevision = " & sInfo.wProcessorRevision

        msg2 = "wProcessorArchitecture = " & sInfo2.wProcessorArchitecture & vbCrLf & _
               "wReserved = " & sInfo2.wReserved & vbCrLf & _
               "dwPageSize = " & sInfo2.dwPageSize & vbCrLf & _
               "lpMinimumApplicationAddress = " & sInfo2.lpMinimumApplicationAddress.ToString("x") & vbCrLf & _
               "lpMaximumApplicationAddress = " & sInfo2.lpMaximumApplicationAddress.ToString("x") & vbCrLf & _
               "dwActiveProcessorMask = " & sInfo2.dwActiveProcessorMask & vbCrLf & _
               "dwNumberOfProcessors = " & sInfo2.dwNumberOfProcessors & vbCrLf & _
               "dwProcessorType = " & sInfo2.dwProcessorType & vbCrLf & _
               "dwAllocationGranularity = " & sInfo2.dwAllocationGranularity & vbCrLf & _
               "wProcessorLevel = " & sInfo2.wProcessorLevel & vbCrLf & _
               "wProcessorRevision = " & sInfo2.wProcessorRevision

        MessageBox.Show(msg1, "SYSTEM_INFO arg data")
        MessageBox.Show(msg2, "SYSTEM_INFO2 arg data")

        Select Case sInfo.wProcessorArchitecture
            Case PROCESSOR_ARCHITECTURE_INTEL
                MessageBox.Show("32ビット", "OSのタイプ")
            Case PROCESSOR_ARCHITECTURE_ARM
            Case PROCESSOR_ARCHITECTURE_IA64
            Case PROCESSOR_ARCHITECTURE_AMD64
                MessageBox.Show("64ビット", "OSのタイプ", MessageBoxButtons.OK, MessageBoxIcon.Information)
            Case PROCESSOR_ARCHITECTURE_ARM64
            Case PROCESSOR_ARCHITECTURE_UNKNOWN
        End Select

        Select Case sInfo.dwProcessorType
            Case PROCESSOR_INTEL_386
            Case PROCESSOR_INTEL_486
            Case PROCESSOR_INTEL_PENTIUM
                MessageBox.Show("Pentium4", "CPUのタイプ")
            Case PROCESSOR_INTEL_IA64
            Case PROCESSOR_AMD_X8664
                MessageBox.Show("AMD Ryzen?" & vbCrLf & "な訳ありません", "CPUのタイプ", MessageBoxButtons.OK, MessageBoxIcon.Information)
        End Select

        MessageBox.Show(sInfo.dwNumberOfProcessors & " Core", "プロセッサ数", MessageBoxButtons.OK, MessageBoxIcon.Information)

    End Sub
End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

VisualBasicとDLLに実装されたアンマネージド関数との間で、構造体データの受け渡しを試して見ようと、GetSystemInfo関数を呼び出すだけのサンプルを作ってみました(GetNativeSystemInfo関数も同様)。

マイクロソフトのドキュメントによれば、SYSTEM_INFOの構文は、

typedef struct _SYSTEM_INFO {
    union {
        DWORD dwOemId;
        struct {
            WORD wProcessorArchitecture;
            WORD wReserved;
        } DUMMYSTRUCTNAME;
    } DUMMYUNIONNAME;
    DWORD     dwPageSize;
    LPVOID    lpMinimumApplicationAddress;
    LPVOID    lpMaximumApplicationAddress;
    DWORD_PTR dwActiveProcessorMask;
    DWORD     dwNumberOfProcessors;
    DWORD     dwProcessorType;
    DWORD     dwAllocationGranularity;
    WORD      wProcessorLevel;
    WORD      wProcessorRevision;
} SYSTEM_INFO, *LPSYSTEM_INFO;

なので、構造体メンバーの変数型を UShort と UInteger に置き換えれば、上手くゆく筈?


★それにしても、凡そ20年前の WindowsXP と ペンティアム4、現役バリバリで良く頑張っているなぁ。


<注意点>

・Win32 ではC/C++のポインタサイズは4バイト、ポインタ精度変数のサイズも4バイト。つまり、Integer 型です。

・DWORD_PTR は Win32/Win64 でサイズが変化する事に留意。LPVOID は IntPtr の方が好ましい?

・Visual Basic のデフォルトの文字セットは、UNICODE です。


お試し環境
  WindowsXP 32bit Edition
  Visual Basic 2008


/*-------------------------------- お試し結果 ------------------------------*/

・GetSystemInfo 関数の取得データ

取得SYSTEM_INFOデータ


・GetNativeSystemInfo 関数の取得データ

取得SYSTEM_INFO2データ

/*----------------------------------------------------------------------------*/


/*-------------------------------- お試しソース ----------------------------*/

Imports System.Runtime.InteropServices

Public Class Form1

    Const PROCESSOR_ARCHITECTURE_INTEL As Integer = 0 ' x86
    Const PROCESSOR_ARCHITECTURE_ARM As Integer = 5 ' ARM
    Const PROCESSOR_ARCHITECTURE_IA64 As Integer = 6 ' Intel Itanium-based
    Const PROCESSOR_ARCHITECTURE_AMD64 As Integer = 9 ' x64(AMD or Intel)
    Const PROCESSOR_ARCHITECTURE_ARM64 As Integer = 12 ' ARM64
    Const PROCESSOR_ARCHITECTURE_UNKNOWN As Integer = &HFFFF ' Unknown architecture

    Const PROCESSOR_INTEL_386 As Integer = 386
    Const PROCESSOR_INTEL_486 As Integer = 486
    Const PROCESSOR_INTEL_PENTIUM As Integer = 586
    Const PROCESSOR_INTEL_IA64 As Integer = 2200
    Const PROCESSOR_AMD_X8664 As Integer = 8664
    ' Const PROCESSOR_ARM As Integer = Reserved

    Structure SYSTEM_INFO
        Dim wProcessorArchitecture As UShort
        Dim wReserved As UShort
        Dim dwPageSize As UInteger
        Dim lpMinimumApplicationAddress As UInteger ' LPVOID
        Dim lpMaximumApplicationAddress As UInteger ' LPVOID
        Dim dwActiveProcessorMask As UInteger ' DWORD_PTR
        Dim dwNumberOfProcessors As UInteger
        Dim dwProcessorType As UInteger
        Dim dwAllocationGranularity As UInteger
        Dim wProcessorLevel As UShort
        Dim wProcessorRevision As UShort
    End Structure

    Structure SYSTEM_INFO2
        Dim wProcessorArchitecture As UShort
        Dim wReserved As UShort
        Dim dwPageSize As UInteger
        Dim lpMinimumApplicationAddress As IntPtr
        Dim lpMaximumApplicationAddress As IntPtr
        Dim dwActiveProcessorMask As UInteger ' DWOD_PTR
        Dim dwNumberOfProcessors As UInteger
        Dim dwProcessorType As UInteger
        Dim dwAllocationGranularity As UInteger
        Dim wProcessorLevel As UShort
        Dim wProcessorRevision As UShort
    End Structure

    Declare Auto Sub GetSystemInfo Lib "kernel32.dll" (ByRef sysInfo As SYSTEM_INFO)
    Declare Auto Sub GetNativeSystemInfo Lib "kernel32.dll" (ByRef sysInfo As SYSTEM_INFO2)

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim sInfo As SYSTEM_INFO
        Dim sInfo2 As SYSTEM_INFO2
        Dim msg1, msg2 As String

        GetSystemInfo(sInfo)
        GetNativeSystemInfo(sInfo2)

        msg1 = "wProcessorArchitecture = " & sInfo.wProcessorArchitecture & vbCrLf & _
               "wReserved = " & sInfo.wReserved & vbCrLf & _
               "dwPageSize = " & sInfo.dwPageSize & vbCrLf & _
               "lpMinimumApplicationAddress = " & sInfo.lpMinimumApplicationAddress.ToString("x") & vbCrLf & _
               "lpMaximumApplicationAddress = " & sInfo.lpMaximumApplicationAddress.ToString("x") & vbCrLf & _
               "dwActiveProcessorMask = " & sInfo.dwActiveProcessorMask & vbCrLf & _
               "dwNumberOfProcessors = " & sInfo.dwNumberOfProcessors & vbCrLf & _
               "dwProcessorType = " & sInfo.dwProcessorType & vbCrLf & _
               "dwAllocationGranularity = " & sInfo.dwAllocationGranularity & vbCrLf & _
               "wProcessorLevel = " & sInfo.wProcessorLevel & vbCrLf & _
               "wProcessorRevision = " & sInfo.wProcessorRevision

        msg2 = "wProcessorArchitecture = " & sInfo2.wProcessorArchitecture & vbCrLf & _
               "wReserved = " & sInfo2.wReserved & vbCrLf & _
               "dwPageSize = " & sInfo2.dwPageSize & vbCrLf & _
               "lpMinimumApplicationAddress = " & sInfo2.lpMinimumApplicationAddress.ToString("x") & vbCrLf & _
               "lpMaximumApplicationAddress = " & sInfo2.lpMaximumApplicationAddress.ToString("x") & vbCrLf & _
               "dwActiveProcessorMask = " & sInfo2.dwActiveProcessorMask & vbCrLf & _
               "dwNumberOfProcessors = " & sInfo2.dwNumberOfProcessors & vbCrLf & _
               "dwProcessorType = " & sInfo2.dwProcessorType & vbCrLf & _
               "dwAllocationGranularity = " & sInfo2.dwAllocationGranularity & vbCrLf & _
               "wProcessorLevel = " & sInfo2.wProcessorLevel & vbCrLf & _
               "wProcessorRevision = " & sInfo2.wProcessorRevision

        MessageBox.Show(msg1, "sInfo members data")
        MessageBox.Show(msg2, "sInfo2 members data")

        Select Case sInfo.wProcessorArchitecture
            Case PROCESSOR_ARCHITECTURE_INTEL
                MessageBox.Show("32ビット です", "OSのバージョンは", MessageBoxButtons.OK, MessageBoxIcon.Information)
            Case PROCESSOR_ARCHITECTURE_ARM
            Case PROCESSOR_ARCHITECTURE_IA64
            Case PROCESSOR_ARCHITECTURE_AMD64
                MessageBox.Show("64ビット です", "OSのバージョンは", MessageBoxButtons.OK, MessageBoxIcon.Information)
            Case PROCESSOR_ARCHITECTURE_ARM64
            Case PROCESSOR_ARCHITECTURE_UNKNOWN
        End Select

        Select Case sInfo.dwProcessorType
            Case PROCESSOR_INTEL_386
            Case PROCESSOR_INTEL_486
            Case PROCESSOR_INTEL_PENTIUM
                MessageBox.Show("Pentium4 です", "CPUのタイプは", MessageBoxButtons.OK, MessageBoxIcon.Information)
            Case PROCESSOR_INTEL_IA64
            Case PROCESSOR_AMD_X8664
                MessageBox.Show("AMD Ryzen9 です", "CPUのタイプは", MessageBoxButtons.OK, MessageBoxIcon.Information)
        End Select
    End Sub
End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

CPUがx64だと言っても、まだまだx86の呼出し規則が優勢と言うかWOW64の方が全盛の内に、x86呼出し規則を浚ってみました。

C・C++間や他言語からのDLL内関数呼出しを行う場合、大事なのはCリンケージと __stdcall です。

特に指定しない限り、C++コンパイラはC++リンケージであるC++のタイプセーフな名前付け規約(名前装飾)と、C++の呼出し規則を使用します。

Cコンパイラは既定で、Cの呼出し規則とCリンケージを使います。

★C・C++間でDLL内の関数を利用するには、Cリンケージである extern "C" を指定して関数を宣言し、コンパイラがC++の関数名を装飾するのを禁止する必要があります。

★他言語からDLL内の関数を利用するには、__stdcall 呼出し規則を使用する必要があります。


<注意点>

・C++が装飾名を必須とするのは、オーバーロードされた関数、クラスや名前空間のメンバー、コンストラクターやデストラクター、などなどを一意に特定する必要がある事に起因します。

・__stdcall で修飾さる名前には、関数名の前にアンダースコア '_' が、後にアットマーク '@' が、更にその後に引数リストのバイト数が付けられます。例えば、int __stdcall func(int a, double b) 関数は、_func@12 と名前修飾されます。


【Visual C/C++ コンパイラの呼出し規則】

Visual C/C++ コンパイラの呼出し規則一覧
規約 *1コンパイラ
オプション
スタック
一掃
引数渡しCリンケージ
修飾形式 *2
補足
__cdecl /Gd
x86 のみ
呼出し元 右から左の順で
スタックに積まれる
_fname C/C++標準
__clrcall なし N/A 左から右の順で
CLR式スタックに収容
N/A .NET専用
__stdcall /Gz
x86 のみ
呼出し先 右から左の順で
スタックに積まれる
_fname@num Win32API呼出し
他言語アプリからの
関数呼び出し
__fastcall /Gr
x86 のみ
呼出し先 レジスタで渡され
残りは右から左の順で
スタックに積まれる
@fname@num 通常、高速呼出し
__thiscall なし 呼出し先 右から左の順でスタック
に積まれ this ポインタは
ECX レジスタに格納
N/A C++メンバー関数
__vectorcall /Gv
x86, x64
呼出し先 レジスタで渡され
残りは右から左の順で
スタックに積まれる
fname@@num __fastcall より多くの
レジスタで渡すか
既定の x64 呼出し *3
を使う

 *1:Visual C/C++ コンパイラでサポートされている呼び出し規約
 *2:fname は関数名、num は引数リストのバイト数を表す
 *3:x64ABIの既定では4レジスタ高速呼出しの呼び出し規則が使用される

前回に続いて、DLLに実装されたアンマネージド関数をマネージドコードから呼び出すことが出来る「プラットフォーム呼出し(PInvoke)」を使って、VBからWin32APIを呼び出してみます。

取り敢えず、DllImport 属性を使用して MessageBox を呼び出すだけのサンプルを作ってみました。

DllImport 属性でDLL内の名前を指定する事によって、引数と戻り値の相互運用マーシャリングも行われます。

ExactSpelling 項目は CharSet 項目の設定内容に影響を与えます。ExactSpelling を省略すると ExactSpelling:=False として扱われるよです。

ExactSpelling:=False の場合、CharSet の Auto、Ansi、Unicode 設定は柔軟に作用しますが、ExactSpelling:=True の場合は厳格に適用されます。

CharSet と EntryPoint と ExactSpelling 項目間の設定内容に不一致などがあると、メッセージボックス内で文字化けが起きたり、例外が発生したりします。


<注意点>

・DllImport はクラスのインスタンスを必要とするメソッドは使用できません。呼出しで共有(静的)メソッドを参照している場合にのみ、Windows API を呼び出せます。

・Visual Basic のデフォルトの文字セットは、UNICODE です。


お試し環境
  WindowsXP 32bit Edition、Windows7 64bit Edition
  Visual Basic 2008


/*-------------------------------- お試し結果 ------------------------------*/

・<DllImport("user32.dll", ... CharSet:=CharSet.Auto, EntryPoint:="MessageBox", ExactSpelling:=False, ...)> _ の場合

auto&false メッセージボックス表示


・<DllImport("user32.dll", ... CharSet:=CharSet.Ansi, EntryPoint:="MessageBox", ExactSpelling:=False, ...)> _ の場合

ansi&false メッセージボックス表示


・<DllImport("user32.dll", ... CharSet:=CharSet.Unicode, EntryPoint:="MessageBox", ExactSpelling:=False, ...)> _ の場合

unicode&false メッセージボックス表示


・<DllImport("user32.dll", ... CharSet:=CharSet.Auto, EntryPoint:="MessageBox", ExactSpelling:=True, ...)> _ の場合

auto&true-1 例外エラー表示


・<DllImport("user32.dll", ... CharSet:=CharSet.Ansi, EntryPoint:="MessageBox", ExactSpelling:=True, ...)> _ の場合

ansi&true-1 例外エラー表示


・<DllImport("user32.dll", ... CharSet:=CharSet.Unicode, EntryPoint:="MessageBox", ExactSpelling:=True, ...)> _ の場合

unicode&true-1 例外エラー表示


・<DllImport("user32.dll", ... CharSet:=CharSet.Auto, EntryPoint:="MessageBoxA", ExactSpelling:=False, ...)> _
・<DllImport("user32.dll", ... CharSet:=CharSet.Auto, EntryPoint:="MessageBoxA", ExactSpelling:=True, ...)> _ の場合

auto&false, true メッセージボックス文字化け表示


・<DllImport("user32.dll", ... CharSet:=CharSet.Ansi, EntryPoint:="MessageBoxW", ExactSpelling:=False, ...)> _
・<DllImport("user32.dll", ... CharSet:=CharSet.Ansi, EntryPoint:="MessageBoxW", ExactSpelling:=True, ...)> _ の場合

ansi&false, true メッセージボックス文字化け表示


・<DllImport("user32.dll", ... CharSet:=CharSet.Unicode, EntryPoint:="MessageBoxA", ExactSpelling:=False, ...)> _
・<DllImport("user32.dll", ... CharSet:=CharSet.Unicode, EntryPoint:="MessageBoxA", ExactSpelling:=True, ...)> _ の場合

unicode&false, true メッセージボックス文字化け表示


・<DllImport("user32.dll", ... CharSet:=CharSet.Auto, EntryPoint:="MessageBoxW", ExactSpelling:=True, ...)> _ の場合

auto&true-2 メッセージボックス表示


・<DllImport("user32.dll", ... CharSet:=CharSet.Ansi, EntryPoint:="MessageBoxA", ExactSpelling:=True, ...)> _ の場合

ansi&true-2 メッセージボックス表示


・<DllImport("user32.dll", ... CharSet:=CharSet.Unicode, EntryPoint:="MessageBoxW", ExactSpelling:=True, ...)> _ の場合

unicode&true-2 メッセージボックス表示


/*----------------------------------------------------------------------------*/


/*-------------------------------- お試しソース ----------------------------*/

Imports System.Runtime.InteropServices

Public Class Form1

    Const MB_ICONQUESTION As Integer = &H20
    Const MB_YESNO As Integer = &H4
    Const ID_YES As Integer = 6
    Const ID_NO As Integer = 7

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        ' Stores the return value.
        Dim RetVal As Integer

        Try
            RetVal = MBox5(0, "DllImport-exactspelling & unmanaged Windows API Test", "MBox5 charset auto", MB_ICONQUESTION Or MB_YESNO)

            ' Check the return value.
            If RetVal = ID_YES Then
                MsgBox("choice Yes", , "MBox5")
            Else
                MsgBox("choice No", , "MBox5")
            End If

        Catch ex As EntryPointNotFoundException
            MessageBox.Show(ex.Message, "MBox5の例外エラーを捕捉しました")
        Catch ex As Exception
            MessageBox.Show(ex.Message, "その他の例外エラーを捕捉しました")
        End Try

        Try
            RetVal = MBox6(0, "DllImport-exactspelling & unmanaged Windows API Test", "MBox6 charset ansi", MB_ICONQUESTION Or MB_YESNO)

            ' Check the return value.
            If RetVal = ID_YES Then
                MsgBox("choice Yes", , "MBox6")
            Else
                MsgBox("choice No", , "MBox6")
            End If

        Catch ex As EntryPointNotFoundException
            MessageBox.Show(ErrorToString(), "MBox6の例外エラーを捕捉しました")
        Catch ex As Exception
            MessageBox.Show(ErrorToString(), "その他の例外エラーを捕捉しました")
        End Try

        Try
            RetVal = MBox7(0, "DllImport-exactspelling & unmanaged Windows API Test", "MBox7 charset unicode", MB_ICONQUESTION Or MB_YESNO)

            ' Check the return value.
            If RetVal = ID_YES Then
                MsgBox("choice Yes", , "MBox7")
            Else
                MsgBox("choice No", , "MBox7")
            End If

        Catch ex As EntryPointNotFoundException
            MessageBox.Show(ex.Message, "MBox7の例外エラーを捕捉しました")
        Catch ex As Exception
            MessageBox.Show(ex.Message, "その他の例外エラーを捕捉しました")
        End Try

    End Sub

    <DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Auto, EntryPoint:="MessageBoxW", ExactSpelling:=True, SetLastError:=True)> _
    Public Shared Function MBox5(ByVal hWnd As Integer, ByVal text As String, ByVal caption As String, ByVal type As Integer) As Integer
    End Function

    <DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Ansi, EntryPoint:="MessageBoxA", ExactSpelling:=True, SetLastError:=True)> _
    Public Shared Function MBox6(ByVal hWnd As Integer, ByVal text As String, ByVal caption As String, ByVal type As Integer) As Integer
    End Function

    <DllImport("user32.dll", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Unicode, EntryPoint:="MessageBoxW", ExactSpelling:=True, SetLastError:=True)> _
    Public Shared Function MBox7(ByVal hWnd As Integer, ByVal text As String, ByVal caption As String, ByVal type As Integer) As Integer
    End Function

End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

Visual Basic.NET が登場してから早20年。.NETが提供する豊富な機能を利用する事で、Windows API を使用しなくても機能を実装することが出来るようになった、と登場時に言われてから久しいので今更ですが、VBからWin32APIを呼び出してみる事に。

Windows API は、WindowsOSの一部機能であるDLL(ダイナミック・リンク・ライブラリ)ですが、アンマネージド関数であるため、慎重さが必要です。

マイクロソフトのドキュメントによれば、

Windows API を使用する利点は、既に記述され、使用されるのを待っている便利な関数が多数含まれているため、開発時間を節約できる事です。欠点として、Windows API は処理が容易でなく、問題が発生した時に困難な状況に陥る事があります。

Windows API にマネージド・コードは使用されておらず、組み込みのタイプ・ライブラリはありません。また、使用するデータ型は Visual Studio で使用するものとは異なります。Windows API および .NET Framework との相互運用性は、プラットフォーム呼び出し(PInvoke)を使用して実現されます。Visual Basic で PInvoke を使用するには、Declare ステートメントを使用するか、DllImport 属性を「空のプロシージャ」に適用します。

Windows API 呼び出しは、過去においては Visual Basic プログラミングの重要な部分でしたが、Visual Basic .NET ではほとんど必要ありません。可能な限り、Windows API 呼び出しではなく .NET Framework のマネージド関数を使用してタスクを実行するようにして下さい。

の様です。

取り敢えず、Declare ステートメントを使用して MessageBox を呼び出すだけのサンプルを作ってみました。

文字セット修飾子が Auto の場合、関数名に自動的にWを補ってくれるようです。Ansi や Unicode の場合は、関数名にAまたはWが付加されている事を要求します。

Ansi 修飾子は全ての文字列を ANSI 値に、Unicode 修飾子は全ての文字列を UNICODE 値に、自動的にマーシャリングが行われます。

指定した文字型と関数のエントリポイントに不一致などがあると、メッセージボックス内で文字化けが起きたり、例外が発生したりします。


<注意点>

・Declare 宣言の文字セット修飾子の規定値(デフォルト)は、ANSI です。

・Declare 宣言時に Shared 修飾子は使用できませんが、暗黙的に Shared になります。

・Visual Basic のデフォルトの文字セットは、UNICODE です。


お試し環境
  WindowsXP 32bit Edition、Windows7 64bit Edition
  Visual Basic 2008


/*-------------------------------- お試し結果 ------------------------------*/

・Declare Auto Function MBox1 Lib "user32.dll" Alias "MessageBox" (arg1, ...) As Integer の場合

auto-1メッセージボックス表示


・Declare Ansi Function MBox2 Lib "user32.dll" Alias "MessageBox" (arg1, ...) As Integer の場合

ansi-1例外エラー表示


・Declare Unicode Function MBox3 Lib "user32.dll" Alias "MessageBox" (arg1, ...) As Integer の場合

unicode-1例外エラー表示


・Declare Auto Function MBox1 Lib "user32.dll" Alias "MessageBoxA" (arg1, ...) As Integer の場合

auto-2メッセージボックス文字化け表示


・Declare Ansi Function MBox2 Lib "user32.dll" Alias "MessageBoxW" (arg1, ...) As Integer の場合

ansi-2メッセージボックス文字化け表示


・Declare Unicode Function MBox3 Lib "user32.dll" Alias "MessageBoxA" (arg1, ...) As Integer の場合

unicode-2メッセージボックス文字化け表示


・Declare Auto Function MBox1 Lib "user32.dll" Alias "MessageBoxW" (arg1, ...) As Integer の場合

auto-3メッセージボックス表示


・Declare Ansi Function MBox2 Lib "user32.dll" Alias "MessageBoxA" (arg1, ...) As Integer の場合

ansi-3メッセージボックス表示


・Declare Unicode Function MBox3 Lib "user32.dll" Alias "MessageBoxW" (arg1, ...) As Integer の場合

unicode-3メッセージボックス表示


/*----------------------------------------------------------------------------*/


/*-------------------------------- お試しソース ----------------------------*/

Public Class Form1

    Const MB_ICONQUESTION As Integer = &H20
    Const MB_YESNO As Integer = &H4
    Const ID_YES As Integer = 6
    Const ID_NO As Integer = 7

    Declare Auto Function MBox1 Lib "user32.dll" Alias "MessageBox" (ByVal hWnd As Integer, ByVal text As String, ByVal caption As String, ByVal type As Integer) As Integer
    Declare Ansi Function MBox2 Lib "user32.dll" Alias "MessageBoxA" (ByVal hWnd As Integer, ByVal text As String, ByVal caption As String, ByVal type As Integer) As Integer
    Declare Unicode Function MBox3 Lib "user32.dll" Alias "MessageBoxW" (ByVal hWnd As Integer, ByVal text As String, ByVal caption As String, ByVal type As Integer) As Integer

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        ' Stores the return value.
        Dim RetVal As Integer

        RetVal = MBox1(0, "Declare-charsetmodifier & unmanaged Windows API Test", "MBox1 charset auto", MB_ICONQUESTION Or MB_YESNO)

        ' Check the return value.
        If RetVal = ID_YES Then
            MsgBox("choice Yes", , "MBox1")
        Else
            MsgBox("choice No", , "MBox1")
        End If

        Try
            RetVal = MBox2(0, "Declare-charsetmodifier & unmanaged Windows API Test", "MBox2 charset ansi", MB_ICONQUESTION Or MB_YESNO)

            ' Check the return value.
            If RetVal = ID_YES Then
                MsgBox("choice Yes", , "MBox2")
            Else
                MsgBox("choice No", , "MBox2")
            End If

        Catch ex As EntryPointNotFoundException
            MessageBox.Show(ex.Message, "MBox2の例外エラーを捕捉しました")
        Catch ex As Exception
            MessageBox.Show(ex.Message, "その他の例外エラーを捕捉しました")
        End Try

        Try
            RetVal = MBox3(0, "Declare-charsetmodifier & unmanaged Windows API Test", "MBox3 charset unicode", MB_ICONQUESTION Or MB_YESNO)

            ' Check the return value.
            If RetVal = ID_YES Then
                MsgBox("choice Yes", , "MBox3")
            Else
                MsgBox("choice No", , "MBox3")
            End If

        Catch ex As EntryPointNotFoundException
            MessageBox.Show(Err.Description(), "MBox3の例外エラーを捕捉しました")
        Catch ex As Exception
            MessageBox.Show(Err.Description(), "その他の例外エラーを捕捉しました")
        End Try

    End Sub
End Class

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

未だ未だ現役として活躍している「Windows Live Mail 2012」ですが、プロバイダーのBiglobeがメールのリニューアルと称して、Live メールを切り捨ててきました。サポートが終了しているからだそうです。

非常に困りましたが、取り敢えず駄目元で、リニューアルの要求設定だけは遣ってみましょうか。それで現役続行出来たら万々歳じゃぁ、ありませんか。Windows7もきっと喜ぶことでしょう。

駄目だったら、仕方が有りません。

・「Live メールのメインウインドウ」画面で、変更したいメールボックス(=アカウント)をクリック選択後、メニューバーの「アカウント」から「プロパティ」をクリックして、(選択したアカウントの)プロパティのダイアログを表示します。

1.「(アカウントの)プロパティ」画面で、「全般」タブを選択し、内容確認

メールアカウント情報の確認

①メールアカウントの名前:プロパティの名称にも使われます
②名前:受信者側にメールの差出人として表示されます
③電子メールアドレス:xxxyyy@zzz.biglobe.ne.jp 形式の自分のBIGLOBEメールアドレス
 (xxxyyy@zzz は各人で異なる部分です)

2.「(アカウントの)プロパティ」画面で、「サーバー」タブを選択し、内容を変更または設定

メールサーバー情報の設定

①受信メール:mail.biglobe.ne.jp を設定します。
②送信メール:mail.biglobe.ne.jp を設定します。
③ユーザー名:電子メールアドレスを設定します。(example@xyz の部分は各人で異なります)
④ラジオボタン:"クリアテキスト認証~"を選択します。
⑤チェックボックス:チェックします。
⑥「設定」ボタンをクリックします。

3.「送信メールサーバー」画面で

送信メールサーバー認証情報の設定

ラジオボタン:"受信メール サーバーと~"を選択し、「OK」ボタンをクリックします。

4.「(アカウントの)プロパティ」画面で、「詳細設定」タブを選択し、内容を変更または設定

サーバーポート番号とセキュリティの設定

①送信メール:465を設定します。
②チェックボックス:"このサーバーはセキュリティで~"をチェックします。
③受信メール:995を設定します。
④チェックボックス:"このサーバーはセキュリティで~"をチェックします。
⑤「OK」ボタンをクリックします。

セキュリティの強化について

メール送受信機能のセキュリティ強化前後の比較
選択項目後⇒暗号化前⇒非暗号化
送信サーバーポート番号 465 587(または25)
このサーバーはセキュリティ~ 選択 非選択
受信サーバーポート番号 POP3の場合:995 POP3の場合:110
IMAPの場合:993 IMAPの場合:143
このサーバーはセキュリティ~ 選択 非選択

64ビット化擬きの真似事の続きの続きの続きの続きです。

取り敢えず、64ビットでの構造体のアラインメントなどを確認するだけのサンプルも作ってみました。


<注意点>

・メモリ効率を上げてシステムのパフォーマンスを改善しようと「#pragma pack」などを使用すると、アラインメントがズレたメンバにアクセスする際に性能低下 *1 を引き起こしたり、最悪の場合バスエラー *2 が発生する恐れがあります。

・前回の32ビットとの違いは、st13~st15の配列の要素数のみです。

*1:インテルの場合、x86系(x64含む) *2:インテルの場合、Itanium系


【64ビット、構造体のアラインメントデータ】

アラインメント機能64ビット確認
構造体タグsizeofalignmentpadding
本来サイズ 出力結果 メンバ最大 構造体サイズ メンバ間 最終メンバの後
st01 16 16
st02 10 16 16
st03 12 16 16
st04 16 16 16
st05 12 16 16
st06 16 16 16
st07 16 16
st08 10 16 16
st09 12 16 16
st10 16 16 16
st11 12 16 16
st12 16 16 16
st13 17 24 24
st14 26 32 32
st15 40 40 40
st21 10 16 16
st22 12 16 16
st23 802 808 808
st24 804 808 808


お試し環境
  WindowsXP 32bit Edition、Windows7 64bit Edition
  Visual C++ 2008 cross x64、native x64


/*---- 64ビット ------------------- コマンドライン -----------------------*/

D:\vc2008\x86x64>cl test.c

/*----------------------------------------------------------------------------*/


/*---- 64ビットデータ -------------------- お試し結果 --------------------*/

D:\vc2008\x86x64>test
sizeof check
diz01 = 16, &diz01 = 000000014000FB80, &diz01.no = 000000014000FB80, diz01.dat = 000000014000FB88
diz02 = 16, &diz02 = 000000014000FFE0, &diz02.no = 000000014000FFE0, diz02.dat = 000000014000FFE8
diz03 = 16, &diz03 = 000000014000F800, &diz03.no = 000000014000F800, diz03.dat = 000000014000F808
diz04 = 16, &diz04 = 000000014000FC00, &diz04.no = 000000014000FC00, diz04.dat = 000000014000FC08
diz05 = 16, &diz05 = 000000014000FB70, &diz05.no = 000000014000FB70, diz05.dat = 000000014000FB78
diz06 = 16, &diz06 = 000000014000FBD0, &diz06.no = 000000014000FBD0, diz06.dat = 000000014000FBD8

padding check
diz07 = 16, &diz07 = 000000014000FBF0, diz07.dat = 000000014000FBF0, &diz07.no = 000000014000FBF8
diz08 = 16, &diz08 = 000000014000F820, diz08.dat = 000000014000F820, &diz08.no = 000000014000F828
diz09 = 16, &diz09 = 000000014000FBE0, diz09.dat = 000000014000FBE0, &diz09.no = 000000014000FBE8
diz10 = 16, &diz10 = 000000014000FC20, diz10.dat = 000000014000FC20, &diz10.no = 000000014000FC28
diz11 = 16, &diz11 = 000000014000FC70, diz11.dat = 000000014000FC70, &diz11.no = 000000014000FC78
diz12 = 16, &diz12 = 000000014000FBC0, diz12.dat = 000000014000FBC0, &diz12.no = 000000014000FBC8

Alignment check
diz13 = 24, &diz13 = 000000014000FFC0, &diz13.no = 000000014000FFC0, diz13.dat = 000000014000FFD0
diz14 = 32, &diz14 = 000000014000FBA0, &diz14.no = 000000014000FBA0, diz14.dat = 000000014000FBB8
diz15 = 40, &diz15 = 000000014000FC40, &diz15.no = 000000014000FC40, diz15.dat = 000000014000FC60

Arithmetic check
diz21 = 16, &diz21 = 000000014000F810, &diz21.no = 000000014000F810, diz21.ptr = 000000014000F818
diz22 = 16, &diz22 = 000000014000FC10, &diz22.no = 000000014000FC10, diz22.ptr = 000000014000FC18
diz23 = 808, &diz23 = 000000014000F840, &diz23.no = 000000014000F840, diz23.ptr = 000000014000F848
diz24 = 808, &diz24 = 000000014000FC80, &diz24.no = 000000014000FC80, diz24.ptr = 000000014000FC88

sizeof(WORD) + 100 * sizeof(PVOID) = 802
sizeof(DWORD) + 100 * sizeof(PVOID) = 804
FIELD_OFFSET(struct st21, ptr) + 100 * sizeof(PVOID) = 808
FIELD_OFFSET(struct st22, ptr) + 100 * sizeof(PVOID) = 808

/*----------------------------------------------------------------------------*/


/*---- test.c ------------------- 64ビット お試しソース ------------------*/

#include <stdio.h>
#include <windows.h>

// sizeof check
struct st01 {
    char no;
    void *dat[1];
} diz01;

struct st02 {
    short no;
    void *dat[1];
} diz02;

struct st03 {
    int no;
    void *dat[1];
} diz03;

struct st04 {
    long long no;
    void *dat[1];
} diz04;

struct st05 {
    float no;
    void *dat[1];
} diz05;

struct st06 {
    double no;
    void *dat[1];
} diz06;

// padding check
struct st07 {
    void *dat[1];
    char no;
} diz07;

struct st08 {
    void *dat[1];
    short no;
} diz08;

struct st09 {
    void *dat[1];
    int no;
} diz09;

struct st10 {
    void *dat[1];
    long long no;
} diz10;

struct st11 {
    void *dat[1];
    float no;
} diz11;

struct st12 {
    void *dat[1];
    double no;
} diz12;

// Alignment check
struct st13 {
    char no[9];
    void *dat[1];
} diz13;

struct st14 {
    short no[9];
    void *dat[1];
} diz14;

struct st15 {
    long long no[4];
    void *dat[1];
} diz15;

// Arithmetic check
struct st21 {
    WORD no;
    PVOID ptr[1];
} diz21;

struct st22 {
    DWORD no;
    PVOID ptr[1];
} diz22;

struct st23 {
    WORD no;
    PVOID ptr[100];
} diz23;

struct st24 {
    DWORD no;
    PVOID ptr[100];
} diz24;

int main(int argc, char *argv[])
{
    printf("sizeof check\n");
    printf("diz01 = %d, &diz01 = %p, &diz01.no = %p, diz01.dat = %p\n", sizeof(diz01), &diz01, &diz01.no, diz01.dat);
    printf("diz02 = %d, &diz02 = %p, &diz02.no = %p, diz02.dat = %p\n", sizeof(diz02), &diz02, &diz02.no, diz02.dat);
    printf("diz03 = %d, &diz03 = %p, &diz03.no = %p, diz03.dat = %p\n", sizeof(diz03), &diz03, &diz03.no, diz03.dat);
    printf("diz04 = %d, &diz04 = %p, &diz04.no = %p, diz04.dat = %p\n", sizeof(diz04), &diz04, &diz04.no, diz04.dat);
    printf("diz05 = %d, &diz05 = %p, &diz05.no = %p, diz05.dat = %p\n", sizeof(diz05), &diz05, &diz05.no, diz05.dat);
    printf("diz06 = %d, &diz06 = %p, &diz06.no = %p, diz06.dat = %p\n", sizeof(diz06), &diz06, &diz06.no, diz06.dat);

    printf("\npadding check\n");
    printf("diz07 = %d, &diz07 = %p, diz07.dat = %p, &diz07.no = %p\n", sizeof(diz07), &diz07, diz07.dat, &diz07.no);
    printf("diz08 = %d, &diz08 = %p, diz08.dat = %p, &diz08.no = %p\n", sizeof(diz08), &diz08, diz08.dat, &diz08.no);
    printf("diz09 = %d, &diz09 = %p, diz09.dat = %p, &diz09.no = %p\n", sizeof(diz09), &diz09, diz09.dat, &diz09.no);
    printf("diz10 = %d, &diz10 = %p, diz10.dat = %p, &diz10.no = %p\n", sizeof(diz10), &diz10, diz10.dat, &diz10.no);
    printf("diz11 = %d, &diz11 = %p, diz11.dat = %p, &diz11.no = %p\n", sizeof(diz11), &diz11, diz11.dat, &diz11.no);
    printf("diz12 = %d, &diz12 = %p, diz12.dat = %p, &diz12.no = %p\n", sizeof(diz12), &diz12, diz12.dat, &diz12.no);

    printf("\nAlignment check\n");
    printf("diz13 = %d, &diz13 = %p, &diz13.no = %p, diz13.dat = %p\n", sizeof(diz13), &diz13, &diz13.no, diz13.dat);
    printf("diz14 = %d, &diz14 = %p, &diz14.no = %p, diz14.dat = %p\n", sizeof(diz14), &diz14, &diz14.no, diz14.dat);
    printf("diz15 = %d, &diz15 = %p, &diz15.no = %p, diz15.dat = %p\n", sizeof(diz15), &diz15, &diz15.no, diz15.dat);

    printf("\nArithmetic check\n");
    printf("diz21 = %d, &diz21 = %p, &diz21.no = %p, diz21.ptr = %p\n", sizeof(diz21), &diz21, &diz21.no, diz21.ptr);
    printf("diz22 = %d, &diz22 = %p, &diz22.no = %p, diz22.ptr = %p\n", sizeof(diz22), &diz22, &diz22.no, diz22.ptr);
    printf("diz23 = %d, &diz23 = %p, &diz23.no = %p, diz23.ptr = %p\n", sizeof(diz23), &diz23, &diz23.no, diz23.ptr);
    printf("diz24 = %d, &diz24 = %p, &diz24.no = %p, diz24.ptr = %p\n", sizeof(diz24), &diz24, &diz24.no, diz24.ptr);

    printf("\n");
    printf("sizeof(WORD) + 100 * sizeof(PVOID) = %d\n", sizeof(WORD) + 100 * sizeof(PVOID));
    printf("sizeof(DWORD) + 100 * sizeof(PVOID) = %d\n", sizeof(DWORD) + 100 * sizeof(PVOID));

    printf("FIELD_OFFSET(struct st21, ptr) + 100 * sizeof(PVOID) = %d\n", FIELD_OFFSET(struct st21, ptr) + 100 * sizeof(PVOID));
    printf("FIELD_OFFSET(struct st22, ptr) + 100 * sizeof(PVOID) = %d\n", FIELD_OFFSET(struct st22, ptr) + 100 * sizeof(PVOID));
}

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

64ビット化擬きの真似事の続きの続きの続きです。

MSドキュメントの「移植に関する問題のチェックリスト」に書かれている以下の問題点は、本当に64ビット化に関する問題なのか? と首を傾げたくなります。(それとも、隠れている意図を見つけられないだけ?)

単に「アラインメント」と「パディング」の説明書き、だと思うのですが・・・。

struct xx {
   DWORD NumberOfPointers;
   PVOID Pointers[1];
};

NG:malloc(sizeof(DWORD)+100*sizeof(PVOID));
OK:malloc(FIELD_OFFSET(struct xx, Pointers) +100*sizeof(PVOID));

これって、単なる偶然(チョンボ?)の一致ですよね。だって、32ビットの「WORD」でも同じ問題が発生しますから。

そこで、32ビットでの構造体のアラインメントなどを確認するだけのサンプルを作ってみました。

尚、構造体のメンバを「あなた」が適切な順序で並べる事が出来れば、パディングを限り無く0に近付ける事が可能らしいです。


<注意点>

・構造体のサイズには、本来のサイズとアラインメント調整されたサイズとが有ります。

・構造体のアラインメントは、メンバのアラインメント最大値と同じ。

・Cコンパイラの構造体サイズは、メンバの中で最大サイズのアラインメントを整数倍した値と同じ。

・sizeof 演算子の戻り値は、構造体メンバのアラインメント最大値を整数倍に切り上げた値で、本来のサイズではありません。

・つまり、構造体の本来のサイズは電卓を叩けば分かります。アラインメント調整されたサイズは sizeof() を使えば直ぐ分かります。


【32ビット、構造体のアラインメントデータ】

アラインメント機能32ビット確認
構造体タグsizeofalignmentpadding
本来サイズ 出力結果 メンバ最大 構造体サイズ メンバ間 最終メンバの後
st01
st02
st03
st04 12 16 16
st05
st06 12 16 16
st07
st08
st09
st10 12 16 16
st11
st12 12 16 16
st13 12 12
st14 14 16 16
st15 20 24 24
st21
st22
st23 402 404 404
st24 404 404 404


お試し環境
  WindowsXP 32bit Edition、Windows7 64bit Edition
  Visual C++ 2008 cross x64、native x64


/*---- 32ビット ------------------- コマンドライン -----------------------*/

D:\vc2008\x86x64>cl test.c

/*----------------------------------------------------------------------------*/


/*---- 32ビットデータ -------------------- お試し結果 --------------------*/

D:\vc2008\x86x64>test
sizeof check
diz01 = 8, &diz01 = 0040E0A0, &diz01.no = 0040E0A0, diz01.dat = 0040E0A4
diz02 = 8, &diz02 = 0040E2F0, &diz02.no = 0040E2F0, diz02.dat = 0040E2F4
diz03 = 8, &diz03 = 0040DEE0, &diz03.no = 0040DEE0, diz03.dat = 0040DEE4
diz04 = 16, &diz04 = 0040E0F0, &diz04.no = 0040E0F0, diz04.dat = 0040E0F8
diz05 = 8, &diz05 = 0040E098, &diz05.no = 0040E098, diz05.dat = 0040E09C
diz06 = 16, &diz06 = 0040E0D0, &diz06.no = 0040E0D0, diz06.dat = 0040E0D8

padding check
diz07 = 8, &diz07 = 0040E0E8, diz07.dat = 0040E0E8, &diz07.no = 0040E0EC
diz08 = 8, &diz08 = 0040DEF0, diz08.dat = 0040DEF0, &diz08.no = 0040DEF4
diz09 = 8, &diz09 = 0040E0E0, diz09.dat = 0040E0E0, &diz09.no = 0040E0E4
diz10 = 16, &diz10 = 0040E110, diz10.dat = 0040E110, &diz10.no = 0040E118
diz11 = 8, &diz11 = 0040E138, diz11.dat = 0040E138, &diz11.no = 0040E13C
diz12 = 16, &diz12 = 0040E0C0, diz12.dat = 0040E0C0, &diz12.no = 0040E0C8

Alignment check
diz13 = 12, &diz13 = 0040E2E0, &diz13.no = 0040E2E0, diz13.dat = 0040E2E8
diz14 = 16, &diz14 = 0040E0B0, &diz14.no = 0040E0B0, diz14.dat = 0040E0BC
diz15 = 24, &diz15 = 0040E120, &diz15.no = 0040E120, diz15.dat = 0040E130

Arithmetic check
diz21 = 8, &diz21 = 0040DEE8, &diz21.no = 0040DEE8, diz21.ptr = 0040DEEC
diz22 = 8, &diz22 = 0040E100, &diz22.no = 0040E100, diz22.ptr = 0040E104
diz23 = 404, &diz23 = 0040DF00, &diz23.no = 0040DF00, diz23.ptr = 0040DF04
diz24 = 404, &diz24 = 0040E140, &diz24.no = 0040E140, diz24.ptr = 0040E144

sizeof(WORD) + 100 * sizeof(PVOID) = 402
sizeof(DWORD) + 100 * sizeof(PVOID) = 404
FIELD_OFFSET(struct st21, ptr) + 100 * sizeof(PVOID) = 404
FIELD_OFFSET(struct st22, ptr) + 100 * sizeof(PVOID) = 404

/*----------------------------------------------------------------------------*/


/*---- test.c ----------------------- お試しソース -------------------------*/

#include <stdio.h>
#include <windows.h>

// sizeof check
struct st01 {
    char no;
    void *dat[1];
} diz01;

struct st02 {
    short no;
    void *dat[1];
} diz02;

struct st03 {
    int no;
    void *dat[1];
} diz03;

struct st04 {
    long long no;
    void *dat[1];
} diz04;

struct st05 {
    float no;
    void *dat[1];
} diz05;

struct st06 {
    double no;
    void *dat[1];
} diz06;

// padding check
struct st07 {
    void *dat[1];
    char no;
} diz07;

struct st08 {
    void *dat[1];
    short no;
} diz08;

struct st09 {
    void *dat[1];
    int no;
} diz09;

struct st10 {
    void *dat[1];
    long long no;
} diz10;

struct st11 {
    void *dat[1];
    float no;
} diz11;

struct st12 {
    void *dat[1];
    double no;
} diz12;

// Alignment check
struct st13 {
    char no[5];
    void *dat[1];
} diz13;

struct st14 {
    short no[5];
    void *dat[1];
} diz14;

struct st15 {
    long long no[2];
    void *dat[1];
} diz15;

// Arithmetic check
struct st21 {
    WORD no;
    PVOID ptr[1];
} diz21;

struct st22 {
    DWORD no;
    PVOID ptr[1];
} diz22;

struct st23 {
    WORD no;
    PVOID ptr[100];
} diz23;

struct st24 {
    DWORD no;
    PVOID ptr[100];
} diz24;

int main(int argc, char *argv[])
{
    printf("sizeof check\n");
    printf("diz01 = %d, &diz01 = %p, &diz01.no = %p, diz01.dat = %p\n", sizeof(diz01), &diz01, &diz01.no, diz01.dat);
    printf("diz02 = %d, &diz02 = %p, &diz02.no = %p, diz02.dat = %p\n", sizeof(diz02), &diz02, &diz02.no, diz02.dat);
    printf("diz03 = %d, &diz03 = %p, &diz03.no = %p, diz03.dat = %p\n", sizeof(diz03), &diz03, &diz03.no, diz03.dat);
    printf("diz04 = %d, &diz04 = %p, &diz04.no = %p, diz04.dat = %p\n", sizeof(diz04), &diz04, &diz04.no, diz04.dat);
    printf("diz05 = %d, &diz05 = %p, &diz05.no = %p, diz05.dat = %p\n", sizeof(diz05), &diz05, &diz05.no, diz05.dat);
    printf("diz06 = %d, &diz06 = %p, &diz06.no = %p, diz06.dat = %p\n", sizeof(diz06), &diz06, &diz06.no, diz06.dat);

    printf("\npadding check\n");
    printf("diz07 = %d, &diz07 = %p, diz07.dat = %p, &diz07.no = %p\n", sizeof(diz07), &diz07, diz07.dat, &diz07.no);
    printf("diz08 = %d, &diz08 = %p, diz08.dat = %p, &diz08.no = %p\n", sizeof(diz08), &diz08, diz08.dat, &diz08.no);
    printf("diz09 = %d, &diz09 = %p, diz09.dat = %p, &diz09.no = %p\n", sizeof(diz09), &diz09, diz09.dat, &diz09.no);
    printf("diz10 = %d, &diz10 = %p, diz10.dat = %p, &diz10.no = %p\n", sizeof(diz10), &diz10, diz10.dat, &diz10.no);
    printf("diz11 = %d, &diz11 = %p, diz11.dat = %p, &diz11.no = %p\n", sizeof(diz11), &diz11, diz11.dat, &diz11.no);
    printf("diz12 = %d, &diz12 = %p, diz12.dat = %p, &diz12.no = %p\n", sizeof(diz12), &diz12, diz12.dat, &diz12.no);

    printf("\nAlignment check\n");
    printf("diz13 = %d, &diz13 = %p, &diz13.no = %p, diz13.dat = %p\n", sizeof(diz13), &diz13, &diz13.no, diz13.dat);
    printf("diz14 = %d, &diz14 = %p, &diz14.no = %p, diz14.dat = %p\n", sizeof(diz14), &diz14, &diz14.no, diz14.dat);
    printf("diz15 = %d, &diz15 = %p, &diz15.no = %p, diz15.dat = %p\n", sizeof(diz15), &diz15, &diz15.no, diz15.dat);

    printf("\nArithmetic check\n");
    printf("diz21 = %d, &diz21 = %p, &diz21.no = %p, diz21.ptr = %p\n", sizeof(diz21), &diz21, &diz21.no, diz21.ptr);
    printf("diz22 = %d, &diz22 = %p, &diz22.no = %p, diz22.ptr = %p\n", sizeof(diz22), &diz22, &diz22.no, diz22.ptr);
    printf("diz23 = %d, &diz23 = %p, &diz23.no = %p, diz23.ptr = %p\n", sizeof(diz23), &diz23, &diz23.no, diz23.ptr);
    printf("diz24 = %d, &diz24 = %p, &diz24.no = %p, diz24.ptr = %p\n", sizeof(diz24), &diz24, &diz24.no, diz24.ptr);

    printf("\n");
    printf("sizeof(WORD) + 100 * sizeof(PVOID) = %d\n", sizeof(WORD) + 100 * sizeof(PVOID));
    printf("sizeof(DWORD) + 100 * sizeof(PVOID) = %d\n", sizeof(DWORD) + 100 * sizeof(PVOID));

    printf("FIELD_OFFSET(struct st21, ptr) + 100 * sizeof(PVOID) = %d\n", FIELD_OFFSET(struct st21, ptr) + 100 * sizeof(PVOID));
    printf("FIELD_OFFSET(struct st22, ptr) + 100 * sizeof(PVOID) = %d\n", FIELD_OFFSET(struct st22, ptr) + 100 * sizeof(PVOID));
}

/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*============================================================================*/

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

タグクラウド

ウェブページ

NOP法人 アジアチャイルドサポート 最も大切なボランティアは、自分自身が一生懸命に生きること