- PR -

LPCTSTRデータ型をVC#で受け取る

投稿者投稿内容
Izumi, Y.
ベテラン
会議室デビュー日: 2002/03/19
投稿数: 77
お住まい・勤務地: 東京
投稿日時: 2004-05-22 00:22
引用:

ですが、LPCTSTR は OLE オートメーション互換型では無いため、本来は回避するべき設計です。


なるほど、それでデフォルトでは BSTR にマーシャリングされるのか。
#COM 自体には疎いもので。
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2004-05-22 09:56
引用:

ただし、動作環境を NT 系(Windows NT4/2000/XP/2003)に限定すれば LPCTSTR は LPCWSTR と同値になりますので、



それは違います。
文字列を扱う API の規則について不正確に理解されていると思います。

Win32 API で文字列を扱うものの多くは、シングルバイト文字とダブルバイト文字を扱うものの2種類が用意されています。

つまり、ヘルプなどで Hoge(LPCTSTR str) という API が説明されていた場合、実際には HogeA() と HogeW() という DLL 関数が存在します。

Hoge() と LPCTSTR は SDK のヘッダ中で、大雑把に言って

コード:
#ifdef _UNICODE
#define Hoge HogeW
#else
#define Hoge HogeA
#endif

#ifdef _UNICODE
typedef const wchar_t* LPCTSTR;
#else
typedef const char* LPTSTR;
#endif



のようになっています。

どちらの形式を使うかは、VC++ では、ビルド時のオプション指定で決定されます。
(デフォルトはシングルバイト文字を使用)

これらの API を .NET で扱う場合、属性で API 名を明示することが出来ます。

コード:
// HogeA() が呼び出される
[DllImport("hoge.dll", EntryPoint="HogeA")]
static extern void Hoge(string str);



API 名を省略した場合、9x 系OSでは xxxA() 系の DLL 関数が、NT 系OSでは xxxW() 系の DLL 関数が自動的に選択されます。

コード:
// 9x 系OSでは HogeA() が、NT 系OSでは HogeW() が呼び出される
[DllImport("hoge.dll")]
static extern void Hoge(string str);



長くなりましたが、以上の規則と、coclass のメソッド引数のマーシャリングの間には何も関係がありません。

coclass のメソッド引数の形式はその設計時に決定され、ビルド時に固定されます。
これが実行時に変化することはありません。

オートメーション互換型を外れているので、決して奨励しませんが、ある coclass のメソッドが

コード:
HRESULT Hoge(LPCTSTR lpStr);



という形式であったなら、この coclass をビルドした時に _UNICODE マクロが定義されていたかどうかによってのみ、引数型が確定します。

通常、_UNICODE マクロが無効な状態でビルドするでしょうから、上記のメソッドは

コード:
HRESULT Hoge(LPCSTR lpStr);
HRESULT Hoge(const char* lpStr);



と等価となります。

なので、件のメソッド引数のマーシャリング規則を属性で指定するなら、

コード:
public void Foobar([MarshalAs(UnmanagedType.LPStr)] string str)



が妥当です。

_________________
// 渋木宏明 (Hiroaki SHIBUKI)
// http://hidori.jp/
// Microsoft MVP for Visual C#
//
// @IT会議室 RSS 配信中: http://hidori.jp/rss/atmarkIT/
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2004-05-22 10:10
余談になってしまいますが、

コード:
class NgCom
{
  public unsafe void Hoge(byte* str)
  {
  }

  public void Foobar([MarshalAs(UnmanagedType.LPWStr)] string str)
  {
  }
}



というクラスをクラスライブラリとしてビルドし、tlbexp して OLE/COM Viewer で確認をしてみたところ、
コード:
[
(割愛)
]
coclass NgCom {
  [default] interface _NgCom;
  interface _Object;
};

[
(割愛)
]
interface _NgCom : IDispatch {
};



となり、NgCom には1つもメソッドがエクスポートされていませんでした。

.NET のクラスを単純に coclass としてエクスポートした場合、ディスパッチオブジェクトになりますが、オートメーション非互換の型が含まれているメソッドは自動的に除外されてしまうようです。
(警告も出ませんでした)

そこで、

コード:
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface INgCom
{
  unsafe void Hoge(byte* str);
  void Foobar([MarshalAs(UnmanagedType.LPWStr)] string str);
}

public class NgCom : INgCom
{
  unsafe public void Hoge(byte* str)
  {
  }

  public void Foobar([MarshalAs(UnmanagedType.LPStr)] string str)
  {
  }
}



としてみたところ

コード:
[
(割愛)
]
interface INgCom : IUnknown {
  HRESULT _stdcall Hoge([in, out] unsigned char* str);
  HRESULT _stdcall Foobar([in] LPSTR str);
};

[
(割愛)
]
coclass NgCom {
  [default] interface _NgCom;
  interface _Object;
  interface INgCom;
};

[
(割愛)
]
interface _NgCom : IDispatch {
};



という結果を得ました。

カスタムインターフェースになら、オートメーション非互換の型が含まれていてもOKなようです。

_________________
// 渋木宏明 (Hiroaki SHIBUKI)
// http://hidori.jp/
// Microsoft MVP for Visual C#
//
// @IT会議室 RSS 配信中: http://hidori.jp/rss/atmarkIT/
ino
会議室デビュー日: 2004/05/11
投稿数: 13
投稿日時: 2004-05-27 10:27
返信がおくれてすみませんでした。

引用;---------------------------------------------------------------------------

ですが、LPCTSTR は OLE オートメーション互換型では無いため、本来は回避するべ
き設計です。以前にも「BSTR を使ってみてはどうか?」というコメントがあったよう
に、仕様の見直しをするべきです。

それでも、どーしても仕様の変更が難しい場合は
unsafe public void Hoge(byte* str);

とでもすればよいんじゃないでしょうか。
  ----------------------------------------------------------------------------


EXEの方を変更ができないので、なんとかLPCSTRを受け取りたかったので、
ご指導いただいた方法などためしてみたのですが、
やはり、LPCSTRを受け取るのは難しいようなので、
VC++を間に入れる方法で進めようと思います。

色々な情報ありがとうございました。
渋木宏明(ひどり)
ぬし
会議室デビュー日: 2004/01/14
投稿数: 1155
お住まい・勤務地: 東京
投稿日時: 2004-05-27 16:37
引用:

EXEの方を変更ができないので、なんとかLPCSTRを受け取りたかったので、
ご指導いただいた方法などためしてみたのですが、
やはり、LPCSTRを受け取るのは難しいようなので、
VC++を間に入れる方法で進めようと思います。



.exe からの呼び出しがカスタムインターフェース経由なら対応できたはずです。
ディスパッチインターフェース経由で呼び出されていたのですか?

_________________
// 渋木宏明 (Hiroaki SHIBUKI)
// http://hidori.jp/
// Microsoft MVP for Visual C#
//
// @IT会議室 RSS 配信中: http://hidori.jp/rss/atmarkIT/

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