C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[13]: 構造体配列のマーシャリングについて


(過去ログ 101 を表示中)

[トピック内 18 記事 (1 - 18 表示)]  << 0 >>

■60312 / inTopicNo.1)  構造体配列のマーシャリングについて
  
□投稿者/ aaa (1回)-(2011/06/27(Mon) 16:34:55)

分類:[.NET 全般] 

失礼します。
VB.NETのコードからC++のdllを呼び出すためにマーシャリングしようとしているのですが、どうもうまくいきません。
いろんなサイトを見て試してみてもできず、知識が浅いこともあり完全に混乱してしまいました。
構造体内の配列をポインタに渡すところが問題を複雑にしていると思うのですが、どうすればうまくいくのか、アドバイスいただけると幸いです。
よろしくお願いいたします。

***宣言***
Public aaaDispText(199) As DISPTEXT

***構造体***
<StructLayout(LayoutKind.Sequential> Structure DISPTEXT
Public aaaFont() As Byte 'フォント名称

Public Sub Initialize()
ReDim aaaFont(31)
End Sub
End Structure

***コード***
Sub aaa()
dim lng as integer
dim Msg_page as integer = 3
dim lngcount as integer
lng = GetDispText(Msg_page , aaaDispText(0), lngcount)
End Sub

***dll呼び出し***
Public Declare Function GetDispText Lib "aaa.dll" ( ByVal lDNo As Integer, ByRef staTextData As DISPTEXT, ByRef lC As Integer) As Integer

引用返信 編集キー/
■60327 / inTopicNo.2)  Re[1]: 構造体配列のマーシャリングについて
□投稿者/ Azulean (775回)-(2011/06/27(Mon) 22:34:58)
2011/06/27(Mon) 22:35:26 編集(投稿者)

単に Initialize を呼ぶことを忘れているだけということはないですか?
Initialize メソッドを定義しても、勝手に呼んでもらえるわけではありませんので、念のため。
引用返信 編集キー/
■60329 / inTopicNo.3)  Re[2]: 構造体配列のマーシャリングについて
□投稿者/ aaa (3回)-(2011/06/28(Tue) 09:15:08)
No60327 (Azulean さん) に返信
> 2011/06/27(Mon) 22:35:26 編集(投稿者)

Azuleanさん、回答ありがとうございます。
上記では書き忘れてしまいましたが、Initializeは呼んでいます。
シンプルにするため、マーシャリング以外のコードを省略しました。
他の部分がうまく動いていることは確認できています。
考えている手順は、

1.アンマネージメモリの確保
2.マネージメモリのデータを、アンマネージメモリにコピー
3.dllを呼ぶ
4.アンマネージメモリのデータを マネージメモリにコピー
5.アンマネージメモリの解放

だと思っているのですが、
この考え方に誤りがあるのかもしれませんし、合っていたとしても構造体配列とポインタのやりとり自体が(dll側をいじることなしには)できないのではないかと自信をなくしています。
引用返信 編集キー/
■60330 / inTopicNo.4)  Re[3]: 構造体配列のマーシャリングについて
□投稿者/ スーアンコー (1回)-(2011/06/28(Tue) 09:26:15)
No60329 (aaa さん) に返信

うまくいかないとのことですが、どのようにうまくいかないのでしょうか?
例外が発生するとか、思ったようなデータが得られない(初期値のまま Or デタラメな値が入る)

dllの中身が分からないので何とも言えませんが、引数の型がdll内関数の引数と合っているのか、本当に

> lng = GetDispText(Msg_page , aaaDispText(0), lngcount)

のaaaDispText(0)がInitializeされているのかをよく確認した方がよいです。
引用返信 編集キー/
■60332 / inTopicNo.5)  Re[4]: 構造体配列のマーシャリングについて
□投稿者/ Hongliang (789回)-(2011/06/28(Tue) 09:46:10)
aaaFont が実は静的配列に一票。

C++ 側の構造体定義および関数定義もお書きください。
引用返信 編集キー/
■60333 / inTopicNo.6)  Re[5]: 構造体配列のマーシャリングについて
□投稿者/ aaa (4回)-(2011/06/28(Tue) 10:57:34)
2011/06/28(Tue) 11:02:16 編集(投稿者)
2011/06/28(Tue) 11:01:38 編集(投稿者)

スーアンコーさん、Hongliangさん、回答ありがとうございます。
まずdllの中身は以下になります。

***テキストデータを取得するdll***
long _stdcall flGetDispText( long lDispNo, DISPTEXT *stpTextData, long *lpCount )
{
long lCnt;

for( lCnt = 0 ; lCnt < DISPTXTCNT ; lCnt++ )
{
if( estMemPool.aaaDispText[ lDispNo - 1 ][ lCnt ].aaaFont[0] == '\0' )
{
break ;
}

flMemCpy( &stpTextData[ lCnt ], &estMemPool.aaaDispText[ lDispNo - 1 ][ lCnt ],
sizeof( DISPTEXT) ) ;
}

*lpCount = lCnt ;

return( 0 ) ;
}

今まで出たエラーは、例外が発生・保護されているメモリに読み書きしようとした・配列の要素数が1になってしまったなど、ネットで他の方が質問されているものと同じです。
どこに問題があるかを自分でははっきり突き止められないため、
問題を整理していただこうとまっさらなコードを掲載した次第です。

Initializeは、dllを呼ぶ前に以下のように行っています。
Dim i As Integer
For i = 0 To 199
  aaaDispText(i).Initialize()
Next i



引用返信 編集キー/
■60334 / inTopicNo.7)  Re[6]: 構造体配列のマーシャリングについて
□投稿者/ スーアンコー (2回)-(2011/06/28(Tue) 12:17:27)
> 今まで出たエラーは、例外が発生・保護されているメモリに読み書きしようとした・配列の要素数が1になってしまったなど、

ということは、Hongliangさんが指摘しているように、静的配列を渡すべきところを、Redimで作成した動的配列を渡してしまった
というのが濃厚では?

DISPTEXTの構造体定義が提示されていないので何とも言えませんが。
引用返信 編集キー/
■60344 / inTopicNo.8)  Re[7]: 構造体配列のマーシャリングについて
□投稿者/ aaa (5回)-(2011/06/28(Tue) 20:43:22)
No60334 (スーアンコー さん) に返信

遅くなってしまい、申し訳ありません。
DISPTEXTの構造体定義は、残念ながら今見られません。
しかし静的配列で渡すと、うまくいく可能性があるのですね。
試してみます。
よろしければ、なぜ動的配列である必要がありえるのか教えていただけないでしょうか。

それから、VB.NETで構造体配列を静的に定義することってできましたっけ。
再定義する方法しか知らなかったもので。
とりあえず、調べてみます。
ありがとうございます。


引用返信 編集キー/
■60348 / inTopicNo.9)  Re[8]: 構造体配列のマーシャリングについて
□投稿者/ スーアンコー (3回)-(2011/06/29(Wed) 05:03:34)
No60344 (aaa さん) に返信
> DISPTEXTの構造体定義は、残念ながら今見られません。

> しかし静的配列で渡すと、うまくいく可能性があるのですね。
> 試してみます。
> よろしければ、なぜ動的配列である必要がありえるのか教えていただけないでしょうか。

静的配列の要素がアドレス上順番に並んでいるのに対し、動的配列では、要素が順番に並んでいるとは限らないからです。
DISPTEXTの定義が分からないので何とも言えませんが、可能性として、dll側では引数の先頭アドレスから順にメモリを
参照し、演算を行いますが、VB側で用意したDISPTEXTがアドレス上順番に並んでいなければ、VB側に戻った時には
おかしな値になってしまいます。もちろん、別の用途に確保したメモリで演算を行おうとすれば、
「保護されているメモリに読み書きしようとした」エラーが出てしまいます。

