アクティブXDLLをC++から呼ぶには?


ソリティア  2004-03-30 13:20:15  No: 112560  IP: [192.*.*.*]

アクティブXでOO4Oを使ってDBよりデータを取得します。
そのアクティブXで取得したデータをC++で使用したいのですが
取得データを文字列の配列やLONGの配列に入れて渡したいのです。

一応呼び出し自体はLONG変数のみならできたのですが
配列となるとうまくいきません。

どのようなコードを書けばいいのでしょうか?

編集 削除
36NET  2004-03-30 16:15:30  No: 112561  IP: [192.*.*.*]

以前自分が参考にしたページですよろしかったらどうぞ

http://homepage2.nifty.com/DSS/VCPP/DLL/dllvcstr.htm

編集 削除
ソリティア  2004-03-30 17:43:37  No: 112562  IP: [192.*.*.*]

文字列ができたとしても配列と関係あるんでしょうか?
MSDNも理解できないくらい頭が悪いのでいまいち理解できません。

編集 削除
特攻隊長まるるう  2004-03-31 13:03:04  No: 112563  IP: [192.*.*.*]

Cでは文字列を Char 型の配列と考えます。
文字列の配列ならポインタのポインタという表現に
なるでしょうから、ポインタが分からなければ
C側のコードは書けないでしょう。Cのお勉強して
ください。

編集 削除
ソリティア  2004-03-31 13:38:49  No: 112564  IP: [192.*.*.*]

アクティブXはVBで作ってるんですが
ポインタのない言語にポインタを渡してもいいんですか?

実際にポインタのポインタで文字列の配列を渡すサンプルソースとかないでしょうか?

編集 削除
Dental  2004-03-31 14:02:06  No: 112565  IP: [192.*.*.*]

C++では、CHAR配列を文字列として扱うことが多いですけど、
それは「文字の配列」であって、「文字列の配列」とは違うのでは。

VBの文字列配列をそのままDLL側に渡したい(あるいは受け取りたい)なら、SAFEARRAYを使ってください。C++側では、SafeArray系APIを使ってその配列を操作できます。
あるメソッドの引数が、DLL側で、LPSAFEARRAY FAR * として宣言されている場合、VBからは ByRef x() As String のように見えます。

http://www.microsoft.com/japan/msdn/vs_previous/vbasic/docs/dll/#8

編集 削除
ソリティア  2004-03-31 15:53:27  No: 112566  IP: [192.*.*.*]

作ってみましたがうまくいきません。
アクティブXDLL(VB6.0)
////////////////////////////////////////////////////
Public Function test(ByRef Data() As String) As Long
Data(0) = "あ"
Data(1) = "い"
Data(2) = "う"
Data(3) = "え"
Data(4) = "お"
test = 1
End Function
/////////////////////////////////////////////////////
呼び出しVC6.0

// C++から COMコンポーネントにアクセスする
// (C)Yajima, Satoshi

#include "stdio.h"
#include "windows.h"
#include "ATLBASE.H"
#include "objbase.h"
//#include <tchar.h>


//project1.dllを元に、必要な定義ファイルを生成する
#import "Project1.dll" no_namespace named_guids raw_interfaces_only

// メイン処理
void main(){
  USES_CONVERSION;

    long z;     //プロシージャの引数、戻り値用
    HRESULT hResult;  // COM 戻り値用変数
    _DBClass* pClass1; // DLLのオブジェクト名
//  BYTE  *  k[100];
  BSTR * BstrArray[12150];
  VARIANT data;
  SAFEARRAY * psa ;
  SAFEARRAYBOUND  rgb[1];
  rgb[0].cElements = 12150;            // 作成するSAFEARRAYの要素数 = 32
  rgb[0].lLbound = 0;                // 作成するSAFEARRAYの添字の下限値 = 0
  ZeroMemory(&data,sizeof(data));
  // COMの初期化
    ::CoInitialize(0);
    // インスタンスの作成(CLSIDとIIDを指定して、ポインタ pClass0 を取得)
    // [VB] Set obj = New Project1.Class1
    hResult = ::CoCreateInstance((REFCLSID) CLSID_DBClass, 0, CLSCTX_INPROC_SERVER,
                                 (REFIID)   IID__DBClass, (LPVOID*)&pClass1);
    if( ! SUCCEEDED(hResult) ) {
        printf("ERROR occured = %08x\n", hResult);
  }
  psa = SafeArrayCreate(VT_BSTR,1,rgb);      // SAFEARRAY作成
  if(psa == NULL)                  // SAFEARRAY作成結果判定
  {
    printf("SAFEARRAY作成失敗\n");
    return ;
  }
  // 作成したSAFEARRAYのポインタ取得及びロック
  SafeArrayAccessData(psa,(void**)&BstrArray);
  pClass1->test(&psa,&z);  
  printf("%s\n",OLE2T(*BstrArray[0]));
  printf("%s\n",OLE2T(*BstrArray[1]));
    // 終了処理
    pClass1->Release();
SafeArrayUnaccessData(psa);            // SAFEARRAYアンロック
    // COMの終了処理
    ::CoUninitialize();
}

編集 削除
ソリティア  2004-03-31 16:02:12  No: 112567  IP: [192.*.*.*]

すいません。書き込み途中に送信してしまいました。
管理人さんよかったら消してください。
即席でパクリもののソースに手を加えたんですが
まだあまり理解していないので何か勘違いしているかもしれません。
配列の一番目だけがうまくいきます。
2番目以降は取得でうまくいってないみたいですが原因はどこにあるでしょうか?
それともソースそのものが何か勘違いしてるでしょうか?

編集 削除
36NET  2004-03-31 17:00:31  No: 112568  IP: [192.*.*.*]

文字配列ならこんな感じでいかがでしょうか?
これも即席でパクリもののソースに手を加えたものです...

== VB ========================
Option Explicit

Public Function test_sub(strBuf As String) As Long
    strBuf = "てすと"
    test_sub = 0
End Function

== VC ========================
#include <stdio.h>
#include <atlbase.h>

#import "Project1.dll"
using namespace Project1;

void main()
{
  USES_CONVERSION;
  if (SUCCEEDED(::CoInitialize(NULL)))
  {
    {
      _Class1Ptr pClass1(__uuidof(Class1));
      BSTR bbb = 0;
      printf("%d\n", pClass1->test_sub(&bbb));
      LPTSTR paaa = OLE2T(bbb);
    }
    ::CoUninitialize();
  }
}

編集 削除
ソリティア  2004-03-31 17:47:15  No: 112569  IP: [192.*.*.*]

文字列は私もできたので、せめてバイト配列を渡すコードを書きたいのですが
どうにか成らないでしょうか?

編集 削除
36NET  2004-04-01 13:53:36  No: 112570  IP: [192.*.*.*]

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/office97/html/SFEA1.asp
のAllocating Arraysを参考にしました

取得は出来るというレベルで本当にこれで良いと言う保証は出来ませんが...

== VB ========================
Option Explicit

Public Function test_sub2(ByRef Data() As String) As Long
Data(0) = "ああ"
Data(1) = "い"
Data(2) = "う"
Data(3) = "え"
Data(4) = "お"
test_sub2 = 1
End Function

== VC ========================
#include <stdio.h>
#include <atlbase.h>

#import "Project1.dll"
using namespace Project1;

void main()
{
  USES_CONVERSION;
  if (SUCCEEDED(::CoInitialize(NULL)))
  {
    {
      _Class1Ptr pClass1(__uuidof(Class1));

      unsigned long l;
      BSTR bstr;
      LPSAFEARRAY psa;
      SAFEARRAYBOUND sa;

      sa.lLbound = 0;
      sa.cElements = 10;

      psa = SafeArrayCreate(VT_BSTR, 1, &sa); 
      printf("%d\n", pClass1->test_sub2(&psa));
      for (l = sa.lLbound; l < sa.cElements; l++){
        if (FAILED(SafeArrayGetElement(psa, (long *)&l, &bstr)))
          return;
        printf("%s\n", OLE2T(bstr));
      }
    }
    ::CoUninitialize();
  }
}

編集 削除
36NET  2004-04-01 14:14:14  No: 112571  IP: [192.*.*.*]

ごめんなさい嘘ついしゃいました。
参照はAllocating ArraysではなくUsing Arrays of Stringsの間違いです。

編集 削除
ソリティア  2004-04-01 16:43:24  No: 112572  IP: [192.*.*.*]

非常に苦労しましたがまぐれで?できました。
誰か意味がわかる人がいたら教えてください。

VB側

Public Type tagKDATA
    name As String
End Type
Public Type tagDATA
    data(9) As tagKDATA
End Type

Public Function test(ByRef data As tagDATA) As Long
data.data(0).name = "あいうえお"
data.data(1).name = "かきくけこ"
data.data(2).name = "さしすせそ"
data.data(3).name = "たちつてと"
data.data(4).name = "なにぬねの"
data.data(5).name = "はひふへほ"
test = 1
End Function


VC
#include "stdio.h"
#include "windows.h"
#include "ATLBASE.H"
#include "objbase.h"
#import "Project1.dll" no_namespace named_guids raw_interfaces_only

void main(){
  USES_CONVERSION;
    long z; 
    HRESULT hResult;
    _Class1* pClass1;
  tagDATA *  BstrArray;
  LPSAFEARRAY psa ;
  SAFEARRAYBOUND  rgb[1];
  rgb[0].cElements = 10;
  rgb[0].lLbound = 0;
    ::CoInitialize(0);
    hResult = ::CoCreateInstance((REFCLSID) CLSID_Class1, 0, CLSCTX_INPROC_SERVER,
                                 (REFIID)   IID__Class1, (LPVOID*)&pClass1);
    if( ! SUCCEEDED(hResult) ) {
        printf("ERROR occured = %08x\n", hResult);
    return ;
  }
  psa = SafeArrayCreate(VT_BSTR,1,rgb);
  if(psa == NULL)
  {
    printf("SAFEARRAY作成失敗\n");
    return ;
  }
  SafeArrayAccessData(psa,(void**)&BstrArray);
  pClass1->test(BstrArray,&z);  
  printf("%s\n",OLE2T(BstrArray->data[0].name));
  printf("%s\n",OLE2T(BstrArray->data[1].name));
  printf("%s\n",OLE2T(BstrArray->data[2].name));
  printf("%s\n",OLE2T(BstrArray->data[3].name));

    pClass1->Release();
  SafeArrayUnaccessData(psa);
    ::CoUninitialize();
}


VC側でビルドするとtlhという拡張子のファイルができます。
そのファイルの中にDLL側の引数の型の宣言?が出てきます。
その型はVC側で宣言なしに何故か使用できます。クラスビューには出てきません。
その型を使用すると簡単にできます。
構造体にして引数を配列にしなければうまくいきました。
構造体の中に配列を作ります。

編集 削除
ソリティア  2004-04-01 18:46:06  No: 112573  IP: [192.*.*.*]

神!シェンロンですね!!
36NETさんありがとうございました。
またやばくなったら教えてください。

編集 削除