Integrate into images' context menu
Closed this issue · 23 comments
To add "Upload to Imgur" item to image files' context menu is enough to add a registry entry with
path : "HKCR\SystemFileAssociations\image\shell\Upload to Imgur\command
name: ""
type: "REG_SZ"
value: application_exe_path + ' "%1"'
You can add an option to register this context menu item in Settings window, as well as an option to unregister it, for that you would just simply delete "HKCR\SystemFileAssociations\image\shell\Upload to Imgur\
, if it exists.
You can also display some warning in case the registry entry exists, but has different application path set than the current application has.
When user right-clicks on an image file and selects "Upload to Imgur" application_exe_path "image_path"
will run.
Quotes are needed because image path might contain spaces.
When users selects "Upload to Imgur" on multiple images, multiple application_exe_path "image_path"
will run, one for each image.
Heya nurupo,
I've done some experimenting with this (because I quite like having context menu options for EasyImgur), and so far my findings have been that this is quite a hassle to get going properly. Not as far as adding the options themselves is concerned, but rather having the application work correctly with the current history/api access system.
If we were to spawn many instances of the EI process to upload selected images, we'd need some way to put these uploads in the history file. Additionally we'd want the balloon tooltips that are currently in place to notify the user of the current progress of his/her uploads. To do this it would be easiest if we could just tell one main EI process instance to upload everything, which would take care of everything we need.
For this we'd need to utilize something such as remoting, but this does bring with it a number of issues, I've found.
Another possibility would be to have the main application monitor the history file for changes, in which case the file is reloaded. This would allow all processes to upload everything themselves, and to then simply write to the history file and be done with it. However, this introduces the issue of having multiple process trying to (potentially) access the same file at the same time. This probably requires some coordination and synchronization to ensure the data is not somehow corrupted in this process or that all processes are not waiting on other ones for too long.
All in all, this requires a bit more effort than I had initially thought when I read your post, otherwise I would've have a commit and release ready for you by now ;)
Data bases already have synchronization protection... oh, just checked something and found out that you are using a json file, not a SQLite database.
Well, as a solution, when you try to upload multiple images you could just check if a master program is running, if it is then pass the image path you got as a command line argument to the master program one way or another (not sure which is more C#-ish way) and immediately exit. If there is no master program running, just become one and handle the passed image appropriately. Then the master program would just listen for incoming messages and handle them.
The next difficulty that arises is how to put image URLs in the clipboard, which I guess is unsolvable : )
Having the process communicate with an existing application is possible (and I have that working locally). Though to my own surprise having one instance communicate with another instance is actually way more involved than I thought (this is something that C# does not handle very graciously if you ask me). Not impossible, but it will just take a little bit more time than I thought because it'll involve some refactoring of the existing code instead of simply bolting on some extra code.
Most importantly though, I'm trying to find out which method of IPC is best to use here. Remoting seemed like a nice option at first but I'm not sure it actually is now. It kind of forces certain aspects of the architecture of your application, which I'm not 100% happy about. I'll be looking into other ways of accomplishing this cleanly.
As far as putting multiple image URLs on the clipboard is concerned, I'd imagine putting a list of the URLs, separated by newlines, would be fine.
Most importantly though, I'm trying to find out which method of IPC is best to use here.
Regular sockets should work fine (that's also the most crossplatform solution for IPC, afaik).
As far as putting multiple image URLs on the clipboard is concerned, I'd imagine putting a list of the URLs, separated by newlines, would be fine.
The difficulty I mentioned is that you can't differentiate between a user running the context menu option on multiple files at once (upload N image -> get URL list) and a user running the context menu option sequentially on image files (upload -> get a single URL, upload -> get a single URL). Well, you can do some dirty tricks like counting how much time has passed since the last IPC message was received, but I wouldn't suggest doing that : )
Also, a user won't know which URL points to which image when they get the list of URLs, although that might not be an issue.
Warning: wall of text.
This is a bit old, but I have something to add. The other day, I was thinking "hmm, it'd be nice to have a little context menu thing to upload images to imgur." I started doing research into a shell extension and opened a huge can of worms with that, and a friend pointed me to this project. So, I decided I would just modify this program to do what I wanted, since all the framework is there.
I didn't realize until afterward that you had started working on the same thing, or I'd have continued work on that, but I think I like my implementation a little better. It adds another neat feature as well: album uploading. If you right-click on a folder, you can upload the images inside as an album. Additionally, you can set an option to upload as an album when selecting multiple files from the file dialog. It uses named pipes and a mutex to do the inter-process communication; the way it's set up is any subsequent processes will just pipe their arguments to the main process, which triggers an event. Pretty simple, actually.
I'd forgotten about selecting multiple items and right-clicking, but the code can easily handle this. Currently, it uses the context menu options of the thing right-clicked on; if you select three images and an album and right-click on the album, it shows "upload to Imgur as album", but if you right-click on one of the images it shows "upload to Imgur". The code handles it correctly regardless; currently it would upload the three images standalone and the folder as an album. This could be tweaked by looking at the arguments supplied, perhaps with an option, but all the necessary information is available.
As far as putting them on the clipboard is concerned, it handles it just like before; it puts the last thing uploaded on the clipboard. This could easily be modified to add all links to the clipboard, etcetera.
Another cute caveat of the implementation of argument parsing is that a call such as
EasyImgur.exe file1 file2 /anonymous file3 file4
will cause the first two files/directories to be uploaded as the logged in user, and the second two to be uploaded anonymously. However, the upload will fail completely if the user isn't logged in (which could easily be modified). There's no actual way to make use of this behavior from within the program, but it's a feature that does exist.
You can take a look at my github if you're interested, I updated the dist folder so it should Just Work(tm). I'd be happy to make a pull request if you like the changes, as well. I tried to follow your coding style as much as possible, but there's a couple places where I deviated from it.
EDIT: It also checks to see if the application path is incorrect on startup, just like it already does for the "start on boot" option. It also sets the app's icon to appear next to the menu option, which is a nice touch.
I'd like to add a bit of info that I've found while digging through the registry.
nurupo mentioned that the path should be HKCR\SystemFileAssociations\image\shell\Upload to Imgur\command
, but the parent key of command
can actually be anything; it will use the (Default)
value as the command text. If (Default)
is empty, it uses the key name. Setting (Default)
seems to be the preferred method.
Additionally, HCKR is actually just an alias of HKCM\Software\Classes
and HKCU\Software\Classes
combined with the HKCU entries taking precedence; writing to HCKR directly is an implicit write-through to HKLM. (Also, I couldn't get it to work from the app, even with administrator access.) This would cause the context menu to appear for all users; since other users most likely won't have access to EasyImgur.exe
, I wrote to HKCU\Software\Classes
instead (plus, HKCU doesn't require elevation). Additionally, the typename for folders (Directory
) doesn't work if you put it in SystemFileAssociations
, and the other typenames (.png
, .jpg
, etc) don't work outside of SystemFileAssociations
. I didn't use image
for two reasons; firstly, I looked for it and couldn't find it, so I didn't think it existed, and secondly, I wanted to restrict the option to only the filetypes Imgur accepts. Incidentally, Imgur accepts PDFs and XCR (GIMP) files, which don't fall under the image
typename. So, the application adds the required keys to all the filetypes instead. A potential optimization could be removing some of those entries in lieu of For using image
, but you'd have to find a list of which filetypes that affects and make sure you can upload all of them.image
to work, the (Default)
value of all the extensions you want to associate with image
has to be image
, or so it looks like. Applications like Photoshop change these values all the time; and if they do then the context menu entry will vanish. An example is .txt
using the txtfile
alias; if you look at the .txt
key its (Default)
is (should be) set to txtfile
. That's another reason I chose not to use aliases.
OH, one more thing. I had to disable signing the ClickOnce manifests; I got the error "couldn't find key in certificate signing store", or something to that effect. This had the effect of disabling some other "secure debugging" setting that I had never heard of. Thought you should know.
Hi Snoozbuster,
Thanks for contributing! I just had a quick glance over the changes you made and this looks good, I like it :)
I'm currently at work which means I can't test the application with your changes yet, but I'll do so as soon as the opportunity arises (this evening or tomorrow morning). If it all works as intended I'd like you to do a pull request so we can merge these changes into the main EasyImgur codebase. After that happens I'll also add you to a list of contributors in the README.md file as well as the app itself.
In the mean time, would you be able to fix up any code convention inconsistencies as they exist now? For example, I saw a few occurrences of function parameters being simply all lowercase instead of prefixed with an underscore and then starting with a capital. It's a minor detail which obviously doesn't affect the functionality of the program, but I like to keep the code consistent ;)
Thanks again for contributing, it's awesome to see people being interested in EI!
Cheers,
Bryan
Alright, I'll go ahead and do that. Do you mind the use of default parameters? I prefer them, especially when adding parameters to an existing function, because I don't have to create another signature with the old arguments and then just end up calling the new function with default arguments, but some people don't like them. They certainly aren't used very often, for reasons that are beyond me :p
Another convention I used is using
blocks for RegistryKey
(because you are supposed to close them when you're done with them) and Stream
instead of doing it by hand.
I don't mind default parameters at all, if they're used correctly (as in, they don't encourage incorrect usage of a function) then they're a very nice way of saving you and others some time and effort.
The using blocks are also fine by me, I have a stronger C++ background than a C# background and so I don't use using
blocks enough ;)
Yeah, your naming conventions suggested that :p
I've made the changes, I'll commit them and push them sometime tomorrow.
Perfect!
Alright, I've pushed the commit. Let me know when you've tried the program out and if you would like any other modifications before I make a pull request.
Hey Snoozbuster,
I just had the opportunity to test out the executables in the dist folder on your fork, and I'm sad to say I've found a couple of fatal bugs:
- The context menu options don't seem to work at all. The registry keys are set correctly but Windows for some godforsaken reason just opens the "Open with" dialog window instead of actually executing the command set in the registry.
- Manually executing the command that the context menu option should use will not properly handle history. It will open an instance of EasyImgur which seemingly does not load the current history, but it does attempt to save its own history list out, which causes the entire history file to be overwritten with the incorrect contents. In my tests it has shown to actually remove all history and not save anything, not even the newly uploaded item.
- Manually uploading through the command line while an instance is already open will cause EasyImgur to crash after displaying the tooltip "Starting upload".
If I have the time to take a look at these bugs myself today I will, but I can't be sure of that. Could you take a look as well and see what's going on? I assume you've gotten all of these features to work on your own PC, but there may be issues with access rights that are causing these bugs (running as administrator doesn't change anything).
If you need any information from me I'd be happy to provide it.
Cheers,
Sure, I'll take a look when I get up (about to go to bed). I don't recall any of these bugs, maybe there's some sort of conflict between an old version and mine? The Open With dialog is controlled by a different part of the registry, so that's a little weird. You're on Win7 x64 Ultimate, right? Where does your history get saved? Mine saves to the desktop. The loading/saving problem might be conflict-related too, I didn't touch the history code except to add an "album" flag to HistoryItem
. I imagine a conflict could cause that file to get all screwy.
I am indeed on WIn7 x64 Ultimate. My history file is saved in the same location as the executable (as it should, although I might change this into a setting or save it in some other more proper location). I would hope that a conflict would be handled gracefully as in it would just not deserialize that "album" attribute and use the default value for that data type. But I perhaps something nasty is happening that makes it corrupt the file in some way.
I did indeed have some issues with the old version somehow constantly being started by the context menu options even though the registry clearly stated that it would open the new item (I initially saved the executables in different locations for testing). Once I overwrote my old executable with your new one however, it seemed as though it was working fine again (no idea what was happening there). Except of course for the fact that now the context menu will open the Open With dialog.
Huh. I wonder if that has something to do with it; but either way both my history file and log file are written to the desktop. I'll have to look into this a bit more tomorrow.
Alright, cool!
So, I was just going to look at this, and I realized EI didn't start on startup like it was supposed to. So, I looked at the Startup tab on my Task Manager (Win8's task manager is amazing), and it listed another, completely separate program instead. This was odd, so I right clicked and clicked "Properties". It opened up the Properties page for a file "Visual" in ~/Documents; I realized that the registry edit to start the program when it runs fails to correctly quote the filename of the program, causing Windows to stop at the first space. (My projects directory is ~/Documents/Visual Studio 2012/...
) This might be related to the other problems we were having, including the "Open With" dialog; that would be a probable cause. I thought I had correctly quoted all the registry entries I added but maybe not. Anyway, I'll see what I can see in the code.
Another way we could potentially reduce conflicts is to keep all the app's data in %appdata%/EasyImgur
instead of in the directory the file is running in. That way, no matter where you run it from, you still have the same history, logs, and authorization.
I've come across a bit of information regarding vagrant histories and logs. Let me quote to you a bit of the docs for File.Exists...
The path parameter is permitted to specify relative or absolute path information. Relative path information is interpreted as relative to the current working directory. To obtain the current working directory, see GetCurrentDirectory. (emphasis added)
Now, although I haven't actually done tests, I would wager that the working directory is modified by the context menu somehow; it's also possible the Open File dialog could modify it, but I'm not sure. This is the only explanation I can find for why I have a log and history on my desktop, in my Pictures, and basically anywhere I upload things from. In that vein, I'm going to modify the app to save into AppData instead of in the local working directory.
I also decided to go about doing a little more code cleanup; I don't recall what I started with but I ended up thinking "man, it'd be nice to use data binding to bind these controls to the options, that's what it's here for". And then I thought "man, I would bet anything that I could get the history item information to show up with data binding, that's what it's here for." This second thought was a terrible mistake, because although it's possible, the docs are a little less than helpful and I had a hell of a time figuring it out. Turns out half my problems were due to access from a thread that wasn't the main thread.
I did find a few bugs while I was at it, though, including a crash when Imgur returns non-JSON (Imgur went down while testing and responded with 502 and 504 for a while), and some other minor things I can no longer remember.
Alright, I've made all my changes and updated the dist folder again. Hopefully it'll work a little better for you this time!
Alright I just took a look at the changes and did some testing with the executable, looks like it's all working now!
As far as I'm concerned you're more then welcome to submit a pull request for this code now (yay :D).
I do have one minor remark but this is probably an Imgur API issue more so than our own code:
- When removing an album from Imgur through EI, Imgur will remove the album but keep the individual uploaded images.
As mentioned I feel that this is a peculiarity of the Imgur API and not our top concern. We might be able to implement some custom code to individually delete each image of the album before or after removing the album itself, but I feel that we don't really need to spend that much time on it (on the other hand, users may expect their images to be completely gone if they remove them through EI, so it might be good to implement this).
For now though, submit a pull request and I'll merge them, add some credit list feature to add you to and then publish a new release (I'm thinking this change is worthy of a new minor version (0.2.0) instead of just a patch) :)
Thanks a bunch for your work on this!
Sounds good! We can perhaps open a new issue or add a warning to the user; we could fairly easily remove the images by getting the Album data again.
I've just updated the wiki pages and published version 0.2.0 containing the new features: