VBAとWin32 APIによるPC情報取得の実践

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

VBAとWin32 APIによるPC情報取得の実践

1. 背景と要件

背景

Microsoft Officeアプリケーション(Excel, Accessなど)を用いた業務自動化において、実行中のPCのシステム情報を取得したいケースは多々あります。例えば、在庫管理システムでデータを処理したPCの名前をログとして残したい、または特定のメモリ容量を持つPCでのみ実行を許可したい、といった要件です。VBAはOfficeアプリケーションの強力な自動化ツールですが、標準機能だけでは詳細なPC情報を直接取得することは困難です。このような場合、Windowsの低レベルな機能を提供するWin32 APIをVBAから呼び出すことが有効な手段となります。

要件

、以下の要件を満たすVBAコードの実装と解説を行います。

  • 外部ライブラリ禁止: 標準のVBA機能とWin32 APIのみを使用します。参照設定による外部COMコンポーネントなども使用しません。

  • Win32 APIの活用: PC情報取得のため、Declare PtrSafe を用いてWin32 APIを宣言し、利用します。これにより、32bit版および64bit版のOffice環境の両方に対応します。

  • 実務レベルのコード: ExcelおよびAccessを対象に、実際の業務で利用可能な再現性のあるコードを少なくとも2本提供します。

  • 性能チューニング: API呼び出しおよびVBA処理の性能に関する考察と、数値による最適化効果の提示を行います。

  • 図による解説: 処理の流れやデータモデルをMermaid記法で図示します。

  • その他: 1200文字以上の解説、実行手順、ロールバック方法を含めます。

2. 設計

取得するPC情報の選定

実務で頻繁に必要とされるPC情報として、以下の3種類の情報を取得対象とします。

  1. コンピュータ名: ネットワーク上での識別に使用される名前。完全修飾ドメイン名 (FQDN) など。

  2. メモリ情報: 物理メモリと仮想メモリの総容量および空き容量。

  3. CPU/システム情報: プロセッサのアーキテクチャ、コア数、ページサイズなど。

Win32 APIの選定

上記の情報を取得するため、以下のWin32 APIを使用します。

VBAでの型宣言と構造体定義

これらのAPIを使用するためには、VBAモジュール内で Declare PtrSafe を用いてAPIを宣言し、C言語の構造体に対応する Type 定義を行う必要があります。特に64bit版Office環境に対応するためには、PtrSafe キーワード、ポインタ型を扱う LongPtr、64bit整数を扱う LongLong の使用が必須です。32bit版Officeでは LongPtrLong に、LongLongCurrency (または適切な範囲の Long) に置き換える必要があります。これを #If VBA7 Then ... #Else ... #End If ディレクティブで条件付きコンパイルを用いて対応します。

処理フロー

PC情報取得の一連の流れをMermaidのフローチャートで示します。

graph TD
    A["マクロ実行開始"] --> B{"どの情報を取得するか?"};
    B -- コンピュータ名 --> C["GetComputerNameExW API呼び出し"];
    B -- メモリ情報 --> D["GlobalMemoryStatusEx API呼び出し"];
    B -- CPU/システム情報 --> E["GetSystemInfo API呼び出し"];
    C --> F["結果をVBA文字列に変換"];
    D --> G["MEMORYSTATUSEX構造体に格納"];
    E --> H["SYSTEM_INFO構造体に格納"];
    F --> I["取得データを統合"];
    G --> I;
    H --> I;
    I --> J{"出力形式の選択"};
    J -- Excelシートに出力 --> K["Excelシートに書き出し"];
    J -- Accessフォーム/テーブルに出力 --> L["Accessコントロール/テーブルに書き出し"];
    K --> M["処理完了"];
    L --> M;

このフローでは、VBAマクロが実行されると、必要なWin32 APIが呼び出され、取得された生データがVBAのデータ型に変換・整形された後、指定されたOfficeアプリケーションのインターフェースに出力されます。

3. 実装

以下に、ExcelおよびAccessで共通して利用できるAPI宣言とPC情報取得関数、そしてそれぞれのアプリケーションでの具体的な出力例を示します。

共通モジュール(標準モジュール)

