• 如何和C或Assembly的程式鏈結

說明

    基於最佳化的考量,通常程式一些常用的部分會透過組合語言來做最佳化的動作,但這對於VB的使用者而言卻是一項難題,VB並不支援這樣的功能,我們得繞一段遠路,透過如何取得lst檔相同的方法,我們在多做一個鏈節器,Link.EXE做法如下

    1. 先安裝C2.zip中的C2.EXE 安裝方法請參考如何取得VB編譯後的lst檔
    2. 將E:\Program Files\Microsoft Visual Studio\VB98\Link.EXE改名為
      E:\Program Files\Microsoft Visual Studio\VB98\iLink.EXE
    3. 下載Link.zip
    4. 解壓縮後將Link.exe放到E:\Program Files\Microsoft Visual Studio\VB98目錄下

    準備完成後 透過以下範例 將一步步教你如何鏈結
    請先下載以下範例LinkWith_C_asm.zip 內部含有一個VB,C以及ASM的程式
    VB的程式如下

      '以下程式在Form中
      Option Explicit
      Private Declare Function GetTickCount Lib "kernel32" () As Long
      Private Sub Command1_Click()
      Dim i As Long, l As Long
      Dim bgt As Long, ent As Long
      bgt = GetTickCount()

      For i = 1 To 1000000
          l = GetSum()
      Next

      ent = GetTickCount()

      MsgBox "費時:" & ent - bgt

      End Sub

      Private Sub Command2_Click()
      Dim l As Long
      l = GetSum()
      MsgBox l
      End Sub

      '以下程式在模組中
      Option Explicit

      Public Function GetSum() As Long
      Dim i As Long, l As Long

      For i = 1 To 100
          l = l + i
      Next

      GetSum = l
      End Function

    我們動手寫個GetSum這個函數 要做的是1累加到100 組語如下

      .386P
      .model FLAT
      PUBLIC GetSum
      .code

      GetSum PROC NEAR
      xor eax, eax
      mov ecx, 1

      loop1:
        add eax, ecx
        inc ecx
        cmp ecx, 100
      jle loop1
      ret 0
      GetSum ENDP

      END

    另外我們也用C寫一個來比較

      extern "C" long __stdcall GetSum();

      long __stdcall GetSum()
      {
       long i,l=0;
       for(i=1;i<=100;i++)
        l=l+i;
       return l;
      }


    和ASM鏈結的方法如下
    1. 先取得VB編譯後的lst檔 如果不會請參考如何取得VB編譯後的lst檔
      當出現Link畫面時 請先不要Link
      由於GetSum是在Module1.bas中,他的輸出會在Module1.lst
      用記事本打開Module1.lst 內容如下
      •  TITLE C:\gs\Module1.bas
         .386P
        include listing.inc
        if @Version gt 510
        .model FLAT
        else
        _TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
        _TEXT ENDS
        _DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
        _DATA ENDS
        CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
        CONST ENDS
        _BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
        _BSS ENDS
        _TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
        _TLS ENDS
        text$1 SEGMENT PARA USE32 PUBLIC ''
        text$1 ENDS
        ; COMDAT ?GetSum@Module1@@AAGXXZ
        text$1 SEGMENT PARA USE32 PUBLIC ''
        text$1 ENDS
        FLAT GROUP _DATA, CONST, _BSS
         ASSUME CS: FLAT, DS: FLAT, SS: FLAT
        endif
        PUBLIC ?GetSum@Module1@@AAGXXZ    ; Module1::GetSum
        EXTRN __imp____vbaErrorOverflow:NEAR
        ; COMDAT ?GetSum@Module1@@AAGXXZ
        text$1 SEGMENT
        ?GetSum@Module1@@AAGXXZ PROC NEAR   ; Module1::GetSum, COMDAT

        ; 4    : Public Function GetSum() As Long

         xor eax, eax

        ; 5    : Dim i As Long, l As Long
        ; 6    :
        ; 7    : For i = 1 To 100

         mov ecx, 1
        $L27:

        ; 8    :     l = l + i

         add eax, ecx
         jo SHORT $L19
         add ecx, 1
         jo SHORT $L19
         cmp ecx, 100    ; 00000064H
         jle SHORT $L27

        ; 9    : Next
        ; 10   :
        ; 11   : GetSum = l
        ; 12   : End Function

         ret 0
        $L19:
         call DWORD PTR __imp____vbaErrorOverflow
        ?GetSum@Module1@@AAGXXZ ENDP    ; Module1::GetSum
        text$1 ENDS
        END

    2. 注意用紅色標明的部分 GetSum備編譯後 函數名稱已經改為?GetSum@Module1@@AAGXXZ 我們需要修改一下原來組語中的函數名稱 修改如下
      .386P
      .model FLAT
      PUBLIC ?GetSum@Module1@@AAGXXZ
      .code
      ?GetSum@Module1@@AAGXXZ PROC NEAR
       xor eax, eax
       mov ecx, 1
      loop1:
       add eax, ecx
       inc ecx
       cmp ecx, 100   
       jle loop1
       ret 0
      ?GetSum@Module1@@AAGXXZ ENDP
      END
      這個程式我放在Module1.asc
    3. 編譯Module1.asc
      建議使用MASM編譯 比較不會有格式問題 請輸入
      ml /c /Cp /coff /Zm Module1.asc
      然後將編譯出的Module1.OBJ覆蓋原先由VB編譯出的Module1.OBJ
    4. 然後按下Link視窗的開始Link按鈕 即可完成編譯


    和C鏈結的方法如下
    1. 先將C轉為ASM 由於C編譯器和VB函數命名方式不同 不能直接編成OBJ在鏈結 只要把C輸出成LST即可 以下以BCC為例子
      輸入
      BCC32 -5 -S -Ox Module1.cpp
      他會將lst輸出到Module1.asm 之後鏈結方法就和之前的Asm鏈結法一樣


    至於使用這個方法 效能能提升多少呢 以下是在Pentium 4-M 1.8GHz,512MB的機器下的測試結果

      VB      301 ms
      C        160 ms
      ASM   130 ms

    效能確實是有提升

 

程式

    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

    Dim ExitCode As Long
    Dim hProcess As Long


    Private Sub Command1_Click()
    Dim pid As Long

    Command1.Enabled = False
    Me.Hide
    DoEvents

    pid = Shell("iLink " & Command, vbHide)

    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

    Private Sub Form_Load()
    List1.Clear
    List1.AddItem "這個程式可以暫緩Link.exe執行"
    List1.AddItem "再按下""開始Link""前 你可以先編譯"
    List1.AddItem "你的程式(用C或是Assembly)所做出的lst檔"
    List1.AddItem "(記得函數名稱命名必須和和替換掉程式的命名相同)"
    List1.AddItem "編譯只要用任何32bit的Assembly編譯器應該都可以"
    List1.AddItem "    TASM編譯方式是這樣 tasm32 /ml xxx.lst "
    List1.AddItem "    MASM編譯方式是這樣 ml / c / Cp / coff /Zm xxx.lst"
    List1.AddItem "編譯完成後 按下""開始Link""即可"

    End Sub

範例下載

文件出處

    Honey

整理時間

    2003'2,30.

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

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