C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

WinAPIからの値の受け取り方について

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■90644 / inTopicNo.1)  WinAPIからの値の受け取り方について
  
□投稿者/ kuragefuwari (1回)-(2019/03/31(Sun) 13:09:20)

分類:[.NET 全般] 

こんにちは。
C#からWinAPIの「LoadTypeLib関数」を呼ぶプログラムを作ろうとしています。

https://docs.microsoft.com/en-us/windows/desktop/api/oleauto/nf-oleauto-loadtypelibによると、
LoadTypeLib関数の定義は、

HRESULT LoadTypeLib(
  LPCOLESTR szFile,
  ITypeLib  **pptlib
);

となっています。

この関数をC#から呼ぶときの書き方を調べていると、以下の2種類が見つかりました。

書き方A:
[DllImport("oleaut32.dll")]
public static extern int LoadTypeLib([MarshalAs(UnmanagedType.LPWStr)]string fileName, out ITypeLib typeLib);

書き方B:
[DllImport("oleaut32.dll", PreserveSig=false)]
public static extern ITypeLib LoadTypeLib([MarshalAs(UnmanagedType.LPWStr)]string fileName);
※書き方Aの第2引数がなくなり、戻り値で受け取るようになっています。

Aは理解できるのですが、Bが理解できていません。

DllImport属性で、PreserveSig=falseを指定することにより、戻り値のHRESULTがエラーとなった場合に、例外がスローされるようになるため、

[DllImport("oleaut32.dll", PreserveSig=false)]
public static extern void LoadTypeLib([MarshalAs(UnmanagedType.LPWStr)]string fileName, out ITypeLib typeLib);

のように戻り値をintからvoidに変更できるところまでは理解できたのですが、そこからさらに
第2引数の"out ITypeLib typeLib"を戻り値のほうに持ってこれる理由が理解できていません。

このように書き換えることができる理由か、もしくはそのあたりを解説しているページのURLを
ご教示いただだけませんでしょうか。

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

引用返信 編集キー/
■90649 / inTopicNo.2)  Re[1]: WinAPIからの値の受け取り方について
□投稿者/ Atata!! (7回)-(2019/04/01(Mon) 02:27:11)
2019/04/01(Mon) 02:31:26 編集(投稿者)

> このように書き換えることができる理由か、もしくはそのあたりを解説しているページのURLを
> ご教示いただだけませんでしょうか。

昔はあったような気がするのですが、現在の Microsoft のサイトには直接言及している文書は見つかりませんでした。

https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.dllimportattribute.preservesig?view=netframework-4.7.2
の先頭の説明文

> Indicates whether unmanaged methods that have HRESULT or retval return values are directly translated or whether HRESULT or retval return values are automatically converted to exceptions.

を強引に解釈すれば、そのように読み取れないこともないかと思いました。
が、現状では書き換えることができる理由については、相互運用機能がそのような仕様になっているからと言うことしかできないかと思います。


なお、過去に発行されている Microsoft の公式解説書にその辺が言及されている(正確には置き換え可能であることを述べている)書籍がありましたが、
すべて絶版状態であるため入手は難しいかと思います。
引用返信 編集キー/
■90650 / inTopicNo.3)  Re[2]: WinAPIからの値の受け取り方について
□投稿者/ 魔界の仮面弁士 (2133回)-(2019/04/01(Mon) 08:53:23)
2019/04/01(Mon) 10:09:02 編集(投稿者)

No90644 (kuragefuwari さん) に返信
> DllImport属性で、PreserveSig=falseを指定することにより、戻り値のHRESULTがエラーとなった場合に、例外がスローされるようになるため、
> [DllImport("oleaut32.dll", PreserveSig=false)]
> public static extern void LoadTypeLib([MarshalAs(UnmanagedType.LPWStr)]string fileName, out ITypeLib typeLib);
> のように戻り値をintからvoidに変更できるところまでは理解できたのですが、そこからさらに
> 第2引数の"out ITypeLib typeLib"を戻り値のほうに持ってこれる理由が理解できていません。


MSDN Library では、以下のように解説されていますね。
現行の機械翻訳だと読みにくいと思いますので、
人力翻訳されていた VS2008 当時の MSDN Library から引用します。
https://www.microsoft.com/ja-jp/download/details.aspx?id=20955


【PreserveSigAttribute 属性クラスの解説】

