tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルunlha32のコールバックを受け取った時の構造体がおかしい?
記事No10434
投稿日: 2011/02/05(Sat) 20:41
投稿者A.K
こんにちは。
前回 http://hanatyan.sakura.ne.jp/vbnetbbs/wforum.cgi?no=10429&reno=no&oya=10429&mode=msgview&page=0
に引き続きの質問で申し訳ないのですが、どうにもよく分からないので質問させて下さい。

前回の質問のソースコードで、UnlhaSetOwnerWindowEx64を使用したコールバックの設定を行いました。
そこで、書庫を解凍する際のコールバックを受け取るために、下記のようなコードを書きました。
(unlhaが返すWM_ARCEXTRACTの値は、あらかじめRegisterWindowMessageで取得済みです)

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    MyBase.WndProc(m)

    If m.HWnd = _HWnd Then
        If m.Msg = wm_arcextractMsg AndAlso m.WParam.ToInt64 = 1 Then
            'mode=1:解凍処理
            Dim extractingInfo As New EXTRACTINGINFO64
            Dim typeHandle As System.RuntimeTypeHandle = System.Type.GetTypeHandle(extractingInfo)
            Dim extractingInfoType As Type = Type.GetTypeFromHandle(typeHandle)

            '構造体の取得
            extractingInfo = DirectCast(Marshal.PtrToStructure(m.LParam, extractingInfoType), EXTRACTINGINFO64)

            Debug.Print("SourceFileName : " & extractingInfo.szSourceFileName)
            Debug.Print("DestFileName : " & extractingInfo.szDestFileName)

            Debug.Print("FileSize : " & extractingInfo.llFileSize.ToString)
            Debug.Print("WriteSize : " & extractingInfo.llWriteSize.ToString)

        End If
    End If
End Sub

すると、ファイルサイズがあり得ない程大きくなっていたり、ファイル名の文字が欠けていたりと、extractingInfo構造体から得られる値がおかしくなっていました。
ファイル名の文字は、例えば本来は「あいうえお.jpg」であるべきところ、「うえお.jpg」と戻ってきていたため、「もしかしたら、読み出し位置が32ビット分ずれているのでは?」と思い、EXTRACTINGINFO64の宣言を以下のように書き換えてみたところ、ファイル名・サイズ等の情報が正常に得られるようになりました。

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi, Pack:=1)> _
      Public Structure EXTRACTINGINFO64
'        Public dwStructSize As Integer  ←ここをコメントアウトした
        Public exinfo As EXTRACTINGINFO
        Public llFileSize As Long
        Public llCompressedSize As Long
        Public llWriteSize As Long
        Public dwAttributes As Integer
        Public dwCRC As Integer
        Public uOSType As Integer
        Public wRatio As Short
        Public ftCreateTime As Long
        Public ftAccessTime As Long
        Public ftWriteTime As Long
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=8)> _
           Public szMode As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=513)> _
           Public szSourceFileName As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=3)> _
           Public dummy1 As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=513)> _
           Public szDestFileName As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=3)> _
           Public dummy As String
    End Structure

これはコールバックを受け取るフォーム側での宣言です。
UnlhaSetOwnerWindowEx64を呼び出す側のクラスでは、コメントアウトを行わないで構造体宣言を行っています

つまり、現在
○unlha32を使って解凍処理を行うクラス
 EXTRACTINGINFO64の構造を変えずに宣言
 UnlhaSetOwnerWindowEx64を使用
○unlha32のコールバックを受け取るFormクラス
 EXTRACTINGINFO64の構造を変えて宣言(32バイト切り詰め)

という状態にして、ようやく正常に動いている状態です。(これを正常と言えるならですが…)
現状問題ないとはいえ、EXTRACTINGINFO64の宣言が二つあって統一されていないのは気持ち悪いですし、どこか私の方法が間違えているなら直しておきたいです。

当方WinXP、VB2005です。
よろしくお願いします。
どなたか、分かる方がいらっしゃいましたら、よろしくお願いします。



〜UNLHA32 WINMES.TXTより〜
-----------------------------------------------------------------------
%3. ARCHIVERPROC の定義
-----------------------------------------------------------------------

-----------------------------------------------------------------------
BOOL CALLBACK ARCHIVERPROC(
                          HWND    _hwnd,
                          UINT    _uMsg,
                          UINT    _nState,
                          LPVOID  _lpEis
                      );
-----------------------------------------------------------------------

引数
        _hwnd           UnlhaSetOwnerWindowEx で指定したウィンドウハンド
                        ルが渡されます。
        _uMsg           現在のところは wm_arcextract となります。
        _nState         wm_arcextract の wParam,つまり nMode が格納され
                        ます。
        _lpEis          構造体へのポインタです。格納ファイルに関する情報
                        が得られます。実際には,次のどれかとなります。

            LPEXTRACTINGINFOEX _lpEis
                    UnlhaSetOwnerWindowEx() か  sizeof(EXTRACTINGINFOEX)
                    を指定して UnlhaSetOwnerWindowEx64() を使用した場合。
            LPEXTRACTINGINFOEX32 _lpEis
                    sizeof(EXTRACTINGINFOEX32) を指定して UnlhaSetOwner-
                    WindowEx64() を使用した場合。
            LPEXTRACTINGINFOEX64 _lpEis
                    sizeof(EXTRACTINGINFOEX64) を指定して UnlhaSetOwner-
                    WindowEx64() を使用した場合。

戻り値
        TRUE            UNLHA32.DLL に処理の継続を指示します。
        FALSE           UNLHA32.DLL に処理を中止させます。 ただし,
                        _nState が 4  の場合 (作業書庫の書き戻し時) は中
                        止させることができません。

[ツリー表示へ]
タイトルRe: unlha32のコールバックを受け取った時の構造体がおかしい?
記事No10435
投稿日: 2011/02/05(Sat) 23:17
投稿者魔界の仮面弁士
# 再現可能なコードを書くのが面倒なので、検証せずに回答しています。

> CharSet:=CharSet.Ansi
現行バージョンでは、Function も Structure も CharSet.Unicode の方が
良い気がしますが、それぞれの CharSet 指定は一致していますか?

> 前回の質問のソースコードで、UnlhaSetOwnerWindowEx64を使用した
> コールバックの設定を行いました。
WndProc で LParam から受ける代わりに、ARCHIVERPROC のデリゲートを渡して
構造体を直接受け取った場合も、同じ結果になりますか?

> もしかしたら、読み出し位置が32ビット分ずれているのでは
exinfo の 4 バイト前の位置の値が dwStructSize になっていたのでしょうか?

> Dim extractingInfo As New EXTRACTINGINFO64
クラスでは無いので、New は不要な気も。

> EXTRACTINGINFO64の宣言が二つあって統一されていないのは気持ち悪いですし
Marshal.SizeOf の結果は、何が返されていますか?

[ツリー表示へ]
タイトルRe^2: unlha32のコールバックを受け取った時の構造体がおかしい?
記事No10437
投稿日: 2011/02/06(Sun) 07:57
投稿者A.K
魔界の仮面弁士様

ご返答ありがとうございます。
指摘された部分を順番に試してみました。

> 現行バージョンでは、Function も Structure も CharSet.Unicode の方が
> 良い気がしますが、それぞれの CharSet 指定は一致していますか?
全てAnsiで定義しており、定義は一致しています。
が、一応全てUnicodeに直しました。


> exinfo の 4 バイト前の位置の値が dwStructSize になっていたのでしょうか?
これは確認していません。(その前に直ったので…
dwStructSizeをコメントアウトせずに実行し、取得してみても、値はデタラメになっていたのは確認しました。

> クラスでは無いので、New は不要な気も。
Newがないと警告が出るので入れていました。現在は消しましたが、どちらにしても動作には影響ないようです(w

> Marshal.SizeOf の結果は、何が返されていますか?
○unlha32を使って解凍処理を行うクラス
Unicode 4218
Ansi  2146
○unlha32のコールバックを受け取るFormクラス
Unicode 4214
Ansi  2142
でした。(Integerを削ったら32ビット減るはずが… 不思議です)


> WndProc で LParam から受ける代わりに、ARCHIVERPROC のデリゲートを渡して
> 構造体を直接受け取った場合も、同じ結果になりますか?
Form規定のコールバック
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
の代わりに、マニュアルに載っていたコールバック関数
LRESULT CALLBACK WindowProc(
                HWND    _hwnd,
                UINT    _uMsg,      // wm_arcextract
                WPARAM  _wParam,
                LPARAM  _lParam
で受取るようにしたところ、普通に正常な値が取れてしまいました。
どういう理屈があるのかよく分かりませんが、Formを継承したクラス以外でコールバックを受取れる方が便利なので、こちらを使うことにします。
ただ、マニュアルには「Returnに0を返すと継続、それ以外は中断」とあったものが、
0を返すと中断で、それ以外が継続になっているようで面食らいましたが…
(Dllのバージョンが違うのでしょうか? 書き忘れていましたがUnlha32.Dllのバージョンは2.67です)

ともあれ、色々とヒントをいただき、ありがとうございました。
本当に助かりました。


お目汚しですが、今回のソースを挙げてみます。

'モジュール部
    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode, Pack:=1)> _
    Public Structure EXTRACTINGINFO
        Public dwFileSize As Integer
        Public dwWriteSize As Integer
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=513)> _
           Public szSourceFileName As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=3)> _
           Public dummy1 As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=513)> _
           Public szDestFileName As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=3)> _
           Public dummy As String
    End Structure

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode, Pack:=1)> _
      Public Structure EXTRACTINGINFO64
        Public dwStructSize As Integer
        Public exinfo As EXTRACTINGINFO
        Public llFileSize As Long
        Public llCompressedSize As Long
        Public llWriteSize As Long
        Public dwAttributes As Integer
        Public dwCRC As Integer
        Public uOSType As Integer
        Public wRatio As Short
        Public ftCreateTime As Long
        Public ftAccessTime As Long
        Public ftWriteTime As Long
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=8)> _
           Public szMode As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=513)> _
           Public szSourceFileName As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=3)> _
           Public dummy1 As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=513)> _
           Public szDestFileName As String
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=3)> _
           Public dummy As String
    End Structure

    Public Const WM_ARCEXTRACT As String = "wm_arcextract"      'ウィンドウメッセージ用定数
    Friend wm_arcextractMsg As Integer = 0                      'ウィンドウメッセージ用変数



'Unlha使用を使うクラス

    Private Delegate Function WindowProcDelegate(ByVal _hwnd As IntPtr, ByVal _uMsg As Integer, _
                                        ByVal _wParam As IntPtr, ByVal _lParam As IntPtr) As Integer

    <DllImport("unlha32", _
        EntryPoint:="UnlhaSetOwnerWindowEx64", _
        ExactSpelling:=True)> _
    Private Shared Function UnlhaSetOwnerWindowEx64(ByVal _hwnd As IntPtr, _
        ByVal _lpArcProc As WindowProcDelegate, _
        ByVal _dwStructSize As Integer) As Boolean
    End Function


    <DllImport("unlha32", _
        EntryPoint:="UnlhaKillOwnerWindowEx64", _
        ExactSpelling:=True)> _
    Private Shared Function UnlhaKillOwnerWindowEx64(ByVal _hwnd As IntPtr) As Boolean
    End Function

    Private _hwnd As IntPtr

'コード部

    Public Sub New(ByVal hwnd As IntPtr)

        _hwnd = hwnd

        'コールバック取得準備
        If wm_arcextractMsg = 0 Then
            wm_arcextractMsg = RegisterWindowMessage(WM_ARCEXTRACT)
        End If

        'ウィンドウメッセージの設定
        Dim struct As EXTRACTINGINFO64
        Dim winProcDelegate As New WindowProcDelegate(AddressOf WndProc)

        If Not UnlhaSetOwnerWindowEx64(hwnd, winProcDelegate, Marshal.SizeOf(struct)) Then
            'エラーにする
        End If

    End Sub

    Protected Function WndProc(ByVal _hwnd As IntPtr, ByVal _uMsg As Integer, _
                                        ByVal _wParam As IntPtr, ByVal _lParam As IntPtr) As Integer

        If _uMsg = wm_arcextractMsg AndAlso _wParam.ToInt64 = 1 Then
            'mode=1:解凍処理
            Dim extractingInfo As EXTRACTINGINFO64
            Dim typeHandle As System.RuntimeTypeHandle = System.Type.GetTypeHandle(extractingInfo)
            Dim extractingInfoType As Type = Type.GetTypeFromHandle(typeHandle)

            '構造体の取得
            extractingInfo = DirectCast(Marshal.PtrToStructure(_lParam, extractingInfoType), EXTRACTINGINFO64)

            Debug.Print("SourceFileName : " & extractingInfo.szSourceFileName)
            Debug.Print("DestFileName : " & extractingInfo.szDestFileName)

            Debug.Print("FileSize : " & extractingInfo.llFileSize.ToString)
            Debug.Print("WriteSize : " & extractingInfo.llWriteSize.ToString)

        End If

        '処理の継続
        Return -1

    End Function

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' TODO: 明示的に呼び出されたときにマネージ リソースを解放します
            End If

            ' TODO: 共有のアンマネージ リソースを解放します

            'ウィンドウハンドルの解放 
            UnlhaKillOwnerWindowEx64(IntPtr.Zero)

        End If
        Me.disposedValue = True
    End Sub

[ツリー表示へ]