ATL COM サーバとクライアントのデータの受け渡し

環境:XPSP3/VS2005
COM の言語:VC++
COM:インプロセスサーバ/デュアルインターフェイス
コンポーネント名:TestComp
インターフェイス名:TestIF
メソッド名:TestMethod


クライアントでの COM の初期化等


VC++ クライアント

  1. #import "progid:TestComp.TestIF" no_namespace named_guids
  2. ...
  3. CoInitialize(NULL);
  4. CComPtr<ITestIF> pIF;
  5. pIF.CoCreateInstance(CLSID_TestIF);
  6. ...
  7. // 終了時
  8. CoUninitialize();
  9.  

VB クライアント

  1. Dim obj As Object = CreateObject("TestComp.TestIF")
  2.  

VC# クライアント

  1. using System.Reflection;
  2. ...
  3. Type t = Type.GetTypeFromProgID("TestComp.TestIF");
  4. object target = t.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
  5.  


BYTE 型 その1

クライアントからデータを渡す

COM サーバ

IDL メソッド宣言

  1. [id(1), helpstring("メソッド TestMethod")] HRESULT TestMethod([in] BYTE prm);
  2.  

クラスメンバ宣言

  1. STDMETHOD(TestMethod)(BYTE prm);
  2.  

メンバ関数実装

  1. STDMETHODIMP CTestIF::TestMethod(BYTE prm)
  2. {
  3. TCHAR str[16];
  4. _stprintf_s(str, _T("%d"), prm);
  5. ::MessageBox(NULL, (LPCWSTR)str, _T(""), MB_OK);
  6. return S_OK;
  7. }
  8.  

VC++ クライアント

  1. HRESULT hr;
  2. BYTE var = 0;
  3. hr = pIF->TestMethod(var);
  4. if (FAILED(hr)) {
  5. // エラー
  6. }
  7.  

VB クライアント

  1. Dim var As Byte = 0
  2. obj.TestMethod(var)
  3.  

VC# クライアント

  1. byte var = 0;
  2. object[] args = { var }; // ボックス化
  3. t.InvokeMember("TestMethod", BindingFlags.InvokeMethod, null, target, args);
  4.  


BYTE 型 その2

サーバからデータをポインタ渡し

COM サーバ

IDL メソッド宣言

  1. [id(1), helpstring("メソッド TestMethod")] HRESULT TestMethod([out] BYTE* prm);
  2.  

クラスメンバ宣言

  1. STDMETHOD(TestMethod)(BYTE* prm);
  2.  

メンバ関数実装

  1. STDMETHODIMP CTestIF::TestMethod(BYTE* prm)
  2. {
  3. *prm = 123;
  4. return S_OK;
  5. }
  6.  

VC++ クライアント

  1. HRESULT hr;
  2. BYTE var = 0;
  3. hr = pIF->TestMethod(&var);
  4. if (FAILED(hr)) {
  5. // エラー
  6. }
  7. _tprintf_s(_T("%d\n"), var);
  8.  

VB クライアント

  1. Dim var As Byte
  2. obj.TestMethod(var)
  3. Console.WriteLine(var)
  4.  

VC# クライアント

  1. byte var = 0;
  2. object[] args = { var }; // ボックス化
  3. ParameterModifier p = new ParameterModifier(1);
  4. p[0] = true; // 1番目のパラメータの変更許可
  5. ParameterModifier[] mods = { p };
  6. t.InvokeMember("TestMethod", BindingFlags.InvokeMethod, null, target, args, mods, null, null);
  7. var = (byte)args[0]; // ボックス化解除
  8. Console.WriteLine(var);
  9.  


BYTE 型 その3

サーバからデータを戻り値として返す

COM サーバ

IDL メソッド宣言

  1. [id(1), helpstring("メソッド TestMethod")] HRESULT TestMethod([out,retval] BYTE* prm);
  2.  

クラスメンバ宣言

  1. STDMETHOD(TestMethod)(BYTE* prm);
  2.  

メンバ関数実装

  1. STDMETHODIMP CTestIF::TestMethod(BYTE* prm)
  2. {
  3. *prm = 123;
  4. return S_OK;
  5. }
  6.  

