c++ read config file parameters using GetPrivateProfileString - c++

I have a win32 console application in C++, Visual Studio 2012. I cannot get the config parameter from the ini file. I tried different possibilities with file path,like placing the .ini file in source folder, write the full path to is, placing in the folder with generated .exe file. The output on the console after executing of the .exe file is 0 or (null) for string in every case. What I am doing wrong? How can I read the parameters?
v1:
LPCTSTR path = L".\\config.ini";
TCHAR protocolChar[32];
int port = GetPrivateProfileString(_T("PORT"), _T("SETTINGS"), _T(""), protocolChar, 32, path);
printf("***%d***\n", port);
v2:
int port = GetPrivateProfileInt(_T("PORT"), _T("SETTINGS"), 0, _T("config.ini"));
config.ini contains:
[SETTINGS]
USER_NUM_MAX = 256 ; Maximum number of users
PORT = 8080;

Oups, under Windows hitting a ini file in not that easy. In both tries (v1 and v2), you look for the file in current directory and then in Windows directory but not in the directory where the executable file is.
The easy way is to put all ini files under Windows directory. If you find cleaner to have the ini file along with the exe one, you have some more work to do :
find the executable file full path
replace the exe end with ini
use that full path to get access to your private ini file
To get the name of the executable file, simply use GetModuleFileName with a NULL HMODULE :
LPCTSTR getExecPath() {
DWORD len = 64;
for (;;) {
LPTSTR fileName = new TCHAR[len];
if (len == ::GetModuleFileName(NULL, fileName, len)) {
delete fileName;
len *= 2;
}
else {
return fileName;
}
}
}
or if you prefere to directly get the ini file name :
LPCTSTR getIniName() {
DWORD len = 4;
for (;;) {
LPTSTR fileName = new TCHAR[len];
if (len == ::GetModuleFileName(NULL, fileName, len)) {
delete fileName;
len *= 2;
}
else {
::lstrcpy(fileName + lstrlen(fileName) - 3, "ini");
return fileName;
}
}
}
and to not forget to delete the file name when done since it is allocated with new ...
Edit per comment :
For reference, the windows directory may depend on windows version. But it can always be retrieved by the API function GetWindowsDirectory. Extract from the reference page :
UINT WINAPI GetWindowsDirectory(
_Out_ LPTSTR lpBuffer,
_In_ UINT uSize
);
Parameters
lpBuffer [out] A pointer to a buffer that receives the path. This path does not end with a backslash unless the Windows directory is the root directory. For example, if the Windows directory is named Windows on drive C, the path of the Windows directory retrieved by this function is C:\Windows. If the system was installed in the root directory of drive C, the path retrieved is C:.
uSize [in] The maximum size of the buffer specified by the lpBuffer parameter, in TCHARs. This value should be set to MAX_PATH.
Return value
If the function succeeds, the return value is the length of the string copied to the buffer, in TCHARs, not including the terminating null character.
If the length is greater than the size of the buffer, the return value is the size of the buffer required to hold the path.
If the function fails, the return value is zero. To get extended error information, call GetLastError. *

I faced this problem when I updated from VS2010 to VS2012.
On VS 2010 I simply called the function with the file name of the .ini-file as argument for lpFileName (see MSDN Documentation).
This was not working for VS 2012 any more, so I changed to go for the complete path like this:
char directoryPath[MAX_PATH];
char readParameter[MAX_STR_LEN];
GetCurrentDirectory( directoryPath, MAX_PATH );
string directoryPathAsString(directoryPath);
directoryPathAsString = directoryPathAsString + "\\" + filename;
GetPrivateProfileString("section","parameter","0",readParameter,MAX_STR_LEN, directoryPathAsString.c_str());

Related

Read the file version of a dll in C: The system cannot find the file specified

I am new in the forum but I have already found a lot of help for my other projects.
I am using Visual Studio 2019 and I have created a .rc file which contains the file version and a few other things. These information are displayed in the Properties window of the my dll correctly.
I have created a function
void PrintVersion(TCHAR* pszFilePath, void (*printFunc)(const char*, ...));
which receives the file path and a pointer to my logger function. Inside that function I want to read the file version and print it to the logger. But my logger returns Error in GetFileVersionInfoSize: The system cannot find the file specified.
My function call does look like this:
TCHAR* filename = L"mydll.dll";
PrintVersion(filename, gPrintFunc);
And the function is implemented as follows:
// Read the version of the dll and write it to the logger
void PrintVersion(TCHAR* pszFilePath, void (*printFunc)(const char*, ...))
{
DWORD dwSize = 0;
DWORD verHandle = 0;
BYTE* pbVersionInfo = NULL;
VS_FIXEDFILEINFO* pFileInfo = NULL;
UINT puLenFileInfo = 0;
// Get the size of the version information. This is done to check if the file is avaialbe
// If the size is zero then a error occured
dwSize = GetFileVersionInfoSize(pszFilePath, &verHandle);
if (dwSize == 0)
{
gPrintFunc("Error in GetFileVersionInfoSize: ");
PrintLastErrorString(gPrintFunc);
return;
}
// Create some memory for the file version info
pbVersionInfo = malloc(dwSize);
// Store the information into pbVersionInfo
#pragma warning(suppress : 6387)
if (!GetFileVersionInfo(pszFilePath, verHandle, dwSize, pbVersionInfo))
{
gPrintFunc("Error in GetFileVersionInfo: ");
PrintLastErrorString(gPrintFunc);
free(pbVersionInfo);
return;
}
// Make the information easier accessable in pFileInfo
#pragma warning(suppress : 6387)
if (!VerQueryValue(pbVersionInfo, TEXT("\\"), (LPVOID*)&pFileInfo, &puLenFileInfo))
{
gPrintFunc("Error in VerQueryValue: ");
PrintLastErrorString(gPrintFunc);
free(pbVersionInfo);
return;
}
// pFileInfo->dwFileVersionMS and pFileInfo->dwFileVersionLS contain the software version
// Major2B.Minor2B.Revision2B.Build2B
gPrintFunc("File Version of %s: %d.%d.%d.%d\n",
pszFilePath,
(pFileInfo->dwFileVersionMS >> 16) & 0xffff,
(pFileInfo->dwFileVersionMS >> 0) & 0xffff,
(pFileInfo->dwFileVersionLS >> 16) & 0xffff,
(pFileInfo->dwFileVersionLS >> 0) & 0xffff
);
// Free up the reserved memory
free(pbVersionInfo);
}
// Used for receiving the last WIN32 error and write it to the logger
void PrintLastErrorString(void (*printFunc)(const char*, ...))
{
// Get the error id of the last error
DWORD iLastError;
iLastError = GetLastError();
//Ask Win32 to give us the string version of that message ID.
//The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
LPSTR messageBuffer = NULL;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, iLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
gPrintFunc("%s\n", messageBuffer);
return;
}
I created that function by combining a few different C++ and C# examples from this forum. I am not familiar with the TCHAR* datatype. I assume that the problem has maybe something to do with the filename string. Further I am not able to print the filename to the logger with the %s format placeholder. In this case only the first letter of the filename is displayed.
One further info. Before I copied that code to the dll. I created a small console application. And in this case it was possible to read the file version of the exe. I also tried to specify the complete path of the dll. The dll and the exe, which uses the dll are in the same directory.
Maybe someone can help me :)
BR
Thank you for your answers.
I changed now the character set to: Use Unicode Character Set and now it works as expected.

C++ URLDownloadToFile to executable directory in CLR forum

