tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトルメモリマップドファイルのデータ渡し不具合
記事No11570
投稿日: 2008/02/28(Thu) 18:32
投稿者ダリア
いつもお世話になります。ダリアです。
すみませんが、アドバイスを頂きたくお願いいたします。

以下のような構造体(共有メモリ)を作成し、VB6にてメモリマップドファイルを使用しA側、B側からデータを見てます。(A、Bはおのおの独立したプログラムです)
メモリマップドファイルのクリアならびにデータの読込み(および配列切り替え)ははA側、データを書き込むのはB側です。


Public Type Data
    EventCode As Integer        'イベントコード
    Log(45) As String * 100 'ログデータ(100文字X45行)
    Dummy As String * 512   'ログデータ(サイズ)
    passfail(16) As Integer 'パスフェールデータ
    bin(16) As Integer      'BINデータ

End Type

Public Type DataType
    state As Integer        '構造体の書き込み条件
    console As Integer      '配列切り替え

    DATALOG(2) As Data
    
End Type


いままでは、配列を0固定にして(DATALOG(0)において)A→B、B→Aにて動作を確認しました。
上記においては、こちらが思ったとおりの結果になりました。
そこで、配列をボタンにて切り替えて実行してみました。
ボタンクリックにより、配列(1)および(2)へ書き込みを実行します。再度、ボタンクリックにより配列(0)へ処理が切り替わります。

しかし、配列(0)においては問題ないのですが。配列(1),(2)において以下のような問題が発生しています。


1、B側のデータが一部しかA側に渡されない。
 具体的には配列(0)(DATALOG(0))のデータしか渡されない。
 (渡すという表現は適切でないかもしれないのですが。メモリを読み込んで表示させると、(DATALOG(0)のデータしか表示されません)
 同じ構造体(共有メモリ)を見ていないのでしょうか?
2、A側でデータをクリアすると、B側のデータの一部しかクリアされない。
 配列(0)(DATALOG(0))のデータしかクリアされない。

上記のように構造体をきちんと認識していないように見えます。
このような場合、どのようにして原因を特定すればいいのでしょうか?
問題点としては構造体の一部に限って読み込めない、クリアできないということだと思うのですが。
問題解決の方法が全く分からなくて、よろしくお願いします。

[ツリー表示へ]
タイトルRe: メモリマップドファイルのデータ渡し不具合
記事No11580
投稿日: 2008/02/29(Fri) 15:41
投稿者リル
公開できる範囲で一部のソースを載せてもらわないとアドバイスができません。
マッピング部分やクリア部分、書き込み部分等。


ダリアさんがどういった手順でメモリマップしているのか分かりませんが、
VB6にて、

A側
@CreateFile
ハンドル取得。
ACreateFileMapping
名前付きでファイルマッピングオブジェクトを作成。
BMapViewOfFile
ファイルのビューをマップ。

B側
@OpenFileMapping
A側で作成した名前のファイルマッピングオブジェクトを開く。
AMapViewOfFile
ファイルのビューをマップ。
BCopyMemory
CFlushViewOfFile
データをディスクに書き込み。

上記の手順で正常にデータの書き換えができました。
後は書き込み位置の計算等を間違えずに行っていけば、任意の構造体で出来ます。
(様々な構造体でテスト済み)

[ツリー表示へ]
タイトルRe^2: メモリマップドファイルのデータ渡し不具合
記事No11585
投稿日: 2008/02/29(Fri) 19:26
投稿者ダリア
リルさま、お返事ありがとうございます。
どこまでプログラムソースを出すべきか、よく分からず追加情報が遅れましたことお詫びいたします。
現状とプログラムは、このような感じです。
また、色々試した結果も加えてみました。状況は変わらずですが、少し分かったところもあります。

構造体の入れ子構造に問題があるのかと思い、以下のように変更してみました。
Public Type DataType
    state As Integer        '構造体の書き込み条件
    change As Integer      'ステーション切り替え
    EventCode As Integer        'イベントコード

    D0_Log(45) As String * 100 'ログデータ(100文字X45行)
    D0_Dummy As String * 512   'ログデータ(サイズ)
    D0_A(16) As Integer         'Aデータ
    D0_B(16) As Integer      'Bデータ
    
    D1_Log(45) As String * 100 'ログデータ(100文字X45行)
    D1_Dummy As String * 512   'ログデータ(サイズ)
    D1_A(16) As Integer         'Aデータ
    D1_B(16) As Integer      'Bデータ
    
End Type
これで実行しても、状況は変わりませんでした。

次に、プログラムの簡易版を作成しました。
A側でメモリマップドファイルの開始と初期化、change切り替え、表示。
B側でメモリマップドファイルの開始、データの書き込みと表示を行います。

change切り替え時に問題が発生しているかを確認しました。
(本来、change=1でB側がD0へデータを入れる。change=2でB側がD1へデータを入れるのですが。)
change=1の時にデータをD0,D1ともに入れる。B側はD0,D1とも表示するが、A側はD0しか表示しない。
change=2の時にデータをD0,D1ともに入れる。B側はD0,D1とも表示するが、A側はD0しか表示しない。
以上により、change切り替えの処理に(多分)問題がないことが分かりました。


ふと思いついて、以下のように構造体のD0とD1をひっくり返してみました。

Public Type DataType
    state As Integer        '構造体の書き込み条件
    change As Integer      'ステーション切り替え
    EventCode As Integer        'イベントコード

    D1_Log(45) As String * 100 'ログデータ(100文字X45行)
    D1_Dummy As String * 512   'ログデータ(サイズ)
    D1_A(16) As Integer         'Aデータ
    D1_B(16) As Integer      'Bデータ

    D0_Log(45) As String * 100 'ログデータ(100文字X45行)
    D0_Dummy As String * 512   'ログデータ(サイズ)
    D0_A(16) As Integer         'Aデータ
    D0_B(16) As Integer      'Bデータ
    
    
End Type

すると、A側はD1しか表示しなくなったのです。つまり、A側は上半分しか認識していないようです。
構造体の指定の仕方が間違っているのかも?という感じになってきました。
問題は、A側、B側のプログラムというよりは、API関連か、もしくは構造体指定部分のようです。
(プログラムはA側、B側。共通で(おのおの同じ)API関連と構造体指定部を別モジュールで作成しています。A側、B側でのメモリマップドファイルの処理は関数渡しとして別モジュールに渡しています)
これから、A側とB側でサイズが異なっているのでは?という疑いが発生します。
そこで、構造体のサイズを”ファイルマップの参照”Len(lpData)で確認しました。
    A側 10370
    B側 10370
よって、指定しているデータサイズは(多分)問題ないことが分かります。

これらの結果、マッピングして欲しいデータのエリアが異なっているように思います。
マッピング位置がずれているとみるべきなのでしょうか?

API関連の関数は別モジュールに以下のようにプログラムしています。(一部抜粋)
(これは、A,Bともに共通です)

Private DummyData As DataType

' 機能      : ファイルマップの作成(Data)
Public Function MakeFileMapD(ByVal strfilemapname As String, _
                            ByRef hFileMap As Long, _
                            ByRef lpfilemap As Long) As Boolean
                            
    Dim SecurityAttribute As SECURITY_ATTRIBUTES
    Dim Ret As Long
    
    MakeFileMapD = False '' データの初期化
    
    '' ファイルマップがすでに作成されていれば、OpenFileMapping()
    '' 無ければ、CreateFileMapping()
    hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, strfilemapname)
    If (hFileMap = 0) Then '' 新規にファイルマップを生成する。
        '' 構造体を初期化する。
        SecurityAttribute.bInheritHandle = 0&           '' ハンドルの継承しない。
        SecurityAttribute.lpSecurityDescriptor = 0& '' セキュリティをセットしない。
        SecurityAttribute.nLength = 34  '' 構造体のサイズを設定
        
        hFileMap = CreateFileMapping(-1&, _
                                     SecurityAttribute, _
                                     PAGE_READWRITE, _
                                     0&, _
                                     Len(DummyData), _
                                     strfilemapname)
        If (hFileMap = 0) Then Exit Function    '' ハンドルが取れなければ失敗
        
        lpfilemap = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0&, 0&, 0&)
        If lpfilemap = 0 Then Exit Function
        
        '' 新規作成の場合はデータを初期化する。
        CopyMemory ByVal lpfilemap, ByVal DummyData, ByVal Len(DummyData)
        Ret = FlushViewOfFile(ByVal lpfilemap, Len(DummyData))
        If Ret = 0 Then Exit Function
    Else
        lpfilemap = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0&, 0&, 0&)
        If lpfilemap = 0 Then Exit Function
    End If
    MakeFileMapD = True
    
End Function


'データのクリア(Data)
Public Function ClearFileMapD(ByRef lpData As DataType, _
                               ByRef lpfilemap As Long) As Long
    Dim Ret As Long
    
    '' ファイルマップが準備できていない。
    If (lpfilemap = 0) Then ClearFileMapD = -1&

'データのクリア
    CopyMemory ByVal lpfilemap, ByVal DummyData, ByVal Len(DummyData)
    Ret = FlushViewOfFile(ByVal lpfilemap, Len(DummyData))
    If Ret = 0 Then Exit Function

End Function


' 機能      : ファイルマップの終了
Public Function CloseFileMap(ByRef hFileMap As Long, _
                             ByRef lpfilemap As Long) As Boolean
    CloseFileMap = False
    
    If hFileMap <> 0 Then
        If CloseHandle(hFileMap) Then hFileMap = 0&
    End If
    
    '' ポインタ渡しなので、ByVal となる。
    If UnmapViewOfFile(ByVal lpfilemap) Then
        lpfilemap = 0&
        CloseFileMap = True
    End If
    
End Function


' 機能      : ファイルマップの参照(Data)
Public Function GetFileMapDataD(ByRef lpData As DataType, _
                               ByRef lpfilemap As Long) As Long
    Dim Ret As Long
    Dim i As Long
    
    '' ファイルマップが準備できていない。
    If (lpfilemap = 0) Then GetFileMapDataD = -1&
    
    '' ファイルマップデータを取得
    CopyMemory ByVal lpData, ByVal (lpfilemap), ByVal Len(lpData)
    
    GetFileMapDataD = Len(lpData) '' 読み取ったデータバイト数
    
End Function


' 機能      : ファイルマップへデータを設定(Data)
Public Function SetFileMapDataD(ByRef lpData As DataType, _
                               ByRef lpfilemap As Long) As Boolean
    Dim Ret As Long
'    Dim BuffCount As Long   '' 記録するバッファの位置
    SetFileMapDataD = False
    
    '' ファイルマップが準備できていない。
    If (lpfilemap = 0) Then Exit Function
        
    '' ファイルマップデータを設定
    CopyMemory ByVal (lpfilemap), ByVal lpData, ByVal Len(lpData)
    Ret = FlushViewOfFile(ByVal lpfilemap, Len(lpData))
    If Ret <> 0 Then SetFileMapDataD = True
    
End Function

[ツリー表示へ]
タイトルRe^3: メモリマップドファイルのデータ渡し不具合
記事No11586
投稿日: 2008/02/29(Fri) 21:51
投稿者Starfish
 一般的に、WindowsAPIではユーザ定義型は、参照渡しで行います。

>     CopyMemory ByVal lpData, ByVal (lpfilemap), ByVal Len(lpData)
     CopyMemory lpData, ByVal (lpfilemap), ByVal Len(lpData)

>     CopyMemory ByVal (lpfilemap), ByVal lpData, ByVal Len(lpData)
     CopyMemory ByVal (lpfilemap), lpData, ByVal Len(lpData)

 これで、どうでしょうか?

 WindowsAPIでユーザ定義型の文字列は、バイト数になってしまうので
VB上では100文字が、ファイルマップにかかれるデータは100バイトになる
と思います。注意してください。

[ツリー表示へ]
タイトル不具合解消しました。頂いたアドバイスについて質問です
記事No11608
投稿日: 2008/03/03(Mon) 09:54
投稿者ダリア
Starfishさま、アドバイスありがとうございます。
ご指摘の通り、変更しましたところ。うまく動作しました。
本当にありがとうございます。本当に自分ではどうにもできなかったので・・・

なお、お返事が遅れましたこと申し訳ありません。
頂いたアドバイスを理解するのに少々時間がかかってしまったものですから。
なぜに参照渡し?と思ってしまいまして。

しかし、申し訳ありませんが、以下のアドバイスがよく理解できていません。

> WindowsAPIでユーザ定義型の文字列は、バイト数になってしまうので
>VB上では100文字が、ファイルマップにかかれるデータは100バイトになる
>と思います。注意してください。

1文字1バイト(?)と思っていたのですが。
不都合が起こる(もしくは注意をしなければならない)箇所が理解できないのですけれど。
どういうことなのでしょうか?

[ツリー表示へ]
タイトルRe: 不具合解消しました。頂いたアドバイスについて質問です
記事No11621
投稿日: 2008/03/03(Mon) 21:56
投稿者Starfish
> 1文字1バイト(?)と思っていたのですが。
> 不都合が起こる(もしくは注意をしなければならない)箇所が理解できないのですけれど。
> どういうことなのでしょうか?

    D1_Log(45) As String * 100 'ログデータ(100文字X45行)

 VB内部では、全角半角関係なく100文字のデータを格納できますが、
VBから外部(WindowsAPI とか ファイルへ書く場合など)へ出力
するときは、半角1文字=1バイト、全角1文字=2バイトとして
100バイトのデータが出力されます。

[ツリー表示へ]
タイトルRe^2: 回答ありがとうございます。助かりました
記事No11631
投稿日: 2008/03/04(Tue) 11:58
投稿者ダリア
Starfishさま

回答ありがとうございます。
VBでは全角半角関係なく100文字のデータが格納されるのですか。
(勘違いをしていて、全角だと2バイト取られると思っていました)
知らなかったので、とても勉強になります。
予定では半角のみですが、仮に全角で100文字入れた場合は、出力時に全角50文字しか入らないわけですよね?
このたびは、本当にありがとうございました。

[ツリー表示へ]