>> 既定では、タイプ ライブラリ エクスポータ (Tlbexp.exe) によって、HRESULT に S_OK を返す呼び出しは、
>> [out, retval] パラメータが関数の戻り値として使用されるように、変換されます。S_OK の HRESULT は破棄されます。
>> HRESULT が S_OK 以外の場合、共通言語ランタイムは例外をスローして、[out, retval] パラメータを破棄します。
>> PreserveSigAttribute をマネージ メソッドのシグネチャに適用した場合、属性が適用されたメソッドの
>> マネージ シグネチャとアンマネージ シグネチャは同一になります。
>>
>> メンバが複数の成功 HRESULT 値を返し、またその複数の値を検出する必要がある場合は、
>> 元のメソッドのシグネチャの保持が必要となります。ほとんどの COM メンバは HRESULT を 1 つ返すため、
>> PreserveSigAttribute を適用することによって、成功またはエラーの HRESULT を表す整数値を取得できます。
>> Tlbexp.exe はすべての [out, retval] パラメータをマネージ シグネチャに out パラメータとして保持します。
>>
>> タイプ ライブラリ インポータ (Tlbimp.exe) もこの属性を適用します。Tlbimp.exe は、タイプ ライブラリを
>> インポートするときにこの属性をディスパッチ インターフェイスに適用します。
>>
>> メモ :
>> COM からマネージ コードへの相互運用時に、マネージ コードが PreserveSigAttribute クラスで
>> マークされていた場合、PreserveSigAttribute クラスでは、戻り値の Currency、Guid、および Object の型は
>> サポートされません。これらの条件で、これらのいずれかの戻り値の型を PreserveSigAttribute クラスで
>> 使用すると、TypeLoadException がスローされます。



【MarshalAsAttribute 属性クラスの PreserveSig フィールドの解説】

>> PreserveSig フィールドに true を設定すると、HRESULT 値または retval 値でアンマネージ シグネチャが直接変換されます。
>> false を設定すると、HRESULT 値または retval 値が自動的に例外に変換されます。既定では、PreserveSig フィールドは true です。
>>
>> true の場合、結果として生成されるメソッド シグネチャは HRESULT 値を含む整数値を返します。
>> この場合、アプリケーション内で戻り値を手動で検査し、その結果に応じて対応する必要があります。
>>
>> PreserveSig フィールドに false を設定すると、結果として生成されるメソッド シグネチャに含まれる戻り値の型は、
>> 整数型 (HRESULT) ではなく void 型になります。アンマネージ メソッドが HRESULT を生成した場合、ランタイムは
>> 戻り値 S_OK (または 0) を自動的に無視し、例外をスローしません。S_OK 以外の HRESULT の場合、
>> ランタイムはその HRESULT に対応する例外を自動的にスローします。
>> DllImportAttribute 属性は、HRESULT を返すメソッドに対してこの変換を実行するだけです。
>>
>> アプリケーションのエラー報告構造に例外が適している場合は、既定のエラー報告動作を HRESULT から例外に変更できます。
>> このフィールドは PreserveSigAttribute と似ていますが、PreserveSig フィールドとは異なり、この属性の既定値は false です。
>>
>> 場合によっては、Visual Basic の開発者は、マネージ コードで DLL 関数を定義する際、
>> Declare ステートメントを使用する代わりに、DllImportAttribute を使用します。
>> PreserveSig フィールドの設定は、このような事例の 1 つです。
引用返信 編集キー/
■90651 / inTopicNo.4)  Re[3]: WinAPIからの値の受け取り方について
□投稿者/ 魔界の仮面弁士 (2134回)-(2019/04/01(Mon) 09:31:13)
2019/04/01(Mon) 16:04:33 編集(投稿者)

No90650 (魔界の仮面弁士) に追記
> MSDN Library では、以下のように解説されていますね。
> 現行の機械翻訳だと読みにくいと思いますので、
> 人力翻訳されていた VS2008 当時の MSDN Library から引用します。
> https://www.microsoft.com/ja-jp/download/details.aspx?id=20955


C# 向けの記事だと、Microsoft 社員 blog のこのあたりで言及されていました。

[VS, DTE, COM and COMException, Part 2 ― PreserveSig (:-P)]
https://blogs.msdn.microsoft.com/tilovell/2010/10/20/vs-dte-com-and-comexception-part-2-preservesig-p/


と言っても、書かれていることは結局、
【PreserveSigAttribute 属性クラスの解説】
【MarshalAsAttribute 属性クラスの PreserveSig フィールドの解説】
を紹介しているだけではあるのですが、要するに“HRESULT を返す関数呼び出し”に対して、
「PreserveSig 属性を付与する」または「MarshalAs 属性で PreserveSig=false を付与する」ことで、

(1) HRESULT は Exception に変換されるようになる。
(2) out パラメータがある場合はそれが戻り値に変更され、out パラメータが無ければ戻り値無し(void)になる。

ということですね。 



No90649 (Atata!! さん) に返信
> なお、過去に発行されている Microsoft の公式解説書にその辺が言及されている(正確には置き換え可能であることを述べている)書籍がありましたが、
> すべて絶版状態であるため入手は難しいかと思います。

タイトルか ISBN が分かれば教えて欲しいです。
引用返信 編集キー/
■90652 / inTopicNo.5)  Re[4]: WinAPIからの値の受け取り方について
□投稿者/ kuragefuwari (2回)-(2019/04/01(Mon) 12:05:01)
Atata!!さん、魔界の仮面弁士さん、ご回答いただきまして有難うございます。

公式ドキュメントで、「out引数を戻り値に変換できる!」というようなズバリの記述は無い(見つからない)ものの、
魔界の仮面弁士さんがご紹介くださった、Microsoft社員の方のblogでは

>> If you have a method signature with an out param, and it returns HRESULT, you can use PreserveSig, or actually NOT use PreserveSig,
>> and you can call it as if it returned the out param directly.

と書かれているので、「out引数で値を返す関数は、それを戻り値で直接返すことができる」と認識しておき、場合によって上手く使い分けていこうと思います。

本当に、有難うございました。
解決済み
引用返信 編集キー/
■90666 / inTopicNo.6)  Re[4]: WinAPIからの値の受け取り方について
□投稿者/ Atata!! (8回)-(2019/04/03(Wed) 03:43:44)
> タイトルか ISBN が分かれば教えて欲しいです。

すみません、すぐ手元に読める状態にない(実家に置いてある)ので、内容確認次第返答します。

おぼろげな記憶ですが、.NET and COM: The Complete Interoperability Guide だった気がします。
ISBN-10: 067232170X
ISBN-13: 978-0672321702

今見てみると公式解説書ってわけでもないようですね。
引用返信 編集キー/
■90667 / inTopicNo.7)  Re[5]: WinAPIからの値の受け取り方について
□投稿者/ 魔界の仮面弁士 (2138回)-(2019/04/03(Wed) 12:01:10)
No90666 (Atata!! さん) に返信
>>タイトルか ISBN が分かれば教えて欲しいです。
> おぼろげな記憶ですが、.NET and COM: The Complete Interoperability Guide だった気がします。

ありがとうございます。2002年1月発行でこの内容は凄いかも。
Kindle 版もあるようなので、早速買ってみました。


まだ読んでいないので、今回の内容が含まれているかどうかは未確認ですが、
Google Books のサンプルには、PreserveSig についての言及があるようです。
(Kindle 版の サンプル書籍上には記載が見当たりませんでしたが)
https://books.google.co.jp/books?id=x2OIPSyFLBcC&q=PreserveSig
引用返信 編集キー/
■90668 / inTopicNo.8)  Re[6]: WinAPIからの値の受け取り方について
□投稿者/ 魔界の仮面弁士 (2139回)-(2019/04/03(Wed) 12:36:45)
No90667 (魔界の仮面弁士) に追記
> まだ読んでいないので、今回の内容が含まれているかどうかは未確認ですが、

[Appendix A. System.Runtime.InteropServices Reference]
 + [The DllImportAttribute Pseudo-Custom Attribute]
  + [PresereSig]


断片的な斜め読みですが、上記の項によれば、
  PreserveSig が false の場合には、
  .NET シグネチャの戻り値の型が [out, retval] パラメータであると見做され、
  アンマネージシグネチャの戻り値は HRESULT となり、その HRESULT には
  必要に応じて適切な CLR 例外が返される
と書かれているように読み取りました。


実際に PreserveSig によって変換される HRESULT と例外クラスの対応表は
[Appendix C. HRESULT to .NET Exception Transfomations] の項。
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