How to get command line of windows "open with .." function? - c++

I previously asked a question that is about how to get windows "open with.." application list.
Here's a link to that question.
We can use SHAssocEnumHandlers interface to get the file association with specific file extension, ex .png
Then use IAssocHandler and can retrieves the full path and file name of the executable file associated with the file type(.png). ex:['Paint': 'C:\\Windows\\system32\\mspaint.exe', ...]
But I want to get the command line of executing mspaint.exe with a given image.
Like this~ "%systemroot%\system32\mspaint.exe" "%1"
Is there another msdn api could help us to get the "open with.." command?
I think it should have, since windows XP already have this ability.

Use AssocQueryString(..., ASSOCSTR_COMMAND, ...);
Example:
TCHAR commandline[1024];
DWORD size = ARRAYSIZE(commandline);
AssocQueryString(0, ASSOCSTR_COMMAND, _T(".txt"), 0, commandline, &size);

There is the SHOpenWithDialog function.
Link to SHOpenWithDialog on MSDN
However, you can't use this to retrieve the selected program. You can only use it to invoke the "Open With" behaviour and eventually open the file (if OAIF_EXEC is set). If that's all you're interested in, then try it out:
#include <windows.h>
#include <Shlobj.h>
#pragma comment(lib, "Shell32.lib")
int main()
{
OPENASINFO info = { 0 };
info.pcszFile = L"C:\\Temp\\SomeFile.png";
info.pcszClass = NULL;
info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC;
SHOpenWithDialog(NULL, &info);
return 0;
}

Related

How to refer to current user folder in c++? [duplicate]

As you all know, the appdata folder is this
C:\Users\*Username*\AppData\Roaming
on windows 7
Since my application will be deployed on all kinds of Windows OSes i need to be able to get the folder 100% percent of the time.
The question is how do you do it in C++? Since i don't know the the exact Windows OS it could be XP,Vista or 7 and most importantly i don't know what the Username is.
For maximum compatibility with all versions of Windows, you can use the SHGetFolderPath function.
It requires that you specify the CSIDL value for the folder whose path you want to retrieve. For the application data folder, that would be CSIDL_APPDATA.
On Windows Vista and later, you should use the SHGetKnownFolderPath function instead, which requires that you specify the folder's KNOWNFOLDERID value. Again, for the application data folder, the appropriate value is FOLDERID_RoamingAppData.
To use either of these functions from your C++ application, you'll need to include shlobj.h.
You can try the following:
char* appdata = getenv("APPDATA");
This code reads the environment variable APPDATA (you can also see it when you type SET in a command window). It is set by Windows when your system starts.
It will return the path of the user's appdata as an absolute path, including Username and taking into account whichever OS version they're using.
Perhaps fellow Googlers might find it interesting to have a look at std::filesystem. For instance, let's assume the default temp directory location and AppData directory structure in Windows 10:
#include <filesystem>
auto path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path();
path /= "Roaming";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
In the case of OP, I am assuming this doesn't solve the problem. I do want to raise a word of caution against doing the above in a situation that requires a 100% robust implementation, as system configurations can easily change and break the above.
But perhaps new visitors to the question might find std::filesystem useful. Chances are, you're going to want to manipulate the items in the directory if you're looking for it, and for this, std::filesystem can be your friend.
If someone is looking for a simple implementation, here's mine:
#include <windows.h>
#include <shlobj.h>
#include <filesystem>
#include <iostream>
int main(void)
{
std::filesystem::path path;
PWSTR path_tmp;
/* Attempt to get user's AppData folder
*
* Microsoft Docs:
* https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
* https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
*/
auto get_folder_path_ret = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &path_tmp);
/* Error check */
if (get_folder_path_ret != S_OK) {
CoTaskMemFree(path_tmp);
return 1;
}
/* Convert the Windows path type to a C++ path */
path = path_tmp;
/* Free memory :) */
CoTaskMemFree(path_tmp);
std::cout << path << std::endl;
return 0;
}
Use this Code to reads the environment variable "APPDATA"
Include stdio.h file in beginning
char *pValue;
size_t len;
errno_t err = _dupenv_s(&pValue, &len, "APPDATA");
Here is a simple implementation for old C++ versions :
#include <shlobj.h>
// ...
wchar_t* localAppDataFolder;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
std::cerr << "problem getting appdata folder" << std::endl;
}
else std::wcout << L"folder found: " << localAppDataFolder << std::endl;

