ShellExecuteA but I don't know the extension - c++

std::string str1 = "C:\\Users\\user\\Desktop\\Notes";
ShellExecuteA(NULL, "open", str1.c_str(), NULL, NULL, SW_SHOWNORMAL);
The code isn't worth much.
I have an .mp3 in a DJ program. In one of the tags, it holds the filename of the associated video. All the videos are .mpeg.
With the DJ software's API, I can grab the tag. I know where the video files are (all in one folder). I can open the video with ShellExecuteA(), because whilst the tag might not contain the full filename+extension, I know the extension.
Now the problem - I want to start using .avi or .h254 or whatever. I don't know the extension anymore, and ShellExecuteA() needs an extension.
What can I do?
My guesses are:
If ShellExecuteA() returns an error (not sure it does), if it does I could brute-force it; is it .mpeg? Is it .avi? Is it .h264? etc...
Do a search in the known location with the filename missing the extension, and then grab the full filename with whatever it finds (all file names are unique, even excluding the extension).
I know I could add the extension in the .mp3 tag, but there are reasons why I'd rather not do that.

ShellExecute does not need extension. It needs the exact file name. If extensions are hidden in Windows Explorer - make them visible. If you don't know the extension for other reason, use FindFirst with wildcard * (Notes*) to find the full name.

FindFirstA got me the results I wanted
obviously there are more direct ways than sstream but other stuff is going on that isn't important to the actual problem
std::stringstream sstrm1, sstrm2;
sstrm1 << "C:\\Users\\user\\Desktop\\";
sstrm2 << sstrm1.str() << "Notes.*";
HANDLE hFind;
WIN32_FIND_DATAA data;
hFind = FindFirstFileA(sstrm2.str().c_str(), &data);
sstrm1 << data.cFileName;
ShellExecuteA(NULL, "open", sstrm1.str().c_str(), NULL, NULL, SW_SHOWNORMAL);

Related

When my program runs automatically at startup, having trouble append text to file. Why?

