天天看點

剪貼闆大觀園(二): 剪貼闆間諜(ClipSpy)

聲明

:此譯文僅供網友們學習之用!您可以随意轉載此譯文,希望您轉載時保留作譯者和此聲明。如有翻譯不當之處,敬請指正:mailto:[email protected]。近期,我會在《剪貼闆大觀園》系列中補充大量新的文章,希望與您交流,共同提高!

簡介

最近有些空閑時間,我一直在思考Windows程式設計中一個我一直想知道卻不知道的領域,也就是剪貼闆。

我已經自己寫了一個處理剪貼闆上檔案拖放(HDROP) 資料的工具,用它來仿真Explorer(資料總管)的拷貝和粘貼指令,可是呢,僅僅能拷貝粘貼一種資料格式是遠遠不夠的。我想更深入地考察一下剪貼闆上的内容,通過這個過程說不準兒我會找到一些關于應用程式中使用剪貼闆的一些技巧性的東西。(舉個例子,什麼是“Woozle”?――見上邊圖2左邊的清單框。)

好啦,這一段空閑時間已經到了最後的周末了(我已把聖誕節要買的東西都買好了,真是太妙了),而且這個階段的自學的最終結局是“ClipSpy”,一個馬力增強了的剪貼闆觀察器。 另外,它還可以觀察到即拖即存操作中的資料(因為MFC使用同一個類來處理即拖即存和剪貼闆),不過,我還是叫它ClipSpy ,因為ClipboardAndDragAndDropSpy敲起來确實有點兒費勁兒。

如果您急切地想知道更多有關clipboard的知識,請直接跳到“實作細節”部分,這裡我會專門指出代碼中的值得一看的地方。

ClipSpy是用MSVC 6, SP2所寫。我分别在 Windows 98 和 Windows 2000 build 2072(both ANSI and Unicode builds )作了測試。

ClipSpy 做了些什麼

ClipSpy将自己注冊為一個剪貼闆觀察器,這一部分其實沒有什麼神秘的。 可就在剪貼闆上的内容發生變化的同時,ClipSpy 枚舉出所有在剪貼闆上的資料格式(剪貼闆上的資料格式可能有很多種)并且将它們顯示在左側的面闆中。 在格式清單中選擇一種格式并單擊滑鼠左鍵,就會在右側的面闆中顯示放在剪貼闆上的資料。右側面闆的功能有點兒象MSVC的記憶體debug視窗,它不僅有ASCII代碼,還有原始資料。

    左側的面闆亦可做為拖放操作的目标來用,也就是說你可以從一個支援拖放的程式中把任何格式的資料拖到此清單中來,ClipSpy會顯示出全部拖過來的資料。跟前邊說一樣,各種資料格式列于左側的面闆中,單擊任一種格式的資料ClipSpy都會将相應的資料顯示在右邊的面闆中。

    File菜單下也有一對相應的菜單指令。“Read Clipboard”指令為重新讀取剪貼闆上的内容,你可以在執行了拖放操作之後使用此指令來再顯示以下剪貼闆上的内容。 “Empty Clipboard”指令會讓你的剪貼闆空空如也。

實作細節

ClipSpy的大多數工作都是通過MFC類COleDataObject完成的,此COleDataObject實作了接口IdataObject,并且提供了通路剪貼闆上的資料以及拖放的資料源的方法。成員函數CLeftView::ReadDataAndFillList()是用來枚舉剪貼闆上的資料格式并将其寫到清單(list)控件中。枚舉代碼如下所示:

BOOL CLeftView::ReadDataAndFillList ( COleDataObject* pDO )

{

FORMATETC etc;

UINT      uNumFormats = 0;         //.剪貼闆上資料格式種類數

    pDO->BeginEnumFormats();

    while ( pDO->GetNextFormat ( &etc ))

    {

        if ( pDO->IsDataAvailable ( etc.cfFormat ))

        {

            uNumFormats++;

        }

    }

}

對于每一種格式,ClipSpy首先檢查它是否是内建類型。如果不是的話,ClipSpy就會取得此格式的标記字元串,并調用函數GetClipboardFormatName():

    TCHAR szFormat [256];

    GetClipboardFormatName ( etc.cfFormat, szFormat, 256 );

ClipSpy有一用于内建類型的字元串清單(因為GetClipboardFormatName()函數隻識别程式已注冊過的基本類型)。然後,ClipSpy開始試着去讀取此格式對應的資料:

HGLOBAL hgData;

UINT    uDataSize;

CClipSpyDoc* pDoc = GetDocument();

    // Get an HGLOBAL of the data.

    hgData = pDO->GetGlobalData ( etc.cfFormat );

    if ( NULL != hgData )

    {

        uDataSize = GlobalSize ( hgData );

        pDoc->AddDataBlock ( hgData, uDataSize );

    }

    else

    {

        pDoc->AddEmptyBlock();

    }

函數AddDataBlock()和AddEmptyBlock()都是文檔類的成員函數。文檔類則是存放所有資料的拷貝的場所。

當把資料拖到清單空間中時,也會執行一樣的代碼――成員函數CLeftView::OnDrop()中有一COleDataObject 對象,在調用ReadDataAndFillList()時直接将此對象作為參數傳遞過來。

其它你可能感興趣的地方有CLeftView::OnItemchanged() 和 CClipSpyView::OnUpdate()。CLeftView::OnItemchanged()用于監測清單,當你更換了選中的Format項時,會觸發此事件,并調用UpdateAllViews(),而UpdateAllViews()會依次調用OnUpdate()。OnUpdate()用于将資料寫到RechEdit控件中去。

本程式存在的一些問題

在拖放操作之後,函數COleDataObject::GetGlobalData()有時會發生讀取某些資料失敗。由于在拖放這一技術上我也隻是個新手,我還沒有找到其原因(或者說是其解決方案)。呵呵,現在如果這一可怕的情況發生的話,ClipSpy隻會列出其資料格式,而在“Data size”一欄中提示“<Data unavailable>”。

注意,程式的右邊面闆上放的是一個RichEdit控件,你可以從它那裡拷貝東西到剪貼闆上。可我必須告訴你,你不要對它進行拷貝,這樣做會導緻OLE内部沖突。再次聲明:這個問題可能是由于我的代碼自身的錯誤,您就不要用此功能好了。:)

由于剪貼闆資料所占記憶體最初是由GlobalAlloc()函數配置設定的,是以每一塊兒資料都比資料本身要長。(因為“位元組對其(byte-alignment)”的原因,GlobalAlloc()會配置設定一塊兒比你執行請求要大的記憶體)。比如說在Win98中,我用MSVC拷貝8個字元長的單詞到剪貼闆上,剪貼闆上包含32位元組的CF_TEXT(普通的ANSI文本)資料,其中前9個那個單詞(8位元組加上一個字元串結束符null),剩下的都是些亂糟糟的垃圾。ClipSpy把一整塊兒的資料都顯示出來,不作任何的解釋處理。

對了,你一定要記住:如果你要debug這個程式――也就是說當你一步一步執行程式時,千萬不要使用剪貼闆。因為ClipSpy還沒有完全運作起來,我可要祝賀你了,:)全部的剪貼闆操作被挂起,ClipSpy、MSVC以及所有你曾在這一時間内使用剪貼闆操作的程式都統統崩潰!媽呀,好像隻有按Reset鍵重新開機愛機了。

繼續閱讀