I'm building a program, that creates and deletes directories. I use the MSVC compiler (Visual Studio 2017), which is the reason i can't use "getcwd()" or "dirent.h" respectively.
I have tried several different ways to get the current working directory, but there was always a problem. I managed to print the cwd with "_wgetcwd()". However I couldn't find how I could convert it's output to use it in "_rmdir()" or "_wrmdir()" in my research.
My main goal is to be able to remove a directory without having to install some new compiler. If this condition is met, any help is appreciated, because I already tried to install a different Compiler, but I didn't got it to work. I also tried different ways to get the cwd and convert it into the desired datatype, but nothing worked with the scope of my IDE and my very basic knowledge.
I'm pretty much a beginner in programming and I'm learning with this book, that unfortunately uses "dirent.h". The following is a snippet of my current code, where I have eradicated all errors. However I still get this last annoying exception:
#include <iostream>
#include <stdlib.h>
#include<string>
#include <direct.h>
int main() {
int t = 0;
std::string str;
char xy = ' ';
char* _DstBuf = &xy;
const char* uniquename;
uniquename = _getdcwd(0, _DstBuf, _MAX_PATH);
std::cout << "Current path is ";
while (*uniquename != '\0') // this pointer points to the beginning of my path
{
std::cout << char(*uniquename); // prints one char of the path name for each iteration
char var = char(*uniquename);
str.push_back(var); //here the exception below is thrown.
// jump to the next block of memory
uniquename++;
}
std::cout << str <<'\n';
const char* lastchance = str.c_str();
if (_wchdir('..')) {
std::cout << "changing directory failed.\n";
}
if (_rmdir(lastchance) == -1 && errno == ENOTEMPTY) {
std::cout << "Given path is not a directory, the directory is not empty, or the directory is either the current working directory or the root directory.\n";
}
else if (_rmdir(lastchance) == -1 && errno == ENOENT)
{
std::cout << "Path is invalid.\n";
}
else if (_rmdir(lastchance) == -1 && errno == EACCES)
{
std::cout << "A program has an open handle to the directory.\n";
}
else if (_rmdir(lastchance)) {
std::cout << "removing directory still not possible\n";
}
}
This is the exception I get:
Unhandled exception at 0x6E656D69 in Experimentfile.exe: 0xC00001A5: An invalid exception handler routine has been detected (parameters: 0x00000003).
So if you are going to program in a C style (even though you have a C++ compiler) you're going to have to learn how arrays and pointers work. It's far too big a topic to learn from the internet you need a good book.
However I'll point out some of the errors
char xy = ' ';
char* _DstBuf = &xy;
const char* uniquename;
uniquename = _getdcwd(0, _DstBuf, _MAX_PATH);
This is just hopelessly wrong. It compiles but that doesn't mean it's going to work. This is what's required
char DstBuf[_MAX_PATH];
_getdcwd(0, DstBuf, _MAX_PATH);
_getdcwd requires an array which is passed as a pointer (see, you need to learn about arrays and pointers).
Then you attempt to print out the result and assign the result to a string. Again the code is way more complex than it needs to be. Here's the simpler version
std::string str = DstBuf;
std::cout << str <<'\n';
Then you attempt to change directories. I don't know why you are using the wide version _wchdir when you have narrow strings, use _chdir instead. And again the parameter is incorrect, '..' is a multi character literal, but _chdir requires a C-string. Here's the correct verison using _chdir.
if (_chdir("..")) {
std::cout << "changing directory failed.\n";
}
Then you try to remove a directory four times, obviously you can't remove a directory more than once.
And so on, and so on.
There are several problems with your code. I think your exception is caused by this line:
uniquename = _getdcwd(0, _DstBuf, _MAX_PATH);
Which is calling _getdcwd and passing it a pointer to a single character, while also specifying that the pointer can hold up to _MAX_PATH. This is completely undefined behaviour.
To fix this issue I think you should completely change what you are doing. Your code reads like C code, apart from the scattered C++ parts like std::string. Since C++17 the standard library has support for filesystem manipulation built in. Therefore you can rewrite your code using std::filesystem in a much more concise and safe way.
... // get path to delete and store it in del_path (del_path should be an std::filesystem::path object)
std::filesystem::remove_directories(del_path); // Recursive delete the directory specified by del_path
...
Note: std::filesystem::remove_directories will throw an std::filesystem::filesystem_error if it fails.
You can obtain the current directory using std::filesystem through std::filesystem::current_path.
I also recommend picking up a better book on C++ since this one does not seem to be teaching you good practices
Related
Edit 1: This is NOT a duplicate of this question because that person wants to know how to get the executable path. I can get the path just fine, I'm just wondering if there's a more convenient way of USING the path without modifying hundreds of calls to ifstream in my code.
Edit 2: First I'm moving Edit #1 to the top because people still think that the other question answers mine. It does not. I am NOT asking that question. Also I'm going to clarify that my initial question was probably phrased incorrectly. Someone posted in the comments a solution that works for me.
Here is how I would rephrase my question.
I have a program which spawns child processes, in other words it's a program that runs other programs. Program A spawns B, C, D, E, F, etc. My files are organized like so: Program A is at "dir/a/" and B is at "dir/a/b/" and C is at "dir/a/c/" etc.
Now assuming that Program B is written in C++ and it uses ifstream to deal with reading and writing files, my initial question was "is there a way to write Program B's code so that it can deal with reading in files from the executable directory and not from the directory it was spawned?" Because when A runs B, B thinks it's in "dir/a/" when the file is in "dir/a/b/file.txt". So calling ifstream("file.txt") goes to the wrong place. I would need to rewrite the code to ifstream(prefix + "file.txt") everywhere in Program B's code instead. This also needs to work such that I can run B directly rather than through A. Other programs like C, D, E, F, etc. may or may not be written in C++ and may or may not have implemented their own ways of handling this problem.
Many of the solutions posted are just different ways of doing the prefix + filename trick which I already knew about before I asked this question.
The solution posted below that I found which worked is to just change the directory before spawning the process. Thus when the process is spawned, it thinks it is in the new directory. This solves my problem because now it will work for any program, whether it is B, C, D, E, etc or whether it is in C++ or uses ifstream at all. This should have been really obvious in hindsight but for some reason I didn't realize I could run a command to change the directory before spawning the process. So this works for my case and so that's why it's the answer to my question.
Other people keep writing answers to the wrong question, though maybe that's my fault for not writing in the right way or understanding what my problem really was in the first place.
-- The original post --
I have two programs. Program A spawns the child process Program B. Program B uses std::ifstream("file.txt") to read in a file. However, Program A is located in /programs/a/ while Program B is in /programs/a/children/b/. Meaning that when I spawn B as a child process, Program B thinks its current directory is /programs/a and looks for file.txt there (but file.txt is actually in /programs/a/children/b).
I know that the first argument passed to Program B is always the executable path, so I can just take that and modify my calls to file I/O functions to prefix the path with that. But how do I do this in a good way? Is there a convenient way to get this to work without changing much code in Program B? I want to be able to run Program B on its own, as well as from Program A, and in both cases it should be able to read file.txt. That's why I can't just hardcode the filepath, I want it to work in both cases without changing any code between them.
For instance, I can think of making a wrapper class and then changing every call of std::ifstream so that it calls the wrapper instead, kind of like this:
std::ifstream Wrapper::ifstream(const std::string& path)
{
return std::ifstream(prefix + path); // prefix is member variable
}
But for a project with many calls to ifstream this is inconvenient and possibly inconsistent. Any accidental use of ifstream directly could cause a crash. I would also need to do the same thing for any output filestreams too. And I would really like to minimize any external dependencies, so I'd prefer if there was a built-in way to avoid this problem. It would be nice if I could just toggle some kind of option in the ifstream class so that any call to ifstream("file.txt") becomes ifstream("path/to/file.txt"). Is this the best I can do, or is there any better option out there?
There is a standard (since C++17) way to figure this out. It is important to notice that as this is standard, it should work with every platform where C++ is implemented: Windows, Linux, MacOS, etc. The standard will take care of handling idiosyncrasies as separators.
You can use std::filesystem::canonical to get the current executable's location. Then get the location of the parent directory and append the filename you want.
An example is:
namespace fs = std::filesystem;
int main( int argc, char* argv[] ) {
fs::path path( fs::canonical( argv[0] ) );
fs::path file = path.parent_path() / "file.txt";
std::cout << file << std::endl;
}
This should print /programs/a/children/b/file.txt on Linux or Mac and "c:\Programs\A\Children\b\file.txt" on Windows (if paths are like that).
Here is an illustration of using an environment variable as a common information that any program started from a shell in which it is defined can use. The writer writes a short text to a file, the reader reads it out. Both programs try to read the environment variable A_B_PROG_FILE which should contain the path to a file (which may not exist). This variable name is hard-coded common information; if we had the opportunity to use a common header (but not a duplicated one!) we could as well communicate the path itself that way; this is something to consider. But let's assume that the programs do not share a build environment.
First the writer.cpp:
#include <iostream>
#include <fstream>
#include <cstdlib>
/** #return -1 if file could not be opened, -2 if a write error occurred,
or 0 on success
*/
int main(int argc, char **argv)
{
const char *envPath = std::getenv("A_B_PROG_FILE");
const char *fpath = envPath ? envPath : "/tmp/defaultname.txt";
std::ofstream f(fpath);
if(!f)
{
std::cerr << "Couldn't open " << fpath << " for writing, exiting\n";
return -1;
}
f << "Hello, file ->" << fpath << "<- here!\n"
<< "This was written by ->" << (argc? argv[0] : "Unknown exe path") << "<-\n";
return f ? 0 : -2; // if f is not good exit with error code.
}
The reader is very similar:
#include <iostream>
#include <fstream>
#include <cstdlib> // getenv()
/** #return -1 if file could not be opened, -2 if a read error occurred,
or 0 on success
*/
int main()
{
const char *envPath = std::getenv("A_B_PROG_FILE");
const char *fpath = envPath ? envPath : "/tmp/defaultname.txt";
std::ifstream f(fpath);
if(!f)
{
std::cerr << "Couldn't open " << fpath << " for reading, exiting\n";
return -1;
}
std::cout << "file ->" << fpath << "<- contains: ->";
char c;
while(f.get(c))
{
std::cout << c;
}
std::cout << "<-";
if(!f.eof())
{
std::cerr << "Read error from ->" << fpath << "<-, exiting\n";
return -2;
}
}
Here is a sample session:
$ ls /tmp
$ unset A_B_PROG_FILE
$ ./reader
Couldn't open /tmp/defaultname.txt for reading, exiting
$ ./writer
$ ./reader
file ->/tmp/defaultname.txt<- contains: ->Hello, file ->/tmp/defaultname.txt<- here!
This was written by ->./writer<-
<-
$ export A_B_PROG_FILE=/tmp/ab
$ ./reader
Couldn't open /tmp/ab for reading, exiting
$ ./writer
$ ./reader
file ->/tmp/ab<- contains: ->Hello, file ->/tmp/ab<- here!
This was written by ->./writer<-
<-
$
I'm working on a simple game engine just for the experience of it. I've realized, though, that I have no idea how to export the user's custom game as its own standalone executable. For example (this is not my actual game engine, it just provides an easy reference for discussion), suppose we had the following very simple code:
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
void RunGame(string question, string answer)
{
string submission;
cout << question << endl;
getline(cin, submission);
if (submission == answer)
cout << "Correct!";
else
cout << "Wrong!";
}
int main()
{
string question;
string answer;
cout << "Enter Question:" << endl;
getline(cin, question);
cout << "Enter Answer:" << endl;
getline(cin, answer);
RunGame(question, answer);
}
In this example the user gets to create their own customized bit of trivia, and then can test it immediately afterwards when RunGame is called. Now I want to be able to save their game with the trivia information they provided as its own .exe (basically it will perform from the call to RunGame onwards). How would I go about doing that?
To be clear, this isn't a question about what is the easiest/fastest way to make a game. It is looking for how to build a standalone, executable file from within code.
If you really want to store data inside the .exe itself:
An executable has a header that defines its size, boundaries and other useful stuff to the Operating System, so, essentially, the operating system knows where the code and data sections start and end, and it finally uses this information to load up the .exe to memory when it is asked to run.
Since the Operating System knows (besides the .exe's file size) where the executable actually ends, this also means that any data pasted after the .exe's "calculated" end (by headers) won't negatively effect the binary. It will still load and execute just fine.
You can abuse this property to concatenate data after the end of the executable.
I'll leave you with this test, using Windows' bundled WordPad application as a 'host' for some other data:
Go to C:\Windows and copy write.exe (WordPad) to another folder, so we can experiment without damaging anything.
Bring to that folder another file, any file will do. In my example, the data file will be a PDF called "myfancyfile.pdf"
Now, open a command prompt and use the COPY command to stitch both files together, making sure the .exe comes first:
copy /B write.exe+myfancyfile.pdf mynewprogram.exe
copy's /B flag means "binary copy", so essentially both files were pasted together without any kind of text or data conversion.
Try to run "mynewprogram.exe". Realize it runs just fine :-)
Self-modifying your .exe with data is not only feasible, it won't negatively effect functionality. Having that said, it is still a ugly way to persist data.
Have fun coding your solution.
You don't want to do this. A better way is to save the trivia in some custom format (for example, .txt, .dat, ..).
Then the game just handles this data.
So first think about the format inside of the .txt for example.
Lets say at first theres a number, indicating which entry this is. Second follows the question and after that the answer follows. This, you must decide for yourself.
Example trivia-data.txt
1
How old is actor X from show Y?
32 years
2
...
...
#include <iostream> // std::cout, std::endl
#include <fstream> // std::ifstream, std::ofstream
using namespace std;
int main()
{
// create file
ofstream ofile("trivia-data.txt");
// define your data
int num_of_question = 1;
string question, answer;
getline(cin, question);
getline(cin, answer);
// write your data to the file
ofile << num_of_question << '\n';
ofile << question << '\n';
ofile << answer << '\n';
// close the file
ofile.close();
return 0;
}
Now that you created your data you just have to build your program in a way you would like to present this. Instead of writing to a file, you should read from a file and print the questions out and compare answers and what not. Look up std::ifstream for reading your file.
At the start you could ask your user if he would like to create a quiz or play one that already exists.
Edit:
Since this sounds a lot like homework I just provide some pseudo code.
I'd go for an approach like this(pseudo code):
print "Would you like to create(c) or play(p) a quiz? Answer(c/p): "
input = get_input() // 'c' or 'p'
if input == 'c'
// now do what I posted with some loops to create a couple of questions
else
print "Please provide an URL to the quiz-data you would like to play: "
url = get_input() // C:/test.txt
// read in data, print out questions, do comparisons and print answers etc
This is infinitely easier than your approach and this also makes it possible for others to create quizzes not just you.
Building an executable is non-trivial. You will first need to comply with the target operating systems' ABI so that it can find your program's entry point. The next step will be deciding how your program is going to be able to access system resources: probably you'll want your executable to implement dynamic linking so it can access shared libraries, and you'll need to load the various .dll or .so files you're going to need. All the instructions you'll need to write for this will vary from OS to OS, you may need to introduce logic to detect the exact platform and make informed decisions, and you will need to vary for 32 vs 64 bit.
At this point you're about ready to start emitting the machine instructions for your game.
A reasonable alternative here is (as done by Unity) to provide a "blank" executable with your engine. Your engine itself would be a shared library (.dll or .so) and the blank executable would simply be a wrapper that loads the shared library and invokes a function in it with a pointer to something in it's data section.
Generating your user's executable would comprise loading the appropriate blank, making platform-specific modifications to it to tell it the size of the data section you're intended to provide it with and writing your data in the appropriate format. Or, you could simply have a blank that has an embedded copy of the raw structure into which you write values, just like populating a struct in memory:
struct GameDefinition {
constexpr size_t AuthorNameLen = 80;
char author_[AutherNameLen+1];
constexpr size_t PublisherNameLen = 80;
char publisher_[PublisherNameLen+1];
constexpr size_t GameNameLen = 80;
char name_[GameNameLen+1];
constexpr size_t QuestionLen = 80;
constexpr size_t AnswerLen = 80;
char question_[QuestionLen+1];
char answer_[AnswerLen+1];
};
static GameDefinition gameDef;
#include "engine_library.h" // for run_engine
int main() {
run_engine(&gameDef);
}
You'd compile this againsst the shared-library stub for your engine, and emit it as an executable, then you'd look up the platform-specific details of the executable format, locate the position of "gameDef" in it. The you'd read the blank into memory, and write it out with the definition of "gameDef" replaced with the one based on user input.
But what many engines do is simply ship or require the user to install a compiler (Unity relies on C#). So instead of having to tweak executables and do all this crazy platform-specific stuff, they simply output a C/C++ program and compile it.
// game-generator
bool make_game(std::string filename, std::string q, std::string a) {
std::ostream cpp(filename + ".cpp");
if (!cpp.is_open()) {
std::cerr << "open failed\n";
return false;
}
cpp << "#include <engine.h>\n";
cpp << "Gamedef gd(\"" << gameName << "\", \"" << authorName << \");\n";
cpp << "int main() {\n";
cpp << " gd.q = \"" << q << \"\n";
cpp << " gd.a = \"" << a << \"\n";
cpp << " RunGame(gd);\n";
cpp << "}\n";
cpp.close();
if (!invoke_compiler(filename, ".cpp")) {
std::cerr << "compile failed\n";
return false;
}
if (!invoke_linker(filename)) {
std::cerr << "link failed\n";
return false;
}
}
If "RunGame" is not part of your engine but user-supplied, then you could emit that as part of the cpp code. Otherwise, the intent here is that it's making a call into your library.
Under Linux you might compile this with
g++ -Wall -O3 -o ${filename}.o ${filename}.cpp
and then
g++ -Wall -O3 -o ${filename} ${filename}.o -lengine_library
to link it against your engine's library.
I'm trying to open a directory, the name of which (path) is currently in a std::string read in originally from a .csv file (although I don't think that changes anything about the string itself). Calling opendir(path.c_str()) returns NULL. I tried the following code, doing the conversion outside of opendir():
DIR *dir;
bool first = True;
string level = "";
struct dirent *ent;
const char * c = path.c_str();
// A
if ((dir = opendir(c)) != NULL){
// do stuff
// should open the directory and go here
}else{
// always ends up here
}
While this failed with path="LeanDataBase", a directory in the project folder, substituting opendir("LeanDataBase") for opendir(c) does seem to open the directory. However, this function is recursive, so I can't hard code this value or it doesn't work and falls into an infinite loop.
I also tried printing the types, with the following two lines inserted right after "A" in the previous code:
cout << typeid(c).name() << endl;
cout << typeid("LeanDataBase").name() << endl;
Which yielded the following output:
PKc
A13_c
Does this mean that I'm passing the wrong type to opendir()? It seems like it can handle PKc, but not A13_c. Is there a way to convert the path string to the proper type?
Looking at my crystall ball, I see the following issue: path is modified (or even leaves the scope) after path.c_str() is called, but before opendir() is called. It is usually a bad practice to remember result of c_str() in any variable, as it leads to issues like this. c_str() is intended for in-place usage, like following
opendir(path.c_str());
I'm loading a delphi dll in c++. When I use functions with char* as buffers (char* given as parameter to the procedure) I get only trash data.
When I have functions that return char* all is fine.
I'm new to c++ and I spend a lot of time trying to crack this. Please help.
Everything is explained in code below. I have put there 3 functions to show exacly what I mean.
Example function that has problem with buffer is:
DLL_PingConnection(var avXml:PChar):Boolean; - it returns true/false, as parameter it takes buffer and the function is done in buffer there should be valid xml (but there is only trash)
#include <windows.h> //this will load delphi dll
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string.h>
using namespace std;
// ------------------------------------------------ pointers on functions inside Delphi DLL (32 bits)
typedef bool(*TYPE_DLL_SetLicense)(char*, char*); //initialize dll stuff - I load licence from a file into char* - everything works fine
typedef bool(*TYPE_DLL_PingConnection)(char*); //the char* is buffer - I give empty char* as parameter and I should get correct xml with serwer data - I GET ONLY TRASH :(
typedef char*(*TYPE_DLL_ERR_DESCRIPTION)(void); //this function does not use buffer it returns char* - everything works fine
//so as you see problem is with buffers and function like this: DLL_PingConnection(buffer)
int main()
{
// ------------------------------------------------ Loading the library
HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\full_path\\SOMEDLL.dll");
//checking the library
if (hGetProcIDDLL == NULL) {std::cout << "Could NOT load the dynamic library" << std::endl;return EXIT_FAILURE;}
else{std::cout << "dynamic library loaded" << std::endl;}
// ------------------------------------------------ START: resolving functions adresses
TYPE_DLL_SetLicense DLL_SetLicense = (TYPE_DLL_SetLicense)GetProcAddress(hGetProcIDDLL, "DLL_SetLicense");
if (!DLL_SetLicense) {std::cout << "Could NOT locate the function: DLL_SetLicense" << std::endl;return EXIT_FAILURE;}
else{std::cout << "Function DLL_SetLicense located" << std::endl;}
TYPE_DLL_PingConnection DLL_PingConnection = (TYPE_DLL_PingConnection)GetProcAddress(hGetProcIDDLL, "DLL_PingConnection");
if (!DLL_PingConnection) {std::cout << "Could NOT locate the function: DLL_PingConnection" << std::endl;return EXIT_FAILURE;}
else{std::cout << "Function DLL_PingConnection located" << std::endl;}
TYPE_DLL_ERR_DESCRIPTION DLL_ERR_DESCRIPTION = (TYPE_DLL_ERR_DESCRIPTION)GetProcAddress(hGetProcIDDLL, "DLL_ERR_DESCRIPTION");
if (!DLL_ERR_DESCRIPTION) {std::cout << "Could NOT locate the function: DLL_ERR_DESCRIPTION" << std::endl;return EXIT_FAILURE;}
else{std::cout << "Function DLL_ERR_DESCRIPTION located" << std::endl;}
std::cout << "\n\nInitialization over. \n\n" << std::endl;
// ------------------------------------------------ START: calling functions from delphi dll
//DLL_SetLicence - this function take buffer as parameter, but dont return anything into the buffer. All works fine.
//start - we read licence from file
char buffer_licence[1242];
memset(buffer_licence,0,sizeof(buffer_licence));
//I read content of buffer_licence usinf ifstream from the file here (but I don't put the code, to keep sample minimal)
//we set licence with dll function
bool is_licence = DLL_SetLicense(buffer_licence,(char*)"");
//the output
if (is_licence == TRUE)
std::cout << "Licence has been set\n";
else
std::cout << "Licence has been NOT set\n";
//DLL_PingConnection - it takes empty buffer as parameter, it should save xml into buffer but it saves only trash.
//we try to save ping to the file - buffer
char buffor_ping_xml[2000];
memset(buffor_ping_xml,0,sizeof(buffor_ping_xml));
//this should gieve proper xml, but it returns only trash.... please help
bool is_ping = DLL_PingConnection(buffor_ping_xml);
if(is_ping)
{
std::cout << "DLL_PingConnection True\n"; //function returned true, so it worked correct.
std::cout << buffor_ping_xml; //but in the buffer is trash that I show on the screen. I also tried to put buffor_ping_xml info the file (diferent ways) but always result was trash just like on screen.
}
else
{
std::cout << "DLL_PingConnection False: \n";
}
//DLL_ERR_DESCRIPTION - if will automaticly return error description if there is any error to report. No buffer, no problems.
std::cout << buffor_ping_xml; //the data on screet is fine, so is in file and everywhere else.
return EXIT_SUCCESS;
}
PingConnection function will return only this instead of good xml.
EDIT:
Oroginally I used Netbeans + MinGW, but as suggested in comments I have used alternative compilers: Borland builder c++ 6.0, and Embarcadero RAD Studio XE3 (C++ Builder). The problems stayed the same even thou I used all calling convention types Remy Lebeau mentioned.
typedef bool(*TYPE_DLL_PingConnection)(char*); //standard calling convention default for compiler - returns trash
typedef bool(__cdecl *TYPE_DLL_PingConnection)(char*); //returns trash also
typedef bool(__stdcall *TYPE_DLL_PingConnection)(char*); //doesnt write anything to the buffer
typedef bool(__fastcall *TYPE_DLL_PingConnection)(char*); //returns trash
I have encountered small problem under c++ builder. I can't clean buffer under this enviroment:
memset(buffer,0,sizeof(buffer)); // will crash the program under c++ builder
Trying to use 'char *&' will crash the program also.
typedef bool(__cdecl *TYPE_DLL_PingConnection)(char*&);
OR
typedef bool(__stdcall *TYPE_DLL_PingConnection)(char*&);
OR
typedef bool(__fastcall *TYPE_DLL_PingConnection)(char*&);
char * buffer;
bool is_ping = DLL_PingConnection(buffer);
Using char ** will cause type mismatch with buffer.
EDIT2:
As requested by David Heffernan I attach sample of documentation. Important parts are trasnated to english. Rest is just structure of xlm that PIngConnection should return. Not much of help there - entire documentation is like this.
PS: I asked similar question here: Trash characters when using buffers in c++ - code based on WxWidgets (I though WxWidgets creates the problem, but it doesn't. Maybe someone will find WxWidgets code usefull thou).
EDIT 3:
I managed to get some more information about dll.
Delphi version is 7.
For sure calling type is stdcall. ( DLL_PingConnection: function(var avXml: PChar): Boolean; stdcall; )
This is how a function from this dll is called in delphi:
lPointer := nil; //pointer
lOSOZPointer := nil; //pointer
lpXML := nil; //pChar
lpXML:=StringToPChar(lXML);
lPointer := lpXML;
lWynik:=OSOZ_GetServerDataTime(lpXML);
if lWynik then
begin
lOSOZPointer := lpXML;
//akcja na wyniku
end;
if lPointer <> nil then begin
Freemem(lPointer);
end;
if lOSOZPointer <> nil then begin
OSOZ_FreeMem(lOSOZPointer);
end;
DLL_PingConnection(var avXml:PChar):Boolean;
This is not a full declaration. Obviously, it is a function since it has a Boolean return type. But does it also declare a calling convention as well - stdcall (__stdcall in C/C++) or cdecl (__cdecl in C/C++)? If not, then it is using Delphi's default register convention instead (which is __fastcall in Borland/CodeGear/Embarcadero C++ compilers only, but has no equivalent in any other C/C++ compiler). Your existing typedefs are using your C++ compiler's default calling convention, which is usually __cdecl. Calling convention mismatches are the most common problem with using DLLs, as it causes mismanagement of the call stack, which affects how parameters are passed, accessed, and cleaned up.
Also, what version of Delphi was the DLL written in? PChar is PAnsiChar (char* in C++) in Delphi 2007, but is PWideChar (wchar_t* in C++) in Delphi 2009 and later. Chances are, since the data is XML, then PAnsiChar/char* is likely being used.
Also, the PChar parameter is being passed as a var in the Delphi declaration, which is the same as a pointer in C and a reference in C++.
You need these important pieces of information in order to use this DLL function in C/C++ code. Unless the documentation explictly states these details, or the DLL has a C/C++ .h/.hpp file showing the actual declaration, then the best you can do is guess, and there are several variations possible given the incomplete declaration you have shown so far:
(char*& can be replaced with char** if needed):
typedef bool (__cdecl *TYPE_DLL_PingConnection)(char*&);
typedef bool (__stdcall *TYPE_DLL_PingConnection)(char*&);
typedef bool (__fastcall *TYPE_DLL_PingConnection)(char*&);
typedef bool (__cdecl *TYPE_DLL_PingConnection)(wchar_t*&);
typedef bool (__stdcall *TYPE_DLL_PingConnection)(wchar_t*&);
typedef bool (__fastcall *TYPE_DLL_PingConnection)(wchar_t*&);
If the DLL functions are using cdecl or stdcall, then you are OK, as most C/C++ compilers support those calling conventions. However, if the DLL functions are using register instead, and if you are not using a Borland/CodeGear/Embarcadero C++ compiler, then you are SOL. You would have to wrap the DLL inside another Delphi-written DLL that exports wrapper functions that use more portable signatures.
I debugged my application and the code crashes instantly upon the throw statement inside of this code:
try
{
char newbuff[8];
if(strlen(cstr) > sizeof(newbuff))
{
throw BUFFER_TOO_SMALL;
}
if(strlen(cstr) == 0)
{
throw NO_CONTENT;
}
strcpy(newbuff, cstr); //Yeah yeah yeah, I know, I'm just learning
ptr = newbuff;
}
catch(int errn)
{
cout << "error: ";
if(errn == BUFFER_TOO_SMALL)
{
cout << "storage buffer too small.\n";
return 0;
}
if(errn == NO_CONTENT)
{
cout << "no content inside of buffer.\n";
return 0;
}
}
So, upon debugging it crashes right on the throw statement. Interestingly enough, the CLI (in this case, 'cmd.exe') shows this message (which was not put in there by me, and is either from the compiler or the OS):
This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.
I'm leaning more towards C++ now, as I used to just program in C. As you can tell, right now I am trying to manage the try-catch exception handling system that C++ uses.
Your if statement looks incorrect: the name newbuff indicates a pointer, and the size of that will be 4 on a 32-bit system or 8 on a 64-bit system. Oh, right, after I posted that you've edited you code to show the definition of newbuff as an array. But anyway.
The throw can crash if there is no handler. In this case the standard does not require that the stack is rewound (local objects destroyed).
It seems that BUFFER_TOO_SMALL is a constant, probably an integer. You should not throw integers (unless you really know what you're doing). Throw std::exception objects, e.g. std::runtime_error.
Edit: Your updated code shows that you're catching int. That means your uppercase constants are not int. But the advice stands anyway.
There is also a style issue, the use of ALL UPPERCASE for a constant. Don't. That's a Java-ism: in C and C++ by convention all uppercase is for macros and macros only.
Cheers & hth.,
It seems that newbuff don't have space for NULL terminator. you should resize newbuff[8] to newbuff[9].