How to pass several files at once to my registered application? - windows

In a Delphi 10.4.2 win-32 VCL Application associated with graphic file formats in Windows 10, I get parameter files selected in Windows File Explorer. Here I watch the files passed to my app right after Application.Initialize;:
CodeSite.Send('AppInstances: ParamCount', ParamCount);
When I select ONE file in Explorer and then press the ENTER key, this file gets loaded in my application. So far so good.
But when I select THREE files in Explorer and then press the ENTER key, my app gets started THREE times each time with ONE of the 3 file parameters:
Is it possible that Explorer would start my app only ONCE and pass the 3 selected files at once to my app when I press the Enter key?

On Windows 7 and later 1, you can register a MultiSelectModel value (either Document or Player) with your file association verb(s) in the Registry. This is the easiest way to allow Explorer to send multiple files at a time to a single instance of your app, such as in separate command-line parameters.
1: I don't know exactly when this feature was first introduced.
Prior to MultiSelectModel, other ways to handle this included:
implementing the IDropTarget interface in your app, and then registering the DropTarget with your file association verb(s). The Shell can then construct an IDataObject containing information about the files(s), and pass it to your IDropTarget implementation (also see this article). This is the preferred method, as it does not suffer from limitations that other methods have (including MultiSelectModel!), and it allows for more flexibility as the same IDropTarget implementation can accept multiple files executed in Explorer, files dropped onto your app's window, even dropped onto the app's .EXE file itself. It is just a matter of registering the same IDropTarget with the appropriate APIs.
Implementing a DDE server in your app, and then registering the server with your file association verb(s). The Shell can then start a DDE conversation with your app and send the file paths over to it using your specified command(s).
just accepting the Shell starting a separate process for each file. Before your app creates its UI, have its startup code check for a file path on its command-line, and if found then look for another instance of your app already running, and if found then use an Inter-Process Communication mechanism of your choosing (ie, WM_COPYDATA, named pipe, socket, mailslot, etc) to send the file path to that existing instance, and then exit.

Related

Windows Explorer and Reparse Point Files: keep Explorer from opening my files

I've implemented a user mode program and a Windows file system minifilter that creates a skeleton view of users files for a remote file storage system. It maps the remote files to the local drive. The user mode program creates a reparse tag for each file on the remote system. When a create request (e.g., CreateFile for read) is detected, the minifilter asks the user mode program to download the file. This should only happen when a program wants to open the file for viewing or editing.
But, I'm finding that Windows Explorer is triggering my files to download. I'd like to prevent the Explorer File windows and File Open/Save dialogs from
triggering downloads. And, I also want to display the file thumbnails and file
size.
[Update: I've found I can use Windows sparse files to show my remote
file size in Explorer. ]
Therefore, I have also implemented a Shell Extension, IThumbnailProvider, that downloads a rendition of the file. This provides the file thumbnails.
For my testing, I've registered the IThumbnailProvider for all files (*) and for .jpg files.
I'm seeing two interesting behaviors using a combination of Process Monitor and DebugView (both from SysInternals):
1. If I make my minifilter reject requests to open the file from Explorer, then my IThumbnailProvider is invoked.
2. If I permit open requests from Explorer, I see thumbcache.dll in the call stack trying to open the file and my IThumbnailProvider is not called. It appears that the default thumbnail provider reads the downloaded file and creates the thumbnail.
I must be missing something.
Update: if I use InitializeWithStream instead of InitializeWithFile, it appears my handler is invoked. But, that also triggers a download of the file.
There are many shell extension types which can access to your files. Icon handler can read file to create icon, Info tip handler can read file to create text hint, Data object handler can read file to create clipboard data and so on.
Questions from developer with the same problem: first and second. Solution was to create namespace shell extension. NSE can control all access to your files.

How can I associate a file with my app?

I have a Cocoa app "PDFHistory" on Mac OS X that uses the NSDocument architecture to save and load PDF files that are internally formatted specially for my app. I want to make it so whenever I save a file (e.g., "mydoc.pdf") from PDFHistory, then subsequently double-clicking on mydoc.pdf will automatically open it in PDFHistory.app. However, I don't want to make it so all .pdf files are automatically opened in PDFHistory, but rather use the system default (probably Preview.app). The .pdf suffix is a requirement, though, since I need the user to be able to e-mail the files to other users who can view the file in their default PDF viewer.
The problem is that if I set the LSHandlerRank to "Owner", then all .pdf files will be opened with PDFHistory, which is bad (since I only understand the internals of the .pdf file that PDFHistory wrote out). But if I set LSHandlerRank to "Alternate", then all .pdf files will be opened to the system default app (Preview.app), which is confusing for the user who had just created the file using my app.
Once upon a time, "creator codes" could be used to implement this sort of capability, but launch services started ignoring them back in Snow Leopard (see http://tidbits.com/article/10537). UTIs are not a substitute that provide this capability (see http://boredzo.org/blog/archives/2009-09-22/how-not-to-use-utis).
Using Finder to get info on the file allows the user to specify a specific app to use to open the specific file. This supposedly works by setting a "usro" property in a the file's resource. There is some open-source code to mimic this behavior (https://github.com/AlanQuatermain/SetAppAffinity), but is uses deprecated functions, and so would cause Apple to reject the app from the App Store. Similarly, people have posted AppleScript to set this property (https://discussions.apple.com/thread/2597365), but sandboxing would prevent me from invoking it.
Although the .pdf suffix is a requirement in order to be able to send the files to users on other systems/platforms, I considered trying to have the suffix registered with two extensions as ".phistory.pdf", which would allow "file.phistory.pdf" to be opened in PDFHistory, but "file.pdf" would be opened in the default PDF viewer. However, this simply didn't work: it appears that the final suffix is the only one used by launch services, and everything before that is ignored.
So is there any way to have my app be the default app for opening files that it created itself?

Windows API hook, custom save as file dialog to save directly to webserver via POST

I want to write a custom save as dialog that is hooked into the File -> "Save As" of most Windows program. This custom dialog will allow the user to enter their username, password, destination folder and uploads the file to the web server via a POST. If the user clicks cancel, it will call the original file dialog.
I've been reading up about Windows API hooking and this is vaguely how I think I would approach this:
Intercept "Save As"
Display my custom dialog, return some temporary path on the drive
Let the program write file to the temporary path, assume it calls WINAPI CreateFile(...) for now
Read the temporary file and upload to web server
Clean up temporary file
But I still can't get my head around the steps required to pull this off. Assuming I can intercept the "Save As" and CreateFile function, how do I detect the CreateFile was called from a "Save As" and not just any random file creation? I can think of a hack where I keep track of the time difference of when the File dialog got open and CreateFile got called.
My alternative solution is to use the existing file dialog and create a special folder on the disk, that is constantly monitored. When a file gets written there it will call an external program that uploads the file. I haven't looked into how to do this yet. I suspect this is easier.
UPDATE
As a first baby step, I wrote a .NET task tray application that allows the user to enter their login details and a folder to monitor. Whenever a file gets dropped in there there it will upload to the web server. So far it seems to work. Now I just need to figure out how to add a nice shortcut to the left pane of the file dialog. Once that's done I think I got a solution I'm happy with.
There is no need to hook or patch anything. Create a shell namespace extension that supports IStorage::CreateStream and implements it by returning a stream that POSTs its data to the Web server. The user can then choose to save the file to your namespace extension in order to upload the file.
Hooking the standard save dialog requires you to inject a DLL into every running process and have it replace the import stub of the the Win32 API GetSaveFileName() function in the process's PE header (something anti-virus and anti-malware apps are not likely to be happy about).
Then there is the new-style save dialog that was introduced in Vista using the new IFileSaveDialog COM interface instead of GetSaveFileName(). For that, you would have to uninstall and replace Microsoft's default FileDialog COM object with a custom implementation.
That does not count custom-made save dialogs, which you are not likely to hook.
If, by some miracle, you can hook the dialog and have it return a custom path of your own creation, you don't need to hook CreateFile() itself, Just monitor the folder that you create for your purposes. Place it where it is unlikely that any other app (or user) besides you will write files to. You can create a custom subfolder in the user's or system'ss AppData folder for that purpose. You can use SHGetSpecialFolderPath() and/or SHGetKnownFolderPath() to find those folders.
The tricky part will be detecting when the file is finished being written to and has been closed. You will have to monitor the folder for changes, such as with ReadDirectoryChangesW() or SHChangeNotifyRegister(), and periodically open new/modified files for exclusive access. If a file is still open by someone else, you won't be able to open it yourself. But once you do open it, you can do whatever you want with it.

How to pass multiple associated files to a program?

I have written a Windows software and I have associated a file extension with this program. When I double click a data file then my program starts up and opens the file. So far it works. But when I select multiple files and then click "Open" in the context menu then multiple instances of my program are started, one instance for one file. I want Windows to open ALL files with a single instance of my program. Is this possible without implementing a one-instance-handler in my program?
Currently the MyFileType/shell/open/command in the registry looks like this:
"C:\Program Files\MyApp\MyApp.exe" "%1"
Maybe there is some special Token/Variable I have to use instead of the "%1" to get it working? On Linux I have to use %f for a single file and %F for a list of files. Is there something similar on Windows?
The simplest way is to associate your app with the default shell verb (e.g. “open,” or “play,”) for the file type, and implements a drop target that uses SHCreateShellItemArrayFromDataObject to get the selected files. Suggested reading: How the Shell Invokes Verbs
If you don't like to take over the default verb, you can add your verb to the file association's open with list.
Another method is to make your app a singleton and send the document's path to the first instance via inter-process communication methods such as DDE or RPC. This requires you to keep your main window responsive (for example, won't work if you are showing a dialog)
%* might be what you're looking for.

