VBのByte配列をVARIANTに格納してCOMでVCに渡したい

このQ&Aのポイント
  • VB6とVC6の間でデータのやり取りを行う際、VB側からByte配列をVariantで渡してVC側で受け取りたいが、うまく値が渡らない状況です。VB側でVariantにByte配列を代入する際に値渡しになっているのか、それとも他の理由があるのか疑問です。
  • VB側でVariantにByte配列を代入する際、値渡しになっている可能性があります。VB側でのメソッドの宣言やODLファイルの定義を確認し、対策を検討する必要があります。
  • ODLファイルを変更して対処する方法もあるかもしれませんが、MFCのウィザードが注意を促しているため、質問をしました。VB側でVariantにByte配列を代入する際の仕組みや値の渡し方を理解する必要があります。
回答を見る
  • ベストアンサー

VBのByte配列をVARIANTに格納してCOMでVCに渡したい

OS: WinXP sp2 開発: VB6 sp5 <--> VC6 sp5 MFC OCX いままでVB/VC間のデータのやりとりをStringで行っていたアプリがありまして、現在はVB側から見たインターフェースをByte()へ変更する作業を行っています。 まず VB --> VC に関して質問ですが Dim byteStream(5) As Byte byteStream(0) = 1 byteStream(1) = 2 byteStream(2) = 3 byteStream(3) = 4 byteStream(4) = 5 などとし、メソッドに対してbyteStreamをVariantで渡してやって C側から const VARIANT FAR& で参照してきて、 この.pcVal(配列の先頭アドレスが入っているはず)をchar*で受け取ろうと思いました。 するとこのアドレスには、先頭のbyteStream(0)の値しか入ってきません。 VB側でVariant = Byte()とするときに値渡しになっているっぽい?のですが、仕組みがわかりません。 この理由が知りたいです。 ODLファイルを変更して対処すべきかもしれませんが、MFCのウィザードが「慎重に行ってください。」とコメントしているので質問させていただきました。 VB側でのメソッドの宣言: Function SendVariantStream(stream, length As Integer) As Boolean odlファイルでの宣言: boolean SendVariantStream(VARIANT stream, short length) VCの宣言: BOOL SendVariantStream(const VARIANT FAR& stream, short length);

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

  • ベストアンサー
  • liwet
  • ベストアンサー率72% (18/25)
回答No.1

VC側で受け取ったときですが、配列を渡しているので、SafeArrayAccessDataを使ってアクセスするべきだと思います。 参照渡しか、値渡しかで、parrayを使うか、pparrayを使うか変わります。 どちらでもいけるようにすると、こんな感じかな...余分なチェックも入ってる気がするけど (^-^; VARIANT *va; SAFEARRAY *psa; HRESULT hr; VARIANT *pi; if (stream.vt == (VT_VARIANT|VT_BYREF)){ va = stream.pvarVal; } else { va = &stream; } if ((va->vt & VT_ARRAY) != VT_ARRAY){ ThrowError(CTL_E_INVALIDPROPERTYVALUE, "引数が配列ではないぞ", 0); } if (va->vt & VT_BYREF){ psa = *va->pparray; } else { psa = va->parray; } hr = SafeArrayAccessData(psa, (void **)&pi); for(int i=0 ; i<arraylen ; i++){ int type = pi[i].vt; int val = pi[i].cVal // ← 1バイト整数の場合、cValにデータが入る } SafeArrayUnaccessData(psa);

参考URL:
http://program.station.ez-net.jp/special/vc/atl-com/variant.asp,http://www.ops.dti.ne.jp/~allergy/com/com.html
saitoha
質問者

お礼

ご回答ありがとうございます。渡せました! COMの配列がこんなことになってるということ自体を知りませんでした。排他ロックがかかるんですね。 URLを参考に、VC->VBも書いてみました。 // // VC ---> VBのコード // buf: C側のバッファ // bufsize : 渡したい配列の大きさ // SAFEARRAYBOUND bound[1]; rgb[0].cElements = bufsize; rgb[1].lLbound = 0; _variant_t variantStream; variantStream.vt = (VT_ARRAY | VT_UI1); 内部形式をバイト配列とする variantStream.parray = ::SafeArrayCreate(VT_UI1, 1, bound); // Byte配列を作成 char* tmpbuf;   // SafeArrayにアクセスするため一時的に使用するポインタ ::SafeArrayAccessData(variantStream.parray, (void**)&tmpbuf); // アクセス開始 ::RtlCopyMemory(tmpbuf, buf, bufsize); // バッファをSafeArrayにコピー ::SafeArrayUnaccessData(variantStream.parray); // SafeArrayを再びロック ---> variantStreamをVBへ # # まだ、SAFEARRAYBOUNDの意味が微妙によくわかってませんが、 # 一応渡すことができました。ありがとうございます #

関連するQ&A

  • vb.netでByte型の実配列サイズが大きい件

    vb.netでByte型の配列を100byteで宣言しましたが、実際の配列サイズは101byteになりました。 (例) Module Module1 Sub Main() Dim buffSize As Integer = 100 Dim inputBuff(buffSize) As Byte Dim ii As Integer = inputBuff.Length Console.WriteLine("バッファサイズ = {0}", ii) End Sub End Module ここで、iiは、101となります。 CとかC++では、有り得ない仕様ですが、どういう意味が有るのでしょうか? ご教示お願い致します。

  • VB.netの配列とVB6の配列の違い

    VB6で画面を作成し、演算処理を行うDLLをVC6で作成しています。 下記のコードでVB6でSingle型の2次元配列を宣言しま、VCのDLLでエクスポートしている関数に渡 します。 VC6DLL側のコード---------- EXPORT void __stdcall TESTFUNC (float *pfData,long nSize{ } VB側のコード---------- 宣言 Declare Function TESTFUNC Lib "TEST.dll" (ByRef pfData As Single, ByVal nSize As Long) As Long 配列宣言 Public sngDat(999,1) as single 呼び出し TESTFUNC sngDat(0,0),1000 このように呼び出した場合、VC6のTESTFUNC にブレークポイントを設定し、停止するとpfDataの ポインタをインクリメントした場合、sngDat(0,0),sngDat(1,0),sngDat(2,0)と、1次元目の添え字を インクリメントした状態になるように、メモリに格納されています。 この動作を前提として、VB.net側でも同じように呼び出してみました。 VB.net側のコード---------- 宣言 Declare Function TESTFUNC Lib "TEST.dll" (ByRef pfData As Single, ByVal nSize As Integer) As Integer 配列宣言 Public sngDat(999,1) as single 呼び出し TESTFUNC(sngDat(0,0),1000) VC側は全く同じコードを利用するとします。 同じように、VC6のTESTFUNC にブレークポイントを設定し、停止するとpfDataの ポインタをインクリメントした場合、sngDat(0,0),sngDat(0,1),sngDat(1,0)と,sngDat(1,1)、と1次元目の添え字を インクリメントせず、2次元目の添え字をインクリメントした状態になるように、メモリに格納されています。 VB6→VC6のDLLの場合の配列渡しと、VB.net→VC6のDLLの場合の配列渡しで違いがあるのは 何故でしょうか?また、VB.net側の配列渡しの仕様を、VB6側の仕様に合わせる方法は無いのでしょうか? よろしくお願いいたします。

  • VB.netでのVC++呼び出し引数の順番

    VC++で作成されたDLLをVB.NetにてそのDLLを呼び出すアプリを作成して います。以下のパターン2では問題なく動作しますが、パターン1では DLL側で見たときにchar変数に正しく値が入りません(NULL)になる。 パターン1とパターン2の違いは引数の順番です。 開発環境:VisualStudio2008 OS:WindowsXP SP2 パターン1(この場合はNG)  VC++側の宣言   extern "C" __declspec(dllexport) void func1(int i,char*s)  VB.Net側の宣言   <System.Runtime.InteropServices.DllImport("func.dll")> _   Public Sub func1(ByVal i As Long, ByVal s As String)   End Sub パターン2(この場合はOK)  VC++側の宣言   extern "C" __declspec(dllexport) void func1(char*s,int i)  VB.Net側の宣言   <System.Runtime.InteropServices.DllImport("func.dll")> _   Public Sub func1(ByVal s As String ,ByVal i As Long)   End Sub VC++側がパターン1で作成さているため、VC++側を修正をしないで 正常に動かすことは可能でしょうか? よろしくお願いします。

  • VB2005でVC6.0で作成したDLLから値を取得するには

    VB6からVB2005にコンバートをしたのですがVCで作成したDLLの関数の箇所で下記エラーが出てしまいました。(VCのDLLは他で使用の為変更できず。) エラー内容 「保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます。」 いろいろサイト等を調べたのですがうまくいきません。 Marshal.AllocCoTaskMem、Marshal.Copyなどを使用すればできるらしいのですが難しすぎて挫折しました。。 どなたかご教授下さい。 よろしくお願い致します。 VC側ソース(XXX.DLL) GetData( unsigned char far *pRec,   //データ short int *pRecLen,  //サイズ { Char cbBuf[1024]; //バッファ memset(cbBuf, ' ', 1024); //初期化 Call GetBuf(cbBuf); memcpy(pRec, cbBuf, *pRecLen); return(0) ; } VB2005のソース <StructLayout(LayoutKind.Sequential)> Structure St Dim A () As Byte Dim B () As Byte End Structure Declare Function GetData Lib "XXX.DLL" (Byref RecData As St , ByRef Reclen As Short) As Short Sub Dim TmpData As St Dim TmpLen As short =100 Dim X As Byte Dim Y As Byte Redim TmpData.A(49) Redim TmpData.B(49) Call GetData(TmpData, TmpLen) X =TmpData.A(0) Y =TmpData.B(1) ・ ・ End Sub

  • VB2005でバイト配列をコピーするには

    どなたかご教授下さい。 VB6で作成したプログラムをVB2005にコンバートしました。構造体からなる配列変数を一つの配列変数にコピー したいのですが、構造体の最初のメンバーだけ値が同じで以降の値は正しくセットされていませんでした。 どのように記述したらよいのでしょうか。またCopymMemory以外で良い方法があればご教授下さい。 よろしくお願い致します。 ===概略=== Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Byte, ByRef Source As Byte, ByVal Length As Short) 構造体定義 (コピー元) <StructLayout(LayoutKind.Sequential)>Structure ST_MOTO <MarshalAs(UnmanagedType.ByValArray, SizeConst:=4)> Dim byte_strTranCd() As Byte <MarshalAs(UnmanagedType.ByValArray, SizeConst:=5)> Dim byte_strTanSeq() As Byte <MarshalAs(UnmanagedType.ByValArray, SizeConst:=2)> Dim byte_strTxtNo() As Byte <MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)> Dim byte_strTxtSeq() As Byte End Structure Dim A As ST_MOTO (コピー先) <StructLayout(LayoutKind.Sequential)> Structure ST_SAKI Dim lngrecLen As Integer 'データのLength <MarshalAs(UnmanagedType.ByValArray, SizeConst:=100)> Dim bytrecData() As Byte 'データ End Structure Dim B As ST_SAKI Call CopyMemory(B.bytrecData(0), A.byte_strTranCd(0), 14) 出力結果 '最初の構造体のメンバーの値は正しく設定されている B.bytrecData(0) =80    A.byte_strTranCd(0) =80 B.bytrecData(1) =50    A.byte_strTranCd(1) =50 B.bytrecData(2) =57    A.byte_strTranCd(2) =57 B.bytrecData(3) =50    A.byte_strTranCd(3) =50 'これ以降は正しくない。(VB6だと正しく設定されている) B.bytrecData(4) =0     A.byte_strTanSeq(0) =80 B.bytrecData(5) =0     A.byte_strTanSeq(1) =87 B.bytrecData(6) =0     A.byte_strTanSeq(2) =48 B.bytrecData(7) =0     A.byte_strTanSeq(3) =48 B.bytrecData(8) =6     A.byte_strTanSeq(4) =55     ・              ・     ・              ・

  • VCでのDLLからVBへの文字列の引渡し

    ===VB(Ver.6.0 SP5)側=== Public Declare Function GetStrSample Lib "Test.dll" () As String ・・・・・ Label1.Caption = GetStrSample ===VC側(Ver6.0)=== const char *StrSample="ABCDE01234";   ・・・・ _declspec(dllexport) BSTR _stdcall GetStrSample() { CString strResult; strResult = StrSample; return strResult.AllocSysString(); } 上記のようなコードで、VC側をDLLとしてVBから呼び出すと、 Label1には "A" しか表示されず、ブレークを置いてGetStrSample の戻り値を調べてみると、0x41,0x00,0x42,0x00,のように、 0x00が挟まれてしまいました。 ( ?hex(asc(mid(GetStrSample,3,1))) のようにして調べました。)  何故なのでしょうか? 文字コードの違いでしょうか? 正常にABCDE01234が渡せるようにするにはどうすればいいでしょうか? VCは初心者で、本や他人の作ったコードやMSDNを見て作成してみました。 目的は、DLLのバージョン等の情報をVB側に渡したいのです。 よろしくお願いします。

  • VCのDLL内でmallocした構造体をVBで使用

    題名通りですが、 VC側のDLLにてmallocで構造体の領域を確保しています。 この領域をVB側で使用したいのですが、やり方がわかりません。 イメージは Public Declare Function GetData Lib "xxx.dll" (ByRef datas As KOUZOUTAI, ByRef dataCnt As Long) As Boolean みたいな形で、datasに構造体のデータ、及び dataCntに領域確保したデータ数を取得出来ればなと思っております。 VB側では、これもイメージですが、 dim datas() as KOUZOUTAI dim dataCnt as long GetData(datas, dataCnt) for cnt=0 to dataCnt - 1 msgbox datas(cnt).a msgbox datas(cnt).b msgbox datas(cnt).c next みたいな感じで処理をしたいと思っております。 お聞きしたい事は 1)そもそも可能なのか? 2)declare宣言のdatasのところが???です。 3)dim datas() as KOUZOUTAIのところも宣言が???です。 4)datas(cnt).a等としているところも配列?として処理出来るのかが???です。 以上、よろしくお願いします。

  • VC++6.0からVBで作成したActiveX.DLL内の関数が呼出しできない

    VB6.0で作成したActiveX.DLLをVC++6.0(Win32SDK)側で 呼び出すプログラムを作成中です。 VB側(DLL)では問題なく作成できています。 Classに1つPublic関数を作成しただけです。 (Instancingは5MultiUseにしてあります) しかし、VC++側の呼出しでDLL内の関数が 呼び出せないのです。 LoadLibraryExでDLL自体のハンドルは取得できるのですが GetProcAddressで関数の取得時にNULLが返ってきてしまいます。 今まで試したことは ・VBからの呼出しはOK ・MFCだとOK ・SDKでもuser32.dll呼出しのサンプルは関数も実行できる です。 今からMFCへ以降するのは厳しいのでSDkでの方法を 知りたいです。 VB側が悪いのかもしれませんが、関数1つですし、 いじるところもほとんどないので多分VC++側だと 思っています。 なにか注意点とか確認するところをおしえていただければ 幸いです。 環境 Windows2000 SP3 VB6.0 SP5 VC++6.0 SP5 Win32SDK

  • VC6.0で作成したDLLでVBからの配列を受け取る方法

    VC++でDLLを作成しています。 VBから呼び出しで、配列を渡し、その配列の値を VCで使用したいのです。 通常変数でテストして、うまくいっているのですが、 配列に変更したところ、値がうまく渡りません。 どなたかお分かりになられる方いらっしゃいましたら お力をお貸し願えないでしょうか? 変数で成功しているプログラム VB6.0 標準モジュール Declare Function fncTest Lib "fncTest.dll" (a As Long) As Long フォーム Private Sub Test_Click() dim i as integer i = fncTest(1) End Sub VC++6.0 fncTest.h fncTest_API int _stdcall fncTest(int); fncTest.cpp fncTest_API int _stdcall fncTest(int a) { a = a+1; return a; } fncTest.def     省略 変数aを配列に変更して作ってみたもの(配列bにVBからの配列aの値を入れようとしていますが、 うまくいっていません。) VB6.0 標準モジュール Declare Function fncTest Lib "fncTest.dll" (ByRef a() As Long) As Long フォーム Private Sub Test_Click() Dim i As Integer Dim hairetu(7) As Long Dim values As Variant For i = 0 To 7 hairetu(i) = i Next i values = fncTest(hairetu()) End Sub VC++6.0 fncTest.h fncTest_API int _stdcall fncTest(int*); fncTest.cpp fncTest_API int _stdcall fncTest(int* a) { int b[7]; int i; for (i = 0;i <= 7;i++){ b[i] = a[i]; } return 0; } 以上よろしくお願いします。

  • VC++で作成したDLLをVBから呼ぶと戻り値が化けてしまう

    はじめまして。いつも勉強させていただいております。 VCは今回初めてやっておりますので当たり前の質問 になってしまうかもしれませんがご教授願います。 以下のような仕様でものづくりを行っております。 1.VBよりVC++で作成したDLLを呼び出す。 2.VC++で作成したDLL側ではパラメータより取得した   値を元に足し算をした結果を   戻り値にセットして要求元に返却する。 といった流れで考えています。 2の部分についてはExeを一度作成し実行させた ところDOS窓に値が表示されました。(printfで) しかし、正しくOUTPUTされた値がVB側の戻り値と して返却された時点で参照すると「-6348」 となっています。 なぜこうなってしまうのか原因がわからず投稿 させて頂きました。 ご存知の方、ご教授下さい。 ************************************************* VC++ DLL側のソース ************************************************* #include <windows.h> #define DLL_EXPORT __declspec(dllexport) extern "C" { DLL_EXPORT int add(int a,int b); } int add(int a,int b) { return a+b; } ************************************************* VB  DLL呼び出し元 ************************************************* Option Explicit Private Declare Function add Lib "C:\Program Files\Microsoft Visual Studio\test\Debug\test.dll" Alias "_add@8" (a As Long, b As Long) As Integer Private Sub Command1_Click() Dim ret As Long ret = add(1, 2) MsgBox ret End Sub 環境 OS:WinXP 言語:VC++6.0    VB6.0(SP6)

専門家に質問してみよう