Open .chm file at specific page/topic using command line arguments

I am attempting to open a .chm file(A windows help file) at a specific page/topic by using a system call in C++.
I can successfully open the .chm file to the start page through the following code, but how can I open a .chm file to a specific page/topic inside the help file?
system("start c:/help/myhelp.chm");
PS: I know system is evil/discouraged but the system part is not really relevant its the command line arguments I pass with the .chm file(that will specify what page I want to open) that I am trying to determine.
Ok the arguments are like so:
system(" /Q /E:ON /C HH.EXE ms-its:myChm.chm::myPageName.htm");
There is an API in the Windows SDK called HtmlHelp in the HtmlHelp.h file. You can call like so:
HtmlHelp(GetDesktopWindow(), L"C:\\helpfile\\::/helptopic.html", HH_DISPLAY_TOPIC, NULL);
The Microsoft Docs - HtmlHelpA function provides more information about the function. HtmlHelp() will normally resolve to HtmlHelpA() or HtmlHelpW() depending on whether Unicode compiler option is set or not.
See as well Microsoft Docs - HTML Help API Overview.
Another option - use ShellExecute. The Microsoft help is not easy to use. This approach is much easier and in line with your question. Here is a quick routine to open a help file and pass an ID number. I have just set up some simple char’s so you can see what is going on:
void DisplayHelpTopic(int Topic)
{
// The .chm file usually has the same name as the application - if you don’t want to hardcode it...
char *CmndLine = GetCommandLine(); // Gets the command the program started with.
char Dir[255];
GetCurrentDirectory (255, Dir);
char str1[75] = "\0"; // Work string
strncat(str1, CmndLine, (strstr(CmndLine, ".exe") - CmndLine)); // Pull out the first parameter in the command line (should be the executable name) w/out the .exe
char AppName[50] = "\0";
strcpy(AppName, strrchr(str1, '\\')); // Get just the name of the executable, keeping the '\' in front for later when it is appended to the directory
char parms[300];
// Build the parameter string which includes the topic number and the fully qualified .chm application name
sprintf(parms,_T("-mapid %d ms-its:%s%s.chm"), Topic, Dir, AppName);
// Shell out, using My Window handle, specifying the Microsoft help utility, hh.exe, as the 'noun' and passing the parameter string we build above
// NOTE: The full command string will look like this:
// hh.exe -mapid 0 ms-its:C:\\Programs\\Application\\HelpFile.chm
HINSTANCE retval = ShellExecute(MyHndl, _T("open"), _T("hh.exe"), parms, NULL, SW_SHOW);
}
The topics are numbered within your .chm file. I set up a #define for each topic so if I had to change the .chm file I could just change the include file to match and not have to worry about searching through the code for hardcoded values.

How to open a folder in %appdata% with C++?

As you all know, the appdata folder is this
C:\Users\*Username*\AppData\Roaming
on windows 7
Since my application will be deployed on all kinds of Windows OSes i need to be able to get the folder 100% percent of the time.
The question is how do you do it in C++? Since i don't know the the exact Windows OS it could be XP,Vista or 7 and most importantly i don't know what the Username is.
For maximum compatibility with all versions of Windows, you can use the SHGetFolderPath function.
It requires that you specify the CSIDL value for the folder whose path you want to retrieve. For the application data folder, that would be CSIDL_APPDATA.
On Windows Vista and later, you should use the SHGetKnownFolderPath function instead, which requires that you specify the folder's KNOWNFOLDERID value. Again, for the application data folder, the appropriate value is FOLDERID_RoamingAppData.
To use either of these functions from your C++ application, you'll need to include shlobj.h.
You can try the following:
char* appdata = getenv("APPDATA");
This code reads the environment variable APPDATA (you can also see it when you type SET in a command window). It is set by Windows when your system starts.
It will return the path of the user's appdata as an absolute path, including Username and taking into account whichever OS version they're using.
Perhaps fellow Googlers might find it interesting to have a look at std::filesystem. For instance, let's assume the default temp directory location and AppData directory structure in Windows 10:
#include <filesystem>
auto path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path();
path /= "Roaming";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
In the case of OP, I am assuming this doesn't solve the problem. I do want to raise a word of caution against doing the above in a situation that requires a 100% robust implementation, as system configurations can easily change and break the above.
But perhaps new visitors to the question might find std::filesystem useful. Chances are, you're going to want to manipulate the items in the directory if you're looking for it, and for this, std::filesystem can be your friend.
If someone is looking for a simple implementation, here's mine:
#include <windows.h>
#include <shlobj.h>
#include <filesystem>
#include <iostream>
int main(void)
{
std::filesystem::path path;
PWSTR path_tmp;
/* Attempt to get user's AppData folder
*
* Microsoft Docs:
* https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
* https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
*/
auto get_folder_path_ret = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &path_tmp);
/* Error check */
if (get_folder_path_ret != S_OK) {
CoTaskMemFree(path_tmp);
return 1;
}
/* Convert the Windows path type to a C++ path */
path = path_tmp;
/* Free memory :) */
CoTaskMemFree(path_tmp);
std::cout << path << std::endl;
return 0;
}
Use this Code to reads the environment variable "APPDATA"
Include stdio.h file in beginning
char *pValue;
size_t len;
errno_t err = _dupenv_s(&pValue, &len, "APPDATA");
Here is a simple implementation for old C++ versions :
#include <shlobj.h>
// ...
wchar_t* localAppDataFolder;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
std::cerr << "problem getting appdata folder" << std::endl;
}
else std::wcout << L"folder found: " << localAppDataFolder << std::endl;

Problem setting desktop wallpaper using SystemParametersInfo Function

I am just learning C++ and am trying to write a small program to change the desktop wallpaper. Using the documentation here, I wrote this program:
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "user32.lib")
void main(){
BOOL success = SystemParametersInfo(
SPI_SETDESKWALLPAPER, //iuAction
0, //uiParam
"C:\\test.jpg", //pvParam
SPIF_SENDCHANGE //fWinIni
);
if (success){
printf("Success!\n");
}else
printf("Failure =(\n");
}
The program always fails when I try to specify a file path for pvParam. It will correctly clear the wallpaper if I set pvParam to "". What am I doing wrong?
Thanks
-Abhorsen
It addition to Dennis' comment about JPEG files, it is also important whether or not you compile with UNICODE in effect. If you do then you'll have to specify the file as L"C:\test.jpg". Note the L in front of the string, that makes it a wide string. Or use SystemParametersInfoA(), note the A (but it's archaic).
Depending on the OS version, pvParam might not work.
If you are using Windows XP coupled with a JPEG file you are trying to assign as a wallpaper, notice the comment in the docs:
Windows Server 2003 and Windows
XP/2000: The pvParam parameter cannot
specify a .jpg file.

Opening a document programmatically in C++

I have a console program written in C++. Now I want to open a manual document(in .txt or .pdf) everytime a user of the program types 'manual' in console. How can I do this? Any links to a tutorial would be helpful.Thanks
Try to compile this code (Open.cpp) to Open.exe
Then, you can execute it with (for example) these parameters :
Open "C:\your file.doc"
Open "C:\your file.exe"
Open notepad
#include "windows.h"
int main(int argc, char *argv[])
{
ShellExecute(GetDesktopWindow(), "open", argv[1], NULL, NULL, SW_SHOWNORMAL);
}
Explanation of the program :
You should first include windows
library (windows.h) to get
ShellExecute and GetDesktopWindow function.
ShellExecute is the function to execute the file with parameter
argv[1] that is path to the file to be opened
Another option for lpOperation
arguments instead of "open" is
NULL. "explore" and "find" are
also the options but they are not
for opening a file.
SW_SHOWNORMAL is the constant to
show the program in normal mode (not
minimize or maximize)
Assuming you're on Windows, you're looking for the ShellExecute function. (Use the "open" verb)
In standard, platform independent, C and C++ you can use the system function to pass the name of an application to open your files.
For example, using Windows:
const char text_filename[] = "example.txt";
const char text_application[] = "notepad.exe";
std::string system_str;
system_str = text_application;
system_str += " ";
system_str += text_filename;
// Execute the application
system(system_str.c_str());
The text you send to the system function is platform specific.
In Managed C++ is its very easy
System::Diagnostics::Process::Start(path);
done !