Fstream does not recognise relative path [duplicate] - c++

I wrote a simple check_file_ref function using WinAPI to check whether two paths reference the same file. The code is fine. It's compiled with Visual Studio 2017 in C (flag /TC).
The weird thing is CreateFileA (winapi) always fails when the executable is run under Visual Studio (ie execute without debugging), returning an invalid file handle. Otherwise, it works as expected when the executable is run outside of Visual Studio.
The working dir is the same in any case.
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
/**
* Check whether two paths reference the same file.
* #param p1 File path 1.
* #param p2 File path 2.
* #return 0 same file, 1 different files, < 0 on failure.
*/
int
check_file_ref(char const * p1, char const * p2)
{
if ( !p1 || !p2 )
return -1;
if ( p1 == p2 )
return 0;
if ( strcmp(p1, p2) == 0 )
return 0;
int ret;
DWORD share, flags;
HANDLE f1, f2;
BY_HANDLE_FILE_INFORMATION s1, s2;
ret = -1;
share = FILE_SHARE_READ | FILE_SHARE_WRITE;
flags = FILE_ATTRIBUTE_NORMAL;
f1 = CreateFileA(p1, GENERIC_READ, share, NULL, OPEN_EXISTING, flags, NULL);
f2 = CreateFileA(p2, GENERIC_READ, share, NULL, OPEN_EXISTING, flags, NULL);
if ( f1 == INVALID_HANDLE_VALUE ) {
fprintf(stderr, "Could not open %s file, error %d\n", p1, GetLastError());
goto cleanup;
}
if ( f2 == INVALID_HANDLE_VALUE ) {
fprintf(stderr, "Could not open %s file, error %d\n", p2, GetLastError());
goto cleanup;
}
if ( GetFileInformationByHandle(f1, &s1) == 0 ) {
fprintf(stderr, "Could not get %s file information, error %d\n", p1, GetLastError());
goto cleanup;
}
if ( GetFileInformationByHandle(f2, &s2) == 0 ) {
fprintf(stderr, "Could not get %s file information, error %d\n", p2, GetLastError());
goto cleanup;
}
/*
The identifier (low and high parts) and the volume serial number uniquely
identify a file on a single computer. To determine whether two open handles
represent the same file, combine the identifier and the volume serial number
for each file and compare them.
See
https://learn.microsoft.com/fr-fr/windows/desktop/api/fileapi/nf-fileapi-getfileinformationbyhandle
https://learn.microsoft.com/fr-fr/windows/desktop/api/fileapi/ns-fileapi-_by_handle_file_information
*/
ret = !(s1.dwVolumeSerialNumber == s2.dwVolumeSerialNumber
&& s1.nFileIndexLow == s2.nFileIndexLow
&& s1.nFileIndexHigh == s2.nFileIndexHigh);
cleanup:
CloseHandle(f2);
CloseHandle(f1);
return ret;
}
int main()
{
int ret;
char workingDir[256];
/* Both paths reference the same file.
One is relative, the other absolute. */
char * p1 = "hello.txt";
char * p2 = "C:/Users/bro/source/repos/tests/x64/Debug/hello.txt";
ret = GetModuleFileNameA(NULL, workingDir, sizeof(workingDir));
printf("working dir: %s\n", (ret == 0 ? "err" : workingDir));
/* Should return 0. */
ret = check_file_ref(p1, p2);
printf("p1: %s\n", p1);
printf("p2: %s\n", p2);
printf("check_file_ret ret %d ", ret);
if ( ret == 0 ) printf("(same file)\n");
else if ( ret == 1 ) printf("(different files)\n");
else printf("(error)\n");
return 0;
}
Executable run under visual:
CreateFileA fails
Executable run directly from cmd line:
CreateFileA works
Why is CreateFileA failing only when the executable is run under Visual Studio?

When you run an application from VisualStudio the default working directory is a project location, which is defined by VS macro $(ProjectDir). You can change it by openning the project properties: Right click on the project in Solution Explorer and select Properties from the context menu. There select Debugging and change Working Directory property. For example, in your case the solution could be $(OutDir) or absolute path to the required location.

Related

