Passing selected multiple file names to the single instance of an exe file via Windows 10's Context menu - windows

I have written a script in python 3.8 that takes two file names as command-line args and does some operations. And now I wish to add the .exe file of that script to Windows 10's context menu.
I manually added an entry to the registry under "HKEY_CLASSES_ROOT*\shell\MY_APP\command" but what it does is, execute the .exe file twice instead of taking them as multiple parameters to a single instance of my app.
Ask:
How can I make it execute only once by-passing multiple selected file names as parameters to a single app instance?
How can I build an installer that adds an entry to the registry automatically when the user is installing my app?
Thank you

The classic static verb registration you are doing here is always going to launch multiple instances.
There are two possible workarounds:
Implement IExplorerCommand or IDropTarget COM shell extensions.
When your application is started, try to find another instance already running and pass it the new path with some kind of IPC, for example the WM_COPYDATA message.

Related

Self-extracting file that takes arguments and return exitcode

I want to create a self-extracting file (SFX) named Setup.exe which contains a Windows application with some dependencies.
It is possible to start the Windows application with arguments, and I would like to put them at my Setup.exe and then it should pass them to the Windows application. Furtermore, I would like to pass the Windows application exit code to the Setup.exe.
Currently I have tried to use 7zip, but it seems that it isn't sufficient.
Is it even possible with 7z or do you know how to do with other tools?
You've asked a lot of questions and I am trying to answer a few here, maybe not all of them.
Yes it is possible to start a Windows Application with arguments. Ideally for that you need to open command prompt and navigate to the directory of your application then type in theProgramName.exe - arguments options but that I assume you already know.
Another approach would be to Create ShortCut for the Program you are targeting and then you can just Right click -> Properties -> and append your arguments to the Target field. Example : C:\Games\Counter-Strike\hl.exe -steam -game cstrike -noforcemparms -noforcemaccel
Another approach that I assume you would want when you are creating a SFX is to create a .bat (batch) file with the contents being theProgramName.exe - arguments options and put it in the same directory as your program and set it to run as our main application when it extracts. If you want the exe and not the bat you can use some bat to exe conversion tools, there are tons out there.
The best way I can think of is using programs like Advanced Installer. You can directly make an msi or a sfx and send a shortcut anywhere (more than one) and it could contain arguments you specify just like what I described in the second way.
So yeah that other tool I'd vouch for that will do what I think you want to do is Advanced Installer.
After some experiments, I changed the implementation so instead of using 7 zip to create a SFX I just embed all I need like *.msp and such as embedded resources in my Windows application.
On that way my arguments and return code worked out of the box.
I used this link to do actual implementation: https://www.telerik.com/blogs/how-to-merge-assemblies-into-wpf-application

Prevent Program (.exe) from starting DIRECTLY (Only allow execution from shortcut)?

Is there a way to prevent direct startup of a .exe program, and only start up when a shortcut is run? I'd like this to work also when opening up a filetype that is assigned to a program.
The only thing that comes close to what you want is checking for certain command line parameters. This is actually a rather common way for concealed executions - for instance programs that want you to run a loader program first (online games would be an example).
So you would have to generate a shortcut that privdes the specific command line arguments needed for the start. This ensures that your exe cannot be executed directly, however there are no guaranties that a specific shortcut file is startet as it only provides a link to the exe file.
No, that is not possible, in my knowledge.
This is not possible, the shortcut is executing the program in its location. The .exe has to be executable for the shortcut to work. Assigned file extensions also execute the actual .exe the shortcut is just a symbolic link to the actual file.
If you can give more information as to what your trying to accomplish I could maybe offer another solution.
I'm not aware of any way built in to do this, as an executable is still launched as that user by the shortcut. You can disable shortcut locations, allow only certain executables, etc but not deny access to executable but also allow it via shortcut.

Windows: File Monitoring Script (Batch/VBS)

I'm currently in working on a script to create a custom backup script, the only piece I'm missing is a file monitor. I need some form of a script that will monitor a folder for file changes, and then run a command with the file that's changed.
So, for example, if the file changes, it'll execute "c:/syncbatch.bat %Location_Of_File%"
In VBScript, you can monitor a folder for file changes by subscribing to the WMI __InstanceModificationEvent event. These articles contain sample code that you can learn from and adapt to your specific needs:
WMI and File System Monitoring
How Can I Monitor for Different Types of Events With Just One Script?
Calling WMI is fairly cryptic and it causes the WMI service to start running which can contribute to bloat since its fairly large and you really can't cancel the file change notifications you've requested from it without rebooting. Some people experimenting with remote printing from a Dropbox folder found that a simple VBScript program that ran an endless loop with a 10 second WScript.Sleep call in the loop used far less resource. Of course to stop it you have to task kill that script or program into it some exit trigger it can find like a specifically named empty file in the watch folder, but that is still easier to do than messing with WMI.
The Folder Spy http://venussoftcorporation.blogspot.com/2010/05/thefolderspy.html
is a free lightweight DOT.NET based file/folder watching GUI application I'ved used before to run scripts based on file changes. It looks like the new version can pass the event filename to the launched command. The old version I had didn't yet support file event info so when launched, my script had to instance a File System Object and scan the watched folder to locate the new files based on criteria like datestamps and sizes.
This newer version appears to let you pass the file name to the script if you say myscript.vbs "*f" on the optional script call entry. Quotes can be important when passing file paths that have spaces in folder names. Just remember if you are watching change events you will get a lot of them as a file grows or is edited, usually you just want notification of file adds or deletes.
Another trick your script can do is put the file size in a variable, sleep for a few seconds, and check the file again to see if its changed. if it hasn't changed in a few seconds you can usually assume whatever created it is done writing it to disk. if it keeps changing just loop until its stable.

How Can I Get My File Association to Open Multiple Files in a Single Program Instance?

I have set up a file extension in the Registry for my program as Windows requires.
In the Registry, under shell/open/command, I've got:
"C:\MyProgramPath\MyProgram.exe" "%1"
This works fine for me. When someone clicks on one or more files associated with my application, my application correctly opens the file(s) but each one is opened in a separate program instance.
Is there any way I can do this and open up all files in one program instance?
This is a rather common question, and it has really nothing to do with Windows file extensions. When you doubleclick a file of your program's custom type, Windows will start the associated application MyProgram.exe and pass the file name %1 as a command-line argument.
Now, if you want only a single instance of your application, you need to do this:
When your program (MyProgram.exe) starts, it should check if there is already an instance of it running.
If there is a previous instance, the new instance of MyProgram.exe should send a message (of some kind, not necessarily a windows message) to the old instance telling it to open the file %1.
The new instance should now terminate itself.
A very simplistic approach
There are several ways of accomplishing this. One of the simplest ways is to set a registry key/value each time your application starts, and remove it when the application exists. Then, when (a new instance of) your application starts, prior to setting this key/value, it should check if it is already set. If, so, follow the steps (2) and (3) above. This might not be the most stable approach (in fact it is a very bad idea, since you cannot guarantee that the app will remove the key/value when it exists if it does so abnormally), but it will give you the basic idea. Other, perhaps better ways, include FindWindow and, even better, the use of mutexes.
Step two might be implemented by sending a windows message (maybe WM_COPYDATA), or by setting a registry value, or, by writing a file, or ... There are many ways of communication between different processess.
The details
Since this is a rather common question, it has been dealt with before. See, for instance, this Delphi-specific article.
You can when using DDE. See http://cc.embarcadero.com/Item/17787 for an example in Delphi.
EDIT:
The link you gave talks about another method: using IDropTarget. This might fit better with your already running drag and drop capabilities.

How do I set file permissions for non-admin users in VB6?

I have an old update program written in vb6, which runs as admin. However, because it runs as admin, all the files it downloads and saves are read-only to other users. Even files in public places like the shared application data folder (which is where I'm saving the files in question).
I'm lucky I found this before the 'vista-compatible' release. Vista hides the problem by redirecting non-admin writes and future reads to a sortof 'virtual' folder. But the next update may replace the file, and the non-admin program will still go to the virtual folder and use the old file.
As the admin user, how do I allow other users full control of files I write in vb6?
The way I do this is to make it an Installer responsibility.
Use VSI 1.1 to create an Installer MSI for your application. Create an application data folder under CommonAppDataFolder.
As a post-build step run a script to perform the following:
Set the MSI database for a per-machine installation: Property table, row with ALLUSERS set to 1.
In the Directory table, locate the entry for CommonAppDataFolder and obtain its directory Index. Use this Index to query the Directory table for an entry where CommonAppDataFolder is the parent and obtain its Index (this is your app data subfolder).
Look in the File table to obtain the component Index of your program.
Create the CreateFolder table in the database if it isn't present. Add a row to CreateFolder for the desired application subdirectory by its Index, tying it to your program's component Index.
Create the LockPermissions table if it isn't present. Insert a new LockPermissions row for your application data subdirectory, giving it FILE_ALL_ACCESS for Everyone.
That's about it.
You can do it this way, or use VSI 1.1 and then edit the MSI using Orca, or probably by using a 3rd party MSI authoring tool these entries will be settable via its GUI and can be saved in the Installer project. I just use a small WSH script I run after each VSI 1.1 build.
AFAIK this is the recommended way of accomplishing such things according to Windows application guidelines. If your needs are fancier you might use multiple subdirectories or sub-subdirectories some allowing full access, some read-only, etc.
Your program can locate the folder using Shell Automation objects or by calling Shell32 as a standard DLL (using Declare Function or a TLB).
It's not necessarily who writes the file, but where they write it to. The program files folder and it's sub folders are read-only to all standard users by default. Try using the All Users Application Data folder instead.
This is a little tricky for VB6, since it was not at all designed with Vista in mind. Some of the relevant folders were re-named and there's no way I know to get vb6 to give you the exact folder you want short of using a "Declare Function" alias to call directly into the windows API.
So the easiest reliable way I know to find a suitable location is to use the %ALLUSERSPROFILE% environment variable. That returns "C:\Documents and Settings\All Users" by default on XP and "C:\ProgramData" by default on Vista. From there you can look for an "Application Data" folder. It won't be there and you don't need it on Vista, but creating one if it doesn't exist won't hurt anything. That gives you a consistent folder structure on both systems from which you can create a sub folder for your app to use as a work space.
And one final note: this isn't a new change for Vista. Program Files folders have always been read-only to standard users by default. XP worked the same way. It's just that so many people run as administrators in XP you might be able to get away with it.

Resources