Add a pre-declared path to a filename argument (LPCSTR) - c++

I have a list of files I need to open in a certain function via a LPCSTR argument, but unfortunately they're located in certain folders (subdirectories of the main program root) with a very long path, and I don't want to type it in every time I want to pass the filenames.
To be more precise, I have the function D3DXCreateTextureFromFile() which asks for the filename, and for example, I have the files a.png, b.png, c.png located in the "...\Materials\Textures\Textures_For_This\Textures_For_That\More_Specific_Texture_Location\" subdirectory of the main program. How can I register this path and add it to the filename argument in a lean and mean way?
Like some sort of this:
D3DXCreateTextureFromFile(bla, **DECLARED_DIR** + a.png, bla)
//same for b.png, c.png
Or even a subfunction that unifies the path and the filename:
D3DXCreateTextureFromFile(bla, UnifyText(DECLARED_DIR, a.png), bla)

Yep, easiest to make a function to combine the directory with the filename and include separator if required. Because you want to pass this as an argument, you want to avoid allocating a string and having to clean it up... So use the C++ string class.
I generally do something like this:
string TexturePath( LPCSTR filename = NULL )
{
if( filename == NULL ) return string(TEXTURE_DIR);
return string(TEXTURE_DIR) + "\" + filename;
}
Of course, you'll be relying on that string being implicitly cast to LPCSTR later. If you want to be more explicit then do the (slightly ugly) call to string::c_str():
D3DXCreateTextureFromFile(bla, TexturePath("a.png").c_str(), bla)
[Edit]
Another way, if you are doing this single-threaded, is to use a static buffer:
LPCSTR TexturePath( LPCSTR filename = NULL )
{
static char pathBuf[MAX_PATH];
if( filename == NULL ) return TEXTURE_DIR;
sprintf( pathBuf, "%s\\%s", TEXTURE_DIR, filename );
return pathBuf;
}

String main_dir="C:\\\Materials\\Textures\\Textures_For_This\\Textures_For_That\\More_Specific_Texture_Location\\";
String files[]={"a.png","b.png","c.png"};
String fullpath=main_dir;
fullpath+=files[0];
D3DXCreateTextureFromFile(bla, fullpath.c_str(), bla);

If you're using C++ why not use a stringstream to concatenate the 2 strings together and then call .c_str() on the resulting string. Or you can use the c-style, strcat into a large enough buffer.

Related

Using std::filesystem output as LPCWSTR

I'm making a program which recursively lists all files in a certain directory and uploads each file separately to an FTP server using WinINet.
The problem I'm encountering is using filesystem::path::filename in the FtpPutFile() function because a LPCWSTR is needed.
Whats the best and easiest way to convert it (or somehow use it as is)?
std::string path = "C:\\Programs";
for (const auto & entry : std::experimental::filesystem::recursive_directory_iterator(path))
FtpPutFile(hIConnect, entry.path().filename(), entry.path().filename(), FTP_TRANSFER_TYPE_BINARY, 0);
The error I get is:
no suitable conversion function from "const std::experimental::filesystem::v1::path" to "LPCWSTR" exists
EDIT: Here is a solution that worked for me, by following Lightness solution:
std::string path = "C:\\Programs";
for (const auto & entry : std::experimental::filesystem::recursive_directory_iterator(path))
FtpPutFile(hIConnect, entry.path().wstring().c_str(), entry.path().filename().wstring().c_str(), FTP_TRANSFER_TYPE_BINARY, 0);
LPCWSTR is Microsoft's obfuscation of the const wchar_t* type, and filesystem paths conveniently have a wstring() member function. As you may recall, C++ strings give you access to their character buffer, too, via c_str().
So, entry.path().filename().wstring().c_str() is a LPCWSTR you can use (ugh!). Be careful to use that immediately, or store the result of wstring() somewhere for as long as you need the LPCWSTR to survive, because wstring() returns by value and you don't want a dangling pointer.
// Untested, but a logical adaptation of your code
const std::string path = "C:\\Programs";
std::experimental::filesystem::recursive_directory_iterator it(path);
for (const auto& entry : it)
{
const std::wstring filename = entry.path().filename().wstring();
FtpPutFile(
hIConnect,
filename.c_str(),
filename.c_str(),
FTP_TRANSFER_TYPE_BINARY,
0
);
}

How to pass in filename as a parameter and opening it properly?

void openfile(const string &db_filename) {
ifstream file;
file.open("db_filename");
if(file.is_open())
{
cout<<"true"<<endl;
}
else cout<<"false"<<endl;}
I have this simple code here that checks if file is open. However, whenever I run this, I get false. which means that the file is not open. I dont know why but Im sure that the files are in the same folder and the file name is entered correctly. Is there anything wrong with this code?
You are passing a string literal "db_filename" to open() instead of passing your db_filename string object. Simply remove the quotes:
file.open(db_filename);
If your version of the STL doesn't support passing a std::string to open(), call the string's c_str() method instead:
file.open(db_filename.c_str());

