COMの初期化とCoInitialize関数について

解決


どら  2005-10-05 20:54:28  No: 59201

久々の投稿です。
何か変な質問かもしれませんが・・・
COMオブジェクトを初期化するCoInitializeという関数があると思いますが・・・
この関数の戻り値をMSDN等で調べると

    S_FALSE 
       The COM library is already initialized on the calling thread. 

とあります。
これって、「COMの初期化は、そのスレッドですでに呼び出されていなければ必ず成功する」ということなのでしょうか?

というのも、今までいくつかCOMオブジェクトを呼び出す必要があるクラスを作っているのですが、これらを同じスレッドのなかで組み合わせて利用するときにコンストラクタなどでCOMの初期化を実行しているのですが
    ・COMの初期化に失敗->エラー終了
    ・すでに呼び出されている->そのままプログラムを継続
を区別する必要があるのではないかと・・・

これらを判別する方法ってあるんでしょうか?
また、皆さんはどの様にされているのかなどをアドバイスいただきたく・・・。

よろしくお願いいたします。


KING・王  2005-10-05 21:31:20  No: 59202

MSDNを少し見ましたが、
成功の場合は、S_OK、既に呼ばれている場合は、S_FALSEなどとあるので、
戻り値で場合わけできると思うのですが。


Toshi  2005-10-05 22:14:05  No: 59203

>これって、「COMの初期化は、そのスレッドですでに呼び出されていなければ必ず成功する」ということなのでしょうか?

ほぼその解釈でいいと思います。
マルチスレッドの時は RPC_E_CHANGED_MODE に気をつける必要があるっぽいですが・・・詳しくは MSDNのRemark見てください。


生茶  2005-10-05 23:00:27  No: 59204

>・すでに呼び出されている->そのままプログラムを継続
>を区別する必要があるのではないかと・・・

もしかして複数回初期化したらダメとか思ってるのかな?

S_FALSEはれっきとした成功を表す値で、この場合は
「既に初期化されてた」ということを知らせてるだけです。
何度も初期化しようが、成功した回数分CoUninitializeを呼べばいいのです。


どら  2005-10-06 05:59:53  No: 59205

KING・王さん、Toshiさん、生茶さん。レス、ありがとうございます。

話を総合すると・・・
    ・S_FALSEになったとき、「すでに起動されている/単純に失敗」を識別できない
    ・というよりも、失敗はしない
    ・必要なたびに呼び出し、成功した回数分CoUninitializeを呼べばいい
ということになるのでしょうか?

ん〜・・・
すると、COMを利用するクラスが複数あって、同一のスレッドでも利用する可能性がある場合
    ・S_FALSEになっても無視する(呼び出されていないのであれば必ず成功するので)
    ・クラスの外で(利用するクラスを実行するスレッド上で)CoInitializeを実行
のどちらかを取っているということでしょうか・・・?

私は今まで、クラスのなかでInitという関数を作りその中で

HRESULT hr = CoInitialize( NULL );
if(FAILED(hr))
{
   //エラー扱いにして初期化失敗のフラグをたてる
}
//その他クラス事の初期化処理

として、コンストラクタのなかで実行→成功/失敗のフラグを作成し、その後の処理はフラグで判断して処理をしていたのですが、これでは、同様に設定して作成した複数のクラスを同じスレッドのなかで使用したときに、最初にInitを行ったもの以外はエラー扱いになってしまうな〜と・・・。

皆さんはどうされているのでしょうか?


シャノン  2005-10-06 06:15:16  No: 59206

> 最初にInitを行ったもの以外はエラー扱いになってしまうな〜と・・・。

そのコード、動かしてみました?
FAILED( S_FALSE ) はどういう結果になるでしょうか?


どら  2005-10-06 06:37:56  No: 59207

シャノンさん

このコートは動かしてないです。
ネットのサンプルコードから抜粋していたので。
実は、私は
if(hr != S_OK)
{
    //失敗したときの処理
}
としてました(^^;
S_FALSEのことをきちんと見ていなかったので、「S_OKの場合だけ初期化できている」と思い込んでいたので・・・。

んで、ネットでいろんなサイトのサンプルを見て、この形を見て使えるのかな〜と・・・。
確かに、S_FALSE = 0x00000001Lになっているので、これを使っていると・・・

ますますわからなくなって来た・・・
S_FALSEは失敗ではない??


シャノン  2005-10-06 07:08:40  No: 59208

> S_FALSEは失敗ではない??

生茶さんのレス見ました?

> S_FALSEはれっきとした成功を表す値で、

と書かれています。
HRESULT で、S_ で始まる値は成功を示します。失敗は E_ で始まります(用途によっては、ナントカ_E_カントカ っていう場合もありますが)。


どら  2005-10-06 16:09:16  No: 59209

シャノンさん

ありがとうございます。

ということは、E_・・・という値が無い以上、「エラーを起こすことのない関数」と思っていた方がいいんですかね・・・。

大変参考になりました。
早速ソースを書いてみることにします。

皆さん、書き込みありがとうございました。


Ban  2005-10-06 17:05:49  No: 59210

> ということは、E_・・・という値が無い以上、
> 「エラーを起こすことのない関数」と思っていた方がいいんですかね・・・。

MSDN の CoInitialize には、例えば以下のようにエラーコードも
ちゃんと書かれていますが。
<MSDN>
This function supports the standard return values E_INVALIDARG, E_OUTOFMEMORY, and E_UNEXPECTED, as well as the following: 
 -- snip --
</MSDN>

また、一般的に HRESULT である以上、書かれている以外のエラーコードが
返る可能性(将来を含む)はあると思います。

HRESULT のルールは原則として、
0<=hr  : 成功
hr < 0 : 失敗
なので、たいていの場合、S_OKだけを判定するのは間違いです。

# 実装上は、例えば S_OK:0, S_FALSE:1, E_FAIL:-1 などの値になってます。

個別の API に特に断りがない場合、単純な成否を見るときは、
SUCCEEDED または FAILED マクロを使うべきです。

<winerror.h>
#define SUCCEEDED(Status) ((HRESULT)(Status) >= 0)
#define FAILED(Status) ((HRESULT)(Status)<0)
</winerror.h>


どら  2005-10-06 17:26:47  No: 59211

Banさん

レスありがとうございます。
勉強になりました。

まだまだ勉強不足ですが、また何かありましたら、よろしくお願いします(^^)


生茶  2005-10-06 18:36:27  No: 59212

一度読んでおくといいでしょう

http://msdn.microsoft.com/library/en-us/com/html/15f3ae3e-1794-4948-a7aa-6309a703364b.asp


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加