簡體   English   中英

攔截 MS Windows 'SendTo' 菜單調用?

[英]Intercept MS Windows 'SendTo' menu calls?

設想

我白天管理和組織許多文件, SendTo是我在 Windows 上使用最多的功能。

問題

默認情況下,當用戶單擊上下文菜單的項目/鏈接以發送文件時,操作系統不會顯示任何類型的通知/通知程序,指示文件正在復制到所選目的地。

我認為這是一個非常錯誤的設計問題,因為對於大文件它沒問題......將顯示一個進度條,但如果文件太小,它將不會顯示任何進度條/視覺指示器,因此無法確保復制文件(無需手動操作)因為我是人,我可能會錯誤地在SendTo上下文菜單之外單擊。

因此,我想開發一個個人迷你工具,幫助我優化我的時間,當我使用上下文菜單中的SendTo功能發送/復制文件時,在屏幕上的任何位置顯示通知程序 window,並且僅使用SendTo功能。

問題

簡而言之,我想檢測來自SendTo菜單的復制/發送操作,以確保在菜單項上(而不是在菜單外)正確完成了點擊,還提供了其他基本信息,例如源文件夾、目標文件夾,以及文件數量或文件路徑。

有什么想法可以在正確的方向上開始開發這個工具嗎?

我將感謝C#VB.Net中的代碼示例,最好是最后一個。

方法

因為我不知道如何開始這樣做我的意思是這可能是攔截那些SendTo調用的最簡單或最有效的方法,首先我想掛鈎CopyFileCopyFileEx API 函數,但它們沒有提供我需要的信息因為 function 將在任何類型的復制操作中被調用,而不僅僅是在我使用SendTo功能時,所以我迷路了。

我不確定我是否應該更多地調查內部調用,或者更多地調查 windows 上下文菜單本身,而不是搞亂 function 鈎子和我可以避免的丑陋事情。

我的主要想法是開發一個隱藏的 WinForms(或者一個 windows 服務),當我使用SendTo功能時(當我點擊SendTo菜單的一個項目時),它會在后台等待,然后在屏幕以確保我正確地單擊了該菜單項,並且可能會告知我正在移動的文件數量以及我將它們移動到哪里。

研究

這是一個代碼示例,我認為它演示了如何實例化SendTo com object 以創建您自己的?,但它寫在 c++ 中,我不確定該示例是否有幫助,因為我的目的不是替換SendTo菜單,而是我會在這里保留這些有用的信息,它有其他用途:

如何在名稱空間擴展中添加(啟用)標准“發送到”上下文菜單選項

KNOWNFOLDERID常量文檔提供了一些有關SendTo文件夾的有用信息,我再次不確定這是否有助於讀取/訪問監控方法?我只是將信息保留在這里:

GUID:{8983036C-27C0-404B-8F08-102D10DCFD74}

默認路徑:%APPDATA%\Microsoft\Windows\SendTo

舊版默認路徑:%USERPROFILE%\SendTo

Shell Extension Handlers文檔中有一個Copy 掛鈎處理程序,我不知道它是否與SendToCOM組件有關系,如果這可以在某種程度上幫助我,同樣的無知IContextMenu::InvokeCommand方法參考也許我可以攔截它來識別SendTo調用?

到那一刻,我覺得自己像瞎了眼。

我最近發現了這個A managed "Send To" menu class但它又是一個用 C/C++ 編寫的示例(我認為之前是相同的來源)我完全不理解,我再次不確定這是否有幫助我因為我重復替換SendTo不是我的想法(只是因為我不知道如何正確地避免所有可能的風險,我更願意仍然讓 Windows 邏輯復制/發送文件,我只是想檢測檢索信息的復制操作)

預期結果和使用

第1步:

Select 一個隨機文件並使用SendTo菜單(在我的語言中,西班牙語,命令名稱是“ Enviar a ”)

在此處輸入圖像描述

第2步:

讓 .net 應用程序的邏輯(在后台工作)攔截SendTo操作以檢索信息。

(我只需要這一步的幫助)

第 3 步:

在屏幕上某處顯示信息以確保執行了SendTo操作,以確保我正確單擊了SendTo項目(我的鏈接)。

在此處輸入圖像描述

(那個彈出窗口只是一個模擬,我不知道有什么方法可以檢索所有這些信息)

一旦你理解了SendTo真正做的事情,它就變得非常簡單了,它根本不涉及COM或shell擴展。 基本上,發送到菜單中填充了用戶配置文件的SendTo文件夾的內容(在Windows 6.x中默認為C:\\ Users \\\\ AppData \\ Roaming \\ Microsoft \\ Windows \\ SendTo)。