How does the OS know what to do with a file when the "Open With..." option is selected?

Explanation:
I don't remember about Linux and I don't know about OS X, but in Windows you can right-click a file and select a program to open it. But how does the OS know exactly how to make the program open it? Does it keep track of the "Open file" dialogs the program has? Does the developer have to specify a special event handler or something for these cases?
For Windows, the answer is in the registry. If you're comfortable reading the registry, run regedit.exe on your Windows machine.
Under HKEY_CLASSES_ROOT, you will see a key list of all file types, .doc, .txt, etc. Each of these keys contains a key called "OpenWithList" or "OpenWithProgIds". An application may have a registered "ProgId", also found under HKEY_CLASSES_ROOT, and it can register it's ProgId to the file types it wants to handle in OpenWithProgIds. Otherwise it registers itself in OpenWithList.
In response to the comment to the first answer (cos I don't have enough rep to comment):
You're thinking of DDE, which is a near-enough deprecated technology. The Windows shell executes the application with the selected file as the first parameter.
The Windows Explorer remembers your previous "Open With..." choices based on file extension in the following key:
HKCR\.ext\OpenWithList
Next time you right-click the file, it looks there and build a list of programs you have previously used to open a particular file type.
Say it finds a key named "myapp.exe". It then looks up the application here:
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Applications\myapp.exe
fetches the info where the application is installed. And it goes here:
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\myapp.exe
follows the GUID stored there to find out the display name of the application.
To add to the fun, the primary associated application is in the list as well, also everything in the OpenWithProgIds key and everything in:
HKEY_CLASSES_ROOT\*\OpenWithList
as well as their respective HKEY_CURRENT_USER counterparts.
The resulting list of applications is then made unique, sorted and displayed. On selection the file is started like any other file you click on - i.e.:
C:\path\to\myapp.exe "C:\path\to\the\file.ext"
http://windowsxp.mvps.org/OpenWith.htm
File associations are stored in the Registry in: HKEY_CLASSES_ROOT.
You can manage them graphically by using Windows Explorer: (WinXP)
Click Tools/Options/File Types
Or with the "Default Programs" applet in Control Panel. (Vista)
Just for some nostalgia the rather brilliant Acorn archimedes had a much better system with each file type having a unique type number, with a manufacturer and application code (rather like a MAC address) which was written with the file.
This means you can have different files all called .bak opened by the correct application - unlike the windows case where a new installed app steals the ownership of every exiting file of that type. Autocad is especialy bad for this, registering about 20 file types.
The operating system runs the specified program sending as parameter the path of the file to open.
For example, in C#, if you want to know which file the operating system wants you to open you'll need to do:
class Program
{
static void Main(string[] args)
{
if (args.Length == 1) //The OS wants me to open a file
openSomeFileJustBecauseTheOSWantsIt(args[0]);
}
}

Resources