SetEndOfFile error 1224: ERROR_USER_MAPPED_FILE, even the file mapping closed successfully

I'm writing a program which logs out to the file with file-mapping.
When I want to read the log, Logger::Destroy() is called so that the content writed on file-mapped-view could be flushed to the physical file.
The code is as follows:
int Logger::Destroy() {
if (m_lpMapAddress) {
auto bRet = UnmapViewOfFile(m_lpMapAddress);
// succeed
}
if (m_hMapFile) {
auto bRet = CloseHandle(m_hMapFile);
// succeed
m_hMapFile = NULL;
}
int nSize = m_lpCurAddress - m_lpMapAddress;
if (nSize > 0
&& nSize < (1024 * 1024 * 16 * 2))
{
DWORD dwPtr = SetFilePointer(m_hFile, nSize, 0, FILE_BEGIN);
///// Succeed
// if (dwPtr == INVALID_SET_FILE_POINTER)
// DbgViewOut(__FUNCTION__ " SetFilePointer error: %d \n", GetLastError());
//// Error occurs : "SetEndOfFile returned : 0 1224"
BOOL bRet = SetEndOfFile(m_hFile);
DbgViewOut(__FUNCTION__ " SetEndOfFile returned : %d %d\n", bRet, GetLastError());
....
}
m_lpMapAddress = m_lpCurAddress = NULL;
return 0;
}
The problem is that SetEndOfFile() fails with ERROR_USER_MAPPED_FILE, even CloseHandle(m_hMapFile) succeed.
So I googled microsoft's manuals about file mapping and I comment some of them.
https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
Closing a handle to a file mapping can succeed even when there are file views that are still open. For more information, see Closing a File Mapping Object.
https://learn.microsoft.com/en-us/windows/win32/memory/closing-a-file-mapping-object
When each process finishes using the file mapping object and has unmapped all views, it must close the file mapping object's handle and the file on disk by calling CloseHandle. These calls to CloseHandle succeed even when there are file views that are still open. However, leaving file views mapped causes memory leaks.
It says that I can't believe the result of CloseHandle().
And I can't find out the solution.
Anyone could help me? Thanks.
Addition: I called Logger::Destroy() in std::lock_guard<std::mutex>, can this affect to the trouble?
: I've tested this, lock doesn't affect to it.
UPDATE: I've read When error 1224: ERROR_USER_MAPPED_FILE occurs?. And I think there isn't a very big difference.
I append the code of Logger::Initialize() which initializes the filemapping. And also, Logger::Initialize() and Logger::Destroy() both are in same process&thread, no need to be shared with others.
int Logger::Initialize()
{
m_hFile = CreateFileA(
m_zFileName.c_str()
, GENERIC_READ | GENERIC_WRITE
, FILE_SHARE_READ | FILE_SHARE_WRITE
, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL);
if (m_hFile == INVALID_HANDLE_VALUE) {
DbgViewOut(__FUNCTION__ " CreateFileA error: %d \n", GetLastError());
return -1;
}
m_hMapFile = CreateFileMappingA(
m_hFile
, NULL
, PAGE_READWRITE
, 0
, 1024 * 1024 * 16 * 2
, m_zMapKey.c_str());
if (!m_hMapFile) {
DbgViewOut(__FUNCTION__ " CreateFileMapping error: %d \n", GetLastError());
return -1;
}
m_hMapFile = OpenFileMappingA(FILE_MAP_WRITE, TRUE, m_zMapKey.c_str());
if (m_hMapFile == NULL) {
DbgViewOut(__FUNCTION__ " OpenFileMapping error: %d \n", GetLastError());
return -1;
}
m_lpMapAddress = (BYTE*)MapViewOfFile(
m_hMapFile // handle to mapping object
, FILE_MAP_ALL_ACCESS // read/write
, 0 // high-order 32 bits of file offset
, 0 // low-order 32 bits of file offset
, 0); // number of bytes
if (m_lpMapAddress == NULL) {
DbgViewOut(__FUNCTION__ " MapViewOfFile error: %d \n", GetLastError());
return -1;
}
m_lpCurAddress = m_lpMapAddress;
return 0;
}
I'll answer my own question.
The problem was that I called OpenFileMappingA() after CreateFileMappingA(), so the m_hMapFile returned by CreateFileMappingA() is leaking.
I've removed OpenFileMappingA() and the problem disappeared.
I used to know that it must be once opened after create handle, and I used to do that in memory sharing between processes, monthes ago.
But during this arguement, I've realized that it's unecessary open handle after creating.
Thank you very much, #RemyLebeau, for your detailed suggestion.
int Logger::Initialize()
{
m_hFile = CreateFileA(
m_zFileName.c_str()
, GENERIC_READ | GENERIC_WRITE
, FILE_SHARE_READ | FILE_SHARE_WRITE
, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL);
if (m_hFile == INVALID_HANDLE_VALUE) {
DbgViewOut(__FUNCTION__ " CreateFileA error: %d \n", GetLastError());
return -1;
}
m_hMapFile = CreateFileMappingA(
m_hFile
, NULL
, PAGE_READWRITE
, 0
, LINM_LOGGER_FILE_MAXSIZE * 2
, m_zMapKey.c_str());
if (!m_hMapFile) {
DbgViewOut(__FUNCTION__ " CreateFileMapping error: %d \n", GetLastError());
return -1;
}
//// This was the solution - CreateFileMappingA & OpenFileMappingA mustn't be used for same process or thread.
//
// m_hMapFile = OpenFileMappingA(FILE_MAP_WRITE, TRUE, m_zMapKey.c_str());
// if (m_hMapFile == NULL) {
// DbgViewOut(__FUNCTION__ " OpenFileMapping error: %d \n", GetLastError());
// return -1;
// }
m_lpMapAddress = (BYTE*)MapViewOfFile(
m_hMapFile // handle to mapping object
, FILE_MAP_ALL_ACCESS // read/write
, 0 // high-order 32 bits of file offset
, 0 // low-order 32 bits of file offset
, 0); // number of bytes
if (m_lpMapAddress == NULL) {
DbgViewOut(__FUNCTION__ " MapViewOfFile error: %d \n", GetLastError());
return -1;
}
m_lpCurAddress = m_lpMapAddress;
return 0;
}

