spawnl and mode commands - c++

I want to use spawnl to restore a DOS terminal session after my application has completed, I'm doing the following:
static char* pszMode = "mode.com";
int intRC = spawnl(P_WAIT, pszMode, pszMode, "co80", NULL);
char szCOM2setup[80];
sprintf(szCOM2setup, "%s:9600,n,8,1", clsPort::mpcszCOM2);
intRC = spawnl(P_WAIT, pszMode, pszMode, szCOM2setup, NULL);
mpcszCOM contains COM2
In both cases intRC contains -1, I've single stepped execution and it doesn't look like these commands are being properly executed, what haven't I done?
I'm using ROM-DOS version 6.22 on an embedded PC104 platform.
I've checking with perrror and using strError, the actual error is:
No such file or directory
But why? The path is set-up before the application is launched and mode.com is accessible from the command line in the same folder as the application.
Tried using spawnlp instead of spawnl, no better same error.

For a reason I don't understand even though the path is set-up correctly, the application was returning "No such file or directory" so I modified the application to include the path and this resolved the issue.

Related

How to open/spawn a file with glib/gtkmm in Windows

I've already tried:
GError *pError = NULL;
string uri = g_filename_to_uri(file.c_str(), NULL, &pError);
if (!g_app_info_launch_default_for_uri(uri.c_str(), NULL, &pError)) {
cout << "Failed to open uri: " << pError->message;
}
Here I get the error "URIs not supported". Is the uri I create here wrong?
My second approach was to spawn the file with an asynchronous command line:
file = quoteStr(file);
try {
Glib::spawn_command_line_async(file);
} catch (Glib::SpawnError error) {
cout << error.what();
} catch (Glib::ShellError error) {
cout << error.what();
}
Here the Glib::SpawnError exception is thrown with the error: "Failed to execute helper program (Invalid argument)". I mean, when I execute the quoted absolute file path in the Windows cmd, it opens the file (in this case a pdf file). Does this function work different?
Hopefully this is related and can provide a real answer rather than just a (clever!) workaround.
I ran into a strange situation: Launching a file (specifically an HTML document) by g_app_info_launch_default_for_uri() or gtk_show_uri_on_window() worked when the executable was run from my build directory. However, it did not work if I copied the exe to another directory (for distribution) and ran it from there.
In the latter case, I got the same error as your 2nd quote:
Failed to execute helper program (Invalid argument)
The build directory is not in my path, and nor is it special for any other reason (it's in a temp RAM drive). So I was completely baffled.
I then thought about that error... What helper program could it possibly be talking about?
And why might that program be found when running from the build directory? Well, my build uses a libtool wrapper, and that puts a bunch of things in the path, so that we don't need to copy all the DLLs etc in just to test builds.
So, I went to investigate whether there was anything relevant-looking in paths that might be searched by the MSYS2 shell and its libtool wrapper. The prime suspect, of course, is C:\msys64\mingw64\bin. And look what I found there:
gspawn-win64-helper-console.exe
After copying this executable to the directory from which my application is launched, my program now successfully launches the URI, regardless of which folder its executable currently resides in.
Edit
After updating my packages in MSYS2, it was back to the same error - as it seems now this is the helper that is required:
gspawn-win64-helper.exe
That actually makes more sense, since my application is graphical, not console. I guess maybe something changed here recently. You could distribute both to be extra safe.
I had a similar problem and I had to give up using glib to do that and ended up implementing a simple crossplatform (win, mac and linux) compatible way to do it:
// open an URI, different for each operating system
void
openuri(const char *url)
{
#ifdef WIN32
ShellExecute(GetActiveWindow(),
"open", url, NULL, NULL, SW_SHOWNORMAL);
#elif defined(__APPLE__)
char buffer[512];
::snprintf(buffer, sizeof(buffer), "open %s", url);
::system(buffer);
#else
char buffer[512];
::snprintf(buffer, sizeof(buffer), "xdg-open %s", url);
::system(buffer);
#endif
}
... it's not very nice but it's small and it works :)

_findfirst and WinRT access denied