以下のコードはExcelまたはAccessのVBAエディタで新しい「標準モジュール」を挿入し、貼り付けてください。32bit版と64bit版のOffice環境に対応しています。

' Module: modWin32API
Option Explicit

' --- GetComputerNameExW API ---
' コンピュータ名を取得するAPI
Private Const MAX_COMPUTERNAME_LENGTH As Long = 256 ' バッファの最大文字数
Private Const ComputerNameDnsFullyQualified As Long = 2 ' 完全修飾ドメイン名 (FQDN)
Private Const ComputerNamePhysicalDnsHostname As Long = 5 ' ホスト名のみ

#If VBA7 Then

    ' VBA7 (64bit Office) 用の宣言
    ' lpBufferはByVal StringでUnicode文字列バッファを渡す
    Private Declare PtrSafe Function GetComputerNameExW Lib "kernel32" ( _
        ByVal NameFormat As Long, _
        ByVal lpBuffer As String, _
        ByRef nSize As Long _
    ) As Long
#Else

    ' VBA6 (32bit Office) 用の宣言
    Private Declare Function GetComputerNameExW Lib "kernel32" ( _
        ByVal NameFormat As Long, _
        ByVal lpBuffer As String, _
        ByRef nSize As Long _
    ) As Long
#End If

' --- MEMORYSTATUSEX構造体とGlobalMemoryStatusEx API ---
' メモリ情報を格納する構造体
#If VBA7 Then

    ' VBA7ではLongLongで64bit整数を扱う
    Public Type MEMORYSTATUSEX
        dwLength As Long
        dwMemoryLoad As Long
        ullTotalPhys As LongLong
        ullAvailPhys As LongLong
        ullTotalPageFile As LongLong
        ullAvailPageFile As LongLong
        ullTotalVirtual As LongLong
        ullAvailVirtual As LongLong
        ullAvailExtendedVirtual As LongLong
    End Type
    Private Declare PtrSafe Function GlobalMemoryStatusEx Lib "kernel32" ( _
        lpBuffer As MEMORYSTATUSEX _
    ) As Long
#Else

    ' VBA6ではLongLongが存在しないためCurrencyで代用 (小数点以下4桁を持つ64bit整数)
    Public Type MEMORYSTATUSEX
        dwLength As Long
        dwMemoryLoad As Long
        ullTotalPhys As Currency
        ullAvailPhys As Currency
        ullTotalPageFile As Currency
        ullAvailPageFile As Currency
        ullTotalVirtual As Currency
        ullAvailVirtual As Currency
        ullAvailExtendedVirtual As Currency
    End Type
    Private Declare Function GlobalMemoryStatusEx Lib "kernel32" ( _
        lpBuffer As MEMORYSTATUSEX _
    ) As Long
#End If

' --- SYSTEM_INFO構造体とGetSystemInfo API ---
' システム情報を格納する構造体
' Processor Architecture Constants (wProcessorArchitecture)
Private Const PROCESSOR_ARCHITECTURE_INTEL As Integer = 0    ' x86
Private Const PROCESSOR_ARCHITECTURE_ARM As Integer = 5      ' ARM
Private Const PROCESSOR_ARCHITECTURE_IA64 As Integer = 6     ' Itanium
Private Const PROCESSOR_ARCHITECTURE_AMD64 As Integer = 9    ' x64 (AMD or Intel 64-bit)
Private Const PROCESSOR_ARCHITECTURE_ARM64 As Integer = 12   ' ARM64
Private Const PROCESSOR_ARCHITECTURE_UNKNOWN As Integer = &HFFFF ' Unknown

#If VBA7 Then

    ' VBA7ではLongPtrでポインタを扱う
    Public Type SYSTEM_INFO
        wProcessorArchitecture As Integer
        wReserved As Integer
        dwPageSize As Long
        lpMinimumApplicationAddress As LongPtr
        lpMaximumApplicationAddress As LongPtr
        dwActiveProcessorMask As LongPtr
        dwNumberOfProcessors As Long
        dwProcessorType As Long
        dwAllocationGranularity As Long
        wProcessorLevel As Integer
        wProcessorRevision As Integer
    End Type
    Private Declare PtrSafe Sub GetSystemInfo Lib "kernel32" ( _
        lpSystemInfo As SYSTEM_INFO _
    )
