- PR -

【C#】IMalloc.FreeとMarshal.FreeHGlobalの違い

投稿者投稿内容
清華
ベテラン
会議室デビュー日: 2005/12/21
投稿数: 50
投稿日時: 2005-12-22 19:24
清華ですよろしくお願いします。

さて早速ですがIMalloc.FreeとMarshla.FreeHGlobalには違いがあるのでしょうか?

Microsoft Win32とMicrosoft .NET Framework APIとの対応のメモリ関連項目には

引用:

GlobalAlloc 指定されたバイト数をヒープから割り当てます。 System.Runtime.InteropServices.Marshal.AllocHGlobal
GlobalFree 指定されたグローバル メモリ オブジェクトを解放します。 System.Runtime.InteropServices.Marshal.FreeHGlobal
GlobalReAlloc 指定されたグローバル メモリ オブジェクトのサイズまたは属性を変更します。 System.Runtime.InteropServices.Marshal.ReAllocHGlobal



と書かれています、MSDNのGlobal Freeに関する記述では

引用:

指定されたグローバルメモリオブジェクトを解放し、そのハンドルを無効にします。



IMallocに関する記述では

引用:

Frees a previously allocated block of memory.
前もって割り当てられたメモリブロックを解放します。



とかかれています、

GlobalAllocとIMalloc::Allocの違いも良く分かりません。

GlobalAlloc

引用:

指定されたバイト数のメモリをヒープから割り当てます。Win32 のメモリ管理機能は、グローバルヒープとローカルヒープを区別していません。



IMalloc:Alloc
引用:

Allocates a block of memory.
メモリブロックを割り当てます。



結局GlobalAllocでもIMalloc:Allocでもメモリブロックを割り当てるしGlobalFreeでもIMalloc::Freeでもメモリブロックを解放すると解釈していいのでしょうか?

IMallocを使用するためのインターフェイスの記述などでコードが重くなりますし、Marshalのほうが簡単に使えるので区別する必要が無いならばMarshalで書くつもりです。

よろしくお願いします。
_________________
9uiet Design - http://quietdesign.rental.allinoneserver.net/
デザインにこだわったソフトの配布とプログラミングTipsの公開(予定)をしています。
9uiet Blog - http://seiga.blog44.fc2.com/
笑ったことやプログラミングのことなど書
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2005-12-22 20:34
こんばんは。

引用:

清華さんの書き込み (2005-12-22 19:24) より:

さて早速ですがIMalloc.FreeとMarshla.FreeHGlobalには違いがあるのでしょうか?



えっと、比べる対象がちょっと違うように感じます。
IMallocインターフェイスは、あくまで「インターフェイス」ですので
自分でIMallocを実装してもいいわけであって、どのメモリ領域に領域を確保するかは実装依存なはずです。

ちなみに、
CoTaskMemAlloc
CoGetMallocで得られるIMallocインターフェイスの実装や
Marshal.AllocCoTaskMem
「COM タスクメモリ」上にアロケートするようですが、

一方
GlobalAlloc
Marshal.AllocHGlobal
「グローバルヒープ」上にアロケートします。

私には、「COM タスクメモリ」と「グローバルヒープ」が、どのように違うのか知りませんが、
区別して使ったほうが良いと思います。


[ メッセージ編集済み 編集者: Tdnr_Sym 編集日時 2005-12-22 20:41 ]
清華
ベテラン
会議室デビュー日: 2005/12/21
投稿数: 50
投稿日時: 2005-12-22 20:47
引用:

えっと、比べる対象がちょっと違うように感じます。
IMallocインターフェイスは、あくまで「インターフェイス」ですので
自分でIMallocを実装してもいいわけであって、どのメモリ領域に領域を確保するかは実装依存なはずです。


言葉足らずでした、SHGetMallocを使ってMallocオブジェクトを作成しています、自分で実装はしませんよ(苦笑。

コード:
public static IMalloc GetMalloc()
{
	IntPtr ptrRet;
	SHGetMalloc(out ptrRet);
		
	Object obj = Marshal.GetTypedObjectForIUnknown(ptrRet,typeof(IMalloc));
	IMalloc im = (IMalloc)obj;
		
	return im;
}




引用:

Allocates a block of task memory in the same way that IMalloc::Alloc does.



これですっきりしました、IMalloc::Allocの説明を見ても確保するとしか書いていなかったので(苦笑。

違う場所から持ってきているのならば区別する意義がありますね、まぁどちらにせよ
Marshal.FreeCoTaskMem()
* FreeCoTaskMem exposes the CoTaskMemFree COM API function *
領域の確保もMarshal.AllocCoTaskMemを使うようにしてやればこれで問題なさそうですね、in the same way that IMalloc::Alloc does.って言ってますし。

有難うございました、すっきりしました。
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2005-12-22 21:20
こんばんは。

引用:

清華さんの書き込み (2005-12-22 20:47) より:
言葉足らずでした、SHGetMallocを使ってMallocオブジェクトを作成しています、自分で実装はしませんよ(苦笑。



私もIMallocインターフェイスを実装したことないですね(~_~;)
IStreamインターフェイスなら実装したことはありますが。(Win32リソースからデータを読み出すよう実装しました)

そういえば、SHGetMallocというAPIもありましたね。
もうサポートされないようですが。
引用:

This function should no longer be used. Use the CoTaskMemFree and CoTaskMemAlloc functions in its place.


とありますので、代わりにMarshal.AllocCoTaskMemMarshal.FreeCoTaskMemを使っても良いのでしょう。

とにかく、ちゃんと対応した関数(メソッド)を使ってAlloc/Freeすることは、最低限守ったほうがよいでしょうね。
yayadon
常連さん
会議室デビュー日: 2003/07/23
投稿数: 41
投稿日時: 2005-12-23 16:02
シェルオブジェクトも含めて,COMオブジェクトを相手にする場合は,
インターフェイスの参照カウントの扱いや
参照でやり取りする引数をどっちが開放するのか
等の細かい決まりがあるので,
.NET Framework のマーシャリング機能にゆだねた方が無難です。

SHGetMalloc ですでに IMallocインターフェイスが返ってきているので,
Marshal.GetTypedObjectForIUnknown(ptrRet, typeof(IMalloc))
の typeof(IMalloc) は無視されているのでいいけど,
本来は,ヘルプにもあるように,
CoClass(コクラス)のインターフェイスの方を,指定するのでなく,
Interop(相互運用機能)が作成した ○○Class というクラスの方を指定しなさい
となっているので,そのようにします。

それと,SHGetMallocでIMallocインターフェイスを取得する際に,
シェル側でAddRefしてるので,
こちらでReleaseしてやらないといけないんじゃないか?とか,
でも,GetTypedObjectForIUnknownを使ってから,
マーシャラが面倒見てくれる(Releaseしてくれる)んじゃないか?
とか...どうなんだ??となって...
ちゃんぽんにするとわけがわからなくなります。

なので,
上のやりとりで出た結論のように,COM相手の場合は,最初から,
Marshal.AllocCoTaskMem (.FreeCoTaskMem) 等を使ったやりとりに限定した方が
危なげないというか,無難なような気がします。


Marshal.AllocHGlobal (.FreeHGlobal) の方は,
インターフェイスとか,その参照カウントとかとは無縁な世界の時の
関数の引数のやりとりに使うためのものでしょう。


※ CoClass(コクラス)
COMのクラスには,
タレントで言うところのマネージャに相当するオブジェクトが
それぞれに専用に作ってあって,
それを CoClass(コクラス) とか Class Object(クラス・オブジェクト)と言っています。
CoClassに対してインターフェイスを要求することで,
CoClassを介して,COMオブジェクトを取得する仕組みになっています。
COMオブジェクトの寿命は,CoClassが管理しているので
直接やりとりしようと思うと結構大変なことになります。

[ メッセージ編集済み 編集者: 稍丼 編集日時 2005-12-23 16:08 ]
清華
ベテラン
会議室デビュー日: 2005/12/21
投稿数: 50
投稿日時: 2005-12-24 06:15
返信が適用されていない……

お二方、どうもありがとうございます、やはり揃えるというのが大事ですね。

あと、
引用:

SHGetMalloc ですでに IMallocインターフェイスが返ってきているので,
Marshal.GetTypedObjectForIUnknown(ptrRet, typeof(IMalloc))
の typeof(IMalloc) は無視されているのでいいけど,
本来は,ヘルプにもあるように,
CoClass(コクラス)のインターフェイスの方を,指定するのでなく,
Interop(相互運用機能)が作成した ○○Class というクラスの方を指定しなさい

となっているので,そのようにします。


の意味が良く分かりません(汗。インターフェイスを指定するなとその意味は大体つかめていると思うのですが「○○Class というクラスの方を指定しなさい」の意味が少し……よろしければもう少し詳しく教えていただけないでしょうか、申し訳ありません。
_________________
9uiet Design - http://quietdesign.rental.allinoneserver.net/
デザインにこだわったソフトの配布とプログラミングTipsの公開(予定)をしています。
9uiet Blog - http://seiga.blog44.fc2.com/
笑ったことやプログラミングのことなど書
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2005-12-24 17:19
こんにちは。

引用:

清華さんの書き込み (2005-12-24 06:15) より:
引用:

SHGetMalloc ですでに IMallocインターフェイスが返ってきているので,
Marshal.GetTypedObjectForIUnknown(ptrRet, typeof(IMalloc))
の typeof(IMalloc) は無視されているのでいいけど,
本来は,ヘルプにもあるように,
CoClass(コクラス)のインターフェイスの方を,指定するのでなく,
Interop(相互運用機能)が作成した ○○Class というクラスの方を指定しなさい

となっているので,そのようにします。


の意味が良く分かりません(汗。インターフェイスを指定するなとその意味は大体つかめていると思うのですが「○○Class というクラスの方を指定しなさい」の意味が少し……



なんだかMarshal.GetTypedObjectForIUnknownメソッドのMSDNの解説を見てみると、
第2引数の"Type t"って制約が多いんですね。
引用:

解説
t パラメータは、COM インポート型または COM インポート型のサブタイプである必要があります。さらに、 t は、タイプ ライブラリ インポータ ( Tlbimp.exe) によってメタデータがインポートされた型である必要があります。



よく分からないですが…解説から推測するに
たとえば、ExcelをCOM参照設定で追加した場合、
「ApplicationClassクラス」と「Applicationインターフェイス」が自動的に定義されますが、

【OK】Object o = Marshal.GetTypedObjectForIUnknown(ptrUnknown, typeof(ApplicationClass));
【NG】Object o = Marshal.GetTypedObjectForIUnknown(ptrUnknown, typeof(Application));

ってことでしょうか。

でも、今回の場合おそらくIMallocインターフェイスは「タイプライブラリ インポータ」によって定義されたものではないですよね?
この場合はどうすればいいんでしょうね?

Marshal.GetTypedObjectForIUnknownではなくMarshal.GetObjectForIUnknownを使ったほうがいいんでしょうかね!?こんな感じで↓。
#キャスト部分がちょっと気持ち悪いですけれども…

コード:

public static IMalloc GetMalloc()
{
// SHGetMalloc関数からIMallocインターフェイスのポインタを取得
IntPtr ptrRet;
SHGetMalloc(out ptrRet);

// Object型(ランタイム呼び出し可能ラッパー)として取得する
Object obj = Marshal.GetObjectForIUnknown(ptrRet);

// IMallocインターフェイスにキャストする
IMalloc im = (IMalloc)obj;

// Marshal.GetObjectForIUnknownメソッドが参照カウントをインクリメントしているので、デクリメントしておく
Marshal.Release(ptr);

return im;
}

public void hoge()
{
IMalloc im = GetMalloc();

・・・ IMallocを使用するコード・・・

// 使い終わったら、解放しておく
Marshal.ReleaseComObject(im);
}




ちょっと、話が変わりますが…
(ご存知のこととは思いますが)よく混乱することがありますので…

COMオブジェクトは”1つ以上の”インターフェイスを持ちます。
#1つはIUnknownインターフェイスであり、このIUnknownインターフェイスを実装することがCOMオブジェクトとしての必要条件です。
つまり、COMオブジェクトのクラス(CoClass)とそのインターフェイスは、「1対1」で対応していません。
それぞれにはGUIDという一意のIDが割り振られているのですが、
CoClassのIDは「CLSID」、インターフェイスのIDは「IID」と呼ばれます。

例えばExcel.ApplicationオブジェクトをよくあるCOMモデルで表現すると、
”多分”こんな感じ↓でしょうか。(ちょっと自信はないですが…)

コード:


 ○ IUnknownインターフェイス
 | IID={00000000-0000-0000-C000-000000000046}
 |
| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|−○ IDispatchインターフェイス
|Excel.Application              |   IID={00020400-0000-0000-C000-000000000046}
| COMオブジェクト              |
|                      |−○ IConnectionPointContainerインターフェイス
|CLSID={00024500-0000-0000-C000-000000000046}|   IID={B196B284-BAB4-101A-B69C-00AA00341D07}
|                      |
|                      |−○ Excel._Applicationインターフェイス
|                      |   IID={000208D5-0000-0000-C000-000000000046}
  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄




[ メッセージ編集済み 編集者: Tdnr_Sym 編集日時 2005-12-25 00:50 ]
yayadon
常連さん
会議室デビュー日: 2003/07/23
投稿数: 41
投稿日時: 2005-12-25 09:16
# 余計なことを書いたのかもしれません。

GetObjectForIUnknown() の方には,

引用:

汎用的なラッパー型ではなく、特定のマネージ クラス型でラップするオブジェクトを使用する場合は、次の要件に従う必要があります。

COM オブジェクトの IProvideClassInfo インターフェイスを実装する。
アセンブリ登録ツール (Regasm.exe) を使用して、対象のアセンブリを登録する。


のように厳しいことを言っておきながら,

引用:

なお、 Marshal.GetTypedObjectForIUnknown メソッドを使用すれば、これらの要件を回避しながら、特定のマネージ クラス型でラップされたオブジェクトを取得することができます。


なんていう記述が,わざわざありますね(汗)。


で,あらためて,ヘルプを読み直すと...

ツールを使うと,
 ○○Class という方は,CoClass起源で
 ○○ という方は,デフォルトインターフェイス起源
になるお約束になっているので,
 インスタンスを要求したい場合は,○○Class の方を指定しなさい...
という意味で,その理由は,
 CoCreateInstance() が必要な時は,CoClass起源のマネージ型が必要になるから
 ○○Class を指定しないさい
という意味なんでしょう。
VBからは,デフォルトのインターフェイスは見えないので,
指定しようがないので,
C#固有の問題として書いてあるだけなのかもしれません。

で,

> pUnk パラメータのオブジェクトが既に取得されている場合、
> t は無視され、既存のオブジェクトが返されます。

は,また別問題で,

 QueryInterface() に相当するような時なら,
 インターフェイス起源のマネージ型を指定してもいい...

ということなのかもしれません。


なので,今回の場合は,
インターフェイス起源の定義でOKでしたね。


ただ,

コード:

IMalloc im = (IMalloc)Marshal.GetTypedObjectForIUnknown(ptrRet,typeof(IMalloc));


は良くても,直接,

コード:

IMalloc im = Marshal.GetTypedObjectForIUnknown(ptrRet,typeof(IMalloc));


は,もちろんダメなんですよね?

だったら,
引用:
コード:
 

SHGetMalloc(out ptrRet);

// Object型(ランタイム呼び出し可能ラッパー)として取得する
Object obj = Marshal.GetObjectForIUnknown(ptrRet);

// IMallocインターフェイスにキャストする
IMalloc im = (IMalloc)obj;



の方が,いい感じがしないでもないんだけど...

[ メッセージ編集済み 編集者: 稍丼 編集日時 2005-12-25 09:20 ]

スキルアップ/キャリアアップ(JOB@IT)