處理命令介面資料傳輸案例

Shell 資料物件檔討論使用拖放或剪貼簿傳輸殼層資料的一般方法。 不過,若要在應用程式中實作殼層資料傳輸,您也必須瞭解如何將這些一般原則和技術套用至 Shell 資料可傳輸的各種方式。 本檔提供常見的殼層資料傳輸案例,並討論如何在應用程式中實作每個案例。

注意

雖然每個案例都討論特定的資料傳輸作業,但其中許多案例都適用于各種相關案例。 例如,大部分剪貼簿和拖放傳輸之間的主要差異在於資料物件抵達目標的方式。 一旦目標具有資料物件 IDataObject 介面的指標,擷取資訊的程式對於這兩種類型的資料傳輸而言大致相同。 不過,某些案例僅限於特定類型的作業。 如需詳細資訊,請參閱個別案例。

 

一般準則

下列各節會討論單一、相當特定的資料傳輸案例。 不過,資料傳輸通常較為複雜,而且可能涉及數個案例的層面。 您通常事先不知道您需要處理的案例。 以下是一些要記住的一般指導方針。

針對資料來源:

  • Shell 剪貼簿格式,除了 CF_HDROP之外,不會預先定義。 您必須呼叫 RegisterClipboardFormat來註冊您想要使用的每個格式。
  • 資料物件中的格式會依來源的喜好設定順序提供。 列舉資料物件,並挑選您可以取用的第一個物件。
  • 包含您可以支援的格式數目。 您通常不知道要卸載資料物件的位置。 這個做法可改善資料物件將包含置放目標可接受格式的機率。
  • 現有的檔案應以 CF_HDROP 格式提供。
  • 提供CFSTR_FILECONTENTS CFSTR_FILEDESCRIPTOR/ 格式類似檔案資料。 這種方法可讓目標從資料物件建立檔案,而不需要知道基礎資料儲存體的任何專案。 您通常會將資料呈現為 IStream 介面。 此資料傳輸機制比全域記憶體物件更有彈性,而且使用較少的記憶體。
  • 拖曳來源應該會在拖曳 Shell 專案時提供 CFSTR_SHELLIDLIST 格式。 您可以透過 IShellFolder::GetUIObjectOfIShellItem::BindToHandler 方法來取得專案的資料物件。 資料來源可以使用SHCreateDataObject來建立支援CFSTR_SHELLIDLIST格式的標準資料物件實作。
  • 卸載想要使用殼層專案程式設計模型拖曳之專案的相關原因的目標,可以使用SHCreateShellItemArrayFromDataObject將 IDataObject 轉換成IShellItemArray
  • 使用標準意見反應資料指標。
  • 支援左右拖曳。
  • 從内嵌物件使用資料物件本身。 這種方法可讓您的應用程式擷取資料物件必須提供的任何額外格式,並避免建立額外的內含專案層。 例如,從伺服器 A 的内嵌物件從伺服器/容器 B 拖曳,並放在容器 C 上。C 應該建立伺服器 A 的内嵌物件,而不是伺服器 B 包含伺服器 A 内嵌物件的内嵌物件。
  • 請記住,Shell 可能會在移動檔案時使用 優化的移動 或刪除 貼上 作業。 您的應用程式應該能夠辨識這些作業並適當地回應。

針對資料目標:

  • Shell 剪貼簿格式,除了 CF_HDROP之外,不會預先定義。 您必須呼叫 RegisterClipboardFormat來註冊您想要使用的每個格式。
  • 實作並註冊 OLE 置放目標。 盡可能避免使用 Windows 3.1 目標或 WM_DROPFILES 訊息。
  • 資料物件所包含的格式會根據物件的來源而有所不同。 由於您通常不知道資料物件來自何處,因此請勿假設會出現特定格式。 資料物件應該依品質順序列舉格式,從最佳開始。 因此,為了取得最佳的可用格式,應用程式通常會列舉可用的格式,並在列舉中使用可支援的第一種格式。
  • 支援右拖曳。 您可以建立 拖放處理常式來自訂拖曳快捷方式功能表。
  • 如果您的應用程式將接受現有的檔案,它必須能夠處理 CF_HDROP 格式。
  • 一般而言,接受檔案的應用程式也應該處理CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR格式。 雖然來自檔案系統的檔案具有CF_HDROP格式,但命名空間延伸模組等提供者的檔案通常會使用CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR。 範例包括Windows CE資料夾、檔案傳輸通訊協定 (FTP) 資料夾、網頁資料夾和 CAB 資料夾。 來源通常會實作 IStream 介面,以檔案的形式呈現其儲存體中的資料。
  • 請記住,Shell 可能會在移動檔案時使用 優化的移動 或刪除 貼上 作業。 您的應用程式應該能夠辨識這些作業並適當地回應。