How to check if any file exist in specific folder?

I am using CreateProcess to copy files. Also I can catch different errors, if PC is offline, if directory does not exist.
Here is the problem I have: It returns 0 as error code, if all copying is successful and also returns 0 if there were zero files in source folder, so no copying is done. I must detect whether there are no files in source folder. How can I do it in MFC VC++ 2013?
I have spent hours trying different solutions, but my knowledge is not high enough to implement all I find on internet. So I have to ask for code, then I will understand. Thank you in advance.
This is code I use:
temp_dest = _T("/min /c xcopy \"D:\\Test\\*.*\" \"") + m_destination + _T("\" /Y /E /Q");
LPTSTR temp_dest2 = (LPTSTR)(LPCTSTR)temp_dest;
STARTUPINFO sinfo;
PROCESS_INFORMATION pinfo;
memset(&sinfo, 0, sizeof(STARTUPINFO));
memset(&pinfo, 0, sizeof(PROCESS_INFORMATION));
sinfo.dwFlags = STARTF_USESHOWWINDOW;
sinfo.wShowWindow = SW_HIDE;
BOOL bSucess = CreateProcess(L"C:\\Windows\\System32\\cmd.exe", temp_dest2, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL, &sinfo, &pinfo);
DWORD dwCode;
TerminateProcess(pinfo.hProcess, 2);
GetExitCodeProcess(pinfo.hProcess, &dwCode);
TCHAR msg2[100];
StringCbPrintf(msg2, 100, TEXT("%X"), dwCode);
MessageBox(msg2, (LPCWSTR)L"DWCode 2", MB_OK | MB_ICONERROR);
if (dwCode == 4)
{
MessageBox((LPCWSTR)L"DW 4", (LPCWSTR)L"Path not found", MB_OK | MB_ICONERROR);
}
if (dwCode == 2)
{
MessageBox((LPCWSTR)L"DW 4", (LPCWSTR)L"PC Offline", MB_OK | MB_ICONERROR);
}
If you can use directory_iterator from <filesystem> header file introduced in C++17:
bool IsEmptyDirectory( const wchar_t* dir )
{
return std::filesystem::directory_iterator( std::filesystem::path( dir ) )
== std::filesystem::directory_iterator();
}
May be needed std::experimental::filesystem instead of std::filesystem.
I have tried to port it to VC 2013, but only char version seems to compile
bool IsEmptyDirectory( const char* dir )
{
return std::tr2::sys::directory_iterator( std::tr2::sys::path( dir ) )
== std::tr2::sys::directory_iterator();
}
If you want (or have) to use WinAPI:
bool IsEmptyDirectory( const wchar_t* dir )
{
wstring mask( dir);
mask += L"\\*";
WIN32_FIND_DATA data;
HANDLE find_handle = FindFirstFile( mask.c_str(), &data );
if ( find_handle == INVALID_HANDLE_VALUE )
{
// Probably there is no directory with given path.
// Pretend that it is empty.
return true;
}
bool empty = true;
do
{
// Any entry but . and .. means non empty folder.
if ( wcscmp( data.cFileName, L"." ) != 0 && wcscmp( data.cFileName, L".." ) != 0 )
empty = false;
} while ( empty && FindNextFile( find_handle, &data ) );
FindClose( find_handle );
return empty;
}
You can use the WIN32 function GetFileAttributes(..) to check if a file exists or not:
if (GetFileAttributes("C:\\test.txt") != INVALID_FILE_ATTRIBUTES)
{
/* C:\test.txt is existing */
}
Another way just might be trying to open the file (and if successful to close it again).