I am porting existing C++ app (game engine) to support Windows Store 8 and Windows Phone 8.1 Apps and I am having problem with the _wfindfirst function. On regular Win32 it returns a handle to the first found element matching the pattern. we use it mainly to get information about directory or file.
The function I am trying to get working on WindowsPhone/Windows Store App is like this:
bool sys_GetFileInfo(const std::string& path, FileInfo* info) {
...
long handle = _wfindfirst(p.c_str(), &item); // path gets converted to wstring
if (handle != -1L) {
info->size = (item.attrib & _A_SUBDIR) ? -1 : item.size;
info->modifiedAt = item.time_write;
_findclose(handle);
return true;
}
...
}
So it was used to retrieve file size and modification date (and if it happened to be directory the size was set to -1)
The first usage was to get info about working directory of an exe so in case of the WinRT/WP/WS I am using it with the path provided by
std::wstring wpath = Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data();
The path in this case is:
wpath = L"C:\\foo\\winrt\\winrt\\Debug\\foo_winrt.Windows\\AppX"
The problem is that it always returns -1, when I check the error string with GetLastError() code I get the access denied error. This is confusing as in my understanding application should have read access to this location so _wfindfirst is a read operation right? On Win32 it worked given a regular directory path.
Why this function fails? Is there any other viable option to achieve the same result for WinRT ?
If still relevant, it seems you are not able to use standard C++ functions to access file system outside your app's sandbox (installed location and local app folder). They will fail with Access denied error. I think they behave pretty much the way CreateFile2 behaves, and according to MSDN:
When called from a Windows Store app, CreateFile2 is simplified. You can open only files or directories inside the ApplicationData.LocalFolder or Package.InstalledLocation directories.
In your case, as I can see, the installed location points to a development folder, and I think the system decides that this location isn't inside your app's sandbox.
Consider using Windows Runtime APIs from Windows.Storage namespace. These APIs can be used to access any file in the file system.

Correctly creating and running a win32 service with file I/O

I've written a very simple service application based on this code example.
The application as part of its normal running assumes there exists a file in the directory it is found, or in its execution path.
When I 'install' the service and then subsequently 'start' the service from the service manager in control panel. The application fails because it can't find the file to open and read from (even though the file is in the same directory as the installed executable).
My question is when a windows service is run, which is the expected running path supposed to be?
When calling 'CreateService' there only seems to be a path parameter for the binary, not for execution. Is there someway to indicate where the binary should be executed from?
I've tried this on windows vista and windows 7. Getting the same issues.
Since Windows services are run from a different context than normal user-mode applications, it's best if you don't make any assumptions about working directories or relative paths. Aside from differences in working directories, a service could run using a completely different set of permissions, etc.
Using an absolute path to the file that your service needs should avoid this problem entirely. Absolute paths will be interpreted the same regardless of the working directory, so this should make the working directory of your service irrelevant. There are several ways to go about this:
Hard-code the absolute path - This is perhaps the easiest way to avoid the problem, however it's also the least flexible. This method is probably fine for basic development and testing work, but you probably want something a bit more sophisticated before other people start using your program.
Store the absolute path in an environment variable - This gives you an extra layer of flexibility since the path can now be set to any arbitrary value and changed as needed. Since a service can run as a different user with a different set of environment variables, there are still some gotchas with this approach.
Store an absolute path in the registry - This is probably the most fool-proof method. Retrieving the path from the registry will give you the same result for all user accounts, plus this is relatively easy to set up at install time.
By default, the current directory for your Windows service is the System32 folder.
A promising solution is creating an environment variable that keeps the full path of your input location and retrieving the path from this variable at runtime.
If you use the same path as binary, you could just read binary path and modify it accordingly. But this is rather quick-fix rather than designed-solution. If I were you, I would either create system-wide environment variable and store value there, or (even better) use windows registry to store service configuration.
Note:
You will need to add Yourself some privileges using AdjustTokenPrivileges function, you can see an example here in ModifyPrivilege function.
Also be sure to use HKEY_LOCAL_MACHINE and not HKEY_CURRENT_USER. Services ar running under different user account so it's HKCU's will be different than what you can see in your registry editor.
Today I solved this problem as it was needed for some software I was developing.
As people above have said; you can hardcode the directory to a specific file - but that would mean whatever config files are needed to load would have to be placed there.
For me, this service was being installed on > 50,000 computers.
We designed it to load from directory in which the service executable is running from.
Now, this is easy enough to set up and achieve as a non-system process (I did most of my testing as a non-system process). But the thing is that the system wrapper that you used (and I used as well) uses Unicode formatting (and depends on it) so traditional ways of doing it doesn't work as well.
Commented parts of the code should explain this. There are some redundancies, I know, but I just wanted a working version when I wrote this.
Fortunately, you can just use GetModuleFileNameA to process it in ASCII format
The code I used is:
char buffer[MAX_PATH]; // create buffer
DWORD size = GetModuleFileNameA(NULL, buffer, MAX_PATH); // Get file path in ASCII
std::string configLoc; // make string
for (int i = 0; i < strlen(buffer); i++) // iterate through characters of buffer
{
if (buffer[i] == '\\') // if buffer has a '\' in it, replace with doubles
{
configLoc = configLoc + "\\\\"; // doubles needed for parsing. 4 = 2(str)
}
else
{
configLoc = configLoc + buffer[i]; // else just add char as normal
}
}
// Complete location
configLoc = configLoc.substr(0, configLoc.length() - 17); //cut the .exe off the end
//(change this to fit needs)
configLoc += "\\\\login.cfg"; // add config file to end of string
From here on, you can simple parse configLoc into a new ifsteam - and then process the contents.
Use this function to adjust the working directory of the service to be the same as the working directory of the exe it's running.
void AdjustCurrentWorkingDir() {
TCHAR szBuff[1024];
DWORD dwRet = 0;
dwRet = GetModuleFileName(NULL, szBuff, 1024); //gets path of exe
if (dwRet != 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
*(_tcsrchr(szBuff, '\\') + 1) = 0; //get parent directory of exe
if (SetCurrentDirectory(szBuff) == 0) {
//Error
}
}
}

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.

64 bit windows file opening

I'm trying to use a dialog box to open a file in my program. This works perfectly on a 32 bit system, but when I try to use it on 64 bit it is unable to open the file. I've figured out that if the file trying to be opened is in the same directory as my program, it works fine. Trying to open a file from another folder, however, doesn't work at all.
So, I tried to copy the file to the program folder. This also works fine on 32 bit but doesn't work at all on a 64 system. Any thoughts why?
char cwdl[500];
getcwd(cwdl,500);
string mystring = string(cwdl);
CFileDialog fileDlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, "All Files (*.*)|*.*||", this);
fileDlg.m_ofn.lpstrTitle = "Select New File";
if( fileDlg.DoModal() == IDOK)
{
CString newFile= fileDlg.GetFileName();
mystring+="\\"+newFile;
const char * newLoc = mystring.c_str();
CopyFile(newFile,newLoc,true);
this is just a snippet of the code.
UAC and file system redirection are related yet different.
User account control is a permissions based security to prevent unauthorized users from changing your file system or executing applications which may affect other users. The prompt allows you to override the security by providing administrator privileges temporarily if that was your intent.
File system redirection is to allow backwards compatibility with 32bit applications by having a mirrored 32bit system folders and registry. In fact if the action causes UAC to kick in redirection does not occur it will always try to use the 64bit version of the file in that case. Unless you specify the redirection directory explicitly or run the 32bit application with administrator privileges to bypass UAC.
Ok that said you are using a relative path so it will look for the file in the current directory for the process. If it's compiled as 32 bit process running it on systems with different architectures may not behave as expected due to aforementioned redirection.
You can use GetCurrentDirectory windows API to see what directory the current process is using and verify it is what you expected. If not you have a few options.
The easiest would be to use fully qualified file paths.
You could also have two builds one targeted for each architecture you intend to deploy on. After all if you're on a 64bit system you might as well deploy 64bit applications.
A more involved option would be to subclass CFileDialog and disable redirection by calling Wow64DisableWow64FsRedirection in the constructor and Wow64RevertWow64FsRedirection in the desctructor. However this is meant to be a system setting so you may get new problems by forcing your 32bit application on 64bit windows.
There are probably plenty of other options as well since there is usually many ways to skin a cat. However the first step is to put some debug code in place and verify or eliminate redirection as a culprit with GetCurrentDirectory
Maybe it's just me, but I'm seeing a strange result: in 64-bit mode, the first four bytes of the buffer used to store the location of the path, are filled with zeros.
char wk[_MAX_PATH];
char *ret = _getcwd(wk, _MAX_PATH); // wk = "\0\0\0\0C:\\MyFolder\\..."
// ret = "C:\\MyFolder\\..."
The return value OTOH is correct. "ret" points to wk + 4;
In 32-bit mode, there are no leading zeros, the path starts at the first byte. Note: this is a Multi-byte app, not Unicode.
I'm using Visual Studio 2010 (10.0.40219.1 SP1Rel).
Anyway, if you're getting the same result, that would explain why your example doesn't work. You would have to say (in 64-bit mode only):
string mystring = string(cwdl + 4); // 64-bit only
Weird or What?
Edit: Seems to be an alignment problem. Works OK if the _getcwd is in a separate function.