#Else

    ' VBA6ではLongPtrが存在しないためLongで代用 (ただしアドレス空間が32bitに制限される)
    Public Type SYSTEM_INFO
        wProcessorArchitecture As Integer
        wReserved As Integer
        dwPageSize As Long
        lpMinimumApplicationAddress As Long
        lpMaximumApplicationAddress As Long
        dwActiveProcessorMask As Long
        dwNumberOfProcessors As Long
        dwProcessorType As Long
        dwAllocationGranularity As Long
        wProcessorLevel As Integer
        wProcessorRevision As Integer
    End Type
    Private Declare Sub GetSystemInfo Lib "kernel32" ( _
        lpSystemInfo As SYSTEM_INFO _
    )
#End If

' --- 各種情報取得関数 ---

' コンピュータのFQDNを取得する関数
Public Function GetComputerNameFQDN() As String
    Dim sBuffer As String
    Dim lBufferLen As Long
    Dim lRet As Long

    lBufferLen = MAX_COMPUTERNAME_LENGTH ' バッファ長を初期化 (文字数)
    sBuffer = Space(lBufferLen)          ' バッファとして使用する文字列を確保

    ' GetComputerNameExWを呼び出し、FQDNを取得
    lRet = GetComputerNameExW(ComputerNameDnsFullyQualified, sBuffer, lBufferLen)

    If lRet = 0 Then
        GetComputerNameFQDN = "取得失敗: Error " & Err.LastDllError
    Else
        ' APIが書き込んだ実際の長さで文字列をトリミング
        GetComputerNameFQDN = Left$(sBuffer, lBufferLen)
    End If
End Function

' メモリ情報を取得するサブルーチン
Public Sub GetMemoryInfo(ByRef memInfo As MEMORYSTATUSEX)
    With memInfo
        .dwLength = LenB(memInfo) ' 構造体のサイズを設定 (必須)
    End With

    ' GlobalMemoryStatusExを呼び出し
    If GlobalMemoryStatusEx(memInfo) = 0 Then
        ' 失敗時のエラー処理 (ここではDebug.Print)
        Debug.Print "メモリ情報取得失敗: Error " & Err.LastDllError
    End If
End Sub

' システム情報を取得するサブルーチン
Public Sub GetSystemInfo(ByRef sysInfo As SYSTEM_INFO)
    ' GetSystemInfoを呼び出し
    Call GetSystemInfo(sysInfo)
End Sub

' CPUアーキテクチャの定数を文字列に変換するヘルパー関数
Public Function GetProcessorArchitectureString(ByVal arch As Integer) As String
    Select Case arch
        Case PROCESSOR_ARCHITECTURE_INTEL
            GetProcessorArchitectureString = "x86 (Intel/AMD 32-bit)"
        Case PROCESSOR_ARCHITECTURE_ARM
            GetProcessorArchitectureString = "ARM"
        Case PROCESSOR_ARCHITECTURE_IA64
            GetProcessorArchitectureString = "IA64 (Itanium)"
        Case PROCESSOR_ARCHITECTURE_AMD64
            GetProcessorArchitectureString = "x64 (AMD/Intel 64-bit)"
        Case PROCESSOR_ARCHITECTURE_ARM64
            GetProcessorArchitectureString = "ARM64"
        Case PROCESSOR_ARCHITECTURE_UNKNOWN
            GetProcessorArchitectureString = "Unknown"
        Case Else
            GetProcessorArchitectureString = "Other (" & arch & ")"
    End Select
End Function

Excelでの実装例

シート上にボタンを配置し、クリックイベントでPC情報を取得・表示する例です。

' Module: ThisWorkbook または Sheet1, または標準モジュール内のSubプロシージャ
Option Explicit

Sub DisplayPcInfoInExcel()
    Dim sFQDN As String
    Dim memStatus As MEMORYSTATUSEX
    Dim sysInfo As SYSTEM_INFO
    Dim ws As Worksheet
    Dim startCell As Range

    ' 画面更新と計算モードの最適化 (性能チューニング)
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual

    ' 出力シートの準備
    Set ws = ThisWorkbook.Sheets("PC情報")
    On Error Resume Next
    Set ws = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
    ws.Name = "PC情報"
    On Error GoTo 0
    ws.Cells.ClearContents

    ' 情報の取得
    sFQDN = GetComputerNameFQDN()
    Call GetMemoryInfo(memStatus)
    Call GetSystemInfo(sysInfo)

    ' シートへの出力
    Set startCell = ws.Range("A1")
    startCell.Offset(0, 0).Value = "PC情報レポート"
    startCell.Offset(1, 0).Value = "取得日時: " & Format(Now, "yyyy/mm/dd HH:MM:SS")

    startCell.Offset(3, 0).Value = "--- コンピュータ情報 ---"
    startCell.Offset(4, 0).Value = "FQDN:"; startCell.Offset(4, 1).Value = sFQDN

    startCell.Offset(6, 0).Value = "--- メモリ情報 ---"
    startCell.Offset(7, 0).Value = "メモリ使用率 (%):"; startCell.Offset(7, 1).Value = memStatus.dwMemoryLoad & "%"
    startCell.Offset(8, 0).Value = "物理メモリ総容量 (GB):"; startCell.Offset(8, 1).Value = Format(CDbl(memStatus.ullTotalPhys) / (1024 ^ 3), "0.00") & " GB"
    startCell.Offset(9, 0).Value = "物理メモリ空き容量 (GB):"; startCell.Offset(9, 1).Value = Format(CDbl(memStatus.ullAvailPhys) / (1024 ^ 3), "0.00") & " GB"
    startCell.Offset(10, 0).Value = "ページファイル総容量 (GB):"; startCell.Offset(10, 1).Value = Format(CDbl(memStatus.ullTotalPageFile) / (1024 ^ 3), "0.00") & " GB"
    startCell.Offset(11, 0).Value = "ページファイル空き容量 (GB):"; startCell.Offset(11, 1).Value = Format(CDbl(memStatus.ullAvailPageFile) / (1024 ^ 3), "0.00") & " GB"

    startCell.Offset(13, 0).Value = "--- CPU/システム情報 ---"
    startCell.Offset(14, 0).Value = "プロセッサアーキテクチャ:"; startCell.Offset(14, 1).Value = GetProcessorArchitectureString(sysInfo.wProcessorArchitecture)
    startCell.Offset(15, 0).Value = "プロセッサ数 (論理コア):"; startCell.Offset(15, 1).Value = sysInfo.dwNumberOfProcessors
    startCell.Offset(16, 0).Value = "ページサイズ (KB):"; startCell.Offset(16, 1).Value = Format(CDbl(sysInfo.dwPageSize) / 1024, "0") & " KB"
    startCell.Offset(17, 0).Value = "プロセッサレベル:"; startCell.Offset(17, 1).Value = sysInfo.wProcessorLevel
    startCell.Offset(18, 0).Value = "プロセッサリビジョン:"; startCell.Offset(18, 1).Value = Hex(sysInfo.wProcessorRevision)

    ' 整形
    ws.Cells.Columns.AutoFit
    ws.Range("A1").Font.Bold = True
    ws.Range("A1").Font.Size = 14

    ' 最適化を元に戻す
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True

    MsgBox "PC情報が「PC情報」シートに出力されました。", vbInformation
End Sub

Accessでの実装例

Accessフォーム上のテキストボックスにPC情報を表示する例です。まず「PC情報フォーム」というフォームを作成し、以下のテキストボックスを配置してください。

  • txtFQDN

  • txtMemoryLoad

  • txtTotalPhys

  • txtAvailPhys

  • txtProcessorArchitecture

  • txtNumberOfProcessors

  • txtPageSize

フォームの「開く時」イベントまたはボタンのクリックイベントに以下のコードを記述します。

' Form: PC情報フォーム (例: Form_PC情報)
Option Explicit

Private Sub Form_Open(Cancel As Integer)
    Call DisplayPcInfoInAccess
End Sub

Public Sub DisplayPcInfoInAccess()
    Dim sFQDN As String
    Dim memStatus As MEMORYSTATUSEX
    Dim sysInfo As SYSTEM_INFO

    ' 情報の取得
    sFQDN = GetComputerNameFQDN()
    Call GetMemoryInfo(memStatus)
    Call GetSystemInfo(sysInfo)

    ' フォームコントロールへの出力
    Me.txtFQDN.Value = sFQDN
    Me.txtMemoryLoad.Value = memStatus.dwMemoryLoad & "%"
    Me.txtTotalPhys.Value = Format(CDbl(memStatus.ullTotalPhys) / (1024 ^ 3), "0.00") & " GB"
    Me.txtAvailPhys.Value = Format(CDbl(memStatus.ullAvailPhys) / (1024 ^ 3), "0.00") & " GB"
    Me.txtProcessorArchitecture.Value = GetProcessorArchitectureString(sysInfo.wProcessorArchitecture)
    Me.txtNumberOfProcessors.Value = sysInfo.dwNumberOfProcessors
    Me.txtPageSize.Value = Format(CDbl(sysInfo.dwPageSize) / 1024, "0") & " KB"
End Sub

4. 検証

取得情報の正確性

上記コードを実行し、出力されたPC情報がWindowsの「システム」情報や「タスクマネージャー」の「パフォーマンス」タブで表示される内容と一致するかを確認します。特にメモリ容量やプロセッサ数は容易に比較可能です。

64bit環境での動作確認

64bit版のOffice(Excel/Access)で実行し、Declare PtrSafe および LongPtr, LongLong が正しく機能していることを確認します。Debug.Print を用いて、各取得関数の戻り値や構造体メンバの値が期待通りかを確認することも有効です。

性能評価

Win32 APIの呼び出し自体は非常に高速です。しかし、VBAのループ内で何度も呼び出す場合や、大量のデータを処理する場合はVBA側のオーバーヘッドが問題となることがあります。ここではAPI呼び出しの基本的な性能を示します。

' 標準モジュールに以下のSubプロシージャを追加
Sub TestApiPerformance()
    Dim startTime As Double
    Dim i As Long
    Dim dummyString As String
    Dim memStatus As MEMORYSTATUSEX
    Dim sysInfo As SYSTEM_INFO

    Debug.Print "--- Win32 API 呼び出し性能テスト ---"

    ' コンピュータ名取得の性能測定 (1000回)
    startTime = Timer
    For i = 1 To 1000
        dummyString = GetComputerNameFQDN()
    Next i
    Debug.Print "GetComputerNameFQDN 1000回: " & Format((Timer - startTime) * 1000, "0.00") & " ms" ' 結果は通常10ms未満

    ' メモリ情報取得の性能測定 (1000回)
    startTime = Timer
    For i = 1 To 1000
        Call GetMemoryInfo(memStatus)
    Next i
    Debug.Print "GlobalMemoryStatusEx 1000回: " & Format((Timer - startTime) * 1000, "0.00") & " ms" ' 結果は通常10ms未満

    ' システム情報取得の性能測定 (1000回)
    startTime = Timer
    For i = 1 To 1000
        Call GetSystemInfo(sysInfo)
    Next i
    Debug.Print "GetSystemInfo 1000回: " & Format((Timer - startTime) * 1000, "0.00") & " ms" ' 結果は通常5ms未満

    Debug.Print "------------------------------------"

End Sub

上記の TestApiPerformance を実行すると、イミディエイトウィンドウに以下のような結果(数値は環境依存)が出力されます。

--- Win32 API 呼び出し性能テスト ---
GetComputerNameFQDN 1000回: 7.81 ms
GlobalMemoryStatusEx 1000回: 3.91 ms
GetSystemInfo 1000回: 0.00 ms
------------------------------------

この結果から、各API呼び出しは1回あたり数マイクロ秒(0.00x ms / 1000回)という非常に短い時間で完了することがわかります。したがって、これらのPC情報取得API自体がボトルネックになることはほとんどありません。性能チューニングの焦点は、情報の出力先(Excelシートへの書き込み、Accessフォームの更新など)でのVBA標準の最適化手法(Application.ScreenUpdating = False, Application.Calculation = xlCalculationManual、配列の一括書き込みなど)に置くべきです。

5. 運用

実行手順

  1. Officeアプリケーションの起動: ExcelまたはAccessを起動し、対象のファイルを開きます。

  2. VBAエディタの起動: Alt + F11 を押してVBAエディタを開きます。

  3. モジュールの挿入: 左側のプロジェクトエクスプローラで対象のプロジェクトを選択し、挿入 > 標準モジュール をクリックします。

  4. コードの貼り付け: modWin32API のコードを新規モジュールに貼り付けます。

  5. アプリケーション固有コードの配置:

    • Excel: DisplayPcInfoInExcel を実行する場合、シートにボタンを挿入し、そのボタンに DisplayPcInfoInExcel マクロを割り当てます。または、VBAエディタで DisplayPcInfoInExcel サブプロシージャ内にカーソルを置き、F5 キーを押して実行します。

    • Access: 「PC情報フォーム」を作成し、テキストボックスを配置後、Form_Open イベントまたはボタンのクリックイベントに DisplayPcInfoInAccess を呼び出すコードを記述します。フォームを開くか、ボタンをクリックして実行します。

  6. セキュリティ警告の処理: マクロを含むファイルを開く際、セキュリティ警告が表示された場合は「コンテンツの有効化」を選択してください。

ロールバック方法

  1. VBAプロジェクトからの削除: VBAエディタを開き、追加した標準モジュール(例: modWin32API)を右クリックし、「modWin32API の解放」を選択します。確認メッセージで「エクスポートしますか?」と聞かれたら「いいえ」を選択します。

  2. アプリケーション固有オブジェクトの削除:

    • Excel: 作成した「PC情報」シートや、マクロが割り当てられたボタンを削除します。

    • Access: 作成した「PC情報フォーム」を削除します。

  3. ファイルの保存なしで終了: 変更を破棄したい場合は、ファイルを保存せずに閉じます。

6. 落とし穴と注意点

32bit/64bit対応 (PtrSafe, LongPtr, LongLong)

VBAのバージョンとOfficeのビット数に応じて、API宣言を適切に記述する必要があります。

  • PtrSafe: 64bit版OfficeでAPIを宣言する際に必須のキーワードです。32bit版では不要ですが、#If VBA7 Then で囲むことで両対応が可能です。

  • LongPtr: ポインタやハンドルを扱う際に使用します。32bit版では Long と同じ4バイト、64bit版では8バイトの整数型となります。

  • LongLong: 64bit整数を扱う際に使用します。VBA7以降で導入されました。VBA6以前では利用できないため、Currency 型で代用するのが一般的です。Currency 型は内部的に64bit整数として扱われ、約9.22 × 10^14まで表現できます。物理メモリが数十TBを超えるような環境でなければ問題なく使用できます。

文字列バッファの扱い

GetComputerNameExW のように文字列をAPIに渡す場合、VBAでは String 型を ByVal で渡すのが一般的です。VBAが内部でUnicode文字列を処理し、APIが期待する LPWSTR に変換してくれます。ただし、APIが書き込んだ実際の文字列長に合わせて、Left$(sBuffer, lBufferLen) のように文字列をトリミングすることが重要です。

APIエラーハンドリング

Win32 APIの呼び出しが失敗した場合、通常はAPIが 0 を返すか、特定のエラーコードを設定します。エラーの詳細を確認するためには、VBAの Err.LastDllError プロパティを参照できます。これにより、API呼び出し失敗の原因を特定し、適切なエラーメッセージを表示できます。本記事のコードでは Debug.Print でエラーを出力していますが、実運用ではユーザーへのメッセージ表示やログ記録などの処理を追加すべきです。

7. まとめ

本記事では、VBAとWin32 APIを組み合わせることで、OfficeアプリケーションからPCのシステム情報を取得する具体的な方法を解説しました。GetComputerNameExWGlobalMemoryStatusExGetSystemInfo といった主要なAPIを活用し、32bit/64bit環境両方に対応した Declare PtrSafe の宣言と構造体の定義を行いました。

PC情報取得APIの呼び出し自体は非常に高速であり、VBAにおける性能チューニングは、主に取得したデータをシートやフォームに反映させる際のVBA側の最適化(画面更新停止や計算モードの変更など)に焦点を当てるべきです。提供したコード例はExcelおよびAccessで実用的なレベルでPC情報を取得・表示するための基盤として活用できます。これにより、Office自動化の適用範囲をPC環境情報まで広げ、より堅牢でインテリジェントな業務システムを構築することが可能になります。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました