構造体へのRtlMoveMemoryでOutOfMemory?


zzz  2008-02-16 17:22:39  No: 100282  IP: 192.*.*.*

VB.net2003にて、FORTRANのexeファイルが出力した文字列を
共有メモリを介してVBで取得する機能を作っています。
タイマーにより逐次データを取得する仕組みなのですが
徐々にメモリを消費し、OutOfMemoryとなってしまいます。

Private Declare Sub CopyMemoryStruct Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As SEND_DATA, ByVal Source As Integer, ByVal Length As Integer)

  '構造体定義
  <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> _
  Public Structure STRUCT_DATA
    <VBFixedString(DataLength), MarshalAs(UnmanagedType.ByValTStr, SizeConst:=DataLength)> _
    Dim msg As String
    ReadOnly Property getMsg() As String
      Get
        Return msg
      End Get
    End Property
    Sub msgSet(ByVal str As String)
      msg = str
    End Sub
  End Structure

  'データ取得メソッド
  Private Sub getDataMethod(...)

    Dim data As STRUCT_DATA
    Dim str As String
    Dim lpFileMap As Integer  '共有メモリのマッピングアドレス
    
    '構造体にデータを格納
    CopyMemoryStruct(data, lpFileMap, Marshal.SizeOf(data))
    
    str = sendData.getMsg()

    'ここでstrを用いた処理を行いますが何もなくても
    'OutOfMemoryになります

  End Sub

複数のマッピングファイルに対してこのメソッドを呼ぶ構成です。
マッピングファイルのオープン/クローズは
このメソッドの呼び出し元で行っています。
上のプログラムで何かしら原因となる箇所がありましたら
ご指摘いただけますでしょうか。
このメソッドを抜けたときに構造体dataは
メモリから解放されると思っているのですが。

VBでの構造体及び共有メモリについて明るくないので
的外れな質問をしているかもしれませんが、
どうぞよろしくお願いいたします。

編集 削除
やじゅ  2008-02-16 18:12:48  No: 100283  IP: 192.*.*.*

ByVal Source As Integer?はいいのかな

Private Declare Sub CopyMemory Lib "kernel32" 
Alias "RtlMoveMemory" ( _ 
Destination As Any, Source As Any, ByVal length As Long)

編集 削除
やじゅ  2008-02-16 18:35:27  No: 100284  IP: 192.*.*.*

As AnyはVB6でしたね、ByVal Source As Longとか

編集 削除
魔界の仮面弁士  2008-02-16 21:57:26  No: 100285  IP: 192.*.*.*

> Dim lpFileMap As Integer  '共有メモリのマッピングアドレス
そういった物に対しては、IntPtr 型を使うべきかと。

> ご指摘いただけますでしょうか。
ソースから想像すると、共有メモリから得たい値は ANSI 形式の文字列の
ようですが、そこに含まれるデータは、ASCII 文字だけですか?
それとも、2バイト文字を含んだ Shift_JIS データでしょうか?

もし、2バイト文字を含んでいるのだとしたら、構造体に指定している
DataLength というのは、「バイト数」と「文字数」のどちらの意図ですか?


> VBでの構造体及び共有メモリについて明るくないので
構造体のマーシャリングは、それなりの前提知識が必要ですよ。
特に、文字列を含んでいるような場合には。

構造体の前に、単純なバイト配列で受け取る事から始めては如何でしょう? 
たとえば、

  Dim ptr As IntPtr = [参照先のアドレス]

  Dim buf(DataLength - 1) As Byte
  Marshal.Copy(ptr, buf, 0, DataLength)
  Dim str As String = System.Text.Encoding.GetEncoding("Shift_JIS").GetString(buf)

のようにするとか。
(検証しようが無いので、保証はできませんけれども)


> 上のプログラムで何かしら原因となる箇所がありましたら
まずは、プログラムの仕様(共有メモリ上のデータ配置など)を
提示してもらわないことには、どうにもならないです。
(属性の指定が正しく行われているかどうかすら、判断できません)


そもそも、STRUCT_DATA の実装意図もわかりません。

(1) getMsg とくれば、対義語 は setMsg だと思うのですが、
  なぜ、msgSet なのでしょうか? (まぁ、これは本題とは無関係ですが)

(2) msg フィールドを使って、文字列にアクセスできるというのに、
  わざわざ、プロパティ/メソッドを用意しているのはなぜですか?

(3) 代入をメソッド化しているのに、取得がメソッドでは無いのは何故ですか?
  (というよりも、プロパティを Get/Set の両対応にすれば済むような)


>> やじゅさん
ByVal Source As Long ではマズイのでは…?

編集 削除
魔界の仮面弁士  2008-02-16 22:27:46  No: 100286  IP: 192.*.*.*

それからここは [VB2-VB6専用] の掲示板なので、2003 は対象外です。

もし続けるのであれば、隣の掲示板に移動してくださいね。

編集 削除
zzz  2008-02-17 12:18:47  No: 100287  IP: 192.*.*.*

やじゅさん
魔界の仮面弁士さん

ご意見ありがとうございます。
参考にさせていただきます。

あと、質問内容のアバウトさや板間違いについては
申し訳ありません。以後、気をつけます。

編集 削除