共用方式為


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

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

注意

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

 

一般準則

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

針對資料來源:

  • 殼層剪貼簿格式,除了 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 可能會使用 優化的移動 或刪除 貼上 作業。 您的應用程式應該能夠辨識這些作業並適當地回應。

針對資料目標:

  • 殼層剪貼簿格式,除了 CF_HDROP之外,不會預先定義。 您必須呼叫 RegisterClipboardFormat 來註冊您想要使用的每個格式。
  • 實作並註冊 OLE 置放目標。 盡可能避免使用 Windows 3.1 目標或 WM_DROPFILES 訊息。
  • 數據物件所包含的格式會根據物件的來源而有所不同。 由於您通常事先不知道數據對象來自何處,因此請勿假設會出現特定格式。 數據對象應該依質量順序列舉格式,從最佳開始。 因此,為了取得最佳的可用格式,應用程式通常會列舉可用的格式,並在列舉中使用其可支援的第一個格式。
  • 支援由右拖曳。 您可以藉由建立 拖放處理程式來自定義拖曳快捷方式選單。
  • 如果您的應用程式將接受現有的檔案,它必須能夠處理 CF_HDROP 格式。
  • 一般而言,接受檔案的應用程式也應該處理CFSTR_FILECONTENTS CFSTR_FILEDESCRIPTOR/格式。 雖然文件系統中的檔案具有CF_HDROP格式,但命名空間擴展名等提供者的檔案通常會使用CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR。 範例包括 Windows CE 資料夾、檔案傳輸通訊協定 (FTP) 資料夾、Web 資料夾和 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 通常會使用優化的移動來移動檔案。 若要正確處理Shell資料傳輸,您的應用程式必須能夠偵測及處理優化的移動。

 

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

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

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

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

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

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

      • 目標會將它透過其 IDropTarget::D rop 方法收到的 pdwEffect 值設定為DROPEFFECT_MOVE以外的某些值。 它通常會設定為 DROPEFFECT_NONE 或 DROPEFFECT_COPY。 值會由 DoDragDrop 傳回至來源。
      • 目標也會呼叫數據物件的 IDataObject::SetData 方法,並將CFSTR_PERFORMEDDROPEFFECT格式標識碼傳遞給DROPEFFECT_NONE。 這個方法呼叫是必要的,因為某些置放目標可能無法正確設定 DoDragDroppdwEffect 參數。 CFSTR_PERFORMEDDROPEFFECT格式是指出已進行優化移動的可靠方式。

    如果目標執行了未優化移動,來源必須刪除數據。 若要通知來源未優化移動已執行:

      • 目標會將它透過其 IDropTarget::D rop 方法收到的 pdwEffect 值設定為DROPEFFECT_MOVE。 值會由 DoDragDrop 傳回至來源。
      • 目標也會呼叫數據物件的 IDataObject::SetData 方法,並將CFSTR_PERFORMEDDROPEFFECT格式標識碼傳遞給DROPEFFECT_MOVE。 這個方法呼叫是必要的,因為某些置放目標可能無法正確設定 DoDragDroppdwEffect 參數。 CFSTR_PERFORMEDDROPEFFECT格式是指出已進行未優化行動的可靠方式。
  5. 來源會檢查目標可傳回的兩個值。 如果兩者都設定為 DROPEFFECT_MOVE,它會藉由刪除原始數據來完成未優化移動。 否則,目標會執行優化的移動,並已刪除原始數據。

處理貼上刪除作業

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

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

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

注意

當剪下/貼上作業用來移動檔案時,Shell 通常會使用 delete-on-paste。 使用 Shell 物件的刪除貼上作業通常會使用 優化的行動 來移動檔案。 若要正確處理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物件會隨著優化移動移動而移動。
  • 能夠處理 貼上 刪除作業。 當物件從殼層移動時,殼層會使用剪下/貼上作業從殼層移動時,使用 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

快取的數據格式

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

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

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

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

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

下列範例顯示兩個應用程式的快取格式。 MyProg1 應用程式具有 RTF 格式做為優先順序快取格式,而私人格式「我的格式」則為延遲轉譯格式。 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 來通知數據物件擷取完成