C++のActiveX DLLでポインタを受け取る

このQ&Aのポイント
  • C++でVB用にポインタをラップするようなクラスを作成し、正しいポインタを受け取る方法を教えてください。
  • ActiveX DLLを使用してVBからByRefで変数を受け取り、そのポインタを保持し、参照先の変数の値を取得したり代入したりする方法を教えてください。
  • 初めてIDLファイルを使用するため、ByRefで正しいポインタを受け取る方法を教えてください。
回答を見る
  • ベストアンサー

C++のActiveX DLLでポインタを受け取る

C++でVB用にポインタをラップするようなクラスを作ろうと思っています。 VBからByRefで変数を受け取って、そのポインタを保持し、 そのポインタの参照先の変数の値を取得したり、代入するメソッドを実装します。 しかし、ポインタを受け取る時点で、正しいポインタが受け取れていないようです。 idlファイルはおよそウィザードに任せていますが、扱うのは初めてなので、 ドのようにすればByRefで正しいポインタを受け取れるのか教えていただけないでしょうか。 --------------------------------------------- VbHelpLibrary.idl ... interface ILongRef : IDispatch{ [propget, id(1), helpstring("プロパティ Ptr")] HRESULT Ptr([out, retval] long *pVal); [propput, id(1), helpstring("プロパティ Ptr")] HRESULT Ptr([in] long* newVal); [propget, id(2), helpstring("プロパティ Value")] HRESULT Value([out, retval] long *pVal); [propput, id(2), helpstring("プロパティ Value")] HRESULT Value([in] long newVal); }; --------------------------------------------- LongRef.cpp STDMETHODIMP CLongRef::get_Ptr(long *pVal){ *pVal = (long)this->m_ptr; return S_OK; } STDMETHODIMP CLongRef::put_Ptr(long *newVal){ this->m_ptr = newVal; //ここでnewValがVarPtr(X)と一致しない //*newVal = 100; //試しにここでこのポインタの先に値を入れてもXは変化しない return S_OK; } STDMETHODIMP CLongRef::get_Value(long *pVal){ *pVal = *m_ptr; return S_OK; } STDMETHODIMP CLongRef::put_Value(long newVal){ *m_ptr = newVal; return S_OK; } --------------------------------------------- Module1.bas Sub Main() Dim X As Long, Y As New LongRef Debug.Print "VarPtr(X) = ", VarPtr(X) Y.Ptr = X Debug.Print "Y.Ptr = ", Y.Ptr --------------------------------------------- 結果 VarPtr(X) = 1308376 Y.Ptr = 1929464

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

>>STDMETHODIMP CLongRef::get_Ptr(long *pVal) >としたのは、long**にするとVBで扱えなくなってしまったからです。 なるほど、試してみましたが確かに VB ではエラーになってしまいました。 プロパティとして考えると違和感がありますが、これはこれで仕方がないかなと思います。 >[id(3), helpstring("メソッド setPtr")] HRESULT setPtr([in] long* newVal); >を作ってみたのですが、結果が同じでした。 私も試してみましたが、意図したものになりましたよ。 .idl ================================================================================== [propget, id(1), helpstring("プロパティ Ptr")] HRESULT Ptr([out, retval] long * pVal); [id(3), helpstring("メソッド SetPtr")] HRESULT SetPtr(long * newVal); .cpp ================================================================================== STDMETHODIMP CLongRef::get_Ptr(long * pVal) { *pVal = (long)m_ptr; return S_OK; } STDMETHODIMP CLongRef::SetPtr(long * newVal) { m_ptr = newVal; return S_OK; } VB ================================================================================== Dim X As Long Dim Y As New LongRef X = 100 Debug.Print "VarPtr(X) = " & VarPtr(X) 'Y.Ptr = X Y.SetPtr X Debug.Print "Y.Ptr = " & Y.Ptr 実行結果 ================================================================================== VarPtr(X) = 1306696 Y.Ptr = 1306696 STDMETHODIMP CLongRef::SetPtr(long * newVal) の中身はどのようになってますか? >idlとcppとhだけだと思っていたのですが、他にも対にしなければならないものがありますか? .idl、.cpp、.h だけで良いと思います。

haporu
質問者

お礼

遅くなってすみません。 >私も試してみましたが、意図したものになりましたよ もういちどクラスからウィザードで作り直したら、意図した結果になりました。 やはり、クラスウィザードが管理している触ってはいけない部分を、いつの間にか変えてしまっていたのかもしれません。 また、Long変数をputしてlong*の値をgetするのも変なので、Ptrプロパティは単にm_ptrをLongとしてラップするプロパティとして、SetPtrを別に設けることにします。 ありがとうございました。

その他の回答 (1)

回答No.1

VB では Integer や Long 等の値型の変数に対して Z = X のようにすると値渡しで代入されます。これは代入先がプロパティの場合でも同じです。 つまり、 Y.Ptr = X とすると、VB 側で X のコピーが作られ、X のコピーが Y.Ptr に渡されます。 STDMETHODIMP CLongRef::put_Ptr(long *newVal) の newVal は [ X のコピーのアドレス] になります。 VB の言語仕様として Integer や Long の参照型変数というものがない以上、プロパティで参照渡しはできないでしょう。 なので、プロパティでなく関数にしなければならないと思います。(いうまでもなく、引数は参照渡しにしなければなりません。) 実行結果が >VarPtr(X) = 1308376 >Y.Ptr = 1929464 のように、値が大きく異なっているのは、 STDMETHODIMP CLongRef::get_Ptr(long **pVal) とすべきところが STDMETHODIMP CLongRef::get_Ptr(long *pVal) となっているためではないかと思います。 ( ただしいコピーのアドレスであれば、もっと近い値になるのあ普通だと思います。) ちなみに、VB は 6.0 ですか?

haporu
質問者

補足

解凍ありがとうございます。 すいません、バージョンを書いていませんでした。 VBもVC++も6.0です。 >STDMETHODIMP CLongRef::get_Ptr(long *pVal) としたのは、long**にするとVBで扱えなくなってしまったからです。 >VB の言語仕様として Integer や Long の参照型変数というものがない以上、プロパティで参照渡しはできないでしょう VBで書いたら以下のようになることを、C++で書こうと思いました。 Property Get Ptr() As Long Ptr = m_ptr End Property Property Let Ptr(ByRef RHS As Long) m_ptr = VarPtr(RHS) End Property 上記のコードではLetで実際に渡るのはLongポインタで、Getで返すのはLong値だとおもいます。 このため、long *pValとしましたが、イレギュラーでしょうか・・・? ためしに、 [id(3), helpstring("メソッド setPtr")] HRESULT setPtr([in] long* newVal); を作ってみたのですが、結果が同じでした。 ためしに、別のクラスVariantRefを作って同じようなことをやってみたのですが、 [id(3), helpstring("メソッド setPtr")] HRESULT setPtr([in] VARIANT* newVal); は、ちゃんと正しいなポインタを受取れました。 あとから作ったVariantRefはプロパティとメソッドの作成をすべてウィザードに任せたのですが、LongRefでは試行錯誤しながらだったので、メンバを作ったり消したり、型を変えたりしてしまったのが行かなかったのでしょうか? ウィザードがメンバの作成時に追記したのは、質問のところで書いた、idlとcppとhだけだと思っていたのですが、他にも対にしなければならないものがありますか? VBでクラスを作ったり、C++で普通のクラスを作ったりはしているのですが、C++でATLクラスを作るのは初めてなうえ、文献もあまり見つからないため、ご教授いただきたいと思っております。

関連するQ&A

  • #import文とC2011エラーの回避方法

    VC++2003にてCOM/ATL属性つきプログラミングをしております。 前々から気にはなっていたのですが、今回本格的に何とかしたいので 質問させていただきます。OSはXPSP3です。 自家製のライブラリを用途の分類ごとにCOMモジュール(DLL)で分けて作っています。 一番基本的なライブラリモジュールで enum emTestType { ttType1, ttType2 }; などという列挙値があり、COMクラスでプロパティ値として使用しています。 __interface TestObject : public IUnknown { [propput] HRESULT TestType([in] emTestType newVal); [propget] HRESULT TestType([out,retval] emTestType* pVal); }; このモジュールをTEST1.dllとしたとき、ほかのモジュールで二次使用するときに TEST2.dll stdafx.h #import "libid:..............." no_namespace named_guids [import(C:\...\TEST1.idl)]; とインポートしていますが、さらにこのモジュール内のオブジェクトで TEST2.h __interface EditObject : public IUnknown { [propput] HRESULT TestType([in] emTestType newVal); [propget] HRESULT TestType([out,retval] emTestType* pVal); }; とenum値を使いまわしています。そしてさらにもうひとつのモジュールでも TEST3.dll stdafx.h #import "libid:..............." no_namespace named_guids [import(C:\...\TEST1.idl)]; TEST3.h __interface ViewObject : public IUnknown { [propput] HRESULT TestType([in] emTestType newVal); [propget] HRESULT TestType([out,retval] emTestType* pVal); }; と使いまわしていて、さらに四つ目のモジュールで TEST4.dll stdafx.h #import "libid:..............." no_namespace named_guids [import(C:\...\TEST1.idl)]; #import "libid:..............." no_namespace named_guids [import(C:\...\TEST2.idl)]; #import "libid:..............." no_namespace named_guids [import(C:\...\TEST3.idl)]; と三次使用したいのですが、このときにC2011エラー(型の再定義)が発生します。 使いまわしたいから基本モジュールに入れているのですが、、、(;_;) 今までは二次使用から型を使わずにULONG値にしてしまっていました。 これはenum型に限らずクラス型でも当然起こります。 私は、これは#import文の引数で何とかなるんじゃないの?と思っているのですが、どうなんでしょうか。 あるいはもっと根本的な治療法があるのでしょうか? よろしくご教授お願いします。

  • VC6でALTを作成して、VB6やC#で利用する場合の型の定義の仕方

    COM+の引数や型で悩んでいます。 ALTで型が標準(intなど)でないとコンパイルエラーになると解釈して、 型をユーザー定義型(構造体)にしてデータのやり取りをすれば、どんな引数でも送れると思いました。 そして、その際にはポインタ(4バイト)が使われると仮定して、引数をint datとまるで4バイトの数値のようにしました。 (大丈夫か?) そのポインタを受け取り後、構造体のポインタキャストを当てて、ポインタの中身にアクセスできるようにしました。 このようにすると、コンパイルとするとエラー無しになり、戻り値も正しく返されて、これでいけると思ったのですが、 一部望み通りでない結果が出ました。 通常、ポインタで渡された場合、そのポインタを使って中身(ここではc)を変えた場合、反映されるはずでしたが 戻り値とcの値が異なったため、このやり方は正しくないのではないかと思いました。 ALTに詳しい方、以下のやり方のどこが間違っているのでしょうか? まったく分からないまま、VC6でALTを作成してVisualBasic6でテストしました。 まず、VC6でTest1は、 [in] int dat, [out,retval] int* retval 定義は struct S1 int a[10]; int b[10]; int c; } STDMETHODIMP CTest::Test1(int dat, int *retval) { S1* data = (S1 *)dat; data->c = data->a(0) + data->b(0); // ここでは3になっている。 *retval = data->c; return S_OK; } としてできたDLLをVBの参照設定で取得して呼び出した場合 type S1 a(10) as long b(10) as long c as long end type dim x as S1 x.a(0) = 1 x.b(0) = 2 dim dmy as variant dmy = test.Test1( varptr(x) ) dmy値は3で、x.cの値は3ではありませんでした。 int型など一般的な型やユーザー定義型などを引数として解説しているATLの本など ご存知の方、押してください。

  • ポインタを使った構造体のプログラム

    ポインタを使ってメンバに値を入力して表示するプログラムを作ったのですが、mainのstruct XYZ aというオブジェクトと*bというそれを指すポインタを使ってプログラムを表示するにはどうすればいいのでしょうか。 これがプログラムです。 #include <stdio.h> struct XYZ { int x; long int y; double z; }; void set_xyz(struct XYZ *p,int x,long int y,double z) { p->x=x; p->y=y; p->z=z; } //void set_xyz(struct XYZ *,int,long int,double); int main() { struct XYZ a = {12,999999,1.41421356},*b; //ここのポインタ変数bでエラーが表示されます。 set_xyz(b,a.x,a.y,a.z); printf("a.x = %d\na.y = %d\na.z = %lf\n",b->x,b->y,b->z); return 0; } エラーの内容は「初期化されていないローカル変数 'b' が使用されます」となっています。 初歩的な質問ですみません・・・。

  • C++の問題で困っています。

    C++の問題で困っています。 今,C++の入門書を読んでその中の演習問題に取り組んでいるのですが、この本には答えがついていないみたいなので、問題で悩んでいます。 お力を貸してください。 問題 「文字列 s に含まれる最も先頭に位置する文字 c へのポインタを返す関数 strchr_ptr を作成せよ。型は char * strchr_ptr(const char *s, char c); とする。例えば、文字列 s が "ABSZXYX" で文字 c が 'X' であれば、返却するのは &s[4]である。 なお、文字 c が文字列 s に含まれない場合は NULL を返却すること。 」 です。 僕はこの問題に対して、以下のように答えました。 ヘッダのインクルードなどは省きます。 char *strchr_ptr(const char *s, char c) {         for(int i = 0; s[i]; i++)         if(s[i] == c)             return const_cast<char *>(&s[i]);     return NULL; } int main() {     char s[36] = "ABSZXYX";     cout << strchr_ptr(s, 'X') << "\n";     cout << &s + 4 << "\n"; } と書きました。 cout << &s + 4 << "\n"; はこの上の文で導き出したアドレスがあっているか確かめるものです。 ですが、これをコンパイルして実行すると、 ----------------- XYX 0012FFE0 ----------------- となり、関数側のほうはアドレス的な表記をしてくれません・・。 どうにかして、アドレス表記にしようとあれこれ考えたのですが、どうしても出来ませんでした。 どのようにしたらいいのか教えてください。 初歩的な質問ですが、よろしくお願いします。

  • 配列とポインタについて

    #include <stdio.h> int main() { char x[3]; char *y; x[0]='a'; x[1]='b'; x[2]='\0'; y="abc"; printf("xの値は%s\n",x); printf("yの値は%s\n",y); } 通常の配列宣言では、このままだと文字列をまとめて 代入できないのに対して、ポインタ変数ならまとめて代入することができるのは何故ですか?そういう仕組みだと言われてしまえば、それまでなんですが・・・

  • C++のコンストラクタを使った自動ポインタでoperator関数の使い

    C++のコンストラクタを使った自動ポインタでoperator関数の使い方で分からないところがあります。 環境下はVisual C++でC/C++のWin32コンソールアプリケーションを使って行っています。 下記のコードで実行させています。やっていることは文字列を反転させて表示させるだけのことです。 #include "stdafx.h" #include <string> #include <iostream> class AutoPtr { char *ptr; public: AutoPtr():ptr(0) { } ~AutoPtr() { delete [] ptr; } // char *operator=(char *ptr) { delete [] this->ptr; this->ptr = ptr; return this->ptr; } operator char *(){ return ptr; } char &operator[](int index) { return ptr[index]; } }; void reverse(char *str) { int i, n; AutoPtr work; n = strlen(str); work = new char[n+1]; strcpy(work, str); for(i=0; i<n; i++) { str[i] = work[n-i-1]; } printf("%s\n", str); } int _tmain(int argc, _TCHAR* argv[]) { reverse("ABCDEFG"); return 0; } これを実行させると、reverse関数のfor()文の、str[i] = work[n-i-1];を実行させた所で実行エラーになってしまいます。その前の、strcpy(work, str);でworkにstrの内容が正常にコピーされているところまでは確認できています。operator関数の、 char &operator[](int index) { return ptr[index]; } で、operator[]はAutoPtrを配列のように扱っているはずなのですが、何故かstr[i] = work[n-i-1]; の所で実行エラーになってしまいます。 operaror関数の書き方が悪いのか、何が原因なのか分かりかねています。御経験のあるかたは、御教示いただけたらと思っています。 よろしくお願い致します。

  • IDLファイルからソースファイルの実装方法について

    MessageDll.idlファイルで以下のようにコードを書いて、コンパイルしてできた MessageDll_h.hをインクルードして、 MessageComponent.cppに以下のように記述しました。 実装方法を教えてください。 また、間違っている所は指摘して頂きたいです。 DllRegisterServerとか足りない所はありますが、class内部だけでもお願いします。 お願いします。 //MessageDll.idl import "oaidl.idl"; import "ocidl.idl"; [uuid(7BBCC3A6-033D-4306-BC3D-EA358549F30F),version(1.0)] library ComputerLibrary { importlib("stdole2.tlb"); [uuid(9CAEC929-7E14-4af3-80EE-3D4EFECB3DCC), object] interface IMessage : IUnknown { HRESULT Message(); }; [uuid(0689B073-989C-483c-9757-0770F90A8C86)] coclass MessageClass { [default] interface IMessage; }; }; //MessageComponent.cpp #include"MessageDll_h.h" class MessageClass : public IMessage { public: HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { // 指定されたIID(Interface ID)に応じて、そのインターフェースポインタを // *ppvObjectに渡してやります。 if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IMessage)) { *ppvObject = static_cast<IMessage *>(this); } else { *ppvObject = NULL; return E_NOINTERFACE; } return S_OK; } ULONG STDMETHODCALLTYPE AddRef() { // 今は何もしません。 return 0; } ULONG STDMETHODCALLTYPE Release() { // Release関数は、その名の通りコンポーネントの開放を行います。 delete this; return 0; } HRESULT STDMETHODCALLTYPE Message() { MessageBox(NULL,"Hello World","",MB_OK); return 0; } };

  • perlで配列のポインタをdllに渡すには?

    Active Perlの最新版(x86)を使っています。 C言語側で、 //配列の先頭要素へのポインタと配列の要素数を渡すと、要素の平均を整数で返す関数 int avr(int *array,int kazu) { int value=0; for(int i=0;i<kazu;i++) { value+=*(array+i); } return value/kazu; } などと書いて、avr.dllとしてビルドしたものを(モジュール定義はdefファイルにて指定)、 use Win32::API; my $function = Win32::API->new("avr", "avr", "PN", "N"); @array=(1,2,3,4,5); $avr=$function->Call(\@array,5); print "Content-type: text/html\n\n"; print $avr; と使ってみたのですが、出力値が期待の「3」ではなく、-123343252とか435893497などの無茶苦茶な数値になってしまいます。 配列のポインタの渡し方が違うのかとは思いますが、どう書いたら良いのか分からない状況です。 一応、他のプログラムからこのdllを使うと、正常動作するようです。

    • ベストアンサー
    • Perl
  • C言語の、ポインターの問題を教えて下さい

    C言語の、ポインターを使って大文字と小文字を入れ替えるプログラムを教えて下さい。 作ったのですが、うまくいかず困っています。 分かる方、訂正してください。 よろしくお願いいたします。 #include<stdio.h> void reverse(char *str) { int i; for(i=0;str[i]!='\0';i++) { if(str[i]>=0x61) {str[i]-0x20;} if(0x40<str[i]<0x5B) {str[i]+0x20;} } return(str); } int main(void) { int num; char *str="AddsssEEEEwwwJojoHoih"; printf("Before reverse %s \n",str) str=reverse(str); printf("After reverse %s \n",str); return 0; }

  • スタックポインタ取得 アセンブラ

    unsigned long sp(void) { __asm__("movl %esp,%eax"); } int main() { printf("0x%x\n", sp()); return 0; } このプログラムはスタックポインタのアドレスを表示することができるそうなのですが、何故なのか分からないので理由を教えていただきたいです。よろしくお願いします。

専門家に質問してみよう