twain.dll文件 和掃描程式設計說明文件(twain_32呼叫說明)
一、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 作業系統中,Microsoft已經把這些檔案作為系統檔案隨同作業系統一起釋出了。可以在Windows安裝目錄中查詢到這些檔案。通過TWAIN提供的標頭檔案,可以實現掃描功能。
二、TWAIN的結構
TWAIN依靠三個元件協同完成與影象裝置的通訊和資料傳輸工作,這三個元件就是 Application、Source Manager和Source。
元件 說明
Application 就是你要編寫的應用程式。
Source Manager
是由TWAIN提供的一個Source的管理器,它不僅可以收集本地系統已經安裝了的影象裝置,還可以根據需要去載入裝置。同時,它最重要的功能是擔任Application 與Source通訊的橋樑。(其實,它就是我們前面提到的組成檔案中的dll檔案。)
Source 在這裡可以看作是影象裝置。事實上它是由裝置廠家提供的一個dll檔案。這個dll檔案是支援twain介面的。
它們的層次結構圖如下:
從上圖可以看到,Application要從Source獲得影象資料,必須通過Source Manager傳遞來實現。Application與Source Manager 間的通訊是靠呼叫TWAIN提供的OpenDSM( )函式實現。而Application不能直接與Source 通訊,Source Manager與Source 間的通訊是靠呼叫TWAIN提供的OpenDS()函式實現。
三、訊息定義
TWAIN把它定義的操作稱為Triplets操作,就是每個操作用三個定義的引數來表示。這個三個引數用不同字首名來區分。這三個引數型別分別是Data Group、 Data Argument和 Message ID。每個Triplets操作都是唯一的,不會有歧意,它們代表一個特定的操作行為。
四、TWAIN的介面函式
要編寫應用程式實現與支援TWAIN標準的影象裝置通訊,只需要瞭解上面提到的OpenDSM( )介面函式。TWAIN定義了大約140個操作訊息。只要把這些訊息通過OpenDSM( )函式發給Source Manager,就可以實現對選定的Source進行相應的操作。Source Manager會分辨那些訊息屬於自己,那些訊息是該轉發給Source。
在使用OpenDSM( )前,必須要載入TWAIN_32.DLL檔案以獲得OpenDSM( )函式指標。 TWAIN所有的操作都是通過OpenDSM( )函式來實現的。
五、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應該查詢將要被傳輸的影象的相關資訊(解析度,影象大小…)。
狀態 7 –傳輸開始
Source開始進行資料傳輸,它把獲得的資料傳輸給你的應用程式。 傳輸工作要麼成功完成,要麼提前中止。在傳輸工作完成後, Source將會發送一個返回程式碼去表示傳輸工作的最終結果。
八、TWAIN的應用實現
1. 初始化TWAIN
void CLcTwain::InitializeTwain(HWND hMainWnd)
{
appID.Id = 0; appID.Version.MajorNum = 1;
appID.Version.MinorNum = 703;
appID.Version.Language = TWLG_USA;
appID.Version.Country = TWCY_USA;
#ifdef WIN32
lstrcpy (appID.Version.Info, "TWAIN_32 Twacker 1.7.0.3 01/18/1999");
lstrcpy (appID.ProductName, "TWACKER_32");
#else
lstrcpy (appID.Version.Info, "TWAIN Twacker 1.7.0.3 01/18/1999");
lstrcpy (appID.ProductName, "TWACKER_16");
#endif
appID.ProtocolMajor = 1;//TWON_PROTOCOLMAJOR;
appID.ProtocolMinor = 7;//TWON_PROTOCOLMINOR;
appID.SupportedGroups = DG_IMAGE | DG_CONTROL;
lstrcpy (appID.Manufacturer, "TWAIN Working Group");
lstrcpy (appID.ProductFamily, "TWAIN Toolkit");
// pass app particulars to glue code
ASSERT(&appID);
ASSERT(hMainWnd);
hWnd = hMainWnd; // get copy of app window handle
OpenDSM();
return;
}
2. 選擇預設的掃描器
BOOL CLcTwain::TWSelectDefaultDS()
{
TW_UINT16 twRC = TWRC_FAILURE;
TW_IDENTITY NewDSIdentity;
memset(&NewDSIdentity, 0, sizeof(TW_IDENTITY));
if (TWDSOpen)
{
twRC = TWRC_FAILURE;
}
else
{
twRC = CallDSMEntry(&appID,
NULL,
DG_CONTROL,
DAT_IDENTITY,
MSG_GETDEFAULT,
(TW_MEMREF)&NewDSIdentity);
dsID = NewDSIdentity;
}
return (twRC);
}
3. 開啟Source Manager
BOOL CLcTwain::OpenDSM()
{
TW_UINT16 twRC = TWRC_FAILURE;
OFSTRUCT OpenFiles;
#define WINDIRPATHSIZE 160
char WinDir[WINDIRPATHSIZE];
TW_STR32 DSMName;
memset(&OpenFiles, 0, sizeof(OFSTRUCT));
memset(WinDir, 0, sizeof(char[WINDIRPATHSIZE]));
memset(DSMName, 0, sizeof(TW_STR32));
if (TWDSMOpen!=TRUE)
{
GetWindowsDirectory (WinDir, WINDIRPATHSIZE);
strcpy(DSMName,"TWAIN_32.DLL");
if ((hDSMDLL = LoadLibrary(DSMName)) != NULL &&
(hDSMDLL >= (HANDLE)VALID_HANDLE) &&
(lpDSM_Entry = (DSMENTRYPROC)GetProcAddress(hDSMDLL, MAKEINTRESOURCE (1))) != NULL)
{
twRC = CallDSMEntry(&appID,
NULL,
DG_CONTROL,
DAT_PARENT,
MSG_OPENDSM,
(TW_MEMREF)&hWnd);
switch (twRC)
{
case TWRC_SUCCESS:
TWDSMOpen = TRUE;
break;
case TWRC_FAILURE:
default:
// Trouble opening the SM, inform. the user
TWDSMOpen = FALSE;
break;
}
}
else
return -1;
}
// Let the caller know what happened
return (TWDSMOpen);
}
4. 開啟Source
BOOL CLcTwain::OpenDS()
{
TW_UINT16 twRC = TWRC_FAILURE;
if (TWDSMOpen==FALSE)
{
OpenDSM();
}
else
{
if (TWDSOpen==TRUE)
{
}
else
{
twRC = CallDSMEntry(&appID,
NULL,
DG_CONTROL,
DAT_IDENTITY,
MSG_OPENDS,
&dsID);
switch (twRC)
{
case TWRC_SUCCESS:
// do not change flag unless we successfully open
TWDSOpen = TRUE;
break;
case TWRC_FAILURE:
break;
default:
break;
}
}
}
return TWDSOpen;
}
5. 處理Source的事件
BOOL CLcTwain::ProcessTWMessage(LPMSG lpMsg, HWND hWnd)
{
TW_UINT16 twRC = TWRC_NOTDSEVENT;
TW_EVENT twEvent;
memset(&twEvent, 0, sizeof(TW_EVENT));
ASSERT(lpMsg);
ASSERT(hWnd);
if ((IsDSMOpen()) && (TWIsDSOpen()))
{
twEvent.pEvent = (TW_MEMREF)lpMsg;
if(((lpMsg->wParam != 0) && (lpMsg->lParam == 0)) ||((lpMsg->wParam == 0) && (lpMsg->lParam != 0)))
{
twRC = CallDSMEntry(&appID,
&dsID,
DG_CONTROL,
DAT_EVENT,
MSG_PROCESSEVENT,
(TW_MEMREF)&twEvent);
switch (twEvent.TWMessage)
{
case MSG_XFERREADY:
//If AcqFlag >0 then we are in state 5
if (AcqFlag)
{
TWTransferImage(hWnd);
}
break;
case MSG_CLOSEDSREQ:
case MSG_CLOSEDSOK:
if (TWDisableDS())
{
// if (CloseDS())
{
// CloseDSM(NULL);
}
}
break;
case MSG_NULL:
default:
break;
}
}
}
// tell the caller what happened
return (twRC==TWRC_DSEVENT); // returns TRUE or FALSE
}
6. 使用本地模式傳輸資料
void CLcTwain::DoNativeTransfer(HWND hWnd)
{
TW_PENDINGXFERS twPendingXfer;
TW_UINT16 twRC = TWRC_FAILURE;
TW_UINT16 twRC2 = TWRC_FAILURE;
TW_UINT32 hBitMap = NULL;
HANDLE hbm_acq = NULL; char buffer[2048];
CString Weishu1, strTemp, test;
static CString str;
LPBITMAPINFOHEADER lpdib = NULL;
BOOL g_bSpecialMenu = FALSE;
memset(&twPendingXfer, 0, sizeof(TW_PENDINGXFERS));
memset(buffer, 0, sizeof(char[2048]));
ASSERT(hWnd);
twPendingXfer.Count = 0;
do
{
twRC = CallDSMEntry(&appID,
&dsID,
DG_IMAGE,
DAT_IMAGENATIVEXFER,
MSG_GET,
(TW_MEMREF)&hBitMap);
CFileFind finder;
test.Format("%s\\%s\\*.%s",Folder,pici,FileFormat);
BOOL bWorking = finder.FindFile(test);
int j = 0;
int k = 1;
while (bWorking)
{
bWorking = finder.FindNextFile();
if(finder.GetFileName() !='.' && finder.GetFileName() != ".." && finder.GetFileName()!="Thumbs.db")
{
++k;
strTemp.Format("%d",k);
j = strlen(strTemp);
if(j == 0 || j==1)
j =0;
strTemp.Format("%d",j);
}
test = finder.GetFileName();
str.Format("%s\\%s\\%s",Folder,pici,test);
}
if(i == 0)
{
int n = atoi(Weishu);
int m = n -j;
switch(m)
{
case 0: Weishu1 = "";break;
case 1: Weishu1 = "";break;
case 2: Weishu1 = "0";break;
case 3: Weishu1 = "00";break;
case 4: Weishu1 = "000";break;
case 5: Weishu1 = "0000";break;
case 6: Weishu1 = "00000";break;
case 7: Weishu1 = "000000";break;
case 8: Weishu1 = "0000000";break;
case 9: Weishu1 = "00000000";break;
}
str.Format("%s\\%s\\%s%s%d.%s",Folder,pici,FileName,Weishu1,k,FileFormat);
int type = FindType(FileFormat);
CxImage* ima = new CxImage();
ima->CreateFromHANDLE((HBITMAP)hBitMap);
ima->Save(str, type);
if(Single == "0")
{
i++;
}
}
else
{
//單頁tiff
{
FILE* fdest=fopen(str, "r+b");
CxImageTIF* ima2app = new CxImageTIF;
ima2app->CreateFromHANDLE((HBITMAP)hBitMap);
ima2app->SetFrame(i);
ima2app->Encode(fdest, true);
delete ima2app;
fclose(fdest);
}
}
//////////////////////////////////////////////////////////////////
switch (twRC)
{
case TWRC_XFERDONE:
hbm_acq = (HBITMAP)hBitMap;
g_bSpecialMenu = FALSE;
twRC2 = CallDSMEntry(&appID,
&dsID,
DG_CONTROL,
DAT_PENDINGXFERS,
MSG_ENDXFER,
(TW_MEMREF)&twPendingXfer);
if (twRC2 != TWRC_SUCCESS)
{
}
wsprintf(buffer,"Pending Xfers = %d\r\n",twPendingXfer.Count);
if (twPendingXfer.Count == 0)
{
if (hbm_acq&&(lpdib = (LPBITMAPINFOHEADER)GlobalLock(hbm_acq))!=NULL)
{
if(!g_bSpecialMenu)
{
CloseConnection(NULL);
}
GlobalUnlock(hbm_acq);
}
}
if (hbm_acq >= (HANDLE)VALID_HANDLE)
{
SendMessage(hWnd, PM_XFERDONE, (WPARAM)hbm_acq, 0); }
else
{
SendMessage(hWnd, PM_XFERDONE, NULL, 0);
}
break;
case TWRC_CANCEL:
twRC2 = CallDSMEntry(&appID,
&dsID,
DG_CONTROL,
DAT_PENDINGXFERS,
MSG_ENDXFER,
(TW_MEMREF)&twPendingXfer);
if (twRC2 != TWRC_SUCCESS)
{
}
if (twPendingXfer.Count == 0)
{
if(!g_bSpecialMenu)
{
CloseConnection(NULL);
}
}
SendMessage(hWnd, PM_XFERDONE, NULL, 0);
break;
case TWRC_FAILURE:
twRC2 = CallDSMEntry(&appID,
&dsID,
DG_CONTROL,
DAT_PENDINGXFERS,
MSG_ENDXFER,
(TW_MEMREF)&twPendingXfer);
if (twRC2 != TWRC_SUCCESS)
{
}
if (twPendingXfer.Count == 0)
{
if(!g_bSpecialMenu)
{
CloseConnection(NULL);
}
}
SendMessage(hWnd, PM_XFERDONE, NULL, 0);
break;
default:
twRC2 = CallDSMEntry(&appID,
&dsID,
DG_CONTROL,
DAT_PENDINGXFERS,
MSG_ENDXFER,
(TW_MEMREF)&twPendingXfer);
if (twRC2 != TWRC_SUCCESS)
{
}
if (twPendingXfer.Count == 0)
{
if(!g_bSpecialMenu)
{
CloseConnection(NULL);
}
}
SendMessage(hWnd, PM_XFERDONE, NULL, 0);
break;
}
}while(twPendingXfer.Count != 0);
AcqFlag = 0;
}
關於掃描器(一)
[最後版本]利用TWAIN-實現與影象輸入裝置的通訊--孫濤--
利用TWAIN-實現與影象輸入裝置的通訊
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位資料來源進行通訊,它執行時不可見。
注意:在WindowsNT環境下16位資料來源不能夠正常工作。
在Windows作業系統中(Windows9x/2000/XP),Microsoft已經把這些檔案作為系統檔案隨同作業系統一起釋出了。你可以在Windows安裝目錄中查詢到這些檔案。如果我們要程式設計來實現對TWAIN的訪問,還需要最重要的標頭檔案。你通過訪問該http://www.twain.org/devfiles/twain.h地址來獲得TWAIN提供的標頭檔案。
好了,現在就讓我們來了解一下TWAIN的結構吧。
TWAIN的結構
TWAIN依靠三個元件協同完成與影象裝置的通訊和資料傳輸工作,這三個元件就是Application、SourceManager和Source。
元件
說明
Application
就是你要編寫的應用程式。
SourceManager
是由TWAIN提供的一個Source的管理器,它不僅可以收集本地系統已經安裝了的影象裝置,還可以根據需要去載入裝置。同時,它最重要的功能是擔任Application與Source通訊的橋樑。(其實,它就是我們前面提到的組成檔案中的dll檔案。)
Source
在這裡可以看作是影象裝置。事實上它是由裝置廠家提供的一個dll檔案。這個dll檔案是支援twain介面的。(該文不討論關於twain在Source中的應用。)
它們的層次結構圖如下:
從該圖我們可以看到,Application要從Source獲得影象資料,必須通過SourceManager傳遞來實現。Application與SourceManager間的通訊是靠呼叫TWAIN提供的DSM_Entry()函式實現。而Application不能直接與Source通訊,SourceManager與Source間的通訊是靠呼叫TWAIN提供的DS_Entry()函式實現(在這裡,我們不用關心SourceManager如何去呼叫DS_Entry函式。)。
TWAIN的使用者介面
當我們使用TWAIN介面去獲得影象資料的時候,會涉及到一些的使用者介面,首先是我們的應用程式介面,然後是SourceManager提供的使用者介面以及影象裝置(Source)所提供的使用者介面。
這些使用者介面如下:
在我們的應用程式中,可以通過“選擇裝置”來開啟SourceManager的標準使用者介面。SourceManager的介面由SourceManager提供。在這個介面中可以讓使用者選擇他想要使用的影象裝置。選中想要的裝置後,再通過“獲取…”來開啟影象裝置(Source)提供的使用者介面進行現應的操作。(注:Source提供的介面會因為你使用的影象裝置不同而有差異。)
對於這些介面,TWAIN提供了非常靈活的處理方法。對於SourceManager提供的使用者介面以及影象裝置(Source)所提供的使用者介面,我們可以選擇是否顯示它們,甚至我們還可以按自己的要求去改寫這些使用者介面。
TWAIN的介面函式
要編寫應用程式實現與支援TWAIN標準的影象裝置通訊,只需要瞭解上面提到的DSM_Entry()介面函式。TWAIN定義了大約140個操作訊息。你只要把這些訊息通過DSM_Entry()函式發給SourceManager,就可以實現對選定的Source進行相應的操作。SourceManager會分辨那些訊息屬於自己,那些訊息是該轉發給Source。
在介紹DSM_Entry()前,我們先來了解一下的TWAIN定義的訊息格式。TWAIN把它定義的操作稱為Triplets操作,就是每個操作用三個定義的引數來表示。這個三個引數用不同字首名來區分。每個Triplets操作都是唯一的,不會有歧意,它們代表一個特定的操作行為。這三個引數型別分別是DataGroup(字首名DG_)、DataArgument(字首名DAT_)和MessageID(字首名MSG_),每個引數都包含有各自的資訊。比如:DG_CONTROL/DAT_PARENT/MSG_OPENDSM就表示一個開啟SourceManager的操作,這些引數在TWAIN.H中都有定義。其他的操作(設定掃描器的解析度、獲得裝置支援的功能等等…)你可以去檢視TWAIN的參考手冊,我將在後面程式設計應用中介紹幾個最常用的操作。
現在,我們明白了TWAIN定義的Triplets操作,但是這還不夠。在使用DSM_Entry()前,必須要載入TWAIN_32.DLL檔案以獲得DSM_Entry()函式指標。(注意:在你程式中應該新增前面提到的TWAIN.H標頭檔案哦!)
DSMENTRYPROClpDSM_Entry;//*DSM_Entry入口函式的指標
HMODULEhDSMDLL;//*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_UINT16FARPASCALDSM_Entry
(pTW_IDENTITYpOrigin,//*指向操作發起者的指標
pTW_IDENTITYpDest,//*指向目標物件的指標
TW_UINT32DG,//*Triplets操作的DG引數:DG_xxxx
TW_UINT16DAT,//*Triplets操作的DAT引數:DAT_xxxx
TW_UINT16MSG,//*Triplets操作的MSG引數:MSG_xxxx
TW_MEMREFpData//*指向返回資料塊的指標
);
其中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、SourceManager和Source要實現資料傳輸,必須遵循一個操作流程。你要進行的操作應該在這個流程規定的動作佇列中按邏輯去執行。比如,在沒有載入SourceManager前,Application是不能要求Source傳輸資料的。為了更好的去描述這個流程,TWAIN為該流程定義了7個狀態(1-7)。
不要以為整個流程很複雜,只要記住這些下面這些狀態位,對於理解流程和以後的程式設計應用都是很有用的。
l狀態位1,2,3
這幾個狀態是用於描述SourceManager的,它們是SourceManager專有的狀態位,所以SourceManager的標誌位是不會大於3的.
l狀態位4,5,6,7
這幾個狀態是Source專有的。如果Source打開了,Source的標誌位就不會小於4;如果Source關閉了,Source就沒有了標誌位。
要注意,我們的應用程式可以使用了多個Source,每個與Source的連線都是一個單獨的會話,對於開啟的每個Source,他們的標誌位都是相互獨立的,不互相關聯。現在就來看看流程圖。
流程標誌位說明
狀態1–準備會話
在Application和SourceManager建立會話前,SourceManager的標誌位是1.
在這個時候,SourceManager還沒有被載入到記憶體中。如果SourceManager被載入到記憶體中,則狀態位是2或者3。
狀態2–載入SourceManager
SourceManager現在已經被成功地載入到了程式中,但是沒有開啟SourceManager。
在這個時候,SourceManager開始準備去接受Application的Triplets操作。
狀態3–開啟SourceManager
SourceManager已經開啟並且準備去管理Source.
SourceManager現在準備向Source傳送開啟操作,去開啟指定的Source,並等待所有針對Source的操作結束後,去關閉開啟的Source.
SourceManager在會話關閉前,狀態位將保持為3.
當Application開啟的Source沒有關閉時,SourceManager會拒絕關閉。
狀態4–開啟Source
在響應Application的一個指定的Triplets操作後,Source被載入到系統中,並且被Sourcemanager開啟。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操作。
l載入SourceManager並獲得DSM_Entry入口函式(狀態1到2)
應用程式在呼叫DSM_Entry函式指標前必須載入SourceManager。這裡沒有使用Triplets操作。你可以使用LoadLibrary()函式,載入TWAIN_32.DLL檔案。並使用GetProcAddress()函式,獲得DSM_Entry函式指標
l開啟SourceManager(狀態2到3)
Triplets操作:DG_CONTROL/DAT_PARENT/MSG_OPENDSM
通過該操作,你可以開啟SourceManager,並且還要在你的應用程式中,指定一個窗體作為Source的父視窗。SourceManager將通過該窗體,把Source的訊息傳遞給你的應用程式。
l選擇Source(狀態3期間)
Triplets操作:DG_CONTROL/DAT_IDENTITY/MSG_USERSELECT
你的應用程式傳送該操作後,將顯示SourceManager的使用者介面,它是一個對話方塊。這個對話方塊中顯示了系統中所有支援Twain的裝置列表。系統預設裝置將高亮顯示在列表框中。你可以通過該列表框選擇你想要的輸入裝置。
l開啟Source(狀態3到4)
Triplets操作:DG_CONTROL/DAT_IDENTITY/MSG_OPENDS
該操作可以開啟你選擇的Source(影象輸入裝置),同時,SourceManager會給該Source分配一個唯一的識別符號。你要把開啟的這個Source放在一個指定的結構中,以便於在後面和該Source進行通訊。
l設定Source的效能引數(狀態4期間)
Triplets操作:DG_CONTROL/DAT_CAPABILITY/MSG_GET
DG_CONTROL/DAT_CAPABILITY/MSG_SET
這裡有兩個Triplets操作,通過使用這兩個操作可以去查詢當前裝置是否支援的某種功能,如果支援,還可以獲得裝置功能的當前值、預設值、以及可以重新設定的範圍。你還可以根據查詢的結果,按你的要求去重新設定該功能的當前值。
l請求從Source獲取資料(狀態4到5)
Triplets操作:DG_CONTROL/DAT_USERINTERFACE/MSG_ENABLEDS
通過該操作,可以讓Source顯示它的使用者介面,Source會去為資料傳輸作準備。
l確認資料準備傳輸(狀態5到6)
Triplets操作:DG_CONTROL/DAT_EVENT/MSG_PROCESSEVENT
首先要說明一下,從狀態5到狀態6的這個過程,不是由你的應用程式通過Triplets操作來發起的。而是當Source準備好去傳輸資料時,它會發出一個事件訊號來實現的。你的應用程式應該要去檢查這個事件訊號。
如何去檢查這個事件訊號?我們在載入SourceManager時,就為Source指定了一個父視窗,Source會把它事件訊號封裝成一個Windows的訊息結構傳送給它的父視窗。你可以在這個窗體的訊息迴圈中去,使用DG_CONTROL/DAT_EVENT/MSG_PROCESSEVENT操作,來判斷Source是否有事件發生。MSG_XFERREADY就表示這個過程的狀態位從5變為6了。
l開始進行資料傳輸(狀態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點陣圖的指標。
l中止傳輸(狀態7到6到5)
Triplets操作:DG_CONTROL/DAT_PENDINGXFERS/MSG_ENDXFER
在每次資料傳輸結束(成功、退出)後,可以傳送該操作給Source,去表示應用程式已經接受完了所有的資料了。同時還可以根據它的返回值,去檢查是否有其它的影象等待傳送。
l斷開TWAIN會話(狀態5到4)
Triplets操作:DG_CONTROL/DAT_USERINTERFACE/MSG_DISABLEDS
該操作讓開啟Source失效。
l關閉Source(狀態4到3)
Triplets操作:DG_CONTROL/DAT_IDENTITY/MSG_CLOSEDS
該操作可以關閉指定的Source。
l關閉SourceManager(狀態3到2)
Triplets操作:DG_CONTROL/DAT_PARENT/MSG_CLOSEDSM
關閉開啟的SourceManager。
TWAIN的資料傳輸模式
TWAIN定義了三種模式用於Source到Application的資料傳輸:本地模式、檔案模式,和快取模式。現在對每種模式進行一個簡單的介紹。
注:對於音訊資料的傳輸,只能選擇本地模式或者檔案模式來進行傳輸。
本地模式
所有的輸入裝置都支援這種本地資料傳輸模式,同時它也是TWAIN預設的資料傳輸模式,並且它還是最容易使用的資料傳輸模式。但是,它有一定的侷限性,它傳輸的資料必須是DIB影象資料,並且在傳輸時,會受到系統記憶體大小限制。
傳輸資料的格式:DIB(Device-IndependentBitmap)
使用該模式,在資料傳輸時Source分配一塊單獨的記憶體區域,並把圖形資料寫入這個記憶體區域內。然後它通過一個指向該記憶體地址的指標告訴Application,資料存放在什麼地方。你的應用程式通過訪問該記憶體區域去獲得具體的影象資料。注意,Application在獲得資料後要負責去釋放這部分的記憶體。如果你的影象資料大於系統當前可用記憶體,會導致傳輸失敗。
檔案模式
該模式是讓Application建立一個檔案,這個檔案用於儲存傳輸的資料,Source將對該檔案進行讀寫操作。Source將把要傳輸的資料寫到該檔案中,你的應用程式通過訪問該檔案,就可以獲得傳輸的資料。
在使用本地模式傳輸一個大的影象檔案時,如果記憶體不夠大,可以考慮使用檔案傳輸模式來傳輸。檔案傳輸模式與快取傳輸模式相比,在使用方法上要簡單些,但是該模式在傳輸速度上比快取模式的傳輸速度要慢一些,並且在資料傳輸完畢後,你的應用程式還必須去管理這個資料檔案。
快取模式
快取模式在整個傳輸過程中,將使用一個或多個記憶體快取區,記憶體快取區的分配和釋放工作由Application來控制。在傳輸過程中,傳輸資料被當作一個未知格式的點陣圖。Application必須使用TW_IMAGEINFO和TW_IMAGEMEMXFER操作,去得到各個快取區的資訊並把它們正確組織為一個完整的點陣圖。
如果使用本地模式或檔案模式去傳輸資料,整個傳輸過程在只需要一個Triplets操作就可以完成。如果使用快取模式傳輸資料,你的應用程式可能需要使用多個Triplets操作,不停地去獲得快取區的資料資訊。但是,該傳輸模式具有很好的靈活性,可以很好的去控制獲得的資料,只不過在程式設計應用上要麻煩一些。
TWAIN的應用實現
好了,看了前面的對TWAIN的介紹,現在我們就動手開始進行實際的程式設計吧。在這裡,只進行一個最簡單的應用實現。我們的應用程式不去設定裝置的效能引數,不選擇其它資料傳輸模式,僅僅使用TWAIN的預設的本地傳輸模式方式,去獲得影象資料。
在進行實際程式設計應用前,我們可以先安裝TWAIN提供的工具包。它不僅提供了TWAIN應用的例程,還可以在你的計算機系統上安裝一個虛擬的影象輸入裝置(TWAIN_32SampleSource)。這對於沒有掃描器、數碼相機的開發者,提供了一個很好的測試裝置。TWAIN工具包的下載地址:http://www.twain.org/devfiles/twainkit.exe。
由於TWAIN目前提供的是基於C的程式設計介面,所以我們這裡採用VC作為開發工具。我們可以建一個自己的TWAIN類。把一些Triplets操作封裝成這個類的成員函式。以便於程式呼叫。記住:在你的專案中要加入TWAIN提供的標頭檔案。
前面已經介紹了,在進行TWAIN的操作前,如何載入TWAIN_32.dll檔案,獲得DSM_Entry()函式指標。下面僅簡單介紹一下其他的成員函式。
l開啟SourceManager
intCTwain::OpenSourceManager(void)
{
TW_UINT16rc;
...
//lpDSM_Entry是指向DSM_Entry的函式指標
rc=(*lpDSM_Entry)(&AppID,NULL,
DG_CONTROL,DAT_PARENT,MSG_OPENDSM,
(TW_MEMREF)&(*hPWnd));//hPWnd是指定為Source的父視窗的控制代碼
switch(rc)//檢查開啟SourceManager是否成功
{
caseTWRC_SUCCESS://成功
...
caseTWRC_CANCEL:
...
}
...
}
l開啟Source
intCTwain::OpenSource(void)
{
TW_UINT16rc;
rc=(*lpDSM_Entry)(&AppID,NULL,
DG_CONTROL,DAT_IDENTITY,MSG_OPENDS,
(TW_MEMREF)&SourceID);//SourceID是要求開啟Source
switch(rc)//檢查開啟SourceManager是否成功
{
caseTWRC_SUCCESS://成功
...
}
...
}
l處理Source的事件
intCTwain::DealSourceMsg(MSG*pMSG)
{
TW_UINT16rc=TWRC_NOTDSEVENT;
TW_EVENTtwEvent;
twEvent.pEvent=(TW_MEMREF)pMSG;
rc=(*lpDSM_Entry)(&AppID,&SourceID,
DG_CONTROL,DAT_EVENT,MSG_PROCESSEVENT,
(TW_MEMREF)&twEvent);
switch(twEvent.TWMessage)
{
caseMSG_XFERREADY://Source準備好傳輸資料了iStatus=6
iStatus=6;
GetBmpInfo();
DoNativeTransfer();
caseMSG_CLOSEDSREQ://關閉Source使用者介面的申請
caseMSG_CLOSEDSOK:
caseMSG_NULL:
}
...
}
l使用本地模式傳輸資料
intCTwain::DoNativeTransfer(void)
{
TW_UINT32hBitMap=NULL;//指向影象資料地址
TW_UINT16rc;
HANDLEhbm_acq=NULL;
rc=(*lpDSM_Entry)(&AppID,&SourceID,
DG_IMAGE,DAT_IMAGENATIVEXFER,MSG_GET,
(TW_MEMREF)&hBitMap);
switch(rc)
{
caseTWRC_XFERDONE://資料傳輸完成
hbm_acq=(HBITMAP)hBitMap;
//把影象資料地址通過訊息傳送給應用程式
//應用程式通過就可以通過hbm_acq來處理影象資料
SendMessage(*hPWnd,PM_XFERDONE,(WPARAM)hbm_acq,0);
iStatus=7;
break;
caseTWRC_CANCEL:
caseTWRC_FAILURE:
}
...
}
l應用程式處理影象資料
LRESULTCTwainAppView::WindowProc(UINTmessage,WPARAMwParam,LPARAMlParam)
{
if(message==PM_XFERDONE)//收到PM_XFERDONE訊息
{
HBITMAPhBmp=FixUp(HANDLE(wParam));//影象轉換處理
Bitmap*pBm=0;//GDI+的Bitmap物件
pBmp=pBm->FromHBITMAP(hBmp,hDibPal);
Invalidate();
...
}
returnCView::WindowProc(message,wParam,lParam);
}
l在應用程式的檢視視窗上繪圖
voidCTwainAppView::OnDraw(CDC*pDC)
{
...
//使用GDI+在檢視上繪圖
GraphicsmyGraphics(pDC->m_hDC);
myGraphics.DrawImage(pBmp,0,0,
pBmp->GetWidth(),pBmp->GetHeight());
...
}
上面的這些程式碼,只是整個應用程式中的一部分。通過這些程式碼,你可以對TWAIN的程式設計應用有一個最基本的瞭解。如果要進一步實現TWAIN的應用,可參考TWAIN提供的例程。
結束語
目前TWAIN只支援Windows和Mac作業系統,下一版本將支援Unix/Linux和64位的Windows作業系統。我們平常使用的軟體,有很多都支援TWAIN的應用。比如我們很熟悉的Photoshop、ACDSee等軟體,Windows98/2000附件中的“影象”軟體甚至還提供了它們封裝的TWAIN控制元件(KodarkImage)。
該文對於TWAIN只做了很簡單的介紹,主要是起個瞭解的作用。TWAIN還有很多的高階應用沒有涉及到,比如如何設定裝置的效能引數、如何使用不同的資料傳輸模式、如何獲得影象的佈局,如何傳輸壓縮的影象資料、如何更改SourceManager的使用者介面等等。這裡所談到的一切,還只是基於Application級的TWAIN應用。你還可以做基於Source級的應用,讓你生產的輸入裝置,支援TWAIN。
你可以訪問TWAIN的官方網站www.twain.org,來獲得最新最全面的資訊和資料。最後,如果該文能對你在TWAIN應用中有所幫助,或者讓你對TWAIN有所瞭解。那麼我的目的就達到了。