Intercept MS Windows 'SendTo' menu calls?
Asked Answered
P

4

10

SCENARIO

I manage and organize many files during the day, the SendTo is the most used feature that I use on Windows.

PROBLEM

By default, when the user clicks an item/link of the contextmenu to send the files, the O.S does not show any kind of advise/notifier indicating that the files are copying to the selected destination.

I consider it a very wrong design issue because for big files its OK ...a progressbar will be shown, but if the files are to small it will not show any progressbar/visual indicator so is not possible to ensure that the files are copied (without manual effort) because I'm human and I could click outside the SendTo contextmenu by error.

So, I would like to develop a personal mini-tool that will help me to optimize my time showing me a notifier window wherever on the screen when I send/copy files using the SendTo feature from the contextmenu, and only the SendTo feature.

QUESTION

In just simple words, I want to detect a copy/send operation from SendTo menu to ensure that the click was done properly on the menu item (and not outside the menu), by also providing additional basic info such as the source folder, the destination folder, and the amount of files or the filepaths.

Any ideas to start developing this tool in the right direction?.

I will be grateful for a code example in C# or else VB.Net, preferably this last.

APPROACH

Since I don't know how to start doing this I mean which could be the easiest or the efficient way to intercept those SendTo calls, firstly I thought to hook the CopyFile or CopyFileEx API functions, but they do not provide the information that I need because that function will be called in any kind of copy operation and not only when I use the SendTo Feature, so I'm lost.

I'm not sure if I should investigate more about internal calls, or maybe investigate more about the windows contextmenu itself instead of messing with function hooks and ugly things that I could avoid.

My main idea is to develop a hidden WinForms (or else a windows service) that stays in background waiting for when I use the SendTo feature (when I click on an item of the SendTo menu) and then show any kind of visual indicator on the screen to ensure that I properly clicked that menu-item and maybe inform about the amount of files that I'm moving and where I'm moving them.

RESEARCH

Here is a code example that I think it demostrates how to instantiate the SendTo com object to create your own?, but its written in c++ and I'm not sure if the example is helpful because my intention is not to replace the SendTo menu but I'll keep this useful info here it it serves for something else:

How to add(enable) standard "Send To" context menu option in a namespace extension

The KNOWNFOLDERID constants docs gives some useful info about the SendTo folder, again I'm not sure if this could help maybe for a read/access monitoring approach?, I just keep the info here:

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

Default Path: %APPDATA%\Microsoft\Windows\SendTo

Legacy Default Path: %USERPROFILE%\SendTo

In the Shell Extension Handlers docs there is a Copy hook handler which I don't know if it has relation with the SendTo's COM component and if that could help me in some way, the same ignorance for IContextMenu::InvokeCommand method reference which maybe I could intercept it to identify a SendTo invocation?

By the moment I feel like flying blind.

I recently found this A managed "Send To" menu class but again its a example written in C/C++ (I think is the same source before) which I don't understand at all and again I'm not sure if that could help me because I repeat that replacing SendTo is not what I have in mind (just because I don't know how to properly do it avoiding all possible risks, I prefer to still let Windows logic copy/send the files, I just want to detect the copy operation to retrieve info)

EXPECTED RESULTS AND USAGE

Step 1:

Select a random file and use the SendTo menu (in my language, Spanish, the command name is 'Enviar a')

enter image description here

Step 2:

Let the .net application's logic (working in background) intercept the SendTo operation to retrieve info.

(I only need help with this step)

Step 3:

Display the info somewhere over the screen to ensure that the SendTo operation was performed, to ensure that I properly clicked the SendTo item (My Link).

enter image description here

(That popup is just a simulation, I don't know any way to retrieve all that info)

Product answered 9/4, 2015 at 18:46 Comment(14)
The SendTo feature is a shell extension. You would need to write a shell extension that replaces the SendTo feature with your own code, and an installer that removes the old SendTo and replaces it with yours.Polemics
What destination are you using that you get no notice. If I SendTo a folder or device, I get a dialogDidst
@Moby Disk Thanks for your idea, I've developed some times simple shell extensions with SharpShell but as you've seen I totally want to prevent errors, then I don't want to replace windows copy logic for my own copy logic. I would like to find a less intrusive approach than fully replacing that windows feature. Thankyou anyways!Product
Then write a shell extension that just instantiates the Microsoft SendTo Service, and delegates the work to that. Then all your code has to do is show your notifier window, and let the existing shell extension do the copying.Polemics
@Moby Disk Then write a shell extension that just instantiates the Microsoft SendTo Service in case of that will have no-risk then it seems a very good approach!, could you please explain in more detail how to start doing it? I just did simple shell extensions in the past (to catch multiple selected files on explorer), implementing what you said in a shell extension is a little big problem to me without providing a brief example, anyways I'll investigate about it. thanks.Product
@Plutonix Thanks for comment, but if for example you try to send a file of 1 byte, the more probably thing is that no dialog is shown because the file was sent too quickly.Product
Actually, my knowledge ends here as I've never written one. Conceptually, I expect you would implement whatever COM interface the SendTo handler implements. Then in your constructor you instantiate the SendTo handler, and every call you just delegate to that COM object. On my Windows 8.1, it looks like the SendTo shell extension is coclass {7BA4C740-9E81-11CF-99D3-00AA004AE837}. oleview.exe shows that it implements a bunch of interfaces I've never heard of.Polemics
My intuition is that you won't be able to use Sharpshell if you go with the "delegation" approach since it probably abstracts those interfaces away for you. You might have to way implementing your own file-copy logic versus the COM stuff you would have to do to delegate to the Microsoft SendTo handler.Polemics
@Moby Disk Your apporach seems not bad at all for me, I think that if I could create a kinf od SendToEx command to replace the original SendTo but WITHOUT replacing the copy/send logic that uses the original SendTo, just letting the original SendTo COM to do the hard job and I keep just suscribed to the original "command" getting the basic info to display it on the screen, then that could be just perfect, but I'm just dreaming.Product
I really will never understand those downvoters and/or flaggers that has any criteria to do what they do. instead of wasting 1 second doing a "click" to vote a closure, share your oppinion to improve my question content, whats the problem with yours? too much effort to write a comment?, come one and give something usefull to StackOverflow, stop acting in that way. And later these are the people who gives "flags amounts" as a "good reason" for be a moderator, its ridiculous.Product
@Product Are you reffering to the send to menu in Explorer that's reffered to in this link? thewindowsclub.com/add-customize-send-to-menu-windowsCatlaina
@Catlaina Yes that's the windows "SendTo" menu that I am reffering. Thanks for comment.Product
People, I have updated my question to add very intuitive images, I think its very clear what I would like to do.Product
The problem is that there is no real "Send To component" to intercept or hook. It is a part of Shell32 which provides the context menu. It apparently knows to look inside a shortcut and determine if the target it as app or a device. For apps it sends the filename as a cmdline arg, for a device it invokes a shell copy. You are assuming that SendTo exposes a robust API you can monitor and I dont know that to be true. The "easiest" way would be to implement your own SendTo context menu and use IFileOperationProgressSink to report progress.Didst
P
0

Maybe I come a little bit late to answer my own question, which was published on year 2015, but it wasn't until few days ago that I became interested in this matter again, with much more experience and knowledge gained in .NET after these years to start from scratch and try to understand everything that I did not understood in the past.


I just discovered that, as @Ňɏssa Pøngjǣrdenlarp already commented in the comments box, apparently the most viable way to accomplish this would be to implement my own SendTo context menu and use IFileOperationProgressSink interface to report progress, which for this firstly I need to depend on the usage of IFileOperation interface.

The reason to use the IFileOperation interface is because it seems the only way offered in the Windows API to let the developer perform multiple file operations (copy, move, rename, create or delete) all at once within the same progress dialog UI. This is probably the interface used by the system when the user selects multiple files or directories (via the SendTo menu or just CTRL+C and CTRL+V) to move or copy them at once, because it only shows one progress dialog with all the copy operations queued in it...

enter image description here

So clearly IFileOperation and IFileOperationProgressSink interfaces were what I need. But from what I know there is no managed implementation of these interfaces in the .NET framework namespaces (neither in the Microsoft's WindowsAPICodePack library), which seems somewhat inexcusable to me considering that these interfaces exists since the era of Windows VISTA or even earlier, and it is an indisputable improvement being the totally opposite of any built-in members that you can think of to perform copy, move, rename, create or delete operations, like for example System.IO.File.Copy or Microsoft.VisualBasic.FileIO.FileSystem.CopyFile method. All of them only supports a single operation and only on a single progress dialog at once.


So I focused to investigate on that suggested solution, and I ended up finding a good implementation of IFileOperation and IFileOperationProgressSink interfaces in this repository:

And this one which is the original implementation that I found on a old Microsoft's article which also comes with a good PDF reading to learn some things:


Actually, for what I've discussed in this thread to do since year 2015, delving into the use of the IFileOperationProgressSink interface to report progress would not be necessary (only if I really want a deep progress information), since it is enough to determine that the copy was started / the call to IFileOperation.PerformOperations function was performed, and if any problems occur during the copy then it will appear in the progress dialog UI.

But of course what is still necessary is to develop a shell-extension of a custom SendTo menu to replace the one built into Windows. Shell-extensions could be developed with SharpShell library.

I develop in VB.NET language. I managed to extend and document their IFileOperation implementation and the wrapper, and updated the enums adding the newer, missing values added for Windows 7 and Windows 8.

If it can be helpful for someone, I'm going to attach the IFIleOperation and the wrapper in Vb.NET all in a new answer (because it exceeds the maximum character limit allowed for a post).

Note that in my implementation of IFileOperation interface and the wrapper class with name FileSystemOperation you will find some return types and missing classes or methods (like HRESULT return type or NativeMethods class), I just can't share all in one place, but these missing things are things that any experienced programmer will know how to resolve (eg. change HRESULT to UInteger type, and go to Pinvoke.net to find any missing method from my NativeMethods class).

UPDATE

It seems that StackOverflow doesn't allow me to add another answer, so I'll upload my implementation on PasteBin instead:

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

That's all.

Product answered 30/3, 2022 at 23:57 Comment(0)
C
7

It's really simple to do once you understand what SendTo really does, and it doesn't involves COM or shell extensions at all. Basically, the send to menu is populated with the content of the SendTo folder of the user profile (C:\Users\\AppData\Roaming\Microsoft\Windows\SendTo by default in Windows 6.x).

When clicked, if the option is a shortcut to a folder it will copy the files there, but if there is a shortcut to a program (or a program executable itself) it will run that program, passing the paths of the selected files as command-line arguments.

From there, it's really trivial to make some program that simply takes paths as arguments, present some kind of notification and then copies the files or do whatever you want with them.

A quick and dirty example could be as follow (in C#, but could be done with anything else really):

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));
}

This just ask for confirmation for copying a bunch of files. Note that this really doesn't "intercepts" the send to menu, but rather handles it completely, so it's the program responsability to do any meaningful action. A more serious implementation could use the built-in Windows copy dialog and display some screen with progress or anything else, that's up to your needs.

It could also take some more parameters on the command line. When you place a shortcut in the SendTo folder, the destination could add some more parameters that will be passed as the first ones (before the file names). For example the destination of the shortcut can read c:\program files\copyfiles.exe c:\temp to pass the destination folder instead of hardcoding. The called program must then interpret the first parameter as the destination path and subsequent ones as the source files.

Catlaina answered 12/4, 2015 at 1:53 Comment(4)
I appreciate your time and effort but as I've specified many times I'm not planning to replace the copying logic of a MS Windows component that has been under development/updating and bug-fixing during many years for my own "rustic" logic which something could go wrong, its very important for me that the copying stays handled by the SendTo component, I only want to intercept it. I knew the trick of a "loader" program stored in SendTo folder that can take the path arguments but for that principal reason I gave and for other reasons I can't do that sort of application. Thankyou anyways!Product
There is nothing special about the "send to" file copy, it's just a plain file copy operation that's handled by Windows Explorer (in the exactly same way it would do with a plain ctrl+C and ctrl+V) and that itself is exactly what I pointed in the link. The copy itself is always performed by the Windows API and the kernel which File.Copy wraps and Explorer "decorates" with a progress dialog. There is no reimplementation at all here.Catlaina
@Catlaina to fit his request, you could just run a shell command to do the copy instead. Silly and inefficient, but would not "replace" the Windows copy component, as he requested.Horripilation
@IanM Sure, I was just building a trivial sample only to show that it's possible. The link I pointed to does explain how to instruct Explorer to do the copy instead (I've never done that myself). Certainly there is a lot of room for improvement in my improvised code.Catlaina
H
3

I've had to do something like this before. You don't even have to intercept the SendTo() function, you only need to make sure the the file has arrived. How about FileSystemWatcher if it's on the same computer?

You could use a watcher to watch before you send it, then, if the file successfully arrives at it's destination, you can display a successful message, and then kill the watcher.

Code Example

// 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();
}

You can access the desired property information (like in your popup gif) in this way:

  • Folder name: Path.GetDirectory(e.FullPath); (like "C:\yo\")
  • Full file name: e.FullPath (like "C:\yo\hey.exe")
  • File name: e.Name (like "hey.exe")

Non-code execution process:

  1. Before you initiate SendTo(), create an instance of the FileSystemWatcher property, and have it watch for a specific Folder/File name combination, which should show up in the watched folder: WatchFilesBeforeTransfer(FileName, DestinationFolder).
  2. Initiate SendTo().
  3. File received? DisplaySuccessfulSendToMessage(), KillFileWatcher();
  4. ???
  5. Profit.

UPDATE:

I just realized that's just for one file. If you want to check for multiple files, you could either create multiple FileWatcher instances (not recommended), or use a List<string> object, like this:

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.
        }
    }
}

Hope this helps!

Hypnotism answered 14/4, 2015 at 22:47 Comment(3)
Think about what happens if I have +50 .lnk targets inside the SendTo folder?, and what should happen if I add more folders?, or if I change the drive of the target folders, etc. also, one filesystemwatcher per each target folder then what will happens if I just move one file to one of those folders without using the SendTo feature?, sorry but this Isn't a solution in any way, a real implementation of the idea will not have any beneffit because the approach cannot handle the SendTo feature, only file operations in "X" folder. but thanks anywaysProduct
To be a solution, the approach that you shared should "detect" when a SendTo .lnk target is clicked (thing that I still don't know how can I detect it), then instantiate a filesystemwatcher. but anyways that fix will still have a very bad inconvenient because the filesystemwatcher can't know how many files should arrive or are sent from SendTo component.Product
Use a foreach loop on a List<string>. Before you send it, populate it, then check every time a new file arrives. You could delay the 50+ sent message based on the size of the list.Hypnotism
L
1

I'm afraid this ain't that easy. I was playing around with the FileSystemWatcher on this, but with only partly success.

Something that should definitely work are File system drivers but this looks like just too, well, look at it...

In the end it might be the easiest way to write your own shell extension to access the SendTo folder, and use this instead of the SentTo command which would give you full control.

These might be some starters:

Windows shell extensions

Shell Context Menus

Lemay answered 16/4, 2015 at 20:24 Comment(0)
P
0

Maybe I come a little bit late to answer my own question, which was published on year 2015, but it wasn't until few days ago that I became interested in this matter again, with much more experience and knowledge gained in .NET after these years to start from scratch and try to understand everything that I did not understood in the past.


I just discovered that, as @Ňɏssa Pøngjǣrdenlarp already commented in the comments box, apparently the most viable way to accomplish this would be to implement my own SendTo context menu and use IFileOperationProgressSink interface to report progress, which for this firstly I need to depend on the usage of IFileOperation interface.

The reason to use the IFileOperation interface is because it seems the only way offered in the Windows API to let the developer perform multiple file operations (copy, move, rename, create or delete) all at once within the same progress dialog UI. This is probably the interface used by the system when the user selects multiple files or directories (via the SendTo menu or just CTRL+C and CTRL+V) to move or copy them at once, because it only shows one progress dialog with all the copy operations queued in it...

enter image description here

So clearly IFileOperation and IFileOperationProgressSink interfaces were what I need. But from what I know there is no managed implementation of these interfaces in the .NET framework namespaces (neither in the Microsoft's WindowsAPICodePack library), which seems somewhat inexcusable to me considering that these interfaces exists since the era of Windows VISTA or even earlier, and it is an indisputable improvement being the totally opposite of any built-in members that you can think of to perform copy, move, rename, create or delete operations, like for example System.IO.File.Copy or Microsoft.VisualBasic.FileIO.FileSystem.CopyFile method. All of them only supports a single operation and only on a single progress dialog at once.


So I focused to investigate on that suggested solution, and I ended up finding a good implementation of IFileOperation and IFileOperationProgressSink interfaces in this repository:

And this one which is the original implementation that I found on a old Microsoft's article which also comes with a good PDF reading to learn some things:


Actually, for what I've discussed in this thread to do since year 2015, delving into the use of the IFileOperationProgressSink interface to report progress would not be necessary (only if I really want a deep progress information), since it is enough to determine that the copy was started / the call to IFileOperation.PerformOperations function was performed, and if any problems occur during the copy then it will appear in the progress dialog UI.

But of course what is still necessary is to develop a shell-extension of a custom SendTo menu to replace the one built into Windows. Shell-extensions could be developed with SharpShell library.

I develop in VB.NET language. I managed to extend and document their IFileOperation implementation and the wrapper, and updated the enums adding the newer, missing values added for Windows 7 and Windows 8.

If it can be helpful for someone, I'm going to attach the IFIleOperation and the wrapper in Vb.NET all in a new answer (because it exceeds the maximum character limit allowed for a post).

Note that in my implementation of IFileOperation interface and the wrapper class with name FileSystemOperation you will find some return types and missing classes or methods (like HRESULT return type or NativeMethods class), I just can't share all in one place, but these missing things are things that any experienced programmer will know how to resolve (eg. change HRESULT to UInteger type, and go to Pinvoke.net to find any missing method from my NativeMethods class).

UPDATE

It seems that StackOverflow doesn't allow me to add another answer, so I'll upload my implementation on PasteBin instead:

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

That's all.

Product answered 30/3, 2022 at 23:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.