將檔案名從剪貼簿複製到應用程式

場景: 使用者在 Windows 檔案總管中選取一或多個檔案,並將其複製到剪貼簿。 您的應用程式會擷取檔案名,並將其貼到檔中。

例如,此案例可用來允許使用者藉由剪下檔案並貼到您的應用程式來建立 HTML 連結。 然後,您的應用程式可以從資料物件擷取檔案名,並加以處理以建立錨點標記。

當使用者在 Windows 檔案總管中選取檔案並將其複製到剪貼簿時,Shell 會建立資料物件。 然後它會呼叫 OleSetClipboard ,將指標放在剪貼簿上資料物件的 IDataObject 介面。

當使用者從應用程式的功能表或工具列中選取 [貼上 ] 命令時:

  1. 呼叫 OleGetClipboard 以擷取資料物件的 IDataObject 介面。
  2. 呼叫 IDataObject::EnumFormatEtc 以要求列舉值物件。
  3. 使用列舉值物件的 IEnumFORMATETC 介面來列舉資料物件所包含的格式。

注意

此程式中的最後兩個步驟會包含在內,以求完整性。 它們通常不需要用於簡單的檔案傳輸。 用於這種資料傳輸的所有資料物件都應該包含 CF_HDROP 格式,可用來判斷物件所包含的檔案名。 不過,針對較一般的資料傳輸,您應該列舉格式,並選取應用程式可以處理的最佳格式。

 

從資料物件擷取檔案名

下一個步驟是從資料物件擷取一或多個檔案名,並將其貼到您的應用程式中。 請注意,本節所討論從資料物件擷取檔案名的程式同樣適用于拖放傳輸。

從資料物件擷取檔案名最簡單的方式是 CF_HDROP 格式:

  1. 呼叫 IDataObject::GetData。 將FORMATETC結構的cfFormat成員設定為CF_HDROP,並將tymed成員設定為 TYMED_HGLOBALdwAspect成員通常會設定為 DVASPECT_CONTENT。 不過,如果您需要 (8.3) 格式的簡短檔案路徑,請將 dwAspect 設定為 DVASPECT_SHORT。

    當 IDataObject::GetData傳回時,STGMEDIUM結構的hGlobal成員會指向包含資料的全域記憶體物件。

  2. 建立 HDROP 變數,並將其設定為STGMEDIUM結構的hGlobal成員。 HDROP 變數現在是 DROPFILES 結構的控制碼,後面接著包含所複製檔案的完整檔案路徑的雙 Null 終止字串。

  3. 呼叫 DragQueryFile 並將 iFile 參數設定為 0xFFFFFFFF,以判斷清單中的檔案路徑數目。 函式會傳回清單中的檔案路徑數目。 此清單中的檔案路徑以零起始的索引會用於下一個步驟來識別特定路徑。

  4. 從全域記憶體物件擷取檔案路徑,方法是針對每個檔案呼叫 DragQueryFile 一次,並將 iFile 設定為檔案的索引。

  5. 視需要處理檔案路徑,並將其貼到您的應用程式中。

  6. 呼叫ReleaseStgMedium,並在步驟 1 中傳遞至IDataObject::GetData的 STGMEDIUM結構的指標。 發行結構之後,您在步驟 2 中建立的 HDROP 值已不再有效,不應使用。

將已卸載檔案的內容複寫到應用程式

場景: 使用者從 Windows 檔案總管拖曳一或多個檔案,並將其放在應用程式的視窗中。 您的應用程式會擷取檔案的內容 () ,並將其貼到應用程式中。

此案例會使用拖放功能,將檔案從 Windows 檔案總管傳輸到應用程式。 在作業之前,您的應用程式必須:

  1. 呼叫 RegisterClipboardFormat 以註冊任何所需的 Shell 剪貼簿格式。
  2. 呼叫 RegisterDragDrop 以註冊目標視窗和應用程式的 IDropTarget 介面。

使用者選取一或多個檔案並開始拖曳作業之後:

  1. Windows 檔案總管會建立資料物件,並將支援的格式載入其中。
  2. Windows 檔案總管會呼叫 DoDragDrop 來起始拖曳迴圈。
  3. 當拖曳影像到達目標視窗時,系統會呼叫 IDropTarget::D ragEnter通知您。
  4. 若要判斷資料物件包含的內容,請呼叫資料物件的 IDataObject::EnumFormatEtc 方法。 使用 方法所傳回的列舉值物件,列舉資料物件所包含的格式。 如果您的應用程式不想接受任何這些格式,請傳回DROPEFFECT_NONE。 針對此案例的目的,您的應用程式應該忽略不包含用來傳輸檔案格式的任何資料物件,例如 CF_HDROP
  5. 當使用者卸載資料時,系統會呼叫 IDropTarget::D rop
  6. 使用 IDataObject 介面來擷取檔案的內容。

有數種不同的方式可從資料物件擷取 Shell 物件的內容。 一般而言,請使用下列順序:

如果資料擷取程式會冗長,您可能會想要以非同步方式在背景執行緒上執行作業。 然後,您的主要執行緒可以繼續進行,而不會有不必要的延遲。 如需如何處理非同步資料擷取的討論,請參閱 以非同步方式拖放殼層物件

使用 CFSTR_FILECONTENTS 格式從檔案擷取資料

CFSTR_FILECONTENTS格式提供非常有彈性且功能強大的方法來傳輸檔案的內容。 甚至不需要將資料儲存為單一檔案。 此格式所需的所有專案,就是資料物件會將資料呈現至目標,就像是檔案一樣。 例如,實際資料可能是文字檔的區段,或是從資料庫擷取的資料區塊。 目標可以將資料視為檔案,而且不需要知道有關基礎儲存機制的任何資訊。

命名空間延伸通常會使用 CFSTR_FILECONTENTS 來傳輸資料,因為此格式不會假設任何特定的儲存機制。 命名空間延伸模組可以使用任何方便的儲存機制,並使用此格式將物件呈現給應用程式,就像是檔案一樣。

CFSTR_FILECONTENTS的資料傳輸機制通常TYMED_ISTREAM。 傳輸 IStream 介面指標所需的記憶體比將資料載入全域記憶體物件還少, 而 IStream 是比 IStorage更簡單的方式來表示資料。

CFSTR_FILECONTENTS格式一律會伴隨CFSTR_FILEDESCRIPTOR格式。 您必須先檢查此格式的內容。 如果傳輸多個檔案,則資料物件實際上會包含多個 CFSTR_FILECONTENTS 格式,每個檔案各有一個。 CFSTR_FILEDESCRIPTOR格式包含每個檔案的名稱和屬性,並為擷取特定檔案CFSTR_FILECONTENTS格式所需的每個檔案提供索引值。