I'm trying to figure out how I can fix two issues that I've been having with the URLDownloadToFile function in C++. The first is that when attempting to download the file in question, the download doesn't actually appear until the CLR C++ window is closed. The second is that as shown on the image below, the resulting file's file name and extension (Which is above the success window) is messed up (Although opening it normally shows that the file downloaded fine, with the name as the exception). If anyone has any suggestions on what I could do to fix these two issues, I'd greatly appreciate it.
For this logic, I'm using:
{
char buffer[MAX_PATH];
GetModuleFileNameA(NULL, buffer, MAX_PATH);
std::string::size_type pos = std::string(buffer).find_last_of("\\/");
return std::string(buffer).substr(0, pos);
}
void StartDownload()
{
HRESULT downloadUpdate;
LPCTSTR downloadUrl = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", File = "download.png";
string currentDirectory = GetCurrentDirectory();
LPTSTR currentDirectoryLPTSTR = new TCHAR[currentDirectory.size() + 1];
std::string(currentDirectoryLPTSTR).append(File).c_str();
downloadUpdate = URLDownloadToFile(0, downloadUrl, currentDirectoryLPTSTR, 0, 0);
switch (downloadUpdate)
{
case S_OK:
updateSuccess();
break;
case E_OUTOFMEMORY:
updateOOMError();
break;
case INET_E_DOWNLOAD_FAILURE:
updateError();
break;
default:
updateErrorUnknown();
break;
}
}
[STAThread]
int main() {
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
Application::Run(gcnew UpdaterGUIProject::UpdaterGUI());
StartDownload();
return 0;
}
LPTSTR currentDirectoryLPTSTR = new TCHAR[currentDirectory.size() + 1]; allocates memory, but does not fill it with any data.
std::string(currentDirectoryLPTSTR) creates a new string object and tries to copy data from currentDirectoryLPTSTR into the string. Which is undefined behavior since currentDirectoryLPTSTR is not a proper null-terminated string. This code does not cause the string object to point at the memory allocated for currentDirectoryLPTSTR, like you clearly think it does.
You are then append()ing File to that string object, not to the contents of currentDirectoryLPTSTR.
And then you throw away the string object you just created, and pass the unfilled currentDirectoryLPTSTR as-is to URLDownloadToFile(). Which is why your output file has a messed-up filename (you are lucky it even shows up in the correct folder at all).
Try this instead:
std::string GetCurrentDirectory()
{
char buffer[MAX_PATH] = {};
DWORD size = GetModuleFileNameA(NULL, buffer, MAX_PATH);
if (size == 0 || size == MAX_PATH) return "";
std::string fileName(buffer, size);
std::string::size_type pos = fileName.find_last_of("\\/");
return fileName.substr(0, pos + 1);
}
void StartDownload()
{
HRESULT downloadUpdate;
LPCSTR downloadUrl = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", File = "download.png";
string localFile = GetCurrentDirectory() + File;
downloadUpdate = URLDownloadToFileA(0, downloadUrl, localFile.c_str(), 0, 0);
switch (downloadUpdate)
{
case S_OK:
updateSuccess();
break;
case E_OUTOFMEMORY:
updateOOMError();
break;
case INET_E_DOWNLOAD_FAILURE:
updateError();
break;
default:
updateErrorUnknown();
break;
}
}
On a side note, you really shouldn't be downloading files to the same folder that your program is running from. If your program is installed in a folder like C:\Program Files or C:\Program Files (x86), somewhere only admins can write to, then the download will be likely to fail. You should be downloading to a folder that the user has write access to, such as to a subfolder you create under %APPDATA% for your program to use.

How can I create a file when file path name is over 255 characters using MFC in Windows?

I am working in Windows,using vc++2010 and MFC.
Following is my code:
CFile File;
TCHAR lpCause[1024];
CFileException eException;
CString strErrorMessage;
// a very long file path name means a file name over 255 characters
if (!File.Open(_T("a very long file path name"), CFile::modeCreate, &eException))
{
eException.GetErrorMessage(lpCause, 1024);
strErrorMessage = lpCause;
}
else
File.Close();
When I run the code, I got error message:"a very long file path name contains an incorrect path".
My questions are:
How to modify my code to make it work?
I learn that CreateFile() function can add "\\\\?\" in the beginning of file path, then it will extend this limit to 32767 wide characters.How can I do the same thing in MFC?
Cause
In the source of CFile::Open(), there is an explicit check if the path length exceeds _MAX_PATH:
if (lpszFileName != NULL && SUCCEEDED(StringCchLength(lpszFileName, _MAX_PATH, NULL)) )
If _MAX_PATH is exceeded, the function sets pException->m_cause = CFileException::badPath and returns FALSE.
This is true even for the MFC version that comes with VS2017.
So the standard technique to circumvent the _MAX_PATH limit, that is prefixing the path with \\?\ won't work.
Possible Solutions
Call CreateFileW() directly to pass it a path with \\?\ prefix. Use the CFile constructor that accepts a HANDLE to manage the file through a CFile object. The CFile object will take ownership of the handle so you must not call CloseHandle() on the handle.
HANDLE hFile = CreateFileW( L"\\\\?\\a very long file path name", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, NULL );
if( hFile != INVALID_HANDLE_VALUE )
{
// Manage the handle using CFile
CFile file( hFile );
// Use the file...
// The CFile destructor closes the handle here.
}
else
{
DWORD err = GetLastError();
// TODO: do your error handling...
}
Another possibility is to derive a class from CFile that overrides CFile::Open() (which is virtual). For the implementation copy/paste the MFC source, but leave out the _MAX_PATH check. For a big project, this class could be a drop-in replacement for CFile to enable long paths. You could even go so far to prepend the \\?\ prefix if it isn't already there (but that is more involved as the prefix also disables the regular transformations from a Win32 path to a NT-style path, like converting / to \, resolving dots and so on).

PathFileExists returns false when executing application through RemoteApp

