共用方式為


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

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

注意

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

 

一般準則

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

針對資料來源:

  • 殼層剪貼簿格式,除了 CF_HDROP之外,不會預先定義。 您必須呼叫 RegisterClipboardFormat 來註冊您想要使用的每個格式。
  • 數據物件中的格式會依來源的喜好設定順序提供。 列舉數據物件,並挑選您可以取用的第一個物件。
  • 包含您所能支援的許多格式。 您通常不知道數據對象會被放置在哪裡。 這種做法可以增加資料物件包含置放目標可接受格式的機率。
  • 現有的檔案應以 CF_HDROP 格式提供。
  • 提供類似檔案的數據以CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR格式。 此方法可讓目標系統從數據物件建立檔案,而不需要知道基礎資料儲存的任何內容。 您通常應該將數據呈現為 IStream 介面。 此數據傳輸機制比全域記憶體物件更有彈性,而且會使用較少的記憶體。
  • 拖曳來源應該會在拖曳 Shell 專案時提供 CFSTR_SHELLIDLIST 格式。 您可以透過IShellFolder::GetUIObjectOfIShellItem::BindToHandler 方法取得項目的數據物件。 數據源可以使用 SHCreateDataObject 來建立支援CFSTR_SHELLIDLIST格式的標準數據物件實作。
  • 拖曳接收目標想要了解被拖曳專案並使用 Shell 項目程式設計模型時,可以使用 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格式,但命名空間擴展名等提供者的檔案通常會使用/。 範例包括 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::GetDataSTGMEDIUM 結構的指標。 發行結構之後,您在步驟 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::Drop
  6. 使用 IDataObject 介面來擷取檔案的內容。

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

  • 如果檔案包含 CF_TEXT 格式,則數據會是 ANSI 文字。 您可以使用CF_TEXT格式來擷取數據,而不是開啟檔案本身。
  • 如果檔案包含連結或內嵌的 OLE 物件,則數據物件會包含CF_EMBEDDEDOBJECT格式。 使用標準 OLE 技術來擷取數據。 片段文件 一律包含 CF_EMBEDDEDOBJECT 格式。
  • 如果 Shell 物件來自檔案系統,則數據物件會包含以檔名表示的 CF_HDROP 格式。 從 CF_HDROP擷取 檔名,並呼叫 OleCreateFromFile 來建立新的連結或內嵌物件。 如需如何從 CF_HDROP 格式擷取檔名的討論,請參閱 將檔名從剪貼簿複製到應用程式
  • 如果數據物件包含CFSTR_FILEDESCRIPTOR格式,您可以從檔案CFSTR_FILECONTENTS格式擷取檔案的內容。 如需此程序的討論,請參閱 使用 CFSTR_FILECONTENTS Format 從檔案擷取數據。
  • 在 Shell 4.71 版之前,應用程式會透過在 FILEDESCRIPTOR 結構的 dwFlags 成員中設定 FD_LINKUI,指出它正在傳輸快捷方式檔案類型。 對於較新版本的 Shell,指示正在傳送快捷方式的首選方式是使用將 CFSTR_PREFERREDDROPEFFECT 格式設定為 DROPEFFECT_LINK。 這種方法比提取 FILEDESCRIPTOR 結構來檢查旗標更有效率。

如果數據擷取程式會很長,您可能會想要以異步方式在背景線程上執行作業。 然後,您的主要線程可以繼續進行,而不會造成不必要的延遲。 如需討論如何以異步方式處理資料擷取,請參閱 異步方式拖放 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 中鎖定的全域記憶體物件。

處理優化移動操作

案例:檔案從文件系統移動至命名空間擴展,使用優化的移動方式。

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

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

注意

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

 

優化移動的處理方式如下:

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

  2. 目標會透過其 IDropTarget 方法之一接收 DROPEFFECT_MOVE 值,表明允許移動。

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

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

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

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

    如果目標執行了未經優化的操作,數據必須由來源刪除。 若要通知某一來源未經優化的移動操作已經被執行:

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

處理貼上時刪除作業

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

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

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

注意

當使用剪下並貼上操作移動檔案時,Shell 一般採用剪下後貼上即刪除的方式。 使用 Shell 物件的剪下並貼上作業通常會使用 優化移動 來移動檔案。 若要正確處理Shell資料傳輸,您的應用程式必須能夠偵測和處理貼上刪除作業。

 

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

貼上後刪除操作的基本程序如下:

  1. 螢幕上的所選取資料會由來源標示出來。
  2. 來源會建立數據物件。 它會藉由新增格式 CFSTR_PREFERREDDROPEFFECT 和數據值 DROPEFFECT_MOVE,來指出剪下作業。
  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 時,它必須檢查是否也收到了格式 CFSTR_PERFORMEDDROPEFFECT,設定為 DROPEFFECT_MOVE。 如果目標傳送這兩種格式,來源必須刪除數據。 如果只收到 CFSTR_PASTESUCCEEDED 格式,來源端只需將資料從其顯示中移除即可。 如果傳輸失敗,來源會將顯示器更新為其原始外觀。

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

案例: 使用者從虛擬資料夾拖曳或放置物件。

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

不論數據類型或儲存方式為何,Shell 所包含的資料夾和檔案物件都會以一般檔案和資料夾的形式呈現。 虛擬資料夾有責任擷取其所包含的任何數據,並適當地將其呈現給 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 物件將以最佳化方式移動。
  • 能夠處理貼上時刪除的操作。 當使用剪下/貼上的方式將物件從殼層移動時,殼層會使用「貼上即刪除」功能。
  • 能夠透過 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 資料。 若要讓 Shell 建立包含資料的暫存檔案,應用程式的 IDataObject 介面必須支援 CF_EMBEDSOURCE 剪貼簿格式。 若要建立包含連結的檔案, IDataObject 必須支援CF_LINKSOURCE格式。

還有三個選用功能可供應用程式實作以支援報廢檔案:

  • 來回支援
  • 快取的數據格式
  • 延遲轉譯

來回支援

來回行程牽涉到將數據物件傳輸到另一個容器,然後傳回源檔。 例如,使用者可以將一組單元格從電子表格傳輸到桌面,並建立包含數據的臨時檔案。 如果使用者接著將資料片段轉回電子表格,數據必須像原始轉移前一樣整合回文件中。

當Shell建立報廢檔案時,它會將數據表示為內嵌物件。 當廢品傳輸到另一個容器時,即使它被傳回至源檔,也會將其傳輸為內嵌物件。 您的應用程式負責判斷剪貼簿中包含的資料格式,並在必要時將資料放回其原始格式。

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

快取的數據格式

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

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

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

  • 優先順序快取格式。 針對這些格式,數據會被完整複製到剪貼簿中的數據對象。
  • 延遲渲染的格式。 針對這些格式,資料物件不會複製到剪貼簿。 相反地,呈現會延遲直到目標要求資料為止。 下一節會更詳細地討論延遲轉譯。

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

下列範例顯示兩個應用程式的快取格式。 MyProg1 應用程式具有豐富文本格式作為優先的快取記憶體格式,而自訂格式「我的格式」則為延遲轉譯格式。 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 介面對於 scrap 類型將提供延遲呈現格式、原生資料和快取的數據給目標。 如果目標要求延遲轉譯格式,Shell 會執行應用程式,並從使用中物件將數據提供給目標。

注意

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

 

以異步方式拖放殼層物件

案例: 用戶會將大量的數據區塊從來源傳輸到目標。 為了避免這兩個應用程式長時間封鎖,目標會以異步方式擷取數據。

一般而言,拖放是同步作業。 簡言之:

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

簡言之,同步數據傳輸可能會長時間封鎖這兩個應用程式的主要線程。 特別是,當目標擷取數據時,這兩個線程都必須等候。 對於少量的數據,擷取數據所需的時間很小,同步數據傳輸運作良好。 不過,同步擷取大量數據可能會導致冗長的延遲,並干擾目標與來源的UI。

IAsyncOperation介面和IDataObjectAsyncCapability介面是可由數據對象實作的選擇性介面。 它可讓置放目標在背景線程上以異步方式從數據物件擷取數據。 一旦將數據擷取交給背景執行緒,這兩個應用程式的主要執行緒就可以繼續運行。

使用 IASyncOperation/IDataObjectAsyncCapability 進行操作

注意

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

 

IAsyncOperation/ 的目的是允許卸除來源和卸除目標交涉是否可以以異步方式擷取數據。 以下程序概述 drop source 如何使用介面:

  1. 建立一個數據物件,提供IAsyncOperationIDataObjectAsyncCapability
  2. 呼叫 SetAsyncMode 並將 fDoOpAsync 設定為 VARIANT_TRUE ,表示支援異步操作。
  3. DoDragDrop 傳回之後,呼叫 InOperation
    • 如果 InOperation 失敗或傳回 VARIANT_FALSE,則會進行一般同步數據傳輸,且數據擷取程式已完成。 來源應該執行所需的任何清理,然後繼續。
    • 如果 InOperation 傳回 VARIANT_TRUE,則會以異步方式擷取數據。 清除作業應該由 EndOperation 處理。
  4. 釋放數據物件。
  5. 當異步數據傳輸完成時,數據物件通常會透過私人介面通知來源。

下列程序概述置放目标如何使用IAsyncOperation/IDataObjectAsyncCapability 介面以异步方式擷取数据:

  1. 當系統呼叫 IDropTarget::Drop 時,請呼叫 IDataObject::QueryInterface,並從數據物件要求 IAsyncOperationIDataObjectAsyncCapability 介面 (IID_IAsyncOperation/IID_IDataObjectAsyncCapability)。
  2. 呼叫 GetAsyncMode。 如果方法傳 回VARIANT_TRUE,則數據物件支援異步數據擷取。
  3. 建立個別線程來處理數據擷取並呼叫 StartOperation
  4. 傳回 IDropTarget::Drop 呼叫,就像一般數據傳輸作業一樣。 DoDragDrop 會傳回並解除對拖放來源的封鎖。 請勿呼叫 IDataObject::SetData 以表示優化移動或貼上後刪除操作的結果。 等到作業完成為止。
  5. 擷取背景線程上的數據。 目標的主要線程已解除封鎖,並可供繼續。
  6. 如果數據傳輸是優化移動貼上即刪作業,請呼叫IDataObject::SetData以指出結果。
  7. 呼叫 EndOperation 來通知數據物件擷取完成