若要擷取 CFSTR_FILECONTENTS 格式:

  1. CFSTR_FILEDESCRIPTOR 格式擷取為 TYMED_HGLOBAL 值。
  2. 傳回之 STGMEDIUM結構的hGlobal成員會指向全域記憶體物件。 將 hGlobal 值傳遞至 GlobalLock,以鎖定該物件。
  3. GlobalLock 傳回的指標轉換成 FILEGROUPDESCRIPTOR 指標。 它會指向 FILEGROUPDESCRIPTOR 結構,後面接著一或多個 FILEDESCRIPTOR 結構。 每個 FILEDESCRIPTOR 結構都包含其中一個隨附 CFSTR_FILECONTENTS 格式所包含檔案的描述。
  4. 檢查 FILEDESCRIPTOR 結構,以判斷哪一個結構對應至您要擷取的檔案。 該 FILEDESCRIPTOR結構的以零起始的索引可用來識別檔案的CFSTR_FILECONTENTS格式。 因為全域記憶體區塊的大小不是位元組精確,所以請使用 結構的 nFileSizeLownFileSizeHigh 成員來判斷全域記憶體物件中代表檔案的位元組數目。
  5. 呼叫IDataObject::GetData,並將FORMATETC結構的cfFormat成員設定為CFSTR_FILECONTENTS值,並將lIndex成員設定為您在上一個步驟中決定的索引。 tymed成員通常會設定為TYMED_HGLOBAL |TYMED_ISTREAM |TYMED_ISTORAGE。 然後,資料物件可以選擇其慣用的資料傳輸機制。
  6. IDataObject::GetData傳回的STGMEDIUM結構將包含檔案資料的指標。 檢查 結構的 tymed 成員,以判斷資料傳輸機制。
  7. 如果 tymed 設定為 TYMED_ISTREAM 或 TYMED_ISTORAGE,請使用 介面來擷取資料。 如果 tymed 設定為 TYMED_HGLOBAL,則資料會包含在全域記憶體物件中。 如需如何從全域記憶體物件擷取資料的討論,請參閱Shell Data Object從資料物件擷取全域記憶體物件一節。
  8. 呼叫 GlobalLock 以解除鎖定您在步驟 2 中鎖定的全域記憶體物件。

處理優化移動作業

場景: 使用優化的移動,檔案會從檔案系統移至命名空間延伸模組。

在傳統的移動作業中,目標會建立資料的複本,而來源會刪除原始資料。 此程式可能沒有效率,因為它需要兩份資料複本。 使用大型物件,例如資料庫,傳統移動作業甚至可能不實用。

透過優化的移動,目標會使用其對儲存資料的瞭解來處理整個移動作業。 永遠不會有第二個數據複本,而且不需要來源刪除原始資料。 殼層資料非常適合優化移動,因為目標可以使用殼層 API 處理整個作業。 典型的範例是移動檔案。 一旦目標具有要移動的檔案路徑,就可以使用 SHFileOperation 來移動它。 不需要來源刪除原始檔案。

注意

Shell 通常會使用優化的移動來移動檔案。 若要正確處理殼層資料傳輸,您的應用程式必須能夠偵測及處理優化的移動。

 

優化移動會以下列方式處理:

  1. 來源會呼叫 DoDragDrop並將 dwEffect 參數設定為 DROPEFFECT_MOVE,以指出可以移動來源物件。

  2. 目標會透過其中一個 IDropTarget 方法接收DROPEFFECT_MOVE值,指出允許移動。

  3. 目標會複製物件 (未優化移動) ,或 (優化移動) 移動物件。

  4. 然後,目標會告知來源是否需要刪除原始資料。

    優化移動是預設作業,目標會刪除資料。 若要通知來源已執行優化移動:

    如果目標執行未優化移動,則必須由來源刪除資料。 若要通知來源未優化的移動已執行:

  5. 來源會檢查目標可傳回的兩個值。 如果兩者都設定為 DROPEFFECT_MOVE,它會藉由刪除原始資料來完成未優化的移動。 否則,目標會執行優化的移動,並已刪除原始資料。

處理貼上刪除作業

場景: 一或多個檔案會從 Windows 檔案總管中的資料夾剪下,並貼到命名空間副檔名中。 Windows 檔案總管會保留醒目提示的檔案,直到收到貼上作業結果的意見反應為止。

傳統上,當使用者剪下資料時,會立即從檢視中消失。 這可能沒有效率,而且如果使用者擔心資料發生什麼事,可能會導致可用性問題。 替代方法是使用貼上刪除作業。

使用刪除貼上作業時,選取的資料不會立即從檢視中移除。 相反地,來源應用程式會藉由變更字型或背景色彩,將其標示為已選取。 在目標應用程式貼上資料之後,它會通知來源有關作業的結果。 如果目標執行 優化移動,則來源可以直接更新其顯示。 如果目標執行一般移動,來源也必須刪除其資料複本。 如果貼上失敗,來源應用程式會將選取的資料還原為其原始外觀。

注意

當剪下/貼上作業用來移動檔案時,Shell 通常會使用 delete-on-paste 作業。 使用 Shell 物件的刪除貼上作業通常會使用 優化的移動 來移動檔案。 若要正確處理殼層資料傳輸,您的應用程式必須能夠偵測及處理貼上刪除作業。

 

刪除貼上的基本需求是目標必須將作業的結果回報給來源。 不過,標準剪貼簿技術無法用來實作貼上刪除,因為它們不會提供方法讓目標與來源通訊。 相反地,目標應用程式會使用資料物件的 IDataObject::SetData 方法,向資料物件報告結果。 然後,資料物件可以透過私人介面與來源通訊。

刪除貼上作業的基本程式如下:

  1. 來源會標示所選資料的螢幕顯示。
  2. 來源會建立資料物件。 它會藉由新增 資料值為 DROPEFFECT_MOVE 的 CFSTR_PREFERREDDROPEFFECT 格式來指出剪下作業。
  3. 來源會使用 OleSetClipboard將資料物件放在剪貼簿上。
  4. 目標會使用 OleGetClipboard從剪貼簿擷取資料物件。
  5. 目標會擷取 CFSTR_PREFERREDDROPEFFECT 資料。 如果它設定為僅DROPEFFECT_MOVE,則目標可以執行優化的移動,或只是複製資料。
  6. 如果目標未執行優化的移動,它會呼叫 IDataObject::SetData 方法,並將 CFSTR_PERFORMEDDROPEFFECT 格式設定為 DROPEFFECT_MOVE。
  7. 當貼上完成時,目標會呼叫 IDataObject::SetData 方法, 並將CFSTR_PASTESUCCEEDED 格式設定為 DROPEFFECT_MOVE。
  8. 當來源的 IDataObject::SetData 方法呼叫時,會將 CFSTR_PASTESUCCEEDED 格式設定為 DROPEFFECT_MOVE,它必須檢查它是否也收到設定為 DROPEFFECT_MOVE 的CFSTR_PERFORMEDDROPEFFECT 格式。 如果目標傳送這兩種格式,來源就必須刪除資料。 如果只收到 CFSTR_PASTESUCCEEDED 格式,則來源可以直接從其顯示中移除資料。 如果傳輸失敗,來源會將顯示更新為其原始外觀。

從虛擬資料夾來回傳輸資料

場景: 使用者從虛擬資料夾拖曳或卸載物件。

虛擬資料夾包含通常不屬於檔案系統的物件。 某些虛擬資料夾,例如回收站,可以代表儲存在硬碟上但不是一般檔案系統物件的資料。 有些可以代表遠端系統上的預存資料,例如可攜式電腦或 FTP 月臺。 其他專案,例如 Printers 資料夾,包含完全不代表已儲存資料的物件。 雖然某些虛擬資料夾是系統的一部分,但開發人員也可以藉由實作命名空間延伸模組來建立及安裝自訂虛擬資料夾。

不論資料類型或儲存方式為何,Shell 所包含的資料夾和檔案物件都會呈現為一般檔案和資料夾。 虛擬資料夾的責任是接受其包含的任何資料,並適當地將它呈現給殼層。 這項需求表示虛擬資料夾通常支援拖放和剪貼簿資料傳輸。

因此,有兩組開發人員需要關注虛擬資料夾的資料傳輸:

  • 應用程式需要接受從虛擬資料夾傳輸資料的開發人員。
  • 命名空間延伸模組需要正確支援資料傳輸的開發人員。

接受來自虛擬資料夾的資料

虛擬資料夾幾乎可以代表任何類型的資料,而且可以以任何方式儲存該資料。 某些虛擬資料夾實際上可能包含一般檔案系統檔案和資料夾。 例如,其他人可能會將所有物件封裝成單一檔或資料庫。

當檔案系統物件傳送至應用程式時,資料物件通常會包含具有物件完整路徑 的CF_HDROP 格式。 您的應用程式可以擷取此字串,並使用一般檔案系統函式來開啟檔案並擷取其資料。 不過,因為虛擬資料夾通常不包含一般檔案系統物件,所以通常不會使用 CF_HDROP

資料通常會從具有CFSTR_FILEDESCRIPTOR CFSTR_FILECONTENTS/ 格式的虛擬資料夾傳輸,而不是CF_HDROP CFSTR_FILECONTENTS格式具有兩個優於CF_HDROP的優點:

  • 假設沒有特定的資料儲存體方法。
  • 格式更有彈性。 它支援三種資料傳輸機制:全域記憶體物件、 IStream 介面或 IStorage 介面。

全域記憶體物件很少用來將資料傳輸到虛擬物件,因為資料必須完整複製到記憶體中。 傳輸介面指標幾乎不需要記憶體,而且更有效率。 使用非常大型的檔案時,介面指標可能是唯一實用的資料傳輸機制。 一般而言,資料是以 IStream 指標表示,因為該介面比 IStorage更有彈性。 目標會從資料物件擷取指標,並使用 介面方法來擷取資料。

如需如何處理CFSTR_FILEDESCRIPTOR CFSTR_FILECONTENTS/ 格式的進一步討論,請參閱使用CFSTR_FILECONTENTS格式從檔案擷取資料

將資料傳送至 NameSpace 延伸模組及從中傳輸資料

當您實作命名空間延伸模組時,通常會想要支援拖放功能。 請遵循一 般指導方針中討論的卸載來源和目標建議。 特別是,命名空間延伸必須:

  • 能夠處理CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS格式。 這兩種格式通常用來將物件傳送至命名空間延伸模組和從命名空間延伸。
  • 能夠處理 優化的移動。 Shell 預期 Shell 物件會隨著優化的移動移動而移動。
  • 能夠處理 貼上刪除 作業。 當物件從殼層移動時,Shell 會使用剪下/貼上作業來移動物件時,使用 delete-on-paste。
  • 能夠透過 IStreamIStorage 介面處理資料傳輸。 從虛擬資料夾進行資料傳輸通常是透過傳輸這兩個介面指標的其中一個來處理,通常是 IStream 指標。 然後,目標會呼叫 介面方法來擷取資料:
      • 作為卸載來源,命名空間延伸模組必須從儲存體擷取資料,並將它傳遞至目標。
      • 作為置放目標,命名空間延伸模組必須透過這個介面接受來自來源的資料,並正確儲存。

卸載回收站上的檔案

場景: 使用者卸載 回收站上的檔案。 您的應用程式或命名空間延伸模組會刪除原始檔案。

回收站是虛擬資料夾,可作為不再需要之檔案的存放庫。 只要回收站尚未清空,使用者就可以稍後復原檔案,並將它傳回檔案系統。

在大部分的情況下,將 Shell 物件傳送至回收站的運作方式與任何其他資料夾非常類似。 不過,當使用者在 回收站上卸載檔案時,即使資料夾中的意見反應指出複製作業,來源也需要刪除原始檔案。 一般而言,卸載來源無法得知其資料物件已卸載的資料夾。 不過,對於 Windows 2000 和更新版本的系統,當 回收站上卸載資料物件時,Shell 會呼叫資料物件的 IDataObject::SetData 方法,並將 CFSTR_TARGETCLSID 格式設定為回收站的類別識別碼 (CLSID) (CLSID_RecycleBin) 。 若要正確處理回收站案例,您的資料物件應該能夠辨識此格式,並透過私人介面將資訊傳達給來源。

注意

呼叫 IDataObject::SetData,會將CFSTR_TARGETCLSID 格式設定為 CLSID_RecycleBin,資料來源應該關閉從 方法傳回之前所傳輸之物件的任何開啟控制碼。 否則,您可能會建立共用違規。

 

建立和匯入擷取檔案

場景: 使用者從 OLE 應用程式的資料檔案拖曳一些資料,並將它放在桌面或 Windows 檔案總管上。

