Twain 學習紀錄

一、TWAIN的文件組成

TWAIN共包括4個二進制文件。如果要使用該接口,就必須要保證他們被成功地安裝在本地計算機上。

 

文 件 名

說 明

TWAIN_32.DLL

32位應用程序的支持文件,32位程序使用TWAIN通訊必須使用該文件。

TWAIN.DLL

16位應用程序的支持文件,16位程序使用TWAIN通訊必須使用該文件。

TWUNKER_32.EXE

實現32位應用程序與32位數據源進行通訊,它運行時不可見。

TWUNKER_16.EXE

實現32位應用程序與16位數據源進行通訊,它運行時不可見。

注意:在Windows NT 環境下16位數據源不能夠正常工作。

 

Windows 操作系統中(Windows 9x / 2000 / XP ),Microsoft已經把這些文件作爲系統文件隨同操作系統一起發佈了。你可以在Windows安裝目錄中查找到這些文件。如果我們要編程來實現對TWAIN的訪問,還需要最重要的頭文件。你通過訪問該http://www.twain.org/devfiles/twain.h 地址來獲得TWAIN提供的頭文件。

 

二、TWAIN的結構

TWAIN依靠三個組件協同完成與圖像設備的通訊和數據傳輸工作,這三個組件就是 ApplicationSource ManagerSource

 

組件

說明

Application

就是你要編寫的應用程序。

Source Manager

是由TWAIN提供的一個Source的管理器,它不僅可以收集本地系統已經安裝了的圖像設備,還可以根據需要去加載設備。同時,它最重要的功能是擔任Application 與Source通訊的橋樑。(其實,它就是我們前面提到的組成文件中的dll文件。)

Source

在這裏可以看作是圖像設備。事實上它是由設備廠家提供的一個dll文件。這個dll文件是支持twain接口的。(該文不討論關於twain在Source中的應用。)

 

它們的層次結構圖如下:

 

 

從該圖我們可以看到,Application要從Source獲得圖像數據,必須通過Source Manager傳遞來實現。Application與Source Manager 間的通訊是靠調用TWAIN提供的DSM_Entry( )函數實現。而Application不能直接與Source 通訊,Source Manager與Source 間的通訊是靠調用TWAIN提供的DS_Entry( )函數實現(在這裏,我們不用關心Source Manager如何去調用DS_Entry函數。

 

三、TWAIN的用戶界面

當我們使用TWAIN接口去獲得圖像數據的時候,會涉及到一些的用戶界面,首先是我們的應用程序界面,然後是Source Manager提供的用戶界面以及圖像設備(Source)所提供的用戶界面。

在我們的應用程序中,可以通過“選擇設備”來打開Source Manager的標準用戶界面。Source Manager的界面由Source Manager提供。在這個界面中可以讓用戶選擇他想要使用的圖像設備。選中想要的設備後,再通過“獲取…”來打開圖像設備(Source)提供的用戶界面進行現應的操作。(注:Source提供的界面會因爲你使用的圖像設備不同而有差異。)

對於這些界面,TWAIN提供了非常靈活的處理方法。對於Source Manager提供的用戶界面以及圖像設備(Source)所提供的用戶界面,我們可以選擇是否顯示它們,甚至我們還可以按自己的要求去改寫這些用戶界面。

 

四、TWAIN的接口函數

要編寫應用程序實現與支持TWAIN標準的圖像設備通訊,只需要瞭解上面提到的DSM_Entry()接口函數。TWAIN定義了大約140個操作消息。你只要把這些消息通過DSM_Entry()函數發給Source Manager,就可以實現對選定的Source進行相應的操作。Source Manager會分辨那些消息屬於自己,那些消息是該轉發給Source。

在介紹DSM_Entry()前,我們先來了解一下的TWAIN定義的消息格式。TWAIN把它定義的操作稱爲Triplets操作,就是每個操作用三個定義的參數來表示。這個三個參數用不同前綴名來區分。每個Triplets操作都是唯一的,不會有歧意,它們代表一個特定的操作行爲。這三個參數類型分別是Data Group(前綴名DG_ )、 Data Argument(前綴名DAT_ ) 和 Message ID(前綴名MSG_ ),每個參數都包含有各自的信息。比如:DG_CONTROL / DAT_PARENT / MSG_OPENDSM 就表示一個打開Source Manager的操作,這些參數在TWAIN.H中都有定義。其他的操作(設置掃描儀的分辨率、獲得設備支持的功能等等…)你可以去查看TWAIN的參考手冊,我將在後面編程應用中介紹幾個最常用的操作。

現在,我們明白了TWAIN定義的Triplets操作,但是這還不夠。在使用DSM_Entry()前,必須要加載TWAIN_32.DLL文件以獲得DSM_Entry()函數指針。(注意:在你程序中應該添加前面提到的TWAIN.H頭文件哦!)

DSMENTRYPROC lpDSM_Entry;   //* DSM_Entry 入口函數的指針

HMODULE      hDSMDLL;       //* Twain_32.Dll句柄

    ……

//* 加載TWAIN_32.DLL 文件

if ((hDSMDLL = LoadLibrary("TWAIN_32.DLL")) != NULL)

{

    if (hDSMDLL)           //* 檢查TWAIN_32.DLL是否加載

    {

            if ( (lpDSM_Entry =(DSMENTRYPROC) GetProcAddress(hDSMDLL,MAKEINTRESOURCE(1)))!=NULL)

        {

                          //* 成功獲得 DSM_Entry()函數指針;

        }

    }

}

 

現在我們明白了,TWAIN所有的操作都是通過DSM_Entry()函數來實現的,所以瞭解該入口函數很有必要。它定義如下:

TW_UINT16 FAR PASCAL DSM_Entry

( pTW_IDENTITY pOrigin,        //* 指向操作發起者的指針

pTW_IDENTITY pDest,          //* 指向目標對象的指針

TW_UINT32 DG,                //* Triplets 操作的DG參數 : DG_xxxx

TW_UINT16 DAT,               //* Triplets 操作的DAT參數: DAT_xxxx

TW_UINT16 MSG,               //* Triplets 操作的MSG參數: MSG_xxxx

TW_MEMREF pData              //* 指向返回數據塊的指針

);

    其中DG、DAT、MSG參數表示一個你想執行的Triplets操作。pOrigin表示發起Triplets操作的對象。pDest表示接收Triplets操作的對象。pData用於獲得執行Triplets操作後返回的數據。

    對於每個Triplets操作,都是由DG、DAT、MSG三個參數組合構成的。pOrigin、pDest參數會根據不同的Triplets操作,而使用不同的值。

函數執行後會返回一個值來表示操作是否成功。如果返回值爲TWRC_SUCCESS表示操作成功,TWRC_FAILURE表示操作失敗。同樣根據Triplets操作的類型不同,還會有其他的返回值。比如TWRC_CANCEL、TWCC_LOWMEMORY…,具體信息你可以參考TWAIN的說明手冊。

   

五、TWAIN的操作流程

 

Application、 Source Manager 和 Source要實現數據傳輸,必須遵循一個操作流程。你要進行的操作應該在這個流程規定的動作隊列中按邏輯去執行。比如,在沒有加載Source Manager前,Application是不能要求Source傳輸數據的。爲了更好的去描述這個流程,TWAIN爲該流程定義了7個狀態(1-7)。

 

狀態位 1, 2, 3

這幾個狀態是用於描述Source Manager的,它們是Source Manager專有的狀態位,所以Source Manager 的標誌位是不會大於3的.

狀態位4, 5, 6, 7

這幾個狀態是Source專有的。如果Source打開了,Source 的標誌位就不會小於4;如果Source關閉了,Source就沒有了標誌位。

 

要注意,我們的應用程序可以使用了多個Source,每個與Source的連接都是一個單獨的會話,對於打開的每個Source,他們的標誌位都是相互獨立的,不互相關聯。現在就來看看流程圖。

流程標誌位說明

狀態 1 – 準備會話

Application和Source Manager建立會話前,Source Manager的標誌位是1.

在這個時候,Source Manager還沒有被加載到內存中。如果Source Manager 被加載到內存中,則狀態位是2或者3。

 

狀態2 –加載Source Manager

Source Manager現在已經被成功地加載到了程序中,但是沒有打開Source Manager。

在這個時候, Source Manager開始準備去接受Application的Triplets操作。

 

狀態3 – 打開Source Manager

Source Manager已經打開並且準備去管理Source.Source Manager現在準備向Source發送打開操作,去打開指定的Source,並等待所有針對Source的操作結束後,去關閉打開的Source. Source Manager在會話關閉前,狀態位將保持爲3. 當Application打開的Source沒有關閉時,Source Manager 會拒絕關閉。

 

狀態 4 – 打開Source     

在響應Application的一個指定的Triplets操作後,Source被加載到系統中,並且被Source manager 打開。Source在加載前將檢測是否有足夠的系統資源讓自己運行(內存、設備是否可用等等…)。 Application不僅可以查詢Source的性能參數(當前解析度、是否支持彩色或黑白圖像、自動文檔傳送是否可用), Application還可以去設置的Source的性能參數。比如,Application可以要求Source按指定的分辨率傳輸黑白圖像。

 

注意: 可以在Source的狀態位是4, 5, 6, 或 7時,去查詢Source的性能參數。但是要想設置Source的性能參數必須在狀態位是4的時候設置,除非Application和Source有特殊的約定,否則在標誌位爲其他數的時候都不可以進行性能參數設置。

 

狀態 5 – Source可用         

現在可以讓Source工作了,此時Source開始爲數據傳輸做準備。在該狀態下,可以執行一個Triplets操作,用以選擇是否讓Source顯示它自己的用戶界面(Source提供的軟件界面)。當Source準備好給Application傳輸數據時,標誌位就從5變爲6了。

 

狀態 6 –準備數據傳輸

該狀態下,Source已經準備好了爲Application傳輸數據。在傳輸工作開始前,Application應該查詢將要被傳輸的圖像的相關信息(分辨率,圖像大小…), 如果Source還要傳輸音頻數據, 那麼在傳輸圖像數據前,Application必須要把所有的音頻數據先傳完。注:某些數碼相機帶有攝像功能,可以記錄一些聲音信息。

 

狀態 7 –傳輸開始       

Source開始進行數據傳輸,它把獲得的數據傳輸給你的應用程序。 傳輸工作要麼成功完成,要麼提前中止。在傳輸工作完成後, Source將會發送一個返回代碼去表示傳輸工作的最終結果。

 

七、TWAIN最常用的Triplets操作

這裏將對TWAIN中最常用的Triplets操作做一個簡單的介紹,爲了便於理解和記憶,我將結合前面講的操作流程順序去介紹這些常用的Triplets操作。

 

1.加載Source Manager並獲得DSM_Entry入口函數 (狀態1到2)

應用程序在調用DSM_Entry函數指針前必須加載Source Manager。這裏沒有使用Triplets操作。你可以使用LoadLibrary()函數,加載TWAIN_32.DLL文件。並使用GetProcAddress()函數,獲得DSM_Entry函數指針

 

2.打開Source Manager (狀態2到3)

Triplets 操作:DG_CONTROL / DAT_PARENT / MSG_OPENDSM

通過該操作,你可以打開Source Manager,並且還要在你的應用程序中,指定一個窗體作爲Source的父窗口。Source Manager 將通過該窗體,把Source的消息傳遞給你的應用程序。

 

3.選擇Source (狀態3期間)

Triplets 操作:DG_CONTROL / DAT_IDENTITY / MSG_USERSELECT

你的應用程序發送該操作後,將顯示Source Manager的用戶界面,它是一個對話框。這個對話框中顯示了系統中所有支持Twain的設備列表。系統默認設備將高亮顯示在列表框中。你可以通過該列表框選擇你想要的輸入設備。

 

4.打開Source (狀態3到4)

Triplets 操作:DG_CONTROL / DAT_IDENTITY / MSG_OPENDS

該操作可以打開你選擇的Source(圖像輸入設備),同時,Source Manager會給該Source分配一個唯一的標識符。你要把打開的這個Source放在一個指定的結構中,以便於在後面和該Source進行通訊。

 

5.設置Source的性能參數 (狀態4期間)

Triplets 操作:DG_CONTROL / DAT_CAPABILITY / MSG_GET

DG_CONTROL / DAT_CAPABILITY / MSG_SET

這裏有兩個Triplets操作,通過使用這兩個操作可以去查詢當前設備是否支持的某種功能,如果支持,還可以獲得設備功能的當前值、默認值、以及可以重新設置的範圍。你還可以根據查詢的結果,按你的要求去重新設置該功能的當前值。

 

6.請求從Source獲取數據 (狀態4到5)

Triplets 操作:DG_CONTROL / DAT_USERINTERFACE / MSG_ENABLEDS

通過該操作,可以讓Source顯示它的用戶界面,Source會去爲數據傳輸作準備。

 

7.認數據準備傳輸 (狀態5到6)

Triplets 操作:DG_CONTROL /DAT_EVENT / MSG_PROCESSEVENT

首先要說明一下,從狀態5到狀態6的這個過程,不是由你的應用程序通過Triplets操作來發起的。而是當Source準備好去傳輸數據時,它會發出一個事件信號來實現的。你的應用程序應該要去檢查這個事件信號。

如何去檢查這個事件信號?我們在加載Source Manager時,就爲Source指定了一個父窗口,Source會把它事件信號封裝成一個Windows的消息結構發送給它的父窗口。你可以在這個窗體的消息循環中去,使用 DG_CONTROL /DAT_EVENT / MSG_PROCESSEVENT操作,來判斷Source是否有事件發生。MSG_XFERREADY就表示這個過程的狀態位從5變爲6了。

 

8.開始進行數據傳輸 (狀態6到7)

Triplets 操作:DG_IMAGE / DAT_IMAGEINFO / MSG_GET   

DG_IMAGE / DAT_IMAGENATIVEXFER / MSG_GET

在開始數據傳輸前,可以通過 DG_IMAGE / DAT_IMAGEINFO / MSG_GET 操作,去獲得將要傳輸的圖像的相關信息,比如位圖大小、寬度、長度…。

通過 DG_IMAGE / DAT_IMAGENATIVEXFER / MSG_GET 操作,可以實現使用本地傳輸模式去傳輸數據。傳輸結束了,Source 將給它的父窗口一個 PM_XFERDONE 的消息。Source將在 DSM_Entry() 中返回爲一個指向 DIB 位圖的指針。

 

9.中止傳輸 (狀態7到6到5)

Triplets 操作:DG_CONTROL / DAT_PENDINGXFERS / MSG_ENDXFER

在每次數據傳輸結束(成功、退出)後,可以發送該操作給Source,去表示應用程序已經接受完了所有的數據了。同時還可以根據它的返回值,去檢查是否有其它的圖像等待傳送。

 

10.斷開TWAIN會話 (狀態5到4)

Triplets 操作:DG_CONTROL / DAT_USERINTERFACE / MSG_DISABLEDS

該操作讓打開Source失效。

 

11.關閉Source (狀態4到3)

Triplets 操作:DG_CONTROL / DAT_IDENTITY / MSG_CLOSEDS

該操作可以關閉指定的Source。

 

12.關閉Source Manager(狀態3到2)

Triplets 操作: DG_CONTROL / DAT_PARENT/MSG_CLOSEDSM

關閉打開的Source Manager。

 

七、TWAIN的數據傳輸模式

TWAIN定義了三種模式用於Source 到Application的數據傳輸:本地模式、文件模式,和緩存模式。現在對每種模式進行一個簡單的介紹。

注:對於音頻數據的傳輸,只能選擇本地模式或者文件模式來進行傳輸。

 

本地模式 

所有的輸入設備都支持這種本地數據傳輸模式,同時它也是TWAIN默認的數據傳輸模式,並且它還是最容易使用的數據傳輸模式。但是,它有一定的侷限性,它傳輸的數據必須是DIB 圖像數據,並且在傳輸時,會受到系統內存大小限制。

 

傳輸數據的格式:  DIB (Device-Independent Bitmap)

使用該模式,在數據傳輸時Source分配一塊單獨的內存區域,並把圖形數據寫入這個內存區域內。然後它通過一個指向該內存地址的指針告訴Application,數據存放在什麼地方。你的應用程序通過訪問該內存區域去獲得具體的圖像數據。注意,Application在獲得數據後要負責去釋放這部分的內存。如果你的圖像數據大於系統當前可用內存,會導致傳輸失敗。

 

文件模式 

該模式是讓Application 創建一個文件,這個文件用於儲存傳輸的數據,Source將對該文件進行讀寫操作。Source將把要傳輸的數據寫到該文件中,你的應用程序通過訪問該文件,就可以獲得傳輸的數據。

 

在使用本地模式傳輸一個大的圖像文件時,如果內存不夠大,可以考慮使用文件傳輸模式來傳輸。文件傳輸模式與緩存傳輸模式相比,在使用方法上要簡單些,但是該模式在傳輸速度上比緩存模式的傳輸速度要慢一些,並且在數據傳輸完畢後,你的應用程序還必須去管理這個數據文件。

 

緩存模式  

緩存模式在整個傳輸過程中,將使用一個或多個內存緩存區,內存緩存區的分配和釋放工作由Application來控制。在傳輸過程中,傳輸數據被當作一個未知格式的位圖。Application必須使用TW_IMAGEINFO 和 TW_IMAGEMEMXFER操作,去得到各個緩存區的信息並把它們正確組織爲一個完整的位圖。

 

如果使用本地模式 或 文件模式 去傳輸數據,整個傳輸過程在只需要一個Triplets操作就可以完成。如果使用 緩存模式 傳輸數據, 你的應用程序可能需要使用多個Triplets操作,不停地去獲得緩存區的數據信息。但是,該傳輸模式具有很好的靈活性, 可以很好的去控制獲得的數據,只不過在編程應用上要麻煩一些。

 

八、TWAIN的應用實現

好了,看了前面的對TWAIN的介紹,現在我們就動手開始進行實際的編程吧。在這裏,只進行一個最簡單的應用實現我們的應用程序不去設置設備的性能參數,不選擇其它數據傳輸模式,僅僅使用TWAIN的默認的本地傳輸模式方式,去獲得圖像數據。

在進行實際編程應用前,我們可以先安裝TWAIN提供的工具包。它不僅提供了TWAIN應用的例程,還可以在你的計算機系統上安裝一個虛擬的圖像輸入設備(TWAIN_32 Sample Source )。這對於沒有掃描儀、數碼相機的開發者,提供了一個很好的測試設備。TWAIN工具包的下載地址:http://www.twain.org/devfiles/twainkit.exe 。

由於TWAIN目前提供的是基於C的編程接口,所以我們這裏採用VC作爲開發工具。我們可以建一個自己的TWAIN類。把一些Triplets操作封裝成這個類的成員函數。以便於程序調用。記住:在你的項目中要加入TWAIN提供的頭文件。

前面已經介紹了,在進行TWAIN的操作前,如何加載TWAIN_32.dll文件,獲得DSM_Entry()函數指針。下面僅簡單介紹一下其他的成員函數。

 

1. 打開Source Manager

int CTwain::OpenSourceManager(void)

{

 TW_UINT16 rc;

. . .

// lpDSM_Entry 是指向DSM_Entry的函數指針

    rc = (*lpDSM_Entry) (&AppID, NULL, 

                         DG_CONTROL,DAT_PARENT,MSG_OPENDSM,                                 // hPWnd 是指定爲Source的父窗口的句柄

                        (TW_MEMREF) & (*hPWnd)) ; 

     switch (rc)    // 檢查打開Source Manager是否成功

     {

     case TWRC_SUCCESS:   //  成功

         . . .

     case TWRC_CANCEL:

         . . .

     }

    . . .

}

 

2.打開Source

int CTwain::OpenSource(void)

{

    TW_UINT16 rc;

    rc = (*lpDSM_Entry) (&AppID,NULL,

                        DG_CONTROL,DAT_IDENTITY,MSG_OPENDS,

                       (TW_MEMREF) &SourceID);  // SourceID 是要求打開Source

  switch (rc)    // 檢查打開Source Manager是否成功

    {

    case TWRC_SUCCESS: //  成功

     . . .

     }

     . . .

}

 

3.處理Source的事件

int CTwain::DealSourceMsg(MSG *pMSG)

{

    TW_UINT16  rc    = TWRC_NOTDSEVENT;

    TW_EVENT  twEvent;

    twEvent.pEvent = (TW_MEMREF) pMSG;

    rc = (*lpDSM_Entry) (&AppID,&SourceID,

                       DG_CONTROL,DAT_EVENT,MSG_PROCESSEVENT,

                       (TW_MEMREF) &twEvent);

    switch (twEvent.TWMessage)

    {

    case MSG_XFERREADY:  // Source準備好傳輸數據了 iStatus=6

        iStatus=6;

        GetBmpInfo();

        DoNativeTransfer();

    case MSG_CLOSEDSREQ: // 關閉 Source 用戶界面的申請

    case MSG_CLOSEDSOK: 

    case MSG_NULL:

    }  

 . . .

}

 

4.使用本地模式傳輸數據

int CTwain::DoNativeTransfer(void)

{

    TW_UINT32  hBitMap = NULL;  // 指向圖像數據地址

    TW_UINT16  rc;

     HANDLE     hbm_acq = NULL;

     rc = (*lpDSM_Entry)(&AppID,&SourceID,

                        DG_IMAGE,DAT_IMAGENATIVEXFER,MSG_GET,

                        (TW_MEMREF)&hBitMap);

    switch (rc)

    {

    case TWRC_XFERDONE:      // 數據傳輸完成

        hbm_acq = (HBITMAP)hBitMap;

          // 把圖像數據地址通過消息發送給應用程序

// 應用程序通過就可以通過 hbm_acq 來處理圖像數據

        SendMessage(*hPWnd, PM_XFERDONE, (WPARAM)hbm_acq, 0);

        iStatus = 7;

break;

     case TWRC_CANCEL:

     case TWRC_FAILURE:

     }

     . . .

}

5.應用程序處理圖像數據

LRESULT CTwainAppView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)

{

    if (message==PM_XFERDONE)   // 收到 PM_XFERDONE 消息

{

       HBITMAP hBmp=FixUp(HANDLE(wParam));  // 圖像轉換處理

        Bitmap *pBm=0;                           // GDI+的Bitmap對象

       pBmp=pBm->FromHBITMAP(hBmp,hDibPal);

       Invalidate();

       . . .

     }

    return CView::WindowProc(message, wParam, lParam);

}

6.在應用程序的視圖窗口上繪圖

void CTwainAppView::OnDraw(CDC* pDC)

{

. . .

// 使用 GDI+ 在視圖上 繪圖

Graphics   myGraphics ( pDC->m_hDC ) ;

myGraphics.DrawImage ( pBmp , 0, 0,

pBmp->GetWidth(), pBmp->GetHeight() );

    . . .

}

我對twain也只是瞭解很少的一部分,還有很多功能沒有實現,比如如何設置設備的性能參數、如何使用不同的數據傳輸模式、如何獲得圖像的佈局,如何傳輸壓縮的圖像數據、如何更改Source Manager的用戶界面等等。希望領導和同事多多指導交流。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章