VC++ クライアント

  1. BYTE var = pIF->TestMethod();
  2. _tprintf_s(_T("%d\n"), var);
  3.  

VB クライアント

  1. Dim var As Byte = obj.TestMethod()
  2. Console.WriteLine(var)
  3.  

VC# クライアント

  1. byte var = 0;
  2. var = (byte)t.InvokeMember("TestMethod", BindingFlags.InvokeMethod, null, target, null);
  3. Console.WriteLine(var);
  4.  


BSTR 型 その1

クライアントからデータを渡す

COM サーバ

IDL メソッド宣言

  1. [id(1), helpstring("メソッド TestMethod")] HRESULT TestMethod([in] BSTR prm);
  2.  

クラスメンバ宣言

  1. STDMETHOD(TestMethod)(BYTE prm);
  2.  

メンバ関数実装

  1. #include "comutil.h" // _bstr_t
  2. #pragma comment(lib, "comsuppw.lib") // _bstr_t
  3. STDMETHODIMP CTestIF::TestMethod(BSTR prm)
  4. {
  5. _bstr_t str(prm);
  6. ::MessageBox(NULL, (LPCTSTR)str, _T(""), MB_OK);
  7. return S_OK;
  8. }
  9.  

VC++ クライアント

  1. HRESULT hr;
  2. _bstr_t str = _T("test message");
  3. BSTR bstr = str;
  4. hr = pIF->TestMethod(bstr);
  5. if (FAILED(hr)) {
  6. //エラー
  7. }
  8.  

VB クライアント

  1. Dim str As String = "test message"
  2. obj.TestMethod(str)
  3.  

VC# クライアント

  1. string str = "test message";
  2. object[] args = { str }; // ボックス化
  3. t.InvokeMember("TestMethod", BindingFlags.InvokeMethod, null, target, args);
  4.  


BSTR 型 その2

サーバからデータをポインタ渡し

COM サーバ

IDL メソッド宣言

  1. [id(1), helpstring("メソッド TestMethod")] HRESULT TestMethod([out] BSTR* prm);
  2.  

クラスメンバ宣言

  1. STDMETHOD(TestMethod)(BSTR* prm);
  2.  

メンバ関数実装

  1. STDMETHODIMP CTestIF::TestMethod(BSTR* prm)
  2. {
  3. CComBSTR bstr(L"test message");
  4. *prm = bstr;
  5. return S_OK;
  6. }
  7.  

VC++ クライアント

  1. HRESULT hr;
  2. BSTR bstr;
  3. hr = pIF->TestMethod(&bstr);
  4. if (FAILED(hr)) {
  5. // エラー
  6. }
  7. _bstr_t str(bstr);
  8. _tprintf_s(_T("%s\n"), (LPCTSTR)str);
  9.  

VB クライアント

  1. Dim str As String = ""
  2. obj.TestMethod(str)
  3. Console.WriteLine(str)
  4.  

VC# クライアント

  1. string str = "";
  2. object[] args = { str }; // ボックス化
  3. ParameterModifier p = new ParameterModifier(1);
  4. p[0] = true; // 1番目のパラメータの変更許可
  5. ParameterModifier[] mods = { p };
  6. t.InvokeMember("TestMethod", BindingFlags.InvokeMethod, null, target, args, mods, null, null);
  7. str = (string)args[0]; // ボックス化解除
  8. Console.WriteLine(str);
  9.  


BSTR 型 その3

サーバからデータを戻り値として返す

COM サーバ

IDL メソッド宣言

  1. [id(1), helpstring("メソッド TestMethod")] HRESULT TestMethod([out,retval] BSTR* prm);
  2.  

クラスメンバ宣言

  1. STDMETHOD(TestMethod)(BSTR* prm);
  2.  

メンバ関数実装

  1. STDMETHODIMP CTestIF::TestMethod(BSTR* prm)
  2. {
  3. CComBSTR bstr(L"test message");
  4. *prm = bstr;
  5. return S_OK;
  6. }
  7.  