I made a program that runs automatically at startup, but my program does not perform the task of append text to the file when it runs automatically.
Here example code:
#include <Windows.h>
#include <fstream>
#include <iostream>
void AutoRun() {
LONG key;
std::string FP;
char re[MAX_PATH];
FP = std::string(re, GetModuleFileNameA(NULL, re, MAX_PATH));
HKEY hkey;
key = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\Currentversion\\Run", 0, KEY_WRITE, &hkey);
if (key == ERROR_SUCCESS)
{
std::cout << "paketi yüklüyoruzzz";
key = RegSetValueExA(hkey, "testzort", 0, REG_SZ, (BYTE*)FP.c_str(), strlen(FP.c_str()));
RegCloseKey(hkey);
}
else {
std::cout << "Maga mapket müklenemedi:(: " << key;
}
}
int main(){
// open output file in append mode
const char* output_filename = "testing.log";
std::cout << "Logging output to " << output_filename << std::endl;
output_file.open(output_filename, std::ios_base::app);
AutoRun();
output_file << "zozozort\n";
}
When I restart my computer after execute this code, not add my file like:
zozozort
zozozort
What is the problem ?
NOTE: For the first time to run the regedit api, I ran the program with administrator mode when starting
First off, consider using HKEY_CURRENT_USER instead of HKEY_LOCAL_MACHINE (unless you really want all users of your machine running your app). And consider using KEY_SET_VALUE instead of KEY_WRITE (which includes rights you don't need in this code). This will reduce the need for your code to run as an admin when setting up the auto-run.
In any case, when calling RegSetValueEx(), you are setting the data size to strlen(FP.c_str()), which is wrong, as RegSetValueEx() requires the null terminator to be included in the data size:
If the data is of type REG_SZ, REG_EXPAND_SZ, or REG_MULTI_SZ, cbData must include the size of the terminating null character or characters.
So, use strlen(FP.c_str())+1 instead, or better FP.size()+1.
That being said, your app is opening the text file using a relative path, so its location is relative to the app's current working directory, which you don't know what it is when your app is started (you can use GetCurrentDirectory() to determine that). Just because the text file is in the same folder as your app doesn't mean the working directory points to your app's folder. Always use absolute paths when creating/opening files.
If you were using CreateFile() instead of (o)fstream (BTW, where is your output_file variable declared?) to create/open the file, then you could use GetFinalPathNameByHandle() to determine its actual full path, so you can see if it is what you are expecting.
If your really want to create/open the text file in your app's folder, you already know how to get the app's full file path from GetModuleFileName(), so simply replace the filename portion after the last '\' character with your text file's name, and then use that full path to create/open the file. Just make sure your app is not running in a folder that denies write access to non-admins, such as Program Files.
You really should be writing the text file into a folder that is guaranteed to be accessible to the calling user (preferably within their own profile), instead of in the app's folder. For instance, get a user-accessible folder path via either:
SHGetFolderPath(), specifying something like CSIDL_(LOCAL_|COMMON_)APPDATA, CSIDL_DESKTOPDIRECTORY, CSIDL_MYDOCUMENTS, etc.
SHGetKnownFolderPath(), specifying something like FOLDERID_(Roaming|Local)AppData, FOLDERID_ProgramData, FOLDERID_Desktop, FOLDERID_Documents, etc.
Then, create your own subfolder underneath that folder, and create the file inside that subfolder.

Getting the value inside of a file and putting the value inside of a variable using CreateFile

I'm trying to make a code that will read multiple URLs and put each value of the URLs in a variables using windows API ReadFile.
For example
Variables
string urlone;
string urltwo;
Inside of url.txt
www.stackoverflow.com
www.reddit.com
My concern is that is it possible to get the values and put it in a variables using ReadFile or any windows API functions?
from the example above, what will happen is stackoverflow.com will become the value of variable urlone same with the next URL which is reddit.com will become the value of variable urltwo.
I know that using fstream will help me to solve this but I just want to know if there are any other way like using windows API.
Note: I tried to use ReadFile and it gets all the URLs and put the value inside of 1 variable.
//reading url.txt
openFile = CreateFile(L"C:\\Documents and Settings\\Administrator\\My Documents\\url.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//reading the content, on this code, all the URLs will be inside of strVal
rewriFile = ReadFile(openFile, strVal, fSize, &dwNoBytetoRead, NULL);
cout << "The URL is " << strVal << endl;
Any advice? Thanks in advance!
Cheers!
Yes, you can solve it using ReadFile if you want to. Using ReadFile a simpel approach could be:
read entire file using ReadFile into a buffer (I suggest to use
std::string and resize)
use whatever to parse the string, for simpel parsing,
std::istringstream can be used
Coincidently, if the std::istringstream is good enough for parsing your format (im 99% sure that it is), then that may be the case where the std stream classes really shines since then you could use a std::ifstream to do both !

How can I get file's directory by just typing its name. (C++)

Is there a way to globally search for a file name + .exe or whatever, and then get it's directory. That means, let's say i have a file called "food.txt" in my deep C drive. I want to get it's directory, no matter where i put it in my pc by just typing it's name + the .txt.
Isn't there a like a tag or something for "global directory". I mean like "\global\food.txt"? Thank you!
So the thing that i'm trying to do is: When I type an existing file name with the .txt, i want to open this file, no matter where in my pc it is located at. Also i won't have two duplicates with the same name, don't worry :D
Code:
void open_program(string target_name) {
cout << "Opening..." << endl;
string final_directory = target_name;
ShellExecuteA(NULL, "open", final_directory.c_str(), NULL, NULL, SW_SHOWNORMAL);
cout << final_directory;
}
Use WinAPI functions FindFirstFile and FindNextFile for this task.
https://msdn.microsoft.com/ru-ru/library/windows/desktop/aa364418(v=vs.85).aspx
https://msdn.microsoft.com/ru-ru/library/windows/desktop/aa364428(v=vs.85).aspx

C++ folder wont delete until I close program

In a game I'm making, folders with text files inside represent world saves, In the load menu of this game I want to have an option to delete a save. I'm using currently this code to try to delete the saves:
hFind = FindFirstFile((dir+"/*").c_str(), &FindFileData);
if (hFind){
do{
string s = FindFileData.cFileName;
if(s.find('.')){//prevents prossesing of "." and ".."
DeleteFile((dir+"/"+s).c_str());
}
}while(FindNextFile(hFind,&FindFileData));
CloseHandle(hFind);
}
rmdir(dir.c_str());
The only things in these folders are 3 text files so this code should be sufficient, however it isn't. What happens is all the file in the directory are deleted but not the folder, and if I try to delete this folder manually, or edit it in any way while the program is running, windows denies me access. But as soon as I close the game the folder is deleted.
I know the files inside are deleted because I tried the above code with out "rmdir(dir.c_str());" and opened the folder and all the files were gone, also with the above code if I "Delete" the save and then try to load it I there is no world and no inventory, indicating the files have been deleted.
I've tried it with removeDirectory and the same thing happens, It also says it was deleted successfully without any errors.
Why does this happen? How can I avoid this, and get it to work properly?
Any help would be greatly appreciated.
The problem was fixxed with the following code:
hFind = FindFirstFile((dir+"/*").c_str(), &FindFileData);
if (hFind){
do{
string s = FindFileData.cFileName;
if(s.find('.')){//prevents prossesing of "." and ".."
DeleteFile((dir+"/"+s).c_str());
}
}while(FindNextFile(hFind,&FindFileData));
CloseHandle(hFind);
}
findClose(hFind);
rmdir(dir.c_str());
According to the RemoveDirectory documentation:
RemoveDirectory function marks a directory for deletion on close. Therefore, the directory is not removed until the last handle to the directory is closed.
Probably your program has the directory as its current working directory, or perhaps otherwise still has a handle to it open.
In windows, rmdir is a comparability function that calls the native windows functions, so it will behave the same.
The root problem is that the code called CloseHandle instead of FindClose on the handle returned by FindFirstFile.
But the code has several more bugs. In the interest of helping future visitors here, this is the corrected code.
HANDLE hFind = FindFirstFile((dir + "\\*").c_str(), &FindFileData); // See 1 below
if (hFind != INVALID_HANDLE_VALUE) { // 2
do {
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { // 3
const std::string s = dir + "\\" + FindFileData.cFileName;
DeleteFile(s.c_str());
}
} while (FindNextFile(hFind, &FindFileData));
// 4
FindClose(hFind); // 5
}
RemoveDirectory(dir.c_str()); // 6
Windows paths use \ rather than / as separators. Many of the APIs will accept either, but eventually you'll encounter one that doesn't, so it's best to use the correct one consistently.
FindFirstFile returns INVALID_HANDLE_VALUE (not NULL) upon failure. Since INVALID_HANDLE_VALUE is non-zero, you cannot simply test if (hFile) { ... }.
The API enumerates files and directories. The old code was trying to filter out the . and .. directories incorrectly, which could have caused it to skip some files and to attempt to use DeleteFile on other directories. It's simpler (and easier-to-understand) to skip all directories.
Don't call CloseHandle on the handle returned by FindFirstFile.
Do call FindClose on the handle returned by FindFirstFile, but do so only in the case you got a valid handle from FindFirstFile.
As long as you're using Windows-specific APIs, you may as well use them consistently and not mix with library wrappers like rmdir. The library wrappers sometimes introduce surprising limitations or behavior, though I think rmdir would work correctly in this instance.
This still leaves a significant problem: It doesn't handle Unicode in the paths (and it requires you to compile for ANSI which limits Unicode handling in other parts of the project).

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.