單擊時,如果該選項是文件夾的快捷方式,它將復制文件,但如果有程序的快捷方式(或程序可執行文件本身),它將運行該程序,將所選文件的路徑作為命令傳遞-line參數。

從那里開始,制作一些簡單地將路徑作為參數,提供某種通知然后復制文件或用它們做任何你想做的事情都是微不足道的。

一個快速而骯臟的例子可能如下(在C#中,但可以用其他任何東西完成):

private static void Main(string[] args)
{
    if(MessageBox.Show("Are you sure you want to copy files?", "Copy files", MessageBoxButtons.YesNo) == DialogResult.No) return;

    foreach (string file in args)
        File.Copy(file, Path.Combine("c:\\temp", Path.GetFileName(file));
}

這只是要求確認復制一堆文件。 請注意,這實際上不會“攔截”發送到菜單,而是完全處理它,因此程序負責執行任何有意義的操作。 更嚴肅的實現可以使用內置的Windows復制對話框並顯示一些屏幕上的進度或其他任何內容,這些都符合您的需求。

它還可以在命令行上使用更多參數。 在SendTo文件夾中放置快捷方式時,目標可以添加一些將作為第一個參數傳遞的參數(在文件名之前)。 例如,快捷方式的目標可以讀取c:\\program files\\copyfiles.exe c:\\temp以傳遞目標文件夾而不是硬編碼。 然后,被調用程序必須將第一個參數解釋為目標路徑,將后續參數解釋為源文件。

我之前不得不做這樣的事情。 您甚至不必截取SendTo()函數,只需要確保文件已到達。 如果FileSystemWatcher在同一台計算機上怎么樣?

您可以在發送之前使用觀察者進行觀察,然后,如果文件成功到達目的地,您可以顯示成功的消息,然后終止觀察者。

代碼示例

// Create a FileSystemWatcher property.
FileSystemWatcher fsw { get; set; }
// So we can set the FileToWatch within WatchFilesBeforeTransfer().
private string FileToWatch { get; set; }

private void WatchFilesBeforeTransfer(string FileName, string DestinationFolder)
{
    fsw = new FileSystemWatcher();
    fsw.Path = DestinationFolder;
    FileToWatch = FileName;

    // Only if you need support for multiple directories. Code example note included.
    fsw.InclueSubdirectories = true; 

    // We'll be searching for the file name and directory.
    fsw.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName 

    // If it's simply moving the file to another location on the computer.
    fsw.Renamed += new RenamedEventHandler(FileRenamed); 

    // If it was copied, not moved or renamed. 
    fsw.Created += new FileSystemEventHandler(FileCreated);
    fsw.EnableRaisingEvents = true;
}

// If the file is just renamed. (Move/Rename)
private void FileRenamed(Object source, RenamedEventArgs e)
{
    // Do something.
    // Note that the full filename is accessed by e.FullPath.

    if (e.Name == FileToWatch) 
    {
         DisplaySuccessfulMessage(e.Name);
         KillFileWatcher();
    }
}

// If creating a new file. (Copied)
private void FileCreated(Object source, FileSystemEventArgs e)
{
    // Do something.
    // Note that the full filename is accessed by e.FullPath.

    if (e.Name == FileToWatch) 
    {
         DisplaySuccessfulMessage(e.Name);
         KillFileWatcher();
    }
}

private void KillFileWatcher()
{
   fsw.Dispose();
}

您可以通過以下方式訪問所需的屬性信息(例如在彈出式窗口gif中):

  • 文件夾名稱Path.GetDirectory(e.FullPath); (比如“C:\\ yo \\”)
  • 完整文件名e.FullPath (如“C:\\ yo \\ hey.exe”)
  • 文件名e.Name (如“hey.exe”)

非代碼執行過程

  1. 在啟動SendTo()之前,創建FileSystemWatcher屬性的實例,並讓它監視特定的文件夾/文件名組合,該組合顯示在監視文件夾中: WatchFilesBeforeTransfer(FileName, DestinationFolder)
  2. 啟動SendTo()
  3. 收到檔案? DisplaySuccessfulSendToMessage()KillFileWatcher();
  4. ???
  5. 利潤。

更新

我剛剛意識到這只是一個文件。 如果要檢查多個文件,可以創建多個FileWatcher實例(不推薦),也可以使用List<string>對象,如下所示:

private void SendTo(List<string> FileCollection)
{
    // Clear your previous FileList.
    FileList.Clear();

    foreach (string file in FileCollection)
    {
        FileList.Add(file); 
    }
    // Rest of the code. 
}

List<string> FileList { get; set; }
private void WatchFilesBeforeTransfer(string DestinationFolder)
{
    // Same code as before, but delete FileToWatch.
}

private void FileRenamed(Object source, RenamedEventArgs e)
{
    foreach (string file in FileList)
    {
        if (e.Name == file)
        {
            // Do stuff.
        }
    }
}

private void FileCreated(Object source, FileSystemEventArgs e)
{
    foreach (string file in FileList)
    {
        if (e.Name == file)
        {
            // Do stuff.
        }
    }
}

希望這可以幫助!

我擔心這不容易。 我正在玩FileSystemWatcher ,但只取得了部分成功。

應該肯定有用的東西是文件系統驅動程序,但這看起來也是,好吧,看看它......

最后,它可能是編寫自己的shell擴展來訪問SendTo文件夾的最簡單方法,並使用它來代替SentTo命令,這將使您完全控制。

這些可能是一些先發者:

Windows外殼擴展

Shell上下文菜單

也許我來晚了一點來回答我自己的問題,該問題發表於2015年,但直到幾天前我才對這個問題再次感興趣,這些年來在.NET中獲得了更多的經驗和知識從頭開始,嘗試理解我過去不理解的一切。


我剛剛發現,正如@Ňɏssa Pøngjǣrdenlarp 已經在評論框中評論的那樣,顯然最可行的方法是實現我自己的 SendTo 上下文菜單並使用IFileOperationProgressSink接口報告進度,為此我首先需要依賴IFileOperation接口的使用。

使用IFileOperation接口的原因是因為它似乎是 Windows API 中提供的唯一方法,讓開發人員可以在同一個進度對話框 UI 中同時執行多個文件操作(復制、移動、重命名、創建或刪除)。 這可能是當用戶選擇多個文件或目錄(通過SendTo菜單或只是CTRL+CCTRL+V )一次移動或復制它們時系統使用的界面,因為它只顯示一個進度對話框,其中包含所有復制操作在其中排隊...

在此處輸入圖像描述

很明顯, IFileOperationIFileOperationProgressSink接口正是我所需要的。 但據我所知,在 .NET 框架命名空間(也不在 Microsoft 的 WindowsAPICodePack 庫中)中沒有這些接口的托管實現,考慮到這些接口自 Windows VISTA 時代甚至更早時代就存在,這對我來說似乎有些不可原諒,而且它是一個無可爭議的改進,與您可以想到的執行復制、移動、重命名、創建或刪除操作的任何內置成員完全相反,例如System.IO.File.CopyMicrosoft.VisualBasic.FileIO.FileSystem .CopyFile方法。 它們都只支持單個操作,並且一次只支持一個進度對話框。


因此,我專注於研究建議的解決方案,並最終在此存儲庫中找到了IFileOperationIFileOperationProgressSink接口的良好實現:

這是我在一篇舊的 Microsoft 文章中找到的原始實現,它還附帶了一個很好的 PDF 閱讀以學習一些東西:


實際上,對於自 2015 年以來我在該線程中討論要做的事情,沒有必要深入研究IFileOperationProgressSink接口的使用來報告進度(僅當我真的想要深入的進度信息時),因為它足以確定復制已啟動/已執行對IFileOperation.PerformOperations function 的調用,如果在復制過程中出現任何問題,它將出現在進度對話框 UI 中。

但是當然仍然需要開發自定義 SendTo 菜單的 shell 擴展,以替換 Windows 中內置的菜單。可以使用SharpShell庫開發 Shell 擴展。

我用 VB.NET 語言開發。 我設法擴展並記錄了他們的IFileOperation實現和包裝器,並更新了枚舉,添加了為 Windows 7 和 Windows 8 添加的更新的缺失值。

如果它對某人有幫助,我將在新答案中附加IFIleOperation和 Vb.NET 中的包裝器(因為它超過了帖子允許的最大字符數限制)。

請注意,在我的IFileOperation接口實現和名稱為FileSystemOperation的包裝器 class 中,您會發現一些返回類型和缺失的類或方法(如 HRESULT 返回類型或 NativeMethods 類),我無法在一個地方共享所有內容,但這些缺失事情是任何有經驗的程序員都知道如何解決的事情(例如,將 HRESULT 更改為 UInteger 類型,並將 go 更改為 Pinvoke.net 以查找我的 NativeMethods 類中缺少的任何方法)。

更新

似乎 StackOverflow 不允許我添加另一個答案,所以我將在 PasteBin 上上傳我的實現:

Class FileSystemOperation
https://pastebin.com/nvgLWEXu

Interface IFileOperation
https://pastebin.com/GzammHtu

Interface IFileOperationProgressSink
https://pastebin.com/jf9JjzyH

Class ComObjectDisposer(Of T As Class)
https://pastebin.com/7mPeawWr

Enum TransferSourceFlags
https://pastebin.com/V7wSSEvv

Enum FileOperationFlags
https://pastebin.com/A223w9XY

就這樣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM