質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
VC++

VC++ (Visual C++) とは、Microsoft製のC++のための統合開発環境です。

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

Q&A

1回答

1463閲覧

【.net】VBでマーシャリングされた構造体をBSTR構造体としてVC++に渡す方法を知りたいです。

Marino_Y

総合スコア2

VC++

VC++ (Visual C++) とは、Microsoft製のC++のための統合開発環境です。

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

0グッド

0クリップ

投稿2022/11/30 10:56

こんにちは。

先日の質問では大変お世話になりました。

前回はVC++(DLL)がBSTR構造体で出力してVB(画面/フォーム)に渡す方法で行き詰まりましたが、今回はVB(画面/フォーム)からVC++(DLL)へ入力としてBSTR構造体を渡し参照する方法で行き詰まりました。
前回とは逆のことをやればよいと思ったのですが、思うようにいかず1週間以上苦戦しています…。
マネージド(VB.NET/Stringの構造体(n件))からアンマネージド(VC++/Bstrの構造体)へ直接構造体を受け渡す方法を教えていただけないでしょうか。
何卒よろしくお願いいたします。

<やりたいこと>

VBでマーシャリングした構造体をBSTR構造体としてVC++に渡す方法を知りたいです。

VB(screen/FORM):Structure(MarshalAs(UnmanagedType.Bstr))

VC++(DLL):typedef(BSTR)

<開発環境>
Windows7(32bit)
VisualStudio2008(VB/VC++)

<実行環境>
Windows7(32bit)

試したこと:

以前の解決策を参考に以下のソースコードを試してみましたが、VC++側で構造体を参照するとエラーが発生しました。
「保護されたメモリに対して読み取りまたは書き込み操作を試みました。メモリが破損している可能性があります。」というエラーであるため、構造体自体をVBからうまく渡せていないか受け取れていないのではないかと未熟者ながら推察しております。

構造体を参照してからひとつひとつ別の構造体へ値を移していくやり方(wcsncpy_s,strncpy_s)も、MicrosoftやCODE PROJECTも使って調べながら組み込みましたが、果たして合っているか自信がありません。
構文が正しいのかという点でもアドバイスいただけましたら心から助かります。
VB側は「tpNonPosDet2」構造体を引数としてDLLの関数を呼び出し、VC++側は「NPOS_DETAIL_UPD2」構造体としてデータを受け取った後、SQL(DB更新)用の構造体「wk_Detail」に値を移し替えたいのです。

esql_com.h(C++)

1typedef struct { 2 char szFunctionFlag[2]; 3 char szServiceNo[16]; 4 char szPosNo[6]; 5 char szSeqNo[6]; 6 char szCreateDate[10]; 7 char szCreateTime[6]; 8 char szSalMoney[13]; 9 char szRiyuCode[4]; 10 char szServiceRank[4]; 11 char szServiceRankAdd[4]; 12 char szServiceTime[6]; 13 char szChangeTime[6]; 14 char szTantoCode[10]; 15 char szCustKubn[4]; 16 char szKozaNo[27]; 17 char szKozaType[4]; 18} NPOS_DETAIL_UPD; 19 20typedef struct { 21 BSTR szFunctionFlag; 22 BSTR szServiceNo; 23 BSTR szPosNo; 24 BSTR szSeqNo; 25 BSTR szCreateDate; 26 BSTR szCreateTime; 27 BSTR szSalMoney; 28 BSTR szRiyuCode; 29 BSTR szServiceRank; 30 BSTR szServiceRankAdd; 31 BSTR szServiceTime; 32 BSTR szChangeTime; 33 BSTR szTantoCode; 34 BSTR szCustKubn; 35 BSTR szKozaNo; 36 BSTR szKozaType; 37} NPOS_DETAIL_UPD2; 38 39 40 41 42__declspec(dllexport) int __stdcall Esql_UpNonPos(PS_HEADER_UPD *lpHeader, long *nIndexNum, NPOS_DETAIL_UPD2 *lpDetail);

esql_com.sc(C++)