VC++ クライアント

  1. BSTR bstr = pIF->TestMethod();
  2. _bstr_t str(bstr);
  3. _tprintf_s(_T("%s\n"), (LPCTSTR)str);
  4.  

VB クライアント

  1. Dim str As String = obj.TestMethod()
  2. Console.WriteLine(str)
  3.  

VC# クライアント

  1. string str = (string)t.InvokeMember("TestMethod", BindingFlags.InvokeMethod, null, target, null);
  2. Console.WriteLine(str);
  3.  


配列のやり取り その1

クライアントからデータを渡す
SAFEARRAY で 1 バイトサイズの配列を構築したものを VARIANT 型にラップしてパラメータとする。
(VARIANT(SAFEARRAY(BYTE[x])) な感じ)

COM サーバ

IDL メソッド宣言

  1. [id(1), helpstring("メソッド TestMethod")] HRESULT TestMethod([in] VARIANT prm);
  2.  

クラスメンバ宣言

  1. STDMETHOD(TestMethod)(VARIANT prm);
  2.  

メンバ関数実装 その1

CComSafeArray 型を使う
  1. #include "atlsafe.h"
  2. STDMETHODIMP CTestIF::TestMethod(VARIANT prm)
  3. {
  4. HRESULT hr;
  5. CComSafeArray<BYTE> sa;
  6.  
  7. if ((V_VT(prm) & (VT_ARRAY | VT_UI1)) != (VT_ARRAY | VT_UI1)) {
  8. // BYTE型配列ではないのでエラー
  9. }
  10.  
  11. // SAFEARRAY アタッチ
  12. if (V_VT(&prm) & VT_BYREF) {
  13. hr = sa.Attach(*V_ARRAYREF(&prm));
  14. if (FAILED(hr)) {
  15. // Attach エラー
  16. }
  17. } else {
  18. hr = sa.Attach(V_ARRAY(&prm));
  19. if (FAILED(hr)) {
  20. // Attach エラー
  21. }
  22. }
  23.  
  24. // 配列にアクセスする処理
  25. // 例えば…
  26. ULONG size = sa.GetCount();
  27. BYTE *pbuf = (BYTE*)CoTaskMemAlloc(size);
  28. if (pbuf == NULL) {
  29. // CoTaskMemAlloc エラー
  30. }
  31. // コピーとか…
  32. for (ULONG i=0; i<size; i++) {
  33. *(pbuf+i) = sa.GetAt(i);
  34. }
  35.  
  36. // メモリ解放
  37. CoTaskMemFree(pbuf);
  38.  
  39. // SAFEARRAY デタッチ
  40. sa.Detach();
  41.  
  42. return S_OK;
  43. }
  44.  

メンバ関数実装 その2

SAFEARRAY 型を使う
  1. STDMETHODIMP CTestIF::TestMethod(VARIANT prm)
  2. {
  3. HRESULT hr;
  4. SAFEARRAY *psa;
  5. BYTE *p;
  6.  
  7. if ((V_VT(prm) & (VT_ARRAY | VT_UI1)) != (VT_ARRAY | VT_UI1)) {
  8. // BYTE型配列ではないのでエラー
  9. }
  10.  
  11. if (V_VT(&prm) & VT_BYREF) {
  12. psa = *V_ARRAYREF(&prm);
  13. } else {
  14. psa = V_ARRAY(&prm);
  15. }
  16.  
  17. // ロック処理
  18. hr = SafeArrayAccessData(psa, (void**)&p);
  19. if (FAILED(hr)) {
  20. // SafeArrayAccessData エラー
  21. }
  22.  
  23. // 配列にアクセスする処理
  24. // 例えば…
  25. LONG minidx, maxidx, size;
  26. BYTE *pbuf;
  27.  
  28. // 配列のサイズを取得
  29. hr = SafeArrayGetLBound(psa, 1, &minidx);
  30. if (FAILED(hr)) {
  31. // SafeArrayGetLBound エラー
  32. }
  33. hr = SafeArrayGetUBound(psa, 1, &maxidx);
  34. if (FAILED(hr)) {
  35. // SafeArrayGetUBound エラー
  36. }
  37. size = maxidx - minidx + 1;
  38.  
  39. pbuf = (BYTE*)CoTaskMemAlloc(size);
  40. if (pbuf == NULL) {
  41. // CoTaskMemAlloc エラー
  42. }
  43. // コピーとか…
  44. memcpy(pbuf, p, idx+1);
  45.  
  46. // アンロック処理
  47. hr = SafeArrayUnaccessData(psa);
  48. if (FAILED(hr)) {
  49. // SafeArrayUnaccessData エラー
  50. }
  51.  
  52. // メモリ解放
  53. CoTaskMemFree(pbuf);
  54.  
  55. return S_OK;
  56. }
  57.  

VC++ クライアント その1

CComVariant, CComSafeArray を使う
  1. #include "atlsafe.h"
  2. ...
  3. HRESULT hr;
  4. BYTE buf[5] = {1, 2, 3, 4, 5}; // サーバに渡すデータ内容
  5. CComSafeArray<BYTE> sa(sizeof(buf));
  6. CComVariant var;
  7.  
  8. // CComSafeArray 準備
  9. for (int i=0; i<sizeof(buf); i++) {
  10. hr = sa.SetAt(i, buf[i]);
  11. if (FAILED(hr)) {
  12. // SetAt error.
  13. }
  14. }
  15.  
  16. // CComVariant 準備
  17. V_VT(&var) = VT_UI1 | VT_ARRAY | VT_BYREF;
  18. V_ARRAYREF(&var) = sa.GetSafeArrayPtr();
  19.  
  20. // メソッドコール
  21. pIF->TestMethod(var);
  22.  

VC++ クライアント その2

VARIANT, SAFEARRAY 型を使う
  1. HRESULT hr;
  2. BYTE buf[5] = {1, 2, 3, 4, 5}; // サーバに渡すデータ内容
  3. SAFEARRAYBOUND bnd;
  4. SAFEARRAY *psa;
  5. VARIANT var;
  6. BYTE *p;
  7.  
  8. // SAFEARRAY 準備
  9. bnd.lLbound = 0; // 配列のインデックスは 0 オリジン
  10. bnd.cElements = sizeof(buf); // 要素数
  11. psa = SafeArrayCreate(VT_UI1, 1, &bnd);
  12. if (psa == NULL) {
  13. // SafeArrayCreate エラー
  14. }
  15.  
  16. // ロック処理
  17. hr = SafeArrayAccessData(psa, (void**)&p);
  18. if (FAILED(hr)) {
  19. // SafeArrayAccessData エラー
  20. }
  21.  
  22. // 配列にアクセスする処理
  23. memcpy(p, buf, sizeof(buf));
  24.  
  25. // アンロック処理
  26. hr = SafeArrayUnaccessData(psa);
  27. if (FAILED(hr)) {
  28. // SafeArrayUnaccessData エラー
  29. }
  30.  
  31. // VARIANT 準備
  32. VariantInit(&var);
  33. V_VT(&var) = VT_UI1 | VT_ARRAY | VT_BYREF;
  34. V_ARRAYREF(&var) = &psa; // SAFEARREY を突っ込む
  35.  
  36. // メソッドコール
  37. pIF->TestMethod(var);
  38.  
  39. // SAFEARRAY 解放
  40. hr = SafeArrayDestroy(psa);
  41. if (FAILED(hr)) {
  42. // SafeArrayDestroy エラー
  43. }
  44.  

VB クライアント

  1. Dim buf() As Byte = {1, 2, 3, 4, 5}
  2. obj.TestMethod(buf) ' そのまま突っ込む
  3.  

VC# クライアント

  1. byte[] buf = {1, 2, 3, 4, 5};
  2. object[] args = { buf }; // ボックス化
  3. t.InvokeMember("TestMethod", BindingFlags.InvokeMethod, null, target, args);
  4.  


配列のやり取り その2

サーバからデータをポインタ渡し
SAFEARRAY で 1 バイトサイズの配列を構築したものを VARIANT 型にラップしてパラメータとする。
(VARIANT(SAFEARRAY(BYTE[x])) な感じ)

COM サーバ

