プログラミングとか日常とかの覚書っぽいなにか
× [PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
前回の続き。
COMでは呼び出し側が受け取ったインタフェースポインタは呼び出し側がReleaseする責任があるっていうのがルールなわけで、前回の例ではそのルールが徹底されていなかったためにメモリリークが発生していたわけです。 しかし、エラーが発生して途中でreturnしたりする場合に、直前まで取得したオブジェクトすべてをReleaseしていたら、コードのあっちこっちでReleaseしなければならなくなって、本来の処理がわかりにくくなってしまう、なんてことも起こりえます。インターフェースポインタだけでなく、VARIANT型を使っていたらVariantClearも呼び出さなきゃいけないですからね。 C++では、その辺りをうまく自動的に行うようにできる方法が存在します。 C++では、オブジェクトがスコープを抜けて破棄されるときにクラスで定義されたデストラクタが実行されますが、これを利用して、「ポインタを管理するクラス(オブジェクト)」と言うものを作成し、管理オブジェクトが破棄されるときに、保持しているポインタも(デストラクタで)同時に破棄させる、というようにするテクニックが存在します。 このような手法は一般的に『RAII(Resource Acquisition Is Initialization;リソース獲得は初期化である)イディオム』と呼ばれており、特にポインタを管理する場合にはその管理オブジェクトは『スマートポインタ』と呼ばれます。 C++の標準のスマートポインタとしては auto_ptr があり、保持しているポインタを自動的にdeleteしてくれます。新しいC++11では、他にも unique_ptr や shared_ptr といったものも追加されています。 COMインタフェースポインタ用のスマートポインタとしては、ATLライブラリで提供されている CComPtr テンプレートクラスがあります。上記のC++標準のスマートポインタ(auto_ptr)がdeleteを呼ぶのに対して、 CComPtr はCOMインターフェースポインタの Release メソッドを呼び出すようになっています。 テンプレートクラスになっているので、任意のインターフェース型に対してスマートポインタを生成することができます。 いろいろ説明するよりもサンプルを書いた方がわかるかと思いますので以下を参照。 #include <cstdio> #include <exception> #include <atlbase.h> #include <atlsafe.h> #include <wbemcli.h> #include <atlstr.h> #pragma comment(lib, "Wbemuuid.lib") using namespace std; // COM初期化例外クラス class ComInitializationException : public std::exception { public: explicit ComInitializationException(HRESULT hr) : m_result(hr) { } HRESULT Result() const { return m_result; } private: HRESULT m_result; }; // COM初期化クラス class ComInitializer { public: explicit ComInitializer(DWORD dwCoInit) { HRESULT hr = ::CoInitializeEx(NULL, dwCoInit); if (FAILED(hr)) { throw ComInitializationException(hr); } } ~ComInitializer() { ::CoUninitialize(); } }; // Win32_NetworkAdapter インスタンスを列挙 HRESULT EnumNetworkAdapter() { // WMI Locator を取得 CComPtr<IWbemLocator> locator; HRESULT hr = locator.CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER); if (FAILED(hr)) { printf("CoCreateInstance(CLSID_WbemLocator) Error. [%08lX]\n", hr); return hr; } // WMI に接続 CComPtr<IWbemServices> services; hr = locator->ConnectServer(CComBSTR(L"ROOT\\CIMV2"), NULL, NULL, NULL, 0, NULL, NULL, &services); if (FAILED(hr)) { printf("IWbemLocator::ConnectServer(\"ROOT\\CIMV2\") Error. [%08lX]\n", hr); return hr; } // プロキシのセキュリティレベルを設定 hr = ::CoSetProxyBlanket(services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); if (FAILED(hr)) { printf("CoSetProxyBlanket() Error. [%08lX]\n", hr); } // Win32_NetworkAdapter のインスタンスを列挙 CComPtr<IEnumWbemClassObject> enumerator; hr = services->CreateInstanceEnum( CComBSTR(_T("Win32_NetworkAdapter")), WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &enumerator); if (FAILED(hr)) { printf("IWbemServices::CreateInstanceEnum(\"Win32_NetworkAdapter\") Error. [%08lX]\n", hr); return hr; } // 列挙した Win32_NetworkAdapter インスタンスを1つずつ取得 for (int itemIndex = 0;; itemIndex++) { // 現在のインスタンスを取得 CComPtr<IWbemClassObject> networkAdapter; ULONG returnedCount = 0; hr = enumerator->Next(WBEM_INFINITE, 1, &networkAdapter, &returnedCount); if (FAILED(hr)) { printf("IEnumWbemClassObject::Next() Error. [%08lX]\n", hr); return hr; } // すべてのインスタンスを取得済みならループを抜ける if (returnedCount == 0) { break; } printf("<%d>\n", itemIndex + 1); // 各種プロパティを表示 LPCWSTR propertyNames[] = { L"AdapterType", L"Availability", L"Caption", L"Name", L"NetConnectionID", L"NetConnectionStatus", L"MACAddress", L"PhysicalAdapter", }; for (int iProp = 0; iProp < _countof(propertyNames); iProp++) { // プロパティ取得 CComVariant vtProperty; hr = networkAdapter->Get(propertyNames[iProp], 0, &vtProperty, 0, 0); if (FAILED(hr)) { return hr; } // 表示 if (SUCCEEDED(vtProperty.ChangeType(VT_BSTR))) { printf(" %20s : %s\n", static_cast<LPCSTR>(CW2A(propertyNames[iProp])), static_cast<LPCSTR>(CW2A(V_BSTR(&vtProperty)))); } } } return hr; } int main() { try { // COM初期化 ComInitializer scopedComInitializer(COINIT_MULTITHREADED); // ネットワークアダプタを列挙して表示 HRESULT hr = EnumNetworkAdapter(); if (FAILED(hr)) { return 1; } } catch (const ComInitializationException& e) { printf("COM library initialization failed. [%08lX]\n", e.Result()); return 1; } return 0; } ATLではCComPtrの他に、VARIANT型を管理する CComVariant や、BSTRを管理する CComBSTR や、SAFEARRAYを管理する CComSafeArray などといったクラスも提供しています。これらもすべてRAIIイディオムに基づく管理クラスで、VariantInit / VariantClear やら SysAllocString / SysFreeString などの呼び出しを代わりにやってくれるので、アプリケーション側ではこれらの呼び出しを一切行う必要がなくなるので、非常に便利です。 また、上記のサンプルでは CoInitializeEx / CoUninitialize によるCOMの初期化・クリーンアップに関しても、RAIIイディオムに基づくComInitializerを定義して使用しています。まぁ、上記のサンプルではあまり恩恵はないようにも見えますが……。 残念ながら、無料版の Visual C++ Express Edition にはATLが含まれていないので、これらの恩恵を受けることはできないのですけど。 PR |
プロフィール
HN:
はむぱい
職業:
ソフト作ったりしてる人
Twitter
カテゴリー
最新CM
[06/09 replica rolex oyster perpetual datejust]
[06/09 bracelets imitation cartier love]
[06/09 replica the oyster perpetual datejust]
[06/09 datejust rolex oyster perpetual]
[06/09 replica gold love bangle]
カレンダー
ブログ内検索
あ~いい漢字
|