Problem implementing an extension (dll)

Hi!

I hope that this message finds you well :). I created a DLL with c++ to implement A* into a project I'm developing. When I run the code in repl.it it works perfectly, but when I add the extension in Game Maker and call the function the game never runs, it runs if I remove all the other functions inside the function I'm calling.

Example that does not work when calling the dll and crashes the game:

C++:
GMEXPORT char* obtainNextMove(string matrixSTR, string playerPosSTR, string enemyPosSTR){
    vector<vector<char>> matrix = stringToMatrix(matrixSTR);
    Point goal = obtainPointInfo(playerPosSTR);
    Point start = obtainPointInfo(enemyPosSTR);

    Path* GoalPath = reverse( AStar(matrix, goal, start ) ) ;

    if ( GoalPath ){
        Point nextMovePoint = ( GoalPath ->previous  != nullptr )
                            ? GoalPath -> previous ->point
                            : GoalPath -> point;

        string nextMoveString =  to_string( (int) nextMovePoint.xPos ) + ";" + to_string( (int) nextMovePoint.yPos ) + "\0";
        return (char*)nextMoveString.c_str();
    }
    return (char*) "No Path\0";
}
Example that does work when calling the dll:

C++:
GMEXPORT char* obtainNextMove(string matrixSTR, string playerPosSTR, string enemyPosSTR){
    return (char*) "No Path\0";
}
The error that I get on Game maker is the following:

X://windows/Runner.exe exited with non-zero status (-1073741819)
elapsed time 00:00:03.9759511s for command "C:\ProgramData/GameMakerStudio2/Cache/runtimes\runtime-2.3.2.426/bin/Igor.exe" -j=8 -options="C:\Users\berna\AppData\Local\GameMakerStudio2\GMS2TEMP\build.bff" -v -- Windows Run started at 06/12/2021 14:42:50
"cmd" /c subst Z: /d

elapsed time 00:00:00.0247640s for command "cmd" /c subst Z: /d started at 06/12/2021 14:42:54
"cmd" /c subst Y: /d

elapsed time 00:00:00.0289235s for command "cmd" /c subst Y: /d started at 06/12/2021 14:42:54
"cmd" /c subst X: /d

elapsed time 00:00:00.0259305s for command "cmd" /c subst X: /d started at 06/12/2021 14:42:54
FAILED: Run Program Complete
For the details of why this build failed, please review the whole log above and also see your Compile Errors window.

Thanks for taking part of your time to try and help me! Or just to read this post! :)
 

chamaeleon

Member
C++:
        string nextMoveString =  to_string( (int) nextMovePoint.xPos ) + ";" + to_string( (int) nextMovePoint.yPos ) + "\0";
        return (char*)nextMoveString.c_str();
Don't return memory that is freed when your function returns, such as the char pointer of a std::string. This would not be case if your function has a return type of string instead of a char pointer, but obviously that is not compatible with gms. You could make such variables static so they remain valid at all times, and their memory is not freed by the destructor. Or have a static char array of a sufficiently large size and copy your resulting string into it, and return the char array instead.
 
Last edited:
Don't return memory that is freed when your function returns, such as the char pointer of a std::string. This would not be case if your function has a return type of string instead of a char pointer, but obviously that is not compatible with gms. You could make such variables static so they remain valid at all times, and their memory is not freed by the destructor. Or have a static char array of a sufficiently large size and copy your resulting string into it, and return the char array instead.
Thanks! It worked, but now it always crashes when I call another function, should I make those functions return a static variable? I'm using structs and pointers
 

chamaeleon

Member
Thanks! It worked, but now it always crashes when I call another function, should I make those functions return a static variable? I'm using structs and pointers
Non-static local variables allocated on the stack can't be returned (because the stack unwinding on returning from the function will free up the used memory and call destructors as needed). That's the best I can say without specific code. A static variable will not be allocated on the stack and can be returned, with the understanding that doing so is not thread-safe. This shouldn't be an issue for the most part with GMS though, since the engine is single-threaded for code execution.
 
Non-static local variables allocated on the stack can't be returned (because the stack unwinding on returning from the function will free up the used memory and call destructors as needed). That's the best I can say without specific code. A static variable will not be allocated on the stack and can be returned, with the understanding that doing so is not thread-safe. This shouldn't be an issue for the most part with GMS though, since the engine is single-threaded for code execution.
Do you have any guide or resource that I could use to understand how to correctly develop dlls? Because my code works on repl.it or visual studio but it's not working on game maker :( and I don't want to bother you by asking to read all the code.
 

chamaeleon

Member
Do you have any guide or resource that I could use to understand how to correctly develop dlls? Because my code works on repl.it or visual studio but it's not working on game maker :( and I don't want to bother you by asking to read all the code.
Most of the code is probably irrelevant. What matters from gms point of view and from an api point of view is the function declaration. In other words, the function parameters and the return type. Also how variables that are used as return values are defined and the line with the return statement. All code that populates return variables or does other things is probably more or less irrelevant if the code does not have bugs that cause memory corruption.

So from your first post the following would have been sufficient to determine the problem.
Code:
GMEXPORT char* obtainNextMove(string matrixSTR, string playerPosSTR, string enemyPosSTR){
    ... 
    if (... ){
        ... 
        string nextMoveString = ... ;
        return (char*)nextMoveString.c_str();
    }
    return (char*) "No Path\0";
}
Looking at the function declaration again, I'm not sure it is a good idea to have string as an argument since the function call will be assuming it is a C function (gms doesn't know anything about C++), and it should probably be char pointers instead. Just something to keep in mind for other functions.
 
Last edited:

YellowAfterlife

ᴏɴʟɪɴᴇ ᴍᴜʟᴛɪᴘʟᴀʏᴇʀ
Forum Staff
Moderator
Regarding the string issue quite so specifically, a simple workaround would be to declare the return-string as static, like so
Code:
GMEXPORT const char* obtainNextMove(const char* _matrixSTR, const char* _playerPosSTR, const char* _enemyPosSTR){
string matrixSTR = _matrixSTR;
string playerPosSTR =_playerPosSTR;
string enemyPosSTR = _enemyPosSTR;
...
static string ret{};
ret = ...;
return ret.c_str();
}
I would also not take chances with "string" arguments being a char pointer (which is what GM will give you)

It is not a guide, but you can find a handful of various DLLs I made on GitHub, which can be used as reference.
 
Top