IDL メソッド宣言

  1. [id(1), helpstring("メソッド TestMethod")] HRESULT TestMethod([out] VARIANT* prm);
  2.  

クラスメンバ宣言

  1. STDMETHOD(TestMethod)(VARIANT* prm);
  2.  

メンバ関数実装 その1

CComSafeArray 型を使う
  1. STDMETHODIMP CTestIF::TestMethod(VARIANT* prm)
  2. {
  3. HRESULT hr;
  4. BYTE buf[5] = {6, 7, 8, 9, 10}; // クライアントに渡すデータ内容
  5. CComSafeArray<BYTE> sa(sizeof(buf));
  6.  
  7. if ((V_VT(prm) & (VT_ARRAY | VT_UI1)) != (VT_ARRAY | VT_UI1)) {
  8. // BYTE型配列ではないのでエラー
  9. }
  10.  
  11. // CComSafeArray 準備
  12. for (int i=0; i<sizeof(buf); i++) {
  13. hr = sa.SetAt(i, buf[i]);
  14. if (FAILED(hr)) {
  15. // SetAt エラー
  16. }
  17. }
  18.  
  19. // クライアントに渡す
  20. if (V_VT(prm) & VT_BYREF) {
  21. hr = sa.CopyTo(V_ARRAYREF(prm));
  22. if (FAILED(hr)) {
  23. // CopyTo エラー
  24. }
  25. } else {
  26. hr = sa.CopyTo(&V_ARRAY(prm)); // "&V_ARRAY(prm)" で良いのか不明
  27. if (FAILED(hr)) {
  28. // CopyTo エラー
  29. }
  30. }
  31.  
  32. return S_OK;
  33. }
  34.  

メンバ関数実装 その2

SAFEARRAY 型を使う
  1. STDMETHODIMP CTestIF::TestMethod(VARIANT* prm)
  2. {
  3. HRESULT hr;
  4. BYTE buf[5] = {6, 7, 8, 9, 10}; // クライアントに渡すデータ内容
  5. SAFEARRAYBOUND bnd;
  6. SAFEARRAY *psa;
  7. BYTE *p;
  8.  
  9. if ((V_VT(prm) & (VT_ARRAY | VT_UI1)) != (VT_ARRAY | VT_UI1)) {
  10. // BYTE型配列ではないのでエラー
  11. }
  12.  
  13. // SAFEARRAY 準備
  14. bnd.lLbound = 0; // 配列のインデックスは 0 オリジン
  15. bnd.cElements = sizeof(buf); // 要素数
  16. psa = SafeArrayCreate(VT_UI1, 1, &bnd);
  17. if (psa == NULL) {
  18. // SafeArrayCreate エラー
  19. }
  20.  
  21. // ロック処理
  22. hr = SafeArrayAccessData(psa, (void**)&p);
  23. if (FAILED(hr)) {
  24. // SafeArrayAccessData エラー
  25. }
  26.  
  27. // 配列にアクセスする処理
  28. memcpy(p, buf, sizeof(buf));
  29.  
  30. // アンロック処理
  31. hr = SafeArrayUnaccessData(psa);
  32. if (FAILED(hr)) {
  33. // SafeArrayUnaccessData エラー
  34. }
  35.  
  36. // クライアントに渡す
  37. if (V_VT(prm) & VT_BYREF) {
  38. hr = SafeArrayCopy(psa, V_ARRAYREF(prm));
  39. if (FAILED(hr)) {
  40. // SafeArrayCopy エラー
  41. }
  42. } else {
  43. hr = SafeArrayCopy(psa, &V_ARRAY(prm)); // "&V_ARRAY(prm)" で良いのか不明
  44. if (FAILED(hr)) {
  45. // SafeArrayCopy エラー
  46. }
  47. }
  48.  
  49. // SAFEARRAY 解放
  50. hr = SafeArrayDestroy(psa);
  51. if (FAILED(hr)) {
  52. // SafeArrayDestroy エラー
  53. }
  54.  
  55. return S_OK;
  56. }
  57.  

VC++ クライアント その1

CComVariant, CComSafeArray を使う
  1. #include "atlsafe.h"
  2. ...
  3. HRESULT hr;
  4. CComSafeArray<BYTE> sa;
  5. CComVariant var;
  6.  
  7. // CComVariant 準備
  8. V_VT(&var) = VT_UI1 | VT_ARRAY | VT_BYREF;
  9. V_ARRAYREF(&var) = sa.GetSafeArrayPtr();
  10.  
  11. // メソッドコール
  12. pIF->TestMethod(&var);
  13.  
  14. // データ確認
  15. for (ULONG i=0, i<sa.GetCount(); i++) {
  16. _tprintf_s(_T("%d "), sa.GetAt(i));
  17. }
  18. _tprintf_s(_T("\n"));
  19.  
  20. // SAFEARRAY デタッチ
  21. sa.Detach();
  22.  

VC++ クライアント その2

VARIANT, SAFEARRAY 型を使う
  1. HRESULT hr;
  2. SAFEARRAYBOUND bnd;
  3. SAFEARRAY *psa;
  4. VARIANT var;
  5. BYTE *p;
  6.  
  7. // SAFEARRAY 準備
  8. bnd.lLbound = 0; // 配列のインデックスは 0 オリジン
  9. bnd.cElements = 0; // 要素数(空)
  10. psa = SafeArrayCreate(VT_UI1, 1, &bnd);
  11. if (psa == NULL) {
  12. // SafeArrayCreate エラー
  13. }
  14.  
  15. // VARIANT 準備
  16. VariantInit(&var);
  17. V_VT(&var) = VT_UI1 | VT_ARRAY | VT_BYREF;
  18. V_ARRAYREF(&var) = &psa; // SAFEARREY を突っ込む
  19.  
  20. // メソッドコール
  21. pIF->TestMethod(&var);
  22.  
  23. // ロック処理
  24. hr = SafeArrayAccessData(psa, (void**)&p);
  25. if (FAILED(hr)) {
  26. // SafeArrayAccessData エラー
  27. }
  28.  
  29. // 配列にアクセスする処理
  30. // 例えば…
  31. LONG minidx, maxidx, size;
  32.  
  33. // 配列のサイズを取得
  34. hr = SafeArrayGetLBound(psa, 1, &minidx);
  35. if (FAILED(hr)) {
  36. // SafeArrayGetLBound エラー
  37. }
  38. hr = SafeArrayGetUBound(psa, 1, &maxidx);
  39. if (FAILED(hr)) {
  40. // SafeArrayGetUBound エラー
  41. }
  42. size = maxidx - minidx + 1;
  43.  
  44. // データ確認
  45. for (LONG i=0; i<size; i++) {
  46. _tprintf_s(_T("%d "), *(p+i));
  47. }
  48. _tprintf_s(_T("\n"));
  49.  
  50. // アンロック処理
  51. hr = SafeArrayUnaccessData(psa);
  52. if (FAILED(hr)) {
  53. // SafeArrayUnaccessData エラー
  54. }
  55.  
  56. // SAFEARRAY 解放
  57. hr = SafeArrayDestroy(psa);
  58. if (FAILED(hr)) {
  59. // SafeArrayDestroy エラー
  60. }
  61.  

VB クライアント

  1. Dim buf(0) As Byte
  2. obj.TestMethod(buf) ' そのまま突っ込む
  3. Console.WriteLine("{0} {1} {2} {3} {4}", buf(0), buf(1), buf(2), buf(3), buf(4))
  4.  

VC# クライアント

  1. byte[] buf = { };
  2. object[] args = { buf }; // ボックス化
  3. ParameterModifier p = new ParameterModifier(1);
  4. p[0] = true; // 1番目のパラメータの変更許可
  5. ParameterModifier[] mods = { p };
  6. t.InvokeMember("TestMethod", BindingFlags.InvokeMethod, null, target, args, mods, null, null);
  7. buf = (byte[])args[0]; // ボックス化解除
  8. Console.WriteLine("{0} {1} {2} {3} {4}", buf[0], buf[1], buf[2], buf[3], buf[4]);
  9.  

タグ:

COM
最終更新:2012年03月19日 10:17
添付ファイル