none
VB6で作成したバイナリデータをC#で同じ値で読み込むには? RRS feed

  • 質問

  • こんにちは
    VB6のバイナリデータをC#で読み込もうとしたのですが
    違う値になってしまいます。
    アップグレードウィザードで作成したVB.netだと同じです
    ただしOpenMode.RandomをOpenMode.Binaryに変更すると違う値になります。
    C#で同じ値になるようにアドバイスよろしくお願いします。

    【環境】
    WindowsXP+VS2008sp1 .NET Framework 2.0

    【テスト結果】
    要素番号   0  1  2  3  4  5  6  7  8  9 (VBは+1)
    VB6 Prog:  1  1  0  0  0  0  0  0  0  0
    VB Prog1:  1  1  0  0  0  0  0  0  0  0
    VB Prog2:  1  0  0  0  0  0  0  0  0  0
    C# Prog1:  1  0  0  0  0  0  0  0  0  0
    C# Prog2:  1  0  0  0  0  0  0  0  0  0

    ----- VB6 Prog(○) -----
    Public St(10) As Integer
    Open "C:\StratUp.Dat" For Random As #3
    Get #3, 1, St(1)

    ----- VB Prog1(○) -----
    Public St(10) As Short
    FileOpen(3, "C:\StratUp.Dat", OpenMode.Random)
    FileGet(3, St(1), 1)

    ----- VB Prog2(×) -----
    Public St(10) As Short
    FileOpen(3, "C:\StratUp.Dat", OpenMode.Binary)
    FileGet(3, St(1), 1)

    ----- C# Prog1(×) -----
    using Microsoft.VisualBasic;
    static public Int16[] intSt = new Int16[10];
    FileSystem.FileOpen(3, "C:\\StratUp.Dat", OpenMode.Random,
        OpenAccess.Read, OpenShare.Shared, 10);
    for (int i = 0; i < 10; i++)
    {
        Microsoft.VisualBasic.FileSystem.FileGet(3, ref intSt[i], i+1);
    }


    ----- C# Prog2(×) -----
    static public Int16[] intSt = new Int16[10];
    FileStream fs = new FileStream(
      "C:\\StratUp.Dat", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    int intFileSize = (int)fs.Length;
    byte[] buf1 = new byte[intFileSize];
    int intRemainSize = intFileSize;
    int intBufPos = 0;
    int intBufSize = 1024;

    try
    {
      while (intRemainSize > 0)
      {
        int intReadSize = fs.Read(buf1, intBufPos, Math.Min(intBufSize, intRemainSize));
        intBufPos += intReadSize;
        intRemainSize -= intReadSize;
      }
      fs.Dispose();
      for (int i = 0; i < 10; i++)
      {
        intSt[i] = buf1[i];
      }
    }
    catch (Exception ErrMsg)
    {
        MessageBox.Show(ErrMsg.Message);
    }
    2009年6月6日 4:19

回答

  • ----- VB Prog1(○) -----
    Public St(10) As Short
    FileOpen(3, "C:\StratUp.Dat", OpenMode.Random)
    FileGet(3, St(1), 1)

    (中略)

    ----- C# Prog1(×) -----
    using Microsoft.VisualBasic;
    static public Int16[] intSt = new Int16[10];
    FileSystem.FileOpen(3, "C:\\StratUp.Dat", OpenMode.Random,
        OpenAccess.Read, OpenShare.Shared, 10);
    for (int i = 0; i < 10; i++)
    {
        Microsoft.VisualBasic.FileSystem.FileGet(3, ref intSt[i], i+1);
    }
    VisualBasic名前空間のクラスをよく知りませんが、VB.NETのFileOpen関数とC#のFileOpenメソッドではレコード長の指定が変わっていませんか?
    FileOpen関数では引数を省略した場合は -1 になるそうです。このあたりがぱっと見たところでの差でしょうか?

    http://msdn.microsoft.com/ja-jp/library/afh37kh8.aspx
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    • 回答としてマーク aukty 2009年6月6日 11:26
    2009年6月6日 6:36
    モデレータ
  • OpenMode.Random で RecordLength が -1 だと
    ファイル ポインタが 256 バイト進むとか、じゃないかな?
    追記:256 でなく 128 の間違いでした。
    virtual void
    • 編集済み Abstrakt 2009年6月6日 14:41 計算ミス
    • 回答としてマーク aukty 2009年6月7日 1:26
    2009年6月6日 14:27
  • > Seek関数の説明でRandomで開かれたときとBinary , Input , Output , Appendで開かれた時
    > 動作が違いますね。この辺が怪しいのかなって思っているのですが。

    そのようです。OpenMode が Random で、RecordLength をデフォルト (-1) にすると、 Abstrakt さん
    のレスにありますように、128 バイトおきにファイルのデータを読むようで、それゆえ結果が下記のよ
    うになるのだと思います。

    VB6 Prog:  1  1  0  0  0  0  0  0  0  0
    VB Prog1:  1  1  0  0  0  0  0  0  0  0

    VB6 は全くさわったことがなく、VB6 でのファイルアクセスの方法は知らなかったのですが、ちょっと
    調べてみたところ、VB.NET とはずいぶん違うようですね。

    ファイル アクセスの種類と関数
    http://msdn.microsoft.com/ja-jp/library/aa903295(VS.71).aspx

    バイナリファイルを読むなら OpenMode は Binary とするのが正解のようです。

    というわけで、C# で VisualBasic 名前空間を使わずにやるとすると、128 バイトおきに読んでいく他
    手はなさそうです。

    • 回答としてマーク aukty 2009年6月7日 1:26
    2009年6月6日 15:30

すべての返信

  • ----- VB Prog1(○) -----
    Public St(10) As Short
    FileOpen(3, "C:\StratUp.Dat", OpenMode.Random)
    FileGet(3, St(1), 1)

    (中略)

    ----- C# Prog1(×) -----
    using Microsoft.VisualBasic;
    static public Int16[] intSt = new Int16[10];
    FileSystem.FileOpen(3, "C:\\StratUp.Dat", OpenMode.Random,
        OpenAccess.Read, OpenShare.Shared, 10);
    for (int i = 0; i < 10; i++)
    {
        Microsoft.VisualBasic.FileSystem.FileGet(3, ref intSt[i], i+1);
    }
    VisualBasic名前空間のクラスをよく知りませんが、VB.NETのFileOpen関数とC#のFileOpenメソッドではレコード長の指定が変わっていませんか?
    FileOpen関数では引数を省略した場合は -1 になるそうです。このあたりがぱっと見たところでの差でしょうか?

    http://msdn.microsoft.com/ja-jp/library/afh37kh8.aspx
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    • 回答としてマーク aukty 2009年6月6日 11:26
    2009年6月6日 6:36
    モデレータ
  • Azuleanさん、ありがとうございます。
    確かに-1 って書いてますね
    見落としてました、-1に変更したところ上手くいきました。
    VisualBasic名前空間を使いたいわけじゃなく
    C#Prog2の方法など試してるけど上手くいなないので
    VB.netで試して、んじゃそれをC#に直したら上手くいくはず
    でも本音はVisualBasic名前空間を使わずにやりたい

    ずうずうしいのですが もう少し待ってみます。
    2009年6月6日 7:56
  • C:\StratUp.Dat ファイルの中身はどうなっているのでしょうか?

    コードを見る限り、C# Prog2 は正しく、取得した内容も正しいはずですが、そうすると、そもそも
    VB6 Prog では正しく取得できていなかったということになりますが、いかがですか?

    その場合は、VB6 Prog と C# Prog2 の結果が同じ値になるようにすることはできないですね。


    > -1に変更したところ上手くいきました。

    C# Prog1 の結果が VB6 Prog の結果と同じになったということでしょうか?

    以下のコードで試してみましたが、正しくデータを取得するには OpenMode は Binary、intSt は
    byte[] とする必要があるようです。引数 RecordLength は 10 でも -1 でも結果は同じで、正し
    くデータを取得できました。お試しください。

    byte[] data = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a };
    using (FileStream fileStream = File.Create(@"c:\temp\StartUp.dat"))
    {
        fileStream.Write(data, 0, data.Length);
        fileStream.Close();
    }
    
    byte[] intSt = new byte[10];
    Microsoft.VisualBasic.FileSystem.FileOpen(3, @"c:\temp\StartUp.dat", Microsoft.VisualBasic.OpenMode.Binary,
        Microsoft.VisualBasic.OpenAccess.Read, Microsoft.VisualBasic.OpenShare.Shared, 10);
    
    for (int i = 0; i < 10; i++)
    {
        Microsoft.VisualBasic.FileSystem.FileGet(3, ref intSt[i], i + 1);
    }
    
    Microsoft.VisualBasic.FileSystem.FileClose(new Int32[] { 3 });
    
    foreach (Byte b in data)
    {
        Console.Write("{0:X2} ", b);
    }
    
    Console.WriteLine("");
                
    foreach (Byte b in intSt)
    {
        Console.Write("{0:X2} ", b);
    }
    
    Console.WriteLine("");
    
    using (FileStream fileStream = File.Open(@"c:\temp\StartUp.dat", FileMode.Open, FileAccess.Read))
    {
        fileStream.Read(intSt, 0, intSt.Length);
        fileStream.Close();
    }
    
    foreach (Byte b in intSt)
    {
        Console.Write("{0:X2} ", b);
    }
    


    結果は以下の通りです。

    01 02 03 04 05 06 07 08 09 0A
    01 02 03 04 05 06 07 08 09 0A
    01 02 03 04 05 06 07 08 09 0A

    2009年6月6日 8:47
  • C# Prog2のコードと、SurferOnWwwさんが書かれているコードは1バイト毎に1個のデータが前提となっていますよね?
    VB6のコード、VB.NET Prog1のコード、C# Prog1のコードはshort型(Int16型)ベースであるからこそ、うまく動いているのだと推測しています。
    (実際にファイルにはどのように書かれているかは、検討・確認していませんが…)


    C# Prog2のコードで問題点があるとすれば、1バイトずつshort(Int16)型にキャストしているところでしょうか。
    ただ、実行結果の「C# Prog2:  1  0  0  0  0  0  0  0  0  0」が気になります。どこかにもう1個以上、1になっている箇所はありませんか?
    ないとすると、まだ何か問題が埋もれているということに…。


    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年6月6日 9:24
    モデレータ
  • SurferOnWwwさん、こんばんは。

    >>C:\StratUp.Dat ファイルの中身はどうなっているのでしょうか?

    バイナリエディタで確認したところ

    00を省略して書くと次の通り。
         +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
    000| 01
    001|
    002|
    003|
    004|
    005|
    006|
    007|
    008| 01
    009|
    途中省略
    600| 01

     

    >>コードを見る限り、C# Prog2 は正しく、取得した内容も正しいはずですが、そうすると、そもそも
    >>VB6 Prog では正しく取得できていなかったということになりますが、いかがですか?

     

    おっしゃるように、バイナリで読み込むとVB Prog2/VC Prog2が正しく、バイナリエディタと一致します。

    正しい、正しくないは別としても、現状VB6Progは動いていて、設定を変更すると、想定している設定に変わります。

    引き続き調べていたのですが

    Seek関数の説明でRandomで開かれたときとBinary , Input , Output , Appendで開かれた時

    動作が違いますね。この辺が怪しいのかなって思っているのですが。

    http://msdn.microsoft.com/ja-jp/library/7af2feyt.aspx


    > -1に変更したところ上手くいきました。

    >>C# Prog1 の結果が VB6 Prog の結果と同じになったということでしょうか?

    >>以下のコードで試してみましたが、正しくデータを取得するには OpenMode は Binary、intSt は
    >>byte[] とする必要があるようです。引数 RecordLength は 10 でも -1 でも結果は同じで、正し
    >>くデータを取得できました。お試しください。

     

    ありがとうございます。試してみました。

    ややこしくなったので、ちょっと整理しますね

    【VB6と一致(全てOpenMode:Random )】

    VB6 Prog/ VB Prog1/ VC Prog1(-1)

    【バイナリエディタと一致(OpenMode: Binary / VC Prog1のみRandom )】

    SurferOnWwwさんのサンプル/ VB Prog2/ VC Prog1(10)/ VC Prog2

    ちょっと変な感じですがこんな感じです。
    2009年6月6日 13:59
  • short型は怪しいと思いますよね
    最初intで作ったけど、上手くいかないので、VB6のソース(int)をVS2008でアップグレードウィザードしたら
    int16になっていたので、合わせたのですが、intでも動くようになればに戻そうかなと思います。

    1が次に出てくるのは
    80番目その次が600番目です。VBで変更して何が変わるかバイナリエディタで確認したほうがいいのかも
    しれないですね。
    試してみますと下記ながら試しました。

    1をon アドレス000が01
    2をon アドレス080が01
    3をon アドレス100が01

    VC Prog1(-1)では
    1 1 1 0 0 0 0 0 0 0
    VC Prog1(10)では
    1 0 0 0 0 0 0 0 0 0

    どっちも正解というか、理屈が少し分かりました
    ありがとうございます。
    2009年6月6日 14:25
  • OpenMode.Random で RecordLength が -1 だと
    ファイル ポインタが 256 バイト進むとか、じゃないかな?
    追記:256 でなく 128 の間違いでした。
    virtual void
    • 編集済み Abstrakt 2009年6月6日 14:41 計算ミス
    • 回答としてマーク aukty 2009年6月7日 1:26
    2009年6月6日 14:27
  • > Seek関数の説明でRandomで開かれたときとBinary , Input , Output , Appendで開かれた時
    > 動作が違いますね。この辺が怪しいのかなって思っているのですが。

    そのようです。OpenMode が Random で、RecordLength をデフォルト (-1) にすると、 Abstrakt さん
    のレスにありますように、128 バイトおきにファイルのデータを読むようで、それゆえ結果が下記のよ
    うになるのだと思います。

    VB6 Prog:  1  1  0  0  0  0  0  0  0  0
    VB Prog1:  1  1  0  0  0  0  0  0  0  0

    VB6 は全くさわったことがなく、VB6 でのファイルアクセスの方法は知らなかったのですが、ちょっと
    調べてみたところ、VB.NET とはずいぶん違うようですね。

    ファイル アクセスの種類と関数
    http://msdn.microsoft.com/ja-jp/library/aa903295(VS.71).aspx

    バイナリファイルを読むなら OpenMode は Binary とするのが正解のようです。

    というわけで、C# で VisualBasic 名前空間を使わずにやるとすると、128 バイトおきに読んでいく他
    手はなさそうです。

    • 回答としてマーク aukty 2009年6月7日 1:26
    2009年6月6日 15:30
  • 適当に入れたらいいのかと思ってたけど-1って大きな意味があったんですね
    勉強になりました。
    ありがとうございます。
    2009年6月7日 1:26
  • VB6ばっかり使ってますが、知らなかったですよ

    ランダムアクセスも全てバイナリだと思っていたけど
    テキスト型もあるとはやっかいだな。
    singleやdoubleやstringもでてくるしちょっとやってみて時間かかりそうなら
    諦めてVisualBasic 名前空間使うことにします
    ありがとうございます。
    2009年6月7日 1:43