1__declspec(dllexport) int __stdcall Esql_UpNonPos 2 (PS_HEADER_UPD *lpHeader, long *nIndexNum, NPOS_DETAIL_UPD2 *lpDetail) 3{ 4 long lCount=0; 5 int ii; 6 int nSqlstate; // SQL Return Code 7 char *work; // WorkArea 8 PS_HEADER_UPD wk_Header; // Header Information 9 NPOS_DETAIL_UPD wk_Detail; // Detail Information 10 long iRet; // Return Code 11 NPOS_DETAIL_UPD2 npos; // For structure reference 12// LPSAFEARRAY psa = *lpDetail; // For structure reference 13 int idx[1], lb, ub; // For structure reference 14 static char bufa[1024]; // For structure reference 15 static wchar_t bufw[1024]; // For structure reference 16 static size_t st; // Work Area 17 static char mbs[256]; // Test Area For MessageBox 18 19 20//***************************************************************************** 21// Processing start setting 22//***************************************************************************** 23 24 EXEC SQL WHENEVER SQLERROR GOTO :SQL_ERROR; 25 26 EXEC SQL SET TRANSACTION READ WRITE; 27 28//***************************************************************************** 29// SQL 30//***************************************************************************** 31 MessageBox( NULL, TEXT("Esql_UpNonPos"), TEXT("Esql_com.sc"), MB_OK); 32 MessageBox( NULL, TEXT(lpHeader->szServiceNo), TEXT("Esql_com.sc"), MB_OK); 33 34 35 36// Setting for structure reference 37//================================ 38 setlocale(LC_ALL,"Japanese"); 39// SafeArrayLock(psa); 40// SafeArrayGetLBound(psa, 1, &lb); 41// SafeArrayGetUBound(psa, 1, &ub); 42// idx[0] = lb; 43 idx[0] = 0; 44 45 46 47// Setting for detail information 48//================================ 49 EXEC SQL WHENEVER NOT FOUND CONTINUE; 50 for(ii=0; ii<*nIndexNum; ii++) { 51 // I used SAFEARRAY in VC++6.0, but I can't find a method that works well in VC++9.0, so I used another method. 52// SafeArrayGetElement(psa, &idx[0], &npos); 53 npos = lpDetail[idx[0]]; //←I get an error here. 54 55 // Reference of processing flag [add/delete etc] (obtained from argument structure) 56 MessageBox( NULL, TEXT(" 0.1 START"), TEXT("Esql_UpNonPos"), MB_OK); 57 *bufa = 0x00; 58 wcsncpy_s(bufw, 1024, npos.szFunctionFlag, 2); 59 wcstombs_s(&st, NULL, 0, &bufw, 1024); 60 wcstombs_s(&st, &bufa[0], st, &bufw, 1024); 61 memset(wk_Detail.szFunctionFlag, '\0', sizeof(wk_Detail.szFunctionFlag)); 62 strncpy_s(wk_Detail.szFunctionFlag, sizeof(wk_Detail.szFunctionFlag), bufa, 2-1); 63 MessageBox( NULL, TEXT(" 0.1 END"), TEXT("Esql_UpNonPos"), MB_OK); 64 MessageBox( NULL, TEXT(wk_Detail.szFunctionFlag), TEXT("Esql_UpNonPos Structure 1"), MB_OK); 656667

API_Refer.vb(VB)

1<DllImport("Esql_DLL.dll", CharSet:=CharSet.Ansi)> _ 2Public Function Esql_UpNonPos(<[In]()> ByVal Up_dtpPSHead As tpPS_Hed2, ByRef RecordCnt As Long, <[In]()> ByVal Up_dtpNonPosDet() As tpNonPosDet2) As Integer 3End Function

mdlPkSrv.vb(VB)

1 <StructLayout(LayoutKind.Sequential)> _ 2 Structure tpNonPosDet2 3 <MarshalAs(UnmanagedType.BStr)> Dim strShoriFlg As String 4 <MarshalAs(UnmanagedType.BStr)> Dim strSrvNo As String 5 <MarshalAs(UnmanagedType.BStr)> Dim strPosNo As String 6 <MarshalAs(UnmanagedType.BStr)> Dim strTuuban As String 7 <MarshalAs(UnmanagedType.BStr)> Dim strToriDate As String 8 <MarshalAs(UnmanagedType.BStr)> Dim strToriTime As String 9 <MarshalAs(UnmanagedType.BStr)> Dim strKingaku As String 10 <MarshalAs(UnmanagedType.BStr)> Dim strRiyuCd As String 11 <MarshalAs(UnmanagedType.BStr)> Dim strSrvRank As String 12 <MarshalAs(UnmanagedType.BStr)> Dim strAddSrvRank As String 13 <MarshalAs(UnmanagedType.BStr)> Dim strSrvTime As String 14 <MarshalAs(UnmanagedType.BStr)> Dim strChgTime As String 15 <MarshalAs(UnmanagedType.BStr)> Dim strHanbaiinCd As String 16 <MarshalAs(UnmanagedType.BStr)> Dim strCostomKbn As String 17 <MarshalAs(UnmanagedType.BStr)> Dim strKozaNo As String 18 <MarshalAs(UnmanagedType.BStr)> Dim strKozaSbh As String 19End Structure

frmPktest.vb(VB)

1Ret = Esql_UpNonPos(dtp_GotHedData, RecordCnt, dtp_NonPosDet)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

二次元配列だ、と言う話なので。
こういうDLLをつくって、

C

1#include <windows.h> 2#include <stdio.h> 3 4// NPOS_DETAIL_UPD2の定義を省略 5__declspec(dllexport) int __stdcall Esql_UpNonPos 6 (void *lpHeader, int *nIndexNum, NPOS_DETAIL_UPD2 *lpDetail) 7{ 8 int ii,jj; 9 for(ii=0; ii<nIndexNum[0]; ii++) { 10 for (jj = 0; jj < nIndexNum[1]; jj++) { 11 printf("%d,%d=%S\n", ii, jj, lpDetail[ii * nIndexNum[1] + jj].szFunctionFlag); 12 wchar_t msg[128]; 13 int len = swprintf_s(msg, sizeof(msg), L"cd%d.%d", ii, jj); 14 if (lpDetail[ii * nIndexNum[1] + jj].szFunctionFlag) SysFreeString(lpDetail[ii * nIndexNum[1] + jj].szFunctionFlag); 15 lpDetail[ii*nIndexNum[1] + jj].szFunctionFlag = SysAllocStringLen(msg, len ); // 16 } 17 } 18 return 0; 19} 20

こういう呼び出しをすると

vb

1 <DllImport("m.dll", CharSet:=CharSet.Unicode)> 2 Public Shared Function Esql_UpNonPos(<[In]()> ByVal Up_dtpPSHead As System.IntPtr, <[In]()> ByVal RecordCnt() As Integer, <[In](), [Out]()> ByVal Up_dtpNonPosDet() As tpNonPosDet2) As Integer 3 End Function 4 5 Public Shared Sub Main(args() As String) 6 Dim det2d(2, 4) As tpNonPosDet2 7 For i As Integer = 0 To det2d.GetLength(0) - 1 8 For j As Integer = 0 To det2d.GetLength(1) - 1 9 det2d(i, j).strShoriFlg = String.Format("det{0}.{1}", i, j) 10 Next 11 Next 12 13 Dim dims() As Integer = New Integer() {det2d.GetLength(0), det2d.GetLength(1)} 14 Dim det(dims(0) * dims(1)) As tpNonPosDet2 15 System.Console.WriteLine("dim {0}x{1}", dims(0), dims(1)) 16 17 For i As Integer = 0 To det2d.GetLength(0) - 1 18 For j As Integer = 0 To det2d.GetLength(1) - 1 19 det(i * dims(1) + j).strShoriFlg = String.Format("det{0}.{1}", i, j) 20 Next 21 Next 22 23 Esql_UpNonPos(System.IntPtr.Zero, dims, det) 24 25 For i As Integer = 0 To dims(0) - 1 26 For j As Integer = 0 To dims(1) - 1 27 det2d(i, j) = det(i * dims(1) + j) 28 System.Console.WriteLine(det2d(i, j).strShoriFlg) 29 Next 30 Next 31 End Sub

こういう結果になります。

dim 3x5
0,0=det0.0
...
2,4=det2.4
cd0.0
...
cd2.4

投稿2022/11/30 11:51

編集2022/12/14 13:36
matukeso

総合スコア1590

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Marino_Y

2022/12/01 09:37 編集

試してくださりありがとうございます。 事例つきで大変わかりやすく、こちらでも同様のコードを入れて確認してみました。 こちらの環境だと「printf("%S\n", npos.szFunctionFlag);」の箇所で下記のエラーが発生するのですが、どのような原因が考えられるでしょうか・・・?(インデックスが範囲外?)  エラー「アプリケーションのコンポーネントで、ハンドルされていない例外が発生しました。~保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます。」
matukeso

2022/12/01 13:50

奇怪ですねぇ。%pや%sではどうなりますか。 デバッガでアタッチしたら、printfの呼び出し時点でlpDetailはどう見えますか。
Marino_Y

2022/12/13 08:28

体調が優れず、返事が遅くなってしまい申し訳ありません。 %pや%sでも同様のエラーが発生しました。 VBとVCが別プロジェクトになっており、VBから独立したVC(DLL)を呼び出す仕組みを取っている事情でVisualStudioのデバック機能を使用した引数の確認ができず、呼び出し時点のlpDetailは確認が難しいです…。(うまいやり方はあると思うのですが、現状、メッセージを仕込んでは実行して・・・で切り分けています。) 渡そうとしている配列が二次元であることも関係しそうでしょうか。
matukeso

2022/12/13 09:14

Up_dtpNonPosDetが二次元なんですか? その場合、c側もvb側も全然話が違ってきますが、、
matukeso

2022/12/13 09:28

カンタンなのは、マーシャリング上は一次元配列にして二次元でのサイズも渡す手です。 たとえば3x5なら、15要素の一次元配列と、dims={3,5}とかを渡す。c側は[i*dims[0]+j]とかで埋めていく。
Marino_Y

2022/12/13 11:29

度々ご返信ありがとうございます。 言葉上の意味は分かるのですが具体的な実装イメージがつかず…何か参考にできるようなサンプルや事例などもしご存じでしたら教えていただけませんか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問