Serial Comms between Microsoft Visual C++ 2010 and Arduino UNO via USB

I need to establish a serial comm between Microsoft Windows Visual C++ 2010 and an Arduino microcontroller via USB. A motion tracking algorithm produces an X and Y coordinate which needs to be sent to the Arduino which in turn controls two pan and tilt servos.
I am a final year mechanical engineering student and have very little experience with Microsoft Visual Studios and C++, so please bear with me and please forgive me if my terms are incorrect...
I have done extensive research on multiple forums, but cannot find an answer specific to my problem:
All the solutions that I have come across only support comms when a normal/"empty" project is created in Visual Studios. An example can be found here: Serial communication (for Arduino) using Visual Studio 2010 and C
When I try and debug the same body of code (which successfully runs in an "empty" project) in a "Win32 Console Application" project, I am presented with the following errors:
error C2065: 'LcommPort' : undeclared identifier
error C2228: left of '.c_str' must have class/struct/union
Unfortunately I cannot simply change my project from a "Win32 Console Application" to a normal "Empty" project due to the fact that the motion tracking algorithm necessitates the use of the console application type of project.
The main body of code that I am using is as follows (this is a simplified test source file to confirm whether comms are established between MS Visual and the Arduino where the frequency at which an LED turns on and off is altered through the serial connection):
#include <Windows.h>
#include "ArduinoSerial.h"
#include "StdAfx.h"
int main() {
try {
ArduinoSerial arduino( "COM3" );
Sleep( 2000 ); // Initial wait to allow Arduino to boot after reset
char buffer[] = { 25, 100 };
arduino.Write( buffer, 2 ); // Send on/off delays to Arduino (if return value is 0, something went wrong)
}
catch ( const ArduinoSerialException &e ) {
MessageBoxA( NULL, e.what(), "ERROR", MB_ICONERROR | MB_OK );
}
return 0;
}
The corresponding source code which is the home of the error is found in line9 of the code:
#include <algorithm>
#include <sstream>
#include <Windows.h>
#include "stdafx.h"
#include "ArduinoSerial.h"
ArduinoSerial::ArduinoSerial( const std::string commPort ) {
comm = CreateFile( TEXT( commPort.c_str() ),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL );
if ( comm == INVALID_HANDLE_VALUE ) {
std::ostringstream error;
error << "Unable to acquire handle for " << commPort << ": ";
DWORD lastError = GetLastError();
if ( lastError == ERROR_FILE_NOT_FOUND ) {
error << "Invalid port name";
}
else {
error << "Error: " << lastError;
}
throw ArduinoSerialException( error.str() );
}
DCB dcb;
SecureZeroMemory( &dcb, sizeof DCB );
dcb.DCBlength = sizeof DCB;
dcb.BaudRate = CBR_9600;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
if ( !SetCommState( comm, &dcb ) ) {
CloseHandle( comm );
std::ostringstream error;
error << "Unable to set comm state: Error " << GetLastError();
throw ArduinoSerialException( error.str() );
}
PurgeComm( comm, PURGE_RXCLEAR | PURGE_TXCLEAR );
}
std::size_t ArduinoSerial::Read( char buffer[], const std::size_t size ) {
DWORD numBytesRead = 0;
BOOL success = ReadFile( comm, buffer, size, &numBytesRead, NULL );
if ( success ) {
return numBytesRead;
}
else {
return 0;
}
}
std::size_t ArduinoSerial::Write( char buffer[], const std::size_t size ) {
DWORD numBytesWritten = 0;
BOOL success = WriteFile( comm, buffer, size, &numBytesWritten, NULL );
if ( success ) {
return numBytesWritten;
}
else {
return 0;
}
}
ArduinoSerial::~ArduinoSerial() {
CloseHandle( comm );
}
ArduinoSerialException::ArduinoSerialException( const std::string message ) :
std::runtime_error( message ) {
}
Any help or advice will be really greatly appreciated.
I am presented with the following errors:
error C2065: 'LcommPort' : undeclared identifier error C2228: left of '.c_str' must have class/struct/union
This little piece of code
TEXT( commPort.c_str())
becomes actually
LcommPort.c_str()
That's why you get this compiler error.
You should notice that TEXT() is a preprocessor macro meant for character literals, to prefix them with L depending in which mode (Unicode/ASCII) your project is compiled. It doesn't work with any variables obviously.
Use either commPort.c_str() directly, or const std::wstring commPort.