My executable built in C++/WinAPI will check for a file placed in the same folder and I use PathFileExists for that. When I run it on a normal computer it finds the file but when I publish the executable on RemoteApp and I run it from Web Access the file is not found. What would I be missing?
// This is the file I want to find (located in the same directory as the EXE)
wstring myfile = L"myfile.conf";
BOOL abspath = FALSE;
// Trying to get the absolute path first
DWORD nBufferLength = MAX_PATH;
wchar_t szCurrentDirectory[MAX_PATH + 1];
if (GetCurrentDirectory(nBufferLength, szCurrentDirectory) == 0) {
szCurrentDirectory[MAX_PATH + 1] = '\0';
} else {
abspath = true;
}
if (abspath) {
// Create the absolute path to the file
myfile = L'\\' + myfile;
myfile = szCurrentDirectory + myfile ;
MessageBox(hWnd, ConvertToUNC(myfile).c_str(), L"Absolute Path", MB_ICONINFORMATION);
} else {
// Get the UNC path
myfile = ConvertToUNC(myfile);
MessageBox(hWnd, myfile.c_str(), L"UNC Path", MB_ICONINFORMATION);
}
// Try to find file
int retval = PathFileExists(myfile.c_str());
if (retval == 1) {
// Do something
} else {
// File not found
}
The ConvertToUNC function is copied from here.
What I see is that, although the executable lies somewhere else, the absolute path is considered to be C:\Windows. I really don't know what is causing this. The server is Windows 2012 R2 and, like I said, applications are run through RemoteApp Web Access. The returned UNC path is just the name of the file (no volume or folder)

How to get Machine id using c++

am facing two problems one big problem and one small problem :)
problem # 1 : am unable to read Machine ID from below path ... i get my processor name like intel i7 #2.2ghz like that , i do not know why , i should get machine id , long integer string but i not get it , so please help
reg_path="SOFTWARE\\Microsoft\\Cryptography";
rvalue="MachineGuid"; // data value
my registery reading function
string read_reg_sz(char rpath[],char rdata[]) // read registery Loaction
{
REGSAM flag = KEY_WOW64_32KEY or KEY_WOW64_64KEY;
char buffer[MAX];
char Buffer[MAX];
DWORD BufSize = _MAX_PATH;
char dwMHz[MAX];
DWORD dataType = REG_SZ;
HKEY hKey;
long lError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,rpath,NULL, KEY_READ | KEY_WRITE | flag,&hKey);
if(lError != ERROR_SUCCESS)
{// if the key is not found, tell the user why:
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
lError,
0,
Buffer,
_MAX_PATH,
0);
cout<<"\n reg erro : "<<Buffer;
return "N/A";
}
// query the key:
RegQueryValueEx(hKey,rdata,NULL,&dataType,(LPBYTE) &dwMHz,&BufSize);
RegCloseKey(hKey); // close open handle ....
cout<<"\n reg data read: "<<dwMHz;
return dwMHz;
}
second problem :
currently i have function which can totally clean recycle bin :)
SHEmptyRecycleBin(NULL, NULL, SHERB_NOCONFIRMATION | SHERB_NOPROGRESSUI | SHERB_NOSOUND);
but i want to delete single file from recycle bin like passing filename
You should really post it as two different questions, but I'll try to answer both.
1. Get MachineGuid
I think your issue is in this line:
// query the key:
RegQueryValueEx(hKey,rdata,NULL,&dataType,(LPBYTE) &dwMHz,&BufSize);
You should change it to:
// query the key:
RegQueryValueEx(hKey,rvalue,NULL,&dataType,(LPBYTE) &dwMHz,&BufSize);
By the way, dhMHz does not sound like right variable name - change it to reflect reality.
Also, you should have this:
DWORD BufSize = sizeof(Buffer) - 1;
And, it would be nice to NOT have both buffer and Buffer variables.
2. Delete one file from recycle bin
According to Microsoft documentation on SHFileOperation, you should just use DeleteFile on filename like C:\$Recycle.Bin\file.txt:
When used to delete a file, SHFileOperation permanently deletes the file unless you set the FOF_ALLOWUNDO flag in the fFlags member of the SHFILEOPSTRUCT structure pointed to by lpFileOp. Setting that flag sends the file to the Recycle Bin. If you want to simply delete a file and guarantee that it is not placed in the Recycle Bin, use DeleteFile.
To delete a single file from the Recycle Bin, use SHGetSpecialFolderLocation(CSIDL_BITBUCKET) or SHGetKnownFolderIDList(FOLDERID_RecycleBinFolder) to get the absolute PIDL of the Recycle Bin, then use SHBindToObject() to get the IShellFolder interface for it and call its ParseDisplayName() method to convert the desired filename into a relative PIDL, then use SHBindToObject() to get the IContextMenu interface for the file and call its InvokeCommand() method to execute the file's "delete" verb.