Is this correct point to free char*

I have this code
{
char *filename = createFilename(file, extension);
...
...
delete[] filename;
}
inline char *DataSet::createFilename(LPCSTR file, LPCSTR extension)
{
char *path = new char[strlen(file) + strlen(extension) + 1];
strcpy(path, file);
strcat(path, extension);
return path;
}
Am I right to delete "filename" in the main function? I get ERROR_INVALID_NAME on delete. I have checked the filename and that is correct.
I know I should be using std::string but this is existing code. Please help
If it's existing code and you can't change createFilename to return a std::string, then how about changing the call site to use std::unique_ptr. It is specialized for arrays and would be a much safer bet than doing delete on your own. See this answer.
An error of type ERROR_INVALID_NAME usually occurs when the directory name, file name or volume label is incorrect. On Windows, you might have to take care of escape sequences. For example, if the path to the file is C:\Folder\File.ext you should use the string C:\\Folder\\File.ext. In addition, some characters may not be accepted by the API you're using even though Windows allows them to be used in file and directory names. See this.

.wav Player : mmioOpen API

I am trying to make an audio player that plays .wav files. I wrote a function ReadWaveFile(CString szFilename) for reading the wave data of the file into the WAVEHDR structure. In this function
BOOL CWavePlay::ReadWaveFile(CString szFilename)
{
hmmio = mmioOpen((LPTSTR)&szFilename,NULL,MMIO_READ);
ASSERT(hmmio); //error here: hmmio=0x00000000
if(hmmio==0)
return FALSE;
....
}
mmioOpen is always returning 0 whenever I pass a filepath to this function for opening the specified file. And what baffles me is when i pass the filepath explicitly in mmioOpen API the code works; i.e., a valid handle is returned.
can some body explain why is this happening??
What will happen when you say
MessageBox(NULL,(LPTSTR)&szFilename,"Foo",MB_ICONINFORMATION);
When passing strings to system functions you will need to pick up the pointer to the raw string. For example, if you want to use an std::string object to build your path you will need to say
mmioOpen(filename.c_str(),NULL,MMIO_READ);
Your cast assumes from CString* to LPTSTR assumes that a CString is binary compatible with a LPTSRT which is not the case. When you write LPCTSTR on szFilename you will invoke a cast operator defined on CStrings that converts it to apropriate format. Did you tried just
hmmio = mmioOpen((LPCTSTR)szFilename,NULL,MMIO_READ);
The last cast does not do anything real here so it should be enough.

how to convert WIN32_FIND_DATA to string?

im using WIN32_FIND_DATA to store the data findfirstfile outputs. i want the file location (C:\file) as a string but i don't know how to get it or any other data from it.
Edit: here is my code
PTSTR pszFileName;
PTSTR pszFileName2[100];
if (search_handle)
{
do
{
pszFileName = file.cFileName;
pszFileName2[loop] = pszFileName;
Sleep(100);
loop++;
std::wcout << file.cFileName << std::endl;
}
while(FindNextFile(search_handle,&file));
CloseHandle(search_handle);
}
WIN32_FIND_DATA is a struct. Check out the cFileName member.
For example:
WIN32_FIND_DATA FindData = {0};
HANDLE hFind = FindFirstFile(pszPattern, &FindData);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
PTSTR pszFileName = FindData.cFileName;
// TODO: Use pszFileName in some way...
} while (FindNextFile(hFind, &FindData));
FindClose(hFind);
}
Update in response to comments
In this example the storage for the string is on the stack, and the same buffer is used for every call. This means that every FindNextFile() overwrites the previous string. You will have to make a copy of the string.
Since you're using C++ and classes in std I suggest you store it in std::string (or better yet, make sure you define UNICODE and _UNICODE and use wstring.) Initializing a new string class will do the allocation and copying on your behalf.
Alternatively you can copy the string using the typical C techniques (for example: using malloc + memcpy, strdup, or similar), but it sounds like you might want a refresher in strings, pointers, and memory allocation in C before you get to that.
By the way -- to check for error, your code compares the find handle against NULL; this is incorrect. FindFirstFile() returns INVALID_HANDLE_VALUE (which works out to (HANDLE)-1) on failure. Additionally, to close the handle you will want to use FindClose(), and not CloseHandle(). (A "find handle" isn't really a handle to a kernel object in the same sense that a file handle, handle to a module, or a thread or process handle is. They've just overloaded the type.)
The problem is that you're storing the address of the filename in your array. Each time that FindNextFile() is called, it replaces the data in the struct with the information for the next file. You need to allocate memory for the string in your array, and then copy the string from the structure to your array (using something like strncpy_s(), probably).
Your code is just storing the pointer to the filename member of the struct, once per found file. If you look at the address each element in the array is pointing to, they're all pointing to the same place.