> それから、VB.NETで構造体配列を静的に定義することってできましたっけ。
> 再定義する方法しか知らなかったもので。
> とりあえず、調べてみます。

DISPTEXTの定義が分からないので、うまくいくかどうか分かりませんが、以下はどうでしょう。

Public Sub Initialize()
    Dim aaa(31) As Byte 
    aaaFont = aaa
End Sub

引用返信 編集キー/
■60349 / inTopicNo.10)  Re[9]: 構造体配列のマーシャリングについて
□投稿者/ スーアンコー (4回)-(2011/06/29(Wed) 06:01:42)
あっ、構造体がLayoutKind.Sequentialなら、これはだめかも。
単純に構造体内にByte変数を必要数並べた方がいいかも。
引用返信 編集キー/
■60353 / inTopicNo.11)  Re[8]: 構造体配列のマーシャリングについて
□投稿者/ shu (812回)-(2011/06/29(Wed) 12:01:52)
No60344 (aaa さん) に返信

MarshalAs属性を使ってみてはどうでしょう
 UnmanagedType.ByValArray
 SizeConst = 32
かな

もしだめならMarshal.AllocHGlobalで確保してIntPtrを渡すようにしてみてはどうでしょう。
構造体の中身がbyte配列のみなら32*200の領域を確保すればよい気がする。
引用返信 編集キー/
■60368 / inTopicNo.12)  Re[9]: 構造体配列のマーシャリングについて
□投稿者/ aaa (6回)-(2011/06/29(Wed) 19:17:31)
スーアンコーさん、shuさん、回答ありがとうございます。
静的配列の意味が理解できました。
dll側は全て静的配列であることがわかりました。
shuさんのご意見も踏まえ、以下のようにしました。

***構造体***
Structure DISPTEXT

<MarshalAs(UnmanagedType.ByValArray, SizeConst:=31)> Public aaaFont() As Byte 'フォント名称

Public Sub Initialize()
Dim Def_aaaFont(31) As Byte
aaaFont = Def_aaaFont
End Sub
End Structure

***コード***
Sub aaa()
For i = 0 To 199
aaaDispText(i).Initialize()
Next

Dim ptrX as IntPtr
For x = 0 To 31
ptrX = Marshal.AllocHGlobal(1)
Marshal.StructureToPtr(aaaDispText(0).aaaFont(x), ptrX, False)
Next

lng = GetDispText(Msg_page , aaaDispText(0), lngcount)
End Sub

すると以下のエラーが出ます。

種類 'System.ExecutionEngineException' の例外がスローされました。

配列は壊れることなく、値も入っています。
引用返信 編集キー/
■60369 / inTopicNo.13)  Re[10]: 構造体配列のマーシャリングについて
□投稿者/ todo (153回)-(2011/06/29(Wed) 19:54:05)
<StructLayout(LayoutKind.Sequential)> _
Structure DISPTEXT
<MarshalAs(UnmanagedType.ByValArray, SizeConst:=32)> _
Public aaaFont() As Byte
End Structure
かな。

C++側の sizeof(DISPTEXT) と、VB側の Marshal.SizeOf(DISPTEXT) が一致しているのを確認しましょう。


引用返信 編集キー/
■60370 / inTopicNo.14)  Re[11]: 構造体配列のマーシャリングについて
□投稿者/ επιστημη (2649回)-(2011/06/29(Wed) 21:22:32)
επιστημη さんの Web サイト
...なんかもー、マーシャル云々でこんだけ苦労するくらいなら
C++/CLIでラッパー書いた方がよっぽど楽なんじゃないかと。

引用返信 編集キー/
■60371 / inTopicNo.15)  Re[11]: 構造体配列のマーシャリングについて
□投稿者/ aaa (8回)-(2011/06/29(Wed) 21:30:29)
todoさん、ご指摘ありがとうございます。

sizeofの一致を確認する目的は、Marshal.AllocHGlobalでアンマネージメモリに
いくつメモリを割り当てるか見極めるためですよね。

残念ながら、C++のコードを動かせる環境にないのですが、

char aaafont[32]

で定義してありました。そこで、

ptrX = Marshal.AllocHGlobal(32)

と書き換えましたが同様のエラーです。
この例外が発生するということは、何か根本的な間違いがあるのか、
.NET側だけでは解決できない問題である感じもするのですが、どうでしょうか。


引用返信 編集キー/
■60372 / inTopicNo.16)  Re[12]: 構造体配列のマーシャリングについて
□投稿者/ shu (818回)-(2011/06/29(Wed) 22:01:30)
No60371 (aaa さん) に返信

Public Declare Function GetDispText Lib "aaa.dll" ( ByVal lDNo As Integer, ByVal staTextData As IntPtr, ByRef lC As Integer) As Integer


Dim ptrX as IntPtr
ptrX = Marshal.AllocHGlobal(32 * 200)

GetDispText(Msg_page , ptrX , lngcount)

Dim bytRet(32 * 200 - 1) as Byte
Marshal.Copy(ptrX , bytRet, 0, 32 * 200)

という事なんですが、どうでしょう?

引用返信 編集キー/
■60374 / inTopicNo.17)  Re[12]: 構造体配列のマーシャリングについて
□投稿者/ aaa (10回)-(2011/06/30(Thu) 10:06:28)
shuさん!デバッグ通りました!!
今、かなり興奮していますw

まだ確認作業が残ってますので、
うまく動いていることが確認できたら、また報告したいと思います。

引用返信 編集キー/
■60379 / inTopicNo.18)  Re[13]: 構造体配列のマーシャリングについて
□投稿者/ aaa (12回)-(2011/06/30(Thu) 15:13:20)
回答してくださった方々、どうもありがとうございました。

VB.NETの構造体配列をC++のポインタに渡すコードをここに記しておきます。
他の悩める人々の参考になればと思います。


***宣言***
Public aaaDispText(199) As DISPTEXT


***構造体***
Structure DISPTEXT

<MarshalAs(UnmanagedType.ByValArray, SizeConst:=31)> Public aaaFont() As Byte 'フォント名称

Public Sub Initialize()
Dim Def_aaaFont(31) As Byte
aaaFont = Def_aaaFont
End Sub
End Structure


***コード***
Sub aaa()
  Dim lng As Integer
  Dim Msg_page As Integer = 3
  Dim lngcount As Integer
  Dim i As Integer
  Dim x As Integer
  Dim sizeX As Integer
  Dim ptrX As IntPtr

  '初期化
  For i = 0 To 199
    gcaDispText(i).Initialize()
  Next i

  'アンマネージメモリに確保するサイズ
  sizeX = 200 * 104

  'アンマネージメモリにメモリ割り当て
  ptrX = Marshal.AllocHGlobal(sizeX)

  'dll
  GetDispText(Msg_page , ptrX, lngcount)

  'マネージ配列の定義
  Dim byRetX(sizeX - 1) As Byte

  'アンマネージメモリのデータをマネージ配列にコピー
  Marshal.Copy(ptrX, byRetX, 0, sizeX)

  'マネージ配列を元の配列に格納
  For i = 0 To 199
    For x = 0 To 31
      aaaDispText(i).aaaFont(x) = byRetX(x + 104 * i)
    Next x
  Next i

  'アンマネージメモリの解放
  Marshal.FreeHGlobal(ptrX)


  '以下省略

End Sub


***dll呼び出し***
Public Declare Function GetDispText Lib "aaa.dll" ( ByVal lDNo As Integer, Byval staTextData As IntPtr, ByRef lC As Integer) As Integer


解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -