tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルVB.NET で、UnLha32
記事No10803
投稿日: 2012/05/15(Tue) 18:57
投稿者じょば
言語 : Visual Basic 2010 Express
OS : Windows 7 / Windows XP

UnLha32.DLL を使用して、LHA形式の書庫から格納ファイル名の一覧を取得
しようとしています。

途中の、UnlhaFindFirst で、以下のようなエラーメッセージが出ます。
-----
保護されているメモリに読み取りまたは書き込み操作を行おうとしました。
他のメモリが壊れていることが考えられます。
-----

VB6では、同じような流れのコーディングをして、動作していますが、.NETに
移行しようとして、足踏みしている状況です。

INDIVIDUALINFO構造体の使い方が悪いのか、DllImportが悪いのか、色々試して
みたのですが、上手くいきません。

このクラスを、別のフォームから使用し、GetLhaInfo でファイル一覧を取得
しようとしています。取得値を呼び出し元に戻す部分は省略しています。

他のもTry Catch などは省略していますが、現在のコーディングを掲載してみます。
間違っているところを指摘して頂ければ、ありがたいのですが。

よろしくお願いいたします。

Imports System.Runtime.InteropServices

Public Class ComUnLha
    Private Const FNAME_MAX As Integer = 512
    Private Const FNAME_MAXPLUS1 As Integer = FNAME_MAX + 1

    ' INDIVIDUALINFO構造体
    Private Structure typINDIVIDUALINFO
        Public dwOriginalSize As Integer    ' ファイルのサイズ
        Public dwCompressedSize As Integer  ' 圧縮後のサイズ
        Public dwCRC As Integer             ' 格納ファイルのチェックサム
        Public uFlag As Integer             ' 処理結果
        Public uOSType As Integer           ' 書庫作成に使われたOS
        Public wRatio As Short              ' 圧縮率
        Public wDate As Short               ' 格納ファイルの日付(DOS 形式)
        Public wTime As Short               ' 格納ファイルの時刻(〃)
        Public szFilename As String         ' 書庫名
        Public dummy1 As String
        Public szAttribute As String        ' 格納ファイルの属性 書庫固有
        Public szMode As String             ' 格納ファイルの格納モード 〃

        Public Sub New(ByVal intFnLen As Integer)
            szFilename = Strings.LSet(" ", intFnLen)
            dummy1 = Strings.LSet(" ", 3)
            szAttribute = Strings.LSet(" ", 8)
            szMode = Strings.LSet(" ", 8)
        End Sub
    End Structure

    ' LHAファイル情報取得用
    ' ハンドルと書庫ファイルを結び付ける
    <DllImport("UnLha32")> _
    Private Shared Function UnlhaOpenArchive( _
            ByVal hwnd As Integer, _
            ByVal szFilename As String, _
            ByVal dwMode As Integer) As Integer
    End Function

    ' UnLhaOpenArchive() で割り付けたハンドルを解放する
    <DllImport("UnLha32")> _
    Private Shared Function UnlhaCloseArchive( _
            ByVal harc As Integer) As Integer
    End Function

    ' 最初の格納ファイルの情報を得る
    <DllImport("UnLha32")> _
    Private Shared Function UnlhaFindFirst( _
            ByVal harc As Integer, _
            ByVal szWildName As String, _
            ByRef lpSubInfo As typINDIVIDUALINFO) As Integer
    End Function

    ' 2番目以降の格納ファイルの情報を得る
    <DllImport("UnLha32")> _
    Private Shared Function UnlhaFindNext( _
            ByVal harc As Integer, _
            ByRef lpSubInfo As typINDIVIDUALINFO) As Integer
    End Function

    ''' <summary>
    ''' LHAファイル情報取得
    ''' </summary>
    Public Function GetLhaInfo( _
            ByVal inthWnd As Integer, _
            ByVal strFileName As String) As Integer

        GetLhaInfo = -1

        Dim intArcHandle As Integer = UnlhaOpenArchive(inthWnd, strFileName, 0)

        Dim intIdx As Integer = -1

        If intArcHandle <> 0 Then
            Dim udtIndInfo As New typINDIVIDUALINFO(FNAME_MAXPLUS1)

            ' 最初の書庫内のファイルの情報を取り出す。
            Dim intFindRes As Integer = UnlhaFindFirst(intArcHandle, "*.*", udtIndInfo)

            Do While intFindRes = 0
                intIdx = intIdx + 1

                ' ファイル名
                Dim strFileName As String = _
                    udtIndInfo.szFilename.Substring(0, udtIndInfo.szFilename.IndexOf(vbNullChar)
                System.Diagnostics.Debug.Print(strFileName)

                ' 次の格納ファイルの情報を取り出す。
                intFindRes = UnlhaFindNext(intArcHandle, udtIndInfo)
            Loop

            Dim intResult As Integer = UnlhaCloseArchive(intArcHandle)
        End If

        GetLhaInfo = intIdx
    End Function
End Class

[ツリー表示へ]
タイトルRe: VB.NET で、UnLha32
記事No10804
投稿日: 2012/05/16(Wed) 13:33
投稿者shu
とりあえず構造体定義を以下のようにするとどうでしょう?


    Private Const FNAME_MAX32 As Integer = 512

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure typINDIVIDUALINFO
        <MarshalAs(UnmanagedType.U4)> Public dwOriginalSize As Integer   '--- ファイルのサイズ
        <MarshalAs(UnmanagedType.U4)> Public dwCompressedSize As Integer '--- 圧縮後のサイズ
        <MarshalAs(UnmanagedType.U4)> Public dwCRC As Integer            '--- 格納ファイルのチェックサム
        <MarshalAs(UnmanagedType.U4)> Public uFlag As Integer            '--- 処理結果
        '--- Status flag
        <MarshalAs(UnmanagedType.U4)> Public uOSType As Integer          '--- 書庫作成に使われた OS
        <MarshalAs(UnmanagedType.U2)> Public wRatio As Short           '--- 圧縮率
        <MarshalAs(UnmanagedType.U2)> Public wDate As Short            '--- 格納ファイルの日付(DOS 形式)
        <MarshalAs(UnmanagedType.U2)> Public wTime As Short            '--- 格納ファイルの時刻(〃)
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=FNAME_MAX32 + 1)> Public szFileName As String    '--- 書庫名
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=3)> Public dummy1 As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=8)> Public szAttribute As String        '--- 格納ファイルの属性(書庫固有)
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=8)> Public szMode As String             '--- 格納ファイルの格納モード(〃)
    End Structure

[ツリー表示へ]
タイトルRe^2: VB.NET で、UnLha32
記事No10806
投稿日: 2012/05/16(Wed) 15:26
投稿者じょば
shu様

具体的な修正例をお示し頂き、ありがとうございます。

MarshalAs 属性を、お示し頂いたまま適用すると、UnlhaFindFirstが
エラーを出さなくなりました。

私のコード例では、返された文字列を vbNullChar でカットしていますが、
逆にこれは不要という結果になりました。

VB6 で使っていたDLLを使用するには、まだまだ私の知識では難しいのですが、
これで一つ、賢くなりました (^^;

大変、ありがとうございました。

[ツリー表示へ]
タイトルRe: VB.NET で、UnLha32
記事No10805
投稿日: 2012/05/16(Wed) 13:44
投稿者魔界の仮面弁士
> 途中の、UnlhaFindFirst で、
Alias 句を明示しましょう。この API には
・UnlhaFindFirstW
・UnlhaFindFirstA
・UnlhaFindFirst
という 3 種の関数エントリーがあります。
(Ver.2.40 以降の DLL なら、個人的には W 版の利用をお奨めします)


> 以下のようなエラーメッセージが出ます。
文字列操作に問題がありそうですね。

INDIVIDUALINFO 構造体の文字列フィールド(szFileName 等)には、
それぞれ、MarshalAs 属性で SizeConst を指定する必要があります。
http://msdn.microsoft.com/ja-jp/library/s9ts558h.aspx



> INDIVIDUALINFO構造体の使い方が悪いのか、DllImportが悪いのか、色々試して
> みたのですが、上手くいきません。
以下、今回の問題とは直接関係ありませんが、気になった点として:

・HWND や HARC は、Integer ではなく IntPtr で取り扱うべきです。

・文字列を扱う API では、DllImport あるいは Declare での宣言時に、
 Unicode / Ansi 指定を明示しておくことが望ましいです。

・構造体あるいはクラスの場合も同様に、StructLayout 属性にて
 CharSet を明示しましょう。

[ツリー表示へ]
タイトルRe^2: VB.NET で、UnLha32
記事No10807
投稿日: 2012/05/16(Wed) 15:35
投稿者じょば
魔界の仮面弁士様

ご教示を頂き、ありがとうございます。

shu様からのご教示で、エラーは解消されました。

IntPtr にすべきとの件、大変勉強になります。

これは、他のAPIに関しても、元のヘッダファイルを見て、Integerか
IntPtrかを判断し、適切にコーディングしたいと思います。

> ・HWND や HARC は、Integer ではなく IntPtr で取り扱うべきです。
> ・文字列を扱う API では、DllImport あるいは Declare での宣言時に、
>  Unicode / Ansi 指定を明示しておくことが望ましいです。
> ・構造体あるいはクラスの場合も同様に、StructLayout 属性にて
>  CharSet を明示しましょう。

この3点に関して、注意してコーディングしたいと思います。

ありがとうございました。

[ツリー表示へ]