Methods of manipulating/controling Windows Save As/Open File dialogs? - c#

I'm attempting to build a Windows program that would provide a folder-bookmarks-ish functionality, that would allow you to hit a hotkey and navigate the dialog you have open to the folder assigned. Of course, to do this I'd need to be able to manipulate Windows Explorer dialogs and such. Anyone able to point me in the right direction?
Thanks in advance. :)

Looks like you will need to use pinvoke and get into the nitty gritty but it can be done.
http://www.codeproject.com/Articles/19566/Extend-OpenFileDialog-and-SaveFileDialog-the-easy

I added a reference to SHDocVw Described Here: StackOverflow SHDocVw Example:
This will allow you to enumerate all of the open iexplore windows. (including the open Windows Explorer Dialogs; only issue is for certain special folders the full path is not displayed, but this can be remedied with a little creative code) You can then look through all of the open windows for URLs that have File:/// at the beginning, and then you can prompt the user to assign a hotkey for that specific open window. I've found that the SHDocVw does not reliably return all iexplore windows all the time, so this may not be the exact solution you are looking for. It would be easier to have the user type in the path of the Folder into a textbox on the Form, and then click a button that dynamically created a global hotkey and an event handler for the global hotkey keyboard hook. I attempted this a few different ways, and I ended up using a .cs file on codeproject Here: CodeProject: Low Level Global Keyboard Hook
Within the event Handler for the Global Keyboard Hook, you will have to use Process.Start(x.Process), where x is of a custom class type that has the specific hotkey associated with a specific Folders Location. That way you can look at the key that was pressed in the event handler, and start the associated Process (or in this case, open the specific folder) Each time the user adds an additional folder's location, a new object of custom type is created which includes the hotkey parameter and the folders Path (a user generated value). This custom object type (defined below) is then added to a global list of type "folderLocation" such that it can be accessed later when keys are actually pressed. The application added each folderLocation object to the flList as they were created.
I also defined a List of type Keys and then defined the first 12 objects in this list as F1...F12. For each folderlocation that is added, the next Key in the pre-defined list is added as the hotkey. (although you could also have the user define the hotkey)
In the end, the user presses F1, and the result is that the global key press event handler fires, and then the handler looks to see what button was pressed, compares it to the existing List of custom Types: folderLocation, and looks for a matching Key. Once Found, it then starts the associated folderLocation path using Process.Start.... Good Luck...
public class folderLocation
{
public string folderPath { get; set; }
public string folderName { get; set; }
public Keys hotKey { get; set; }
}

Related

What is the best way to store some data in VSTO addin?

I have developed one outlook add-in, that has to be On or Off.
to do that i have declared one static variable as shown below,
ThisAddIn.cs
public static bool isAddInOn = false;
RibbonButton.cs
private void btnRibbon_Click(object sender, RibbonControlEventArgs e)
{
if (ThisAddIn.isAddInOn )
{
ThisAddIn.isAddInOn = false;
btnRibbon.Label = "Disabled";
}
else
{
ThisAddIn.isAddInOn = true;
btnRibbon.Label = "Enabled";
}
}
It is working. But the static variable reset again when i close outlook and open it again. That means when i open outlook by default my add-in is in disabled state.
I just want to store that value at some place. so i can check that value when outlook reopened.
Scenario:
1) open outlook
2) Enable add-in by clicking on its logo (that is in ribbon)
3) now close the outlook
4) when i open outlook again it must enabled
so how can i achieve this ?
There are several techniques you can use to achieve this result. For sure your settings must be serialized to some storage/deserialized during startup of add-in.
One of possible solution could be to use registry for that (in this case probably HKCU (Current user, then it will be private for each user using your add-in) and no special permission is needed.
For storing variable:
public void StoreInRegistry(string keyName, string value)
{
RegistryKey rootKey = Registry.CurrentUser;
string registryPath = #"Software\YourCompanyName\YourAddInName";
using (RegistryKey rk = rootKey.CreateSubKey(registryPath))
{
rk.SetValue(keyName, value, RegistryValueKind.String);
}
}
For reading variable:
public string ReadFromRegistry(string keyName, string defaultValue)
{
RegistryKey rootKey = Registry.CurrentUser;
string registryPath = #"Software\YourCompanyName\YourAddInName";
using (RegistryKey rk = rootKey.OpenSubKey(registryPath, false))
{
if (rk == null)
{
return defaultValue;
}
var res = rk.GetValue(keyName, defaultValue);
if (res == null)
{
return defaultValue;
}
return res.ToString();
}
}
Such stored/retrieved variable should be used during add-in initialization to set your properties. So modification could look like:
ThisAddin.cs
public static bool isAddInOn = ReadFromRegistry("MySetting1", "0") == "1";
RibbonButton.cs
private void btnRibbon_Click(object sender, RibbonControlEventArgs e)
{
if (ThisAddIn.isAddInOn )
{
ThisAddIn.isAddInOn = false;
btnRibbon.Label = "Disabled";
}
else
{
ThisAddIn.isAddInOn = true;
btnRibbon.Label = "Enabled";
}
StoreInRegistry("MySetting1", ThisAddIn.isAddInOn ? "1" : "0");
}
Other options could serialization to file - some class with settings serialized to i.e. isolated storage, database (local or central) etc.
I've used several methods over the years to store configuration data for users.
Properties.Settings.Default.Properties, so writing in the application project properties. It's solid, never had an issue with it, for hundreds of users over several years.
Local config files in text, so writing to a known area for the user with fallback. In a stable environment, one can choose the home area for the user, and read/write to the local config file, which also makes it accessible to support if it breaks and needs manual changes. As a fallback, one could write to the local temp folder.
Registry is an option i have not used in this case, but it is likely to be a good choice.
Performance is likely a key concern considering it will impact the UI for users. Another concern is ease of use for the developer. For both, my choice would be setting it in the application's properties, where reading and writing is very simple and handled within code, and likely very fast.
Write
Properties.Settings.Default.PropertyName = propertValue;
Read
var propertValue = Properties.Settings.Default.PropertyName;
2018 UPDATED ANSWER
The recommended way to achieve this is now to use the already configured settings files in your project's properties. These files are auto-generated when create your project :
And open the following window when clicked :
You can access your settings value programmatically into Properties.Settings.Default.Properties anywhere.
The header bar at the top of the Settings page contains several controls:
Synchronize
Synchronize restores user-scoped settings that the application uses at run time or during debugging to their default values as defined at design time. To restore the data, remove run-time generated application-specific files from disk, not from project data.
Load Web Settings
Load Web Settings displays a Login dialog box that enables you to load settings for an authenticated user or for anonymous users. This button is enabled only when you've enabled client application services on the Services page and specified a Web settings service location.
View Code
For C# projects, the View Code button enables you to view the code in the Settings.cs file. This file defines the Settings class, which enables you to handle specific events on the Settings object. In languages other than Visual Basic, you must explicitly call the Save method of this wrapper class in order to persist the user settings. You usually do this in the Closing event handler of the main form. Following is an example of a call to the Save method:
C#
Properties.Settings.Default.Save();
For Visual Basic projects, the View Code button enables you to view the code in the Settings.vb file. This file defines the MySettings class, which enables you to handle specific events on the My.Settings object. For more information about accessing application settings by using the My.Settings object, see Access application settings.
For more information about accessing application settings, see Application settings for Windows Forms.
Access modifier
The Access modifier button specifies the access level of the Properties.Settings (in C#) or My.Settings (in Visual Basic) helper classes that Visual Studio generates in Settings.Designer.cs or Settings.Designer.vb.
For Visual C# projects, the access modifier can be Internal or Public.
For Visual Basic projects, the access modifier can be Friend or Public.
By default, the setting is Internal in C# and Friend in Visual Basic. When Visual Studio generates helper classes as Internal or Friend, executable (.exe) applications cannot access the resources and settings that you have added to class libraries (.dll files). If you have to share resources and settings from a class library, set the access modifier to Public.
For more information about the settings helper classes, see Manage application settings.
Settings grid
Settings Grid is used to configure application settings. This grid includes the following columns:
Name
Enter the name of the application setting in this field.
Type
Use the drop-down list to select a type for the setting. The most frequently used types appear in the drop-down list, for example, String, (Connection string), and System.Drawing.Font. You can choose another type by selecting Browse at the end of the list, and then selecting a type from the Select a Type dialog box. After you choose a type, it's added to the common types in the drop-down list (for the current solution only).
Scope
Select either Application or User.
Application-scoped settings, such as connection strings, are associated with the application. Users can't change application-scoped settings at run time.
User-scoped settings, such as system fonts, are intended to be used for user preferences. Users can change them at run time.
Value
The data or value associated with the application setting. For example, if the setting is a font, its value could be Verdana, 9.75pt, style=Bold.
Documentation link
Reading settings
Writing settings
Settings can be stored as a hidden (associated) item in a folder, such as the Inbox or the Calendar folder. For example, Outlook stores the list of categories as a hidden item in the Calendar folder. POP3 message ids are stored in a hidden item in the Inbox. The advantage of the hidden items is the roaming capability - Exchange mailbox user can see the data from any computer.
You can see the hidden items in OutlookSpy (I am its author) - click IMAPIFolder button, go to the "Associated Contents" tab.
Programmatically, such items can be accessed using MAPIFolder.GetStorage in the Outlook Object Model.

Register context menu verbs for specific file types

I'm registering extended verbs for all video file types on my system by doing something like this:
foreach (var ext in FileTypes.VideoTypes)
{
var progId = Registry.GetValue($#"HKEY_CLASSES_ROOT\.{ext}", null, null);
if (progId == null)
{
continue;
}
Registry.SetValue(
$#"HKEY_CURRENT_USER\SOFTWARE\Classes\{progId}\shell\dlsub",
null,
"Download subtitle");
Registry.SetValue(
$#"HKEY_CURRENT_USER\SOFTWARE\Classes\{progId}\shell\dlsub\command",
null,
#"""D:\myapp.exe"" ""%1""");
}
Resulting in something like this (mpeg_auto_file for mkv):
[HKEY_CLASSES_ROOT\mpg_auto_file\shell\dlsub]
#="Download subtitle"
[HKEY_CLASSES_ROOT\mpg_auto_file\shell\dlsub\command]
#="\"D:\\myapp.exe\" \"%1\""
and mplayerc.mp4 for mp4:
[HKEY_CLASSES_ROOT\mplayerc.mp4\shell\dlsub]
#="Download subtitle"
[HKEY_CLASSES_ROOT\mplayerc.mp4\shell\dlsub\command]
#="\"D:\\myapp.exe\" \"%1\""
The problem is that the registered verb shows up for mkv (and a couple of other filetypes), but the context menus for the other file types (like mp4) are unaffected. Adding my verb to HKEY_CLASSES_ROOT\*\shell does work for these filetypes, but this is obviously not what I want!
Any ideas on the difference between these filetypes? Perhaps it has something to do with the registered ProgID (all mplayerc types do not seem to work...).
The default value (aka ProgId) that can be found for specific file extension doesn't always point to correct class (the one that will affect menu entries). During my tests, even on fresh copy of Windows 10 - WMP11.AssocFile.AVI is a ProgId for .avi file extension, however when added entry in WMP11.AssocFile.AVI\Shell\ (either HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER) it didn't affect menu at all. I was about to give up but then i found that HKEY_CLASSES_ROOT\.avi\OpenWithProgids store few more values, including WMP11.AssocFile.AVI but also other that starts with word App, i.e. AppX6eg8h5sxqq90pv53845wmnbewywdqq5h. By editing Shell\ for that one, i was able to add menu entry. The downside was - it also affected other file extensions that are somehow linked with AppX6eg8h5sxqq90pv53845wmnbewywdqq5h. I couldn't accept solution that would force me to iterate through all classes found in OpenWithProgids, plus my menu entry had to be shown only for very specific file extensions and not the other. I decided to go with HKEY_CLASSES_ROOT\*\shellex and DLLs that create menu entry dynamically, based on clicked file type (check for extension).

c# customizing controls on a save dialog -- how to disable parent folder button?

I am working from the sample project here: http://www.codeproject.com/Articles/8086/Extending-the-save-file-dialog-class-in-NET
I have hidden the address/location bar at the top and made other modifications but I can't for the life of me manage to disable the button that lets you go up to the parent folder. Ist is in the ToolbarWindow32 class which is the problem. This is what I have at the moment but it is not working:
int parentFolderWindow = GetDlgItem(parent, 0x440);
//Doesn't work
//ShowWindow((IntPtr)parentFolderWindow, SW_HIDE);
//40961 gathered from Spy++ watching messages when clicking on the control
// doesn't work
//SendMessage(parentFolderWindow, TB_ENABLEBUTTON, 40961, 0);
// doesn't work
//SendMessage(parentFolderWindow, TB_SETSTATE, 40961, 0);
//Comes back as '{static}', am I working with the wrong control maybe?
GetClassName((IntPtr)parentFolderWindow, lpClassName, (int)nLength);
Alternatively, if they do use the parent folder button and go where I don't want them to, I'm able to look at the new directory they land in, is there a way I can force the navigation to go back?
Edit: Added screenshot
//Comes back as '{static}', am I working with the wrong control maybe?
You know you are using the wrong control, you expected to see "ToolbarWindow32" back. A very significant problem, a common one for Codeproject.com code, is that this code cannot work anymore as posted. Windows has changed too much since 2004. Vista was the first version since then that added a completely new set of shell dialogs, they are based on IFileDialog. Much improved over its predecessor, in particular customizing the dialog is a lot cleaner through the IFileDialogCustomize interface. Not actually what you want to do, and customizations do not include tinkering with the navigation bar.
The IFileDialogEvents interface delivers events, the one you are looking for is the OnFolderChanging event. Designed to stop the user from navigating away from the current folder, the thing you really want to do.
While this looks good on paper, I should caution you about actually trying to use these interfaces. A common problem with anything related to the Windows shell is that they only made it easy to use from C++. The COM interfaces are the "unfriendly" kind, interfaces based on IUnknown without a type library you can use the easily add a reference to your C# or VB.NET project. Microsoft published the "Vista bridge" to make these interfaces usable from C# as well, it looks like this. Yes, yuck. Double yuck when you discover you have to do this twice, this only works on later Windows versions and there's a strong hint that you are trying to do this on XP (judging from the control ID you found).
This is simply not something you want to have to support. Since the alternative is so simple, use the supported .NET FileOk event instead. A Winforms example:
private void SaveButton_Click(object sender, EventArgs e) {
string requiredDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
using (var dlg = new SaveFileDialog()) {
dlg.InitialDirectory = requiredDir;
dlg.FileOk += (s, cea) => {
string selectedDir = System.IO.Path.GetDirectoryName(dlg.FileName);
if (string.Compare(requiredDir, selectedDir, StringComparison.OrdinalIgnoreCase) != 0) {
string msg = string.Format("Sorry, you cannot save to this directory.\r\nPlease select '{0}' instead", requiredDir);
MessageBox.Show(msg, "Invalid folder selection");
cea.Cancel = true;
}
};
if (dlg.ShowDialog() == DialogResult.OK) {
// etc...
}
}
}
I don't this is going to work. Even if you disable the button they can type ..\ and click save and it will take them up one level. You can't exactly disable the file name text box and maintain the functionality of the dialog.
You'd be better off either using the FolderBrowserDialog and setting it's RootFolder property and asking the user to type the filename in or auto generating it.
If the folder you are wanting to restrict the users to isn't an Environment.SpecialFolder Then you'll need to do some work to make the call to SHBrowseForFolder Manually using ILCreateFromPath to get a PIDLIST_ABSOLUTE for your path to pass to the BROWSEINFO.pidlRoot
You can reflect FolderBrowserDialog.RunDialog to see how to make that call.
Since you want such custom behaviors instead of developing low level code (that is likely yo break in the next versions of windows) you can try to develop your file picker form.
Basically it is a simple treeview + list view. Microsoft has a walk-through .
It will take you half a day but once you have your custom form you can define all behaviors you need without tricks and limits.

Application not adding to startup + startup name is always "visual"

I tried adding my application to startup, if the user chooses that option. I made this code based on multiple answers on stackoverflow:
using Microsoft.Win32;
namespace Clientding
{
class Program
{
static void Main()
{
RegistryKey rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
if (rkApp.GetValue("ItsAnApp") == null)
{
MessageBox.Show("This msgbox is to confirm that the code is being run!");
rkApp.SetValue("ItsAnApp", Application.ExecutablePath.ToString());
}
}
}
}
This does add the application to my startup list, but only with the name "Visual" which should somewhere be caused by visual express.
Also, the application doesn't actually show up on startup.
INFO:
I am running windows 8.
When adding to startup, I believe I hear the sound of a connecting device, then after 2 seconds, the disconnecting sound. I am 99% sure that this has to do with the startup program.
Any ideas why this code doesn't work?
Check the restriction that denies users to run application on the Windows start:
To restrict users from running specific Windows programs by editing the registry, follow these steps:
Click Start, and then click Run.
In the Open box, type regedit, and then click OK.
Create a DWORD value named DisallowRun. To do so:
Locate and then click the following registry key:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion
\Policies\Explorer
On the Edit menu, point to New, and then click DWORD Value.
Type disallowrun, and then press ENTER.
Double-click the DisallowRun value that you created in the previous
step.
Type 1 in the Value data box, and then click OK.
Create a new
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion
\Policies\Explorer\DisallowRun subkey. To do so:
Right-click the following registry key, point to New, and then click
Key: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion
\Policies\Explorer Type disallowrun, and then press ENTER.
For each program that you want to prevent users from running, create
a new string value in the DisallowRun subkey that you created in step
Use consecutive numbers to name the string values (starting with 1), and use the executable file name for the program as the data for
the string value.
For example, if you want to restrict users from running Microsoft Internet Explorer:
Right-click the following registry key, point to New, and then click String Value:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion \Policies\Explorer\DisallowRun
Type 1, and then press ENTER.
Double-click the 1 value that you created in the previous step.
Type iexplore.exe in the Value data box, and then click OK.
Quit Registry Editor, and then restart the computer.

How to get the (.lnk) shortcut filepath in a program which started by the shortcut?

I have a c# program which open *.postfix file.
If a user runs a (.lnk)shortcut which points to my type of file, my program will open the target.
So, how could my program know it is started by a (.lnk)shortcut (and get it's file path)?
In some circumstances,i need to replace the .lnk file.
Thanks!
Edited
First, thanks to guys who answered my question.
By following #Anders answer, i find out my problem lays here.
I made some changes to windows registry, so browser knows to throw customized protocol string to certain program.
some thing like this..
[InternetShortcut]
URL=myProtocol://abcdefg.....
That's maybe why i lost lpTitle. :(
I'm going to try this way:
Whenever my program invoked, of course fed with %1, program checks current opened explorer(Window), and try to get it's current path with IWebBrowserApp. With that path and desktop of course, scan and analyze *.lnk to determine which one to replace.
I think this will probably work, but not be sure. I will try.
continued
In native code you can call GetStartupInfo, if the STARTF_TITLEISLINKNAME bit is set in STARTUPINFO.dwFlags then the path to the .lnk is in STARTUPINFO.lpTitle. I don't know if there is a .NET way to get this info, you probably have to P/Invoke...
You don't. There's no way to do it. End of story.
So this has been brought to my attention due to a recent downvote. There's an accepted answer showing an idea that gets the path to the launching shortcut most of the time. However my answer is to the whole. OP wants the link to the shortcut so he can change it. That is what can't be done most of the time.
Most likely case is the shortcut file exists in the start menu but is unwritable. However other cases involve the shortcut coming from another launching application that didn't even read it from a disk but from a database (I've seen a lot of corporate level restricted application launch tools). I also have a program that launches programs from shortcuts not via IShellLink but by parsing the .lnk file (because it must not start COM for reasons) and launching the program contained. It doesn't pass STARTF_TITLEISLINKNAME because it's passing an actual title.
If you're using Visual Studio Setup Project to build an installer and do the file type association, you should follow these instructions http://www.dreamincode.net/forums/topic/58005-file-associations-in-visual-studio/
Open up your solution in Visual studio.
Add a Setup Project to your solution by file , add project,New project, Setup & Deployment projects,Setup project
Right-click on your setup project in the "Solution Explorer" window,Select view,then select file types.
you'll see the "file types" window displayed in Visual studio.At the top of the window will be "File types on target machine"
Right-click on "File types on target machine".the menu will pop up with Add "file type" Click on this.
you'll see "New document Type#1" added,and "&open"underneath it.
The "new document type#1" can be anything you want - change it to something descriptive.although the user never sees this,never use something common- be as unique as possible,Because you can overlay current file associations without even realizing it.For example,you might think"pngfile" might be a useful name- but using that will now send all"*.png" files to your application,instead of to an image viewer.A good practice maybe "YourCompantName.Filetype",where your company name is your name of your company's name, and "Filetype" is a descriptive text of your file.
In the "properties" window for your new type,you will need to change a few properties.:
Command:Change to the application that you want to run.If you click on the "..." and you will proberly want to locate and use the "primary Output..." File
Description: This is the description of the file type(if it doesn't describe it's self"
Extensions:This your list of extensions for you chosen Program.Separate each one with a ","
Icon:This will associate the icon with your file type,This shows up in the window explorer.
Now we move to that "&open ".This is an action that is available if your right-click on the file.The default action("&Open" is currently set as the default) is what happens when you double click on the file.Right click on your "New document type#1" to add actions,but for the moment,lets define our "&open" action
Click on "&Open".You will see in the properties window "Name","Arguments","Verbs". Verb is hidden from the user,but is the key that is stored in the registry.Leave it same as the name,But without the "&".The default for"Arguments" is "%1",Which means to pass the full path and filename to your application.You can add other stuff here as well,if you need to pass flags to your application to do special stuff.All this infomaton is getting passed to your application on the command line,so you'll need to be familiar with the "Environment.CommandLine" object.
If you need to set a different action as your default,just right click on the action and "set as default"
Basically, you'll pass the file path as an argument to your program. Then if it's a console application or Windows Forms , you should check the arguments in Program.Main
static void Main(string[] args)
{
//if file association done with Arguments %1 as per forum post above
//you file path should be in args[0]
string filePath = null;
if(args != null && args.Length > 0)
filePath = args[0];
}
For a WPF application you'll need to handle that in the StartUp event for your Application
void App_Startup(object sender, StartupEventArgs e)
{
string filePath = null;
if ((e.Args != null) && (e.Args.Length > 0))
{
filePath = e.Args[0];
}
}

Categories

Resources