Writing Performance Data to a Log File

I'm trying to use one of the windows examples to have some CPU data. When I try to debug the below code I receive the message "The command line must include a valid log file name" I am using MS Visual studio 2013 and I am intrested in Total Processor time%.
Pls advise.
More details about the code:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa373228(v=vs.85).aspx
The Code:
#include <windows.h>
#include <stdio.h>
#include <pdh.h>
#include <pdhmsg.h>
#pragma comment(lib, "pdh.lib")
CONST PWSTR COUNTER_PATH = L"\\Processor(0)\\% Processor Time";
CONST ULONG SAMPLE_INTERVAL_MS = 1000;
void DisplayCommandLineHelp(void)
{
wprintf(L"The command line must include a valid log file name.\n");
}
void wmain(int argc, WCHAR **argv)
{
HQUERY hQuery = NULL;
HLOG hLog = NULL;
PDH_STATUS pdhStatus;
DWORD dwLogType = PDH_LOG_TYPE_CSV;
HCOUNTER hCounter;
DWORD dwCount;
if (argc != 2)
{
DisplayCommandLineHelp();
goto cleanup;
}
// Open a query object.
pdhStatus = PdhOpenQuery(NULL, 0, &hQuery);
if (pdhStatus != ERROR_SUCCESS)
{
wprintf(L"PdhOpenQuery failed with 0x%x\n", pdhStatus);
goto cleanup;
}
// Add one counter that will provide the data.
pdhStatus = PdhAddCounter(hQuery,
COUNTER_PATH,
0,
&hCounter);
if (pdhStatus != ERROR_SUCCESS)
{
wprintf(L"PdhAddCounter failed with 0x%x\n", pdhStatus);
goto cleanup;
}
// Open the log file for write access.
pdhStatus = PdhOpenLog(argv[1],
PDH_LOG_WRITE_ACCESS | PDH_LOG_CREATE_ALWAYS,
&dwLogType,
hQuery,
0,
NULL,
&hLog);
if (pdhStatus != ERROR_SUCCESS)
{
wprintf(L"PdhOpenLog failed with 0x%x\n", pdhStatus);
goto cleanup;
}
// Write 10 records to the log file.
for (dwCount = 0; dwCount < 10; dwCount++)
{
wprintf(L"Writing record %d\n", dwCount);
pdhStatus = PdhUpdateLog(hLog, NULL);
if (ERROR_SUCCESS != pdhStatus)
{
wprintf(L"PdhUpdateLog failed with 0x%x\n", pdhStatus);
goto cleanup;
}
// Wait one second between samples for a counter update.
Sleep(SAMPLE_INTERVAL_MS);
}
cleanup:
// Close the log file.
if (hLog)
PdhCloseLog(hLog, 0);
// Close the query object.
if (hQuery)
PdhCloseQuery(hQuery);
}
if (argc != 2)
{
DisplayCommandLineHelp();
goto cleanup;
}
This is your answer right here. You need to set your project up to pass a filename to the program when it runs.
argc counts how many command-line arguments your program has received. It always gets at least one, the name of the program itself. But this program needs a second one, the name of the logfile to write to.

