Link > C++ Builder XE4 > 自分のNICのMACアドレスを取得する > v0.1-v0.3

```txt:動作環境
C++ Builder XE4
```

### 注意
(追記 2018/11/14)

**以下で使用しているZeroMemoryは最適化で吹き飛ぶため、SecureZeroMemory()の使用が推奨されます。**

- [C++ > SecureZeroMemory() > 最適化で吹き飛ぶ ZeroMemory() の代替](https://qiita.com/7of9/items/53fcfc1124b38cde9da1)

### 情報

- [MACアドレスを取得する](http://www.gesource.jp/programming/bcb/83.html) by 山本隆 様
- [MACアドレスの取得 2](http://d.hatena.ne.jp/makky0126/20120608/1339141429) @ たまねぎIT戦士日誌・めも by makky0126 様

情報感謝です。

### 実装 > v0.1, v0.2

山本様の実装をXE4用インクルードなどに変更しました (ソース内のv0.2コメント参照)。

```Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TMemo *Memo1;
	TButton *Button1;
	void __fastcall Button1Click(TObject *Sender);
private:	// ユーザー宣言
	void __fastcall showMacAddress(void);
public:		// ユーザー宣言
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
```

```Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

//#include <tchar.h>
//#include <windows.h>
//#include <assert.h>
//#include <stdio.h>
#include "Nb30.h"
//
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
	Memo1->Lines->Clear();
}
//---------------------------------------------------------------------------

/*
v0.2
	- ZeroMemory()を::ZeroMemory()に変更 (グローバルスコープを明示)
	- refactor > 変数名の変更: ローカル変数、ループ変数など
	- assert()の使用停止。代わりにearly returnとTMemo出力へ
	- インクルードファイルを[C++ Builder XE4]用に変更
v0.1
   - 右記からforkした [ http://www.gesource.jp/programming/bcb/83.html ]
	 Acknowledgement to Takashi YAMAMOTO (山本隆 様)
*/

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
	showMacAddress();
}

//---------------------------------------------------------------------------
// MACアドレス関連処理
//
// { MACアドレス取得用構造体
typedef struct {
	ADAPTER_STATUS status;
	NAME_BUFFER buffer[30];
} ASTAT_t;
// } MACアドレス取得用構造体

void __fastcall TForm1::showMacAddress(void)
{
	NCB ancb; // netbios()にて使用
	LANA_ENUM lenum; // LANA: LAN Adapterのenum

	// NICの数を取得
	::ZeroMemory(&ancb, sizeof(ancb));
	ancb.ncb_command = NCBENUM;
	ancb.ncb_buffer = (UCHAR*)&lenum;
	ancb.ncb_length = sizeof(lenum);
	Netbios(&ancb);

	//
	for(int idx = 0; idx < lenum.length; idx++) {  // NICごとの処理
		// 1. リセット
		::ZeroMemory(&ancb, sizeof(ancb));
		ancb.ncb_command = NCBRESET;
		ancb.ncb_lana_num = lenum.lana[idx];
		UCHAR retCode = Netbios(&ancb);
		//assert(retCode == 0);  // >> early returnを代わりに使用
		if (retCode != 0) {
			Memo1->Lines->Add(L"ERROR: Netbios() failed");
			return;
		}

		// 2. MACアドレスの取得
		ASTAT_t adpt; // アダプタ情報取得用バッファ
		//
		::ZeroMemory(&ancb, sizeof(ancb));
		ancb.ncb_command = NCBASTAT;
		ancb.ncb_lana_num = lenum.lana[idx];
		strcpy(ancb.ncb_callname, "*");
		ancb.ncb_buffer = (UCHAR*)&adpt;
		ancb.ncb_length = sizeof(adpt);
		retCode = Netbios(&ancb);
		//assert(retCode == 0);  // >> early returnを代わりに使用
		if (retCode != 0) {
			Memo1->Lines->Add(L"ERROR: Netbios() failed");
			return;
		}

		// 3. Output to TMemo
		String msg = String().sprintf(L"%02X-%02X-%02X-%02X-%02X-%02X",
		adpt.status.adapter_address[0],
		adpt.status.adapter_address[1],
		adpt.status.adapter_address[2],
		adpt.status.adapter_address[3],
		adpt.status.adapter_address[4],
		adpt.status.adapter_address[5]);
		Memo1->Lines->Add(msg);
	}
}
//---------------------------------------------------------------------------
```

### 動作

- ボタンを押すとNICのMACアドレスがMemo1に表示される
- 複数のNICを持つ場合、複数のMACアドレスが表示される

### 関連

- NetBIOS関連
    - [NetBIOS @ Windows Dev Center](https://docs.microsoft.com/en-us/windows/desktop/api/_netbios/)
    - [_NCB structure @ Windows Dev Center](https://docs.microsoft.com/en-us/windows/desktop/api/nb30/ns-nb30-_ncb)

- ZeroMemory()関連
    - [c++ builder > アプリケーションのオープンで時々エラー > モジュール 'SHELL32.dll'のアドレス 76C03EDD でアドレス 00030002 に対する読取り違反がおきました。 > ::ZeroMemory(&sinfo,sizeof(SHELLEXECUTEINFO)); > 初期化忘れ](https://qiita.com/7of9/items/66dd24cf60273a2f5260)
    - [c++ builder XE4, 10.2 Tokyo > 他のアプリを開く > access violationが出た > ZeroMemory()したら回避できた](https://qiita.com/7of9/items/7b8e8e5ec4111df6dcf5)


### 実装 v0.3 > assignMacAddress()の追加

プロダクトコードで使えるようにさらに変更した。

```Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TMemo *Memo1;
	TButton *Button1;
	void __fastcall Button1Click(TObject *Sender);
private:	// ユーザー宣言
	void __fastcall showMacAddress(void);
	bool __fastcall assignMacAddress(TStringList *dstPtr);
public:		// ユーザー宣言
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
```

```Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

//#include <tchar.h>
//#include <windows.h>
//#include <assert.h>
//#include <stdio.h>
#include "Nb30.h"
//
#include <memory>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
	Memo1->Lines->Clear();
}
//---------------------------------------------------------------------------

/*
v0.3
	- assignMacAddress()を追加
            - showMacAddress()の処理をこちらに移行
	- unique_ptr()使用
v0.2
	- ZeroMemory()を::ZeroMemory()に変更 (グローバルスコープを明示)
	- refactor > 変数名の変更: ローカル変数、ループ変数など
	- assert()の使用停止。代わりにearly returnとTMemo出力へ
	- インクルードファイルを[C++ Builder XE4]用に変更
v0.1
   - 右記からforkした [ http://www.gesource.jp/programming/bcb/83.html ]
	 Acknowledgement to Takashi YAMAMOTO (山本隆 様)
*/

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
	showMacAddress();
}

//---------------------------------------------------------------------------
// MACアドレス関連処理
//
// { MACアドレス取得用構造体
typedef struct {
	ADAPTER_STATUS status;
	NAME_BUFFER buffer[30];
} ASTAT_t;
// } MACアドレス取得用構造体

void __fastcall TForm1::showMacAddress(void)
{
	std::unique_ptr<TStringList> wrkSL(new TStringList);
	assignMacAddress(wrkSL.get());

	for(int idx=0; idx < wrkSL->Count; idx++) {
		Memo1->Lines->Add(wrkSL->Strings[idx]);
	}
}

bool __fastcall TForm1::assignMacAddress(TStringList *dstPtr)
{
	if (dstPtr == NULL) {
		return false; // error
	}

	NCB ancb; // netbios()にて使用
	LANA_ENUM lenum; // LANA: LAN Adapterのenum

	// NICの数を取得
	::ZeroMemory(&ancb, sizeof(ancb));
	ancb.ncb_command = NCBENUM;
	ancb.ncb_buffer = (UCHAR*)&lenum;
	ancb.ncb_length = sizeof(lenum);
	Netbios(&ancb);

	//
	for(int idx = 0; idx < lenum.length; idx++) {  // NICごとの処理
		// 1. リセット
		::ZeroMemory(&ancb, sizeof(ancb));
		ancb.ncb_command = NCBRESET;
		ancb.ncb_lana_num = lenum.lana[idx];
		UCHAR retCode = Netbios(&ancb);
		//assert(retCode == 0);  // >> early returnを代わりに使用
		if (retCode != 0) {
			//Memo1->Lines->Add(L"ERROR: Netbios() failed");
			return false;
		}

		// 2. MACアドレスの取得
		ASTAT_t adpt; // アダプタ情報取得用バッファ
		//
		::ZeroMemory(&ancb, sizeof(ancb));
		ancb.ncb_command = NCBASTAT;
		ancb.ncb_lana_num = lenum.lana[idx];
		strcpy(ancb.ncb_callname, "*");
		ancb.ncb_buffer = (UCHAR*)&adpt;
		ancb.ncb_length = sizeof(adpt);
		retCode = Netbios(&ancb);
		//assert(retCode == 0);  // >> early returnを代わりに使用
		if (retCode != 0) {
			//Memo1->Lines->Add(L"ERROR: Netbios() failed");
			return false;
		}

		// 3. Add to list
		String msg = String().sprintf(L"%02X-%02X-%02X-%02X-%02X-%02X",
		adpt.status.adapter_address[0],
		adpt.status.adapter_address[1],
		adpt.status.adapter_address[2],
		adpt.status.adapter_address[3],
		adpt.status.adapter_address[4],
		adpt.status.adapter_address[5]);
		//Memo1->Lines->Add(msg);
		dstPtr->Add(msg);
	}

	return true;
}
//---------------------------------------------------------------------------
```