• 如何取得VB編譯後的LST檔

說明

    什麼是lst檔呢? 對於使用VB的使用者而言的確是陌生了點,其他編譯器通常會有lst檔的輸出選項,lst檔會將原始程式以及編譯後的組合語言一同輸出,內容大概是這樣

      ?Form_Load@Form1@@AAGXXZ PROC NEAR   ; Form1::Form_Load, COMDAT

      ; 84   : Private Sub Form_Load()

       push ebp
       mov ebp, esp
       sub esp, 12    
      ; 0000000cH
       push OFFSET FLAT:___vbaExceptHandler
       mov eax, DWORD PTR fs:__except_list
       push eax
       mov DWORD PTR fs:__except_list, esp
       sub esp, 136   
      ; 00000088H
       push ebx
       push esi
       push edi
       mov DWORD PTR __$SEHRec$[ebp+8], esp
       mov DWORD PTR __$SEHRec$[ebp+12], OFFSET FLAT:$S39
       mov ebx, DWORD PTR _Me$[ebp]
       mov eax, ebx
       and eax, 1
       mov DWORD PTR __$SEHRec$[ebp+16], eax
       and ebx, -2     ; fffffffeH
       push ebx
       mov DWORD PTR _Me$[ebp], ebx
       mov ecx, DWORD PTR [ebx]
       call DWORD PTR [ecx+4]

      ; 85   : Dim inj As Long, i As Long
      ; 86   :
      ; 87   : comString = Command

       lea edx, DWORD PTR _unnamed_var1$[ebp]
       xor esi, esi
       push edx
       mov DWORD PTR _unnamed_var1$[ebp], esi
       mov DWORD PTR _unnamed_var1$[ebp], esi
       mov DWORD PTR _unnamed_var1$[ebp], esi
       mov DWORD PTR _unnamed_var1$[ebp], esi
       mov DWORD PTR _unnamed_var1$[ebp], esi
       mov DWORD PTR _unnamed_var1$[ebp], esi
       mov DWORD PTR _unnamed_var1$[ebp], esi
       mov DWORD PTR _unnamed_var1$[ebp], esi
       call DWORD PTR __imp____vba@001E714C
       lea eax, DWORD PTR _unnamed_var1$[ebp]
       add ebx, 64     ; 00000040H
       push eax
       call DWORD PTR __imp____vbaStrVarMove
       mov edx, eax
       lea ecx, DWORD PTR _unnamed_var1$[ebp]
       call DWORD PTR __imp_@__vbaStrMove
       mov edx, eax
       mov ecx, ebx
       call DWORD PTR __imp_@__vbaStrCopy
       lea ecx, DWORD PTR _unnamed_var1$[ebp]
       call DWORD PTR __imp_@__vbaFreeStr
       lea ecx, DWORD PTR _unnamed_var1$[ebp]
       call DWORD PTR __imp_@__vbaFreeVar

      ; 88   :
      ; 89   : inj = InStr(LCase(comString), ".obj""")

       lea ecx, DWORD PTR _unnamed_var1$[ebp]
       lea edx, DWORD PTR _unnamed_var1$[ebp]
       push ecx
       push edx
       mov DWORD PTR _unnamed_var1$[ebp+8], ebx
       mov DWORD PTR _unnamed_var1$[ebp], 16392 ; 00004008H
       call DWORD PTR __imp____vba@001F10A4
       lea eax, DWORD PTR _unnamed_var1$[ebp]
       push 1
       lea ecx, DWORD PTR _unnamed_var1$[ebp]
       push eax
       push ecx
       lea edx, DWORD PTR _unnamed_var1$[ebp]
       push esi
       push edx
       mov DWORD PTR _unnamed_var1$[ebp+8], OFFSET FLAT:___vba@001F1F2C
       mov DWORD PTR _unnamed_var1$[ebp], 8
       call DWORD PTR __imp____vbaInStrVar
       push eax
       call DWORD PTR __imp____vbaI4Var
       mov edi, eax
       lea eax, DWORD PTR _unnamed_var1$[ebp]
       lea ecx, DWORD PTR _unnamed_var1$[ebp]
       push eax
       push ecx
       push 2


       

    透過這個檔案,我們可以很清楚的知道程式編譯的結果,由於VB是個經過多層包裝,例如上例可以發現InStr這個函數底層是透過呼叫 __imp____vbaInStrVar去做的.

    但是VB的編譯並不是使用者能控制的,他交給VB6.exe去處理,你可以試著呼叫E:\Program Files\Microsoft Visual Studio\VB98\VB6.EXE/?
    後方加個/?的參數 會顯示出以下視窗

     

    但也是僅限於編譯VBP檔,並不能輸出LST檔,我們必須探討到更底層,VB6.EXE再編譯執行檔時有幾個步驟

    1. 產生一些中繼檔
    2. 透過CreateProcess 呼叫C2.EXE 將所有Form,模組,物件等等編譯成OBJ檔
    3. 透過CreateProcess 呼叫Link.EXE 將所有OBJ檔鏈結成EXE檔

    因此,我們只要對C2.EXE動手腳就行了,我的做法是這樣

    1. 先將E:\Program Files\Microsoft Visual Studio\VB98\C2.EXE
      改名為E:\Program Files\Microsoft Visual Studio\VB98\C3.EXE
      注意 路徑可能跟我的不同
    2. 下載C2.Zip
    3. 解壓縮後將內部的C2.EXE放到目錄E:\Program Files\Microsoft Visual Studio\VB98\底下

    然後就可以用VB來編譯程式了 ,每編譯一個檔案,都會出現以下視窗

    按下開始編譯後就可以在Text內的路徑下得到該lst檔
    如果不想輸出lst檔,只要將產生lst檔的選項拿掉即可

    這個程式式呼叫原C2.EXE(已經改名為C3.EXE)來編譯成OBJ檔 中間額外加入
    -FAs -Fa"C:\Documenuts and Settings\陳駿\桌面\Form1.lst" 這段參數
    其中-FAs是指定要輸出lst檔 -Fa用來指定lst輸出檔名

    這個參數是未公開的  完整程式如下

程式

    '以下程式在Form中
    '需要1個Text,1個Command,1個CheckBox,如上圖
    Option Explicit
    Private Declare Function OpenProcess Lib "kernel32" _
       (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, _
        ByVal dwProcessId As Long) As Long
    Private Declare Function CloseHandle Lib "kernel32" _
       (ByVal hObject As Long) As Long
    Private Declare Function GetExitCodeProcess Lib "kernel32" _
       (ByVal hProcess As Long, lpExitCode As Long) As Long

    Const PROCESS_QUERY_INFORMATION = &H400
    Const SYNCHRONIZE = &H100000
    Const STILL_ALIVE = &H103
    Const INFINITE = &HFFFF

    Dim ExitCode As Long
    Dim hProcess As Long
    Dim lst_FileName As String
    Dim comString As String

    Private Sub Form_Load()
    Dim inj As Long, i As Long

    comString = Command

    inj = InStr(LCase(comString), ".obj""")

    For i = inj To 1 Step -1
        If Mid(comString, i, 1) = """" Then
            lst_FileName = Mid(comString, i + 1, inj - i)
            Exit For
        End If
    Next

    Text1.Text = lst_FileName & "lst"
    End Sub

    Private Sub Command1_Click()
    Dim pid As Long
    Command1.Enabled = False
    Me.Hide
    DoEvents
    If Check1.Value Then
        pid = Shell("c3 " & comString & " -FAs -Fa""" & Text1.Text & """", vbHide)
    Else
        pid = Shell("c3 " & comString, vbHide)
    End If

    hProcess = OpenProcess(PROCESS_QUERY_INFORMATION + SYNCHRONIZE, 0, pid)

    Do
      Call GetExitCodeProcess(hProcess, ExitCode)
      DoEvents
    Loop While ExitCode = STILL_ALIVE

    Call CloseHandle(hProcess)

    Unload Me
    End Sub
     

範例下載

文件出處

    Honey

整理時間

    2003,4,30

VB心得筆記歡迎各位的指教,如果您有任何文章或資料願意提供給我們的,請來信到VBNote

如果對本站有任何建議,歡迎來信給Honey,我們會盡快給您答覆