FtpGetFile WinINEt never returns

I'm experiencing a curious problem (very strange, let me say hehe). During a FTP download of an EXE file (24 MB), if the connection is ever interrupted, it appears that the function FtpGetFile of the WinINEt library has a bug and it never returns. This causes that future file transfers fail (the connection is already opened).
Apparently, I found a workaround by increasing the timeout of the server transfers but I do not like it. I didn't found a similar problem by googling (maybe I introduced the wrong keywords).
I read some forums on the internet and it seems that everyone does not recommend using the FtpGetFile because it is buggy.
This appears in a network scenario that has a big lag (and not always) but in good conditions it disappears (downloads take place correctly and FtpGetFile returns always).
Here is how I use the function:
if( FtpGetFile(m_hFtpSession, strSourcePath.c_str(), strTargetPath.c_str(), 0, 0, FTP_TRANSFER_TYPE_BINARY, 0)==TRUE)
Can anyone confirm that? Should I refactor my code and look for an update?
Thank you
I found a way to download files without using FtpGetFile. I hope this code can help someone:
bool RetrieveFile(const string& strSource, const string& strTarget) {
/* The handle for the transfer */
HINTERNET hTransfer = NULL;
/*
* Set default error
*/
DWORD error = ERROR_SUCCESS;
if( !isConnected ) {
debug("%s(): ERROR not connected\n", __FUNCTION__);
return false;
}
/* Initiate access to a remote FTP connection */
hTransfer = FtpOpenFile(hFtpSession, strSource.c_str(), GENERIC_READ,
FTP_TRANSFER_TYPE_BINARY, 0);
if(hTransfer) {
std::ofstream myostream(strTarget.c_str(), std::ios::binary);
if ( myostream.is_open() ) {
static const DWORD SIZE = 1024;
BYTE data[SIZE];
DWORD size = 0;
do {
BOOL result = InternetReadFile(hTransfer, data, SIZE, &size);
if ( result == FALSE ) {
error = GetLastError();
Debug("InternetReadFile(): %lu\n", error);
}
myostream.write((const char*)data, size);
}
while ((error == ERROR_SUCCESS) && (size > 0));
// Close the stream
myostream.close();
}
else {
Debug("Could not open '%s'.\n", strTarget.c_str());
error = ERROR_FILE_NOT_FOUND; // Not necessarily not found, but it is to describe a file error which is different from ERROR_SUCCESS
}
// Close
const BOOL result = InternetCloseHandle(hTransfer);
if ( result == FALSE ) {
const DWORD error = GetLastError();
debug("InternetClose(): %lu\n", error);
}
/* Check error status of the process */
return (error == ERROR_SUCCESS);
}
DWORD dwInetError;
DWORD dwExtLength = 1000;
TCHAR *szExtErrMsg = NULL;
TCHAR errmsg[1000];
szExtErrMsg = errmsg;
int returned = InternetGetLastResponseInfo( &dwInetError, szExtErrMsg, &dwExtLength );
debug("dwInetError: %d Returned: %d\n", dwInetError, returned);
debug("Buffer: %s\n", szExtErrMsg);
debug("%s() : ERROR to get '%s' file (errorCode=%d)\n", __FUNCTION__, strSource.c_str(), GetLastError());
return false;
}