Windows 可讓使用者從 OLE 應用程式的資料檔案拖曳物件,並將它放在桌面或檔系統資料夾上。 此作業會建立 抓取檔案,其中包含資料或資料的連結。 檔案名取自註冊物件 CLSID 的簡短名稱,以及 CF_TEXT 資料。 若要讓殼層建立包含資料的擷取檔案,應用程式的 IDataObject 介面必須支援CF_EMBEDSOURCE剪貼簿格式。 若要建立包含連結的檔案, IDataObject 必須支援CF_LINKSOURCE格式。

應用程式也可以實作三個選擇性功能來支援抓取檔案:

  • 來回支援
  • 快取的資料格式
  • 延遲轉譯

來回支援

來回行程牽涉到將資料物件傳輸到另一個容器,然後傳回原始檔案。 例如,使用者可以將一組儲存格從試算表傳輸到桌面,以建立含有資料的抓取檔案。 如果使用者接著將擷取移回電子表格,資料必須與原始傳輸之前一樣整合到檔中。

當 Shell 建立抓取檔案時,它會將資料表示為内嵌物件。 當抓取傳輸至另一個容器時,它就會以内嵌物件的形式傳輸,即使它傳回原始檔案也一樣。 您的應用程式負責判斷擷取中包含的資料格式,並視需要將資料放回其原生格式。

若要建立内嵌物件的格式,請擷取物件的CF_OBJECTDESCRIPTOR格式來判斷其 CLSID。 如果 CLSID 指出屬於應用程式的資料格式,它應該傳輸原生資料,而不是呼叫 OleCreateFromData

快取的資料格式

當 Shell 建立抓取檔案時,它會檢查登錄中是否有可用的格式清單。 根據預設,有兩種格式可用:CF_EMBEDSOURCE 和 CF_LINKSOURCE。 不過,在某些情況下,應用程式可能需要有不同格式的擷取檔案:

  • 允許將抓取傳輸至無法接受内嵌物件格式的非 OLE 容器。
  • 允許應用程式套件與私用格式通訊。
  • 讓來回行程更容易處理。

應用程式可以在登錄中快取格式,以將格式新增至抓取。 快取格式有兩種:

  • 優先順序快取格式。 針對這些格式,資料會完整複製到資料物件的抓取中。
  • 延遲轉譯的格式。 針對這些格式,資料物件不會複製到抓取。 相反地,轉譯會延遲到目標要求資料為止。 下一節會更詳細地討論延遲轉譯。

若要新增優先順序快取或延遲轉譯格式,請在應用程式的CLSID索引鍵下建立DataFormat子機碼,該索引鍵是資料來源。 在該子機碼下,建立 PriorityCacheFormatsDelayRenderFormats 子機碼。 針對每個優先順序快取或延遲轉譯格式,建立以零開始的編號子機碼。 將此索引鍵的值設定為具有格式註冊名稱的字串或#X值,其中 X 代表標準剪貼簿格式的格式編號。

下列範例顯示兩個應用程式的快取格式。 MyProg1 應用程式具有 RTF 格式做為優先順序快取格式,而私用格式 「My Format」 是延遲轉譯的格式。 MyProg2 應用程式具有CF_BITMAP格式 (#8「) 做為優先順序快取格式。

HKEY_CLASSES_ROOT
   CLSID
      {GUID}
         (Default) = MyProg1
         DataFormats
            PriorityCacheFormats
               0
                  (Default) = Rich Text Format
            DelayRenderFormats
               0
                  (Default) = My Format
      {GUID}
         (Default) = MyProg2
         DataFormats
            PriorityCacheFormats
               0
                  (Default) = #8

您可以藉由建立其他編號的子機碼來新增其他格式。

延遲轉譯

延遲轉譯格式可讓應用程式建立擷取檔案,但延遲轉譯資料的費用,直到目標要求為止。 擷取的 IDataObject 介面會將延遲轉譯格式提供給目標,以及原生和快取的資料。 如果目標要求延遲轉譯格式,Shell 會執行應用程式,並從使用中物件將資料提供給目標。

注意

因為延遲轉譯有點有風險,所以應該謹慎使用。 如果伺服器無法使用,或未啟用 OLE 的應用程式,它將無法運作。

 

以非同步方式拖放殼層物件

場景: 使用者會將大型資料區塊從來源傳輸至目標。 為了避免同時封鎖這兩個應用程式一段時間,目標會以非同步方式擷取資料。

一般而言,拖放是同步作業。 簡單地說︰

  1. 卸載來源會呼叫 DoDragDrop ,並封鎖其主要執行緒,直到函式傳回為止。 封鎖主要執行緒通常會封鎖 UI 處理。
  2. 呼叫目標的 IDropTarget::D rop 方法之後,目標會從主要執行緒上的資料物件擷取資料。 此程式通常會在擷取程式期間封鎖目標的 UI 處理。
  3. 擷取資料之後,目標會傳回 IDropTarget::D rop 呼叫、系統會傳回 DoDragDrop,而且這兩個執行緒都可以繼續。

簡單地說,同步資料傳輸可能會封鎖這兩個應用程式的主要執行緒一段時間。 特別是,這兩個執行緒必須在目標擷取資料時等候。 對於少量的資料,擷取資料所需的時間很小,同步資料傳輸運作良好。 不過,同步擷取大量資料可能會導致冗長的延遲,並干擾目標與來源的 UI。

IAsyncOperation/IDataObjectAsyncCapability介面是可由資料物件實作的選擇性介面。 它讓卸載目標能夠在背景執行緒上以非同步方式從資料物件擷取資料。 將資料擷取交給背景執行緒之後,這兩個應用程式的主要執行緒就可供繼續。

使用 IASyncOperation/IDataObjectAsyncCapability

注意

介面最初命名為 IAsyncOperation,但稍後已變更為 IDataObjectAsyncCapability。 否則,這兩個介面完全相同。

 

IAsyncOperation/IDataObjectAsyncCapability的目的是允許卸載來源和卸載目標交涉是否可以非同步擷取資料。 下列程式概述卸載來源如何使用 介面:

  1. 建立公開IAsyncOperation/IDataObjectAsyncCapability的資料物件。
  2. 呼叫 SetAsyncMode並將 fDoOpAsync 設定為 VARIANT_TRUE ,以指出支援非同步作業。
  3. DoDragDrop傳回之後,呼叫InOperation
    • 如果 InOperation 失敗或傳回 VARIANT_FALSE,就會進行一般同步資料傳輸,且資料擷取程式已完成。 來源應該執行任何必要清除作業,然後繼續進行。
    • 如果 InOperation 傳回 VARIANT_TRUE,則會以非同步方式擷取資料。 清除作業應該由 EndOperation處理。
  4. 釋放資料物件。
  5. 當非同步資料傳輸完成時,資料物件通常會透過私用介面通知來源。

下列程式概述卸載目標如何使用IAsyncOperation/IDataObjectAsyncCapability介面,以非同步方式擷取資料:

  1. 當系統呼叫IDropTarget::D rop 時,請呼叫IDataObject::QueryInterface,並從資料物件要求IAsyncOperation/IDataObjectAsyncCapability介面 (IID_IAsyncOperation/IID_IDataObjectAsyncCapability) 。
  2. 呼叫 GetAsyncMode。 如果方法傳回 VARIANT_TRUE,則資料物件支援非同步資料擷取。
  3. 建立個別執行緒來處理資料擷取並呼叫 StartOperation
  4. 傳回 IDropTarget::D rop 呼叫,就像一般資料傳輸作業一樣。 DoDragDrop 會傳回並解除封鎖置放來源。 請勿呼叫 IDataObject::SetData 來指出優化移動或刪除貼上作業的結果。 等候作業完成。
  5. 擷取背景執行緒上的資料。 目標的主要執行緒已解除封鎖,且可供繼續。
  6. 如果資料傳輸是優化的移動或刪除貼上作業,請呼叫IDataObject::SetData以指出結果。
  7. 呼叫 EndOperation來通知資料物件擷取已完成。