Jump to content


Photo
* * * * * 5 votes

How To Make A Dll


  • Please log in to reply
24 replies to this topic

#1 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14384 posts
  • Version:GM:Studio

Posted 10 November 2008 - 07:21 PM

  • Title: How To Make A Dll in C++
  • Description: describing the necessary steps to create a DLL in C++
  • GM Version: - (ANY)
  • Registered: yes
  • File Type: In-topic tutorial, with a supportive zip file.
  • File Size: 2.5 MB
  • File Link: Download cpp_dll_tutorial.zip from Host-A
  • Required Extensions: -
  • Required DLLs: -
Summary
This tutorial will teach one what the necessary steps are to write a DLL in C++. The tutorial covers everything from creating a C++ project to using the exported DLL in gamemaker. As prerequiste one should have decent understanding of C++, classes, pointers, heap vs stack are used without explanation.

*Revised and modified by paul23
*Last edited: 10/01/2013
*original tutorial
http://gmc.yoyogames.com/index.php?showtopic=405812&view=findpost&p=3868901


Introduction
Making a DLL is not that hard, but setting up the very first time is tedious. This tutorial teaches the specific points of DLLs, how to create a dll & how to call your own created dlls from Gamemaker.
To fully understand this tutorial a solid knowledge of C++ should already be gained. Please remember the GMC is aimed at Gamemaker, and hence specific questions about the C++ language should be asked elsewhere.


The included zip file contains examples used through this tutorial. Each example (except the last) contains 2 folders, the c++ folder with the source & the GM folder with a click 'n go compilation of the example. The supportive zip can be found here.

Part 1-Setting Up
As this tutorial covers writing dlls in C++ you'll need an IDE/compiler for C++. While there are many more compilers this tutorials uses 2 compilers, both are free:
Visual studio express. Excellent ide with integrated compiler, the intellisense is among the best features, and the integrated debugger is just amazing. If you ever wish to get too serious into developing for windows, you can easily switch to the (expensive) professional options.
Code::Blocks Another amazing option. Comes with the GCC compiler - and having by the most features of the new C++11 standard implemented. Also if one wishes to work cross-platform this is the ide of choice.

One thing I strongly urge everyone: do not use DEV-C++. This ide hasn't been updated for years, has many bugs and lacks many features. The compiler which comes with default to this IDE is subpar in performances and not up to date.


Create an DLL with visual studio
  • Go to file -> new -> new project (ctrl+shift+N)
  • select "win32 application" and type your DLL name in the name field
    Posted Image
  • Press ok
  • Press next
  • Choose as application type "DLL" & tick the box "empty project"
    Posted Image
  • Press finish
  • Add a C++ source file.
    • right click source folder
      Posted Image
    • select C++ file - give it your "main" name)
      Posted Image
  • Compiling is done with pressing F7 (or F5).
  • When developing make sure the project configuration is set to "debug" (see middle top of visual studio bars).
  • When releasing make sure the project configuration is set to "release"
    Posted Image
  • Dlls are found in the <PROJECTNAME>/debug or <PROJECTNAME>/release folder. (up to the reader to decide which one to give to others :P/>/>/>/>)
C-runtime
An often misunderstood concept is that of the C-runtime behaviour. C-runtime are parts that each compiler should provide to handle input-output, and starting/closing applications. In visual studio one can go several ways to "include" this. Either you can link statically -which means the code is included inside the executable- or link dynamically -then a common windows dll is used-. The dlls used are MSVCR100.dll & MSVCP100.dll. Each windows install includes those "by default", however with each new version of visual studio a new dll also comes out, and older OSes might not include the runtime. (IE: windows 98 won't have those dlls, nor will people who choose not to update windows).
If you wish to be 100% sure everyone can run your dll you can link statically. However consider that the dll will then become anywhere between 50 kB & 1 MB larger in size.
To include statically you have to go open the project properties -> c/c++ -> code generation -> runtime library -> select "multithreaded (/MT)" instead of multithreaded DLL. (Or multithreaded debug (/MTd) )
In the last section of this tutorial the c-runtime is explained better.



Part 2 - Writing the DLL base

A first function
Let us first look at a simple function.
#define GMEXPORT extern "C" __declspec (dllexport)

GMEXPORT double SampleFunction(double a, double b) {
	return a * b;
}
Most of it should be easy to understand, but the first line probably looks like gibberish: that line defines GMEXPORT to be a combination of 2 function modifiers. We will return later in great depth to these. Build this small function (press F5 or F7). And you should look into your projectfolder/debug.
There now stays a file with your <projectname>.dll. This is a dll you can use.

Now to call this function from gamemaker. Make first a new project & copy the dll (from above) to the project directory. Now we create 2 scripts: an initialize script, and a "calling" script. - And use 1 object which in the creation event call the initialize script. And the keypress release calls the function:
//InitDLL script:
globalvar foo_FN;

foo_FN = external_define("tutorial2.dll",  "SampleFunction", dll_cdecl, ty_real, 2, ty_real, ty_real);
//Foo script:
return external_call(foo_FN, argument0, argument1)
objDLL
//create event:
InitDLL()

//KeyPress - Enter
var res;
res = Foo(4,5);
show_message(string(res));
And this should work :)/>/>/>/>.
The example files can be found in "/tutorial2/" folder of the included sources.

name mangling
One of the features of C++ is that you can overload functions: foo(double, double) is different from foo(char *, double) or foo(int, int). To handle this, compilers internally change names: the function double SampleFunction(double, double) would be renamed to: "?SampleFunction@@YANNN@Z".

However as we identify functions by string, we wish the name to be left "alone". So we have to disable this name mangling (as it is compiler specific). Visual c++ can also link according to the C language rules, which doesn't have overloading (and no mangling).
In visual studio one defines a function to use "C linkage" by preceding the function with the modifier
extern "C"

This also puts a few more limitations on the formulas:
- Overloading isn't possible: each name must be unique
- Can't use optional arguments
- Can't be used in member functions
- Can't be used with templates

exporting the function
The compiler also needs to know what functions are "exported" - which functions are only used internally and which functions can be called from outside. This is simply done by the function modifier:
__declspec( dllexport )

Calling convention
There are several "conventions" of how a call to a function is made. To understand this fully one needs to understand how the registers, stack & heap work - this is out of the scope of this tutorial. However it should be understood that the convention has to be "similar" at the 2 sides of the line. If gamemaker expects it has to clean up "the stack" - yet the DLL already thought he should clean the stack, the stack becomes corrupted. Similar if both think the other will clean up, there exists a memory leak.
There are 2 calling conventions supported in gamemaker: stdcall & cdecl. cdecl expects the caller (GM) to clean up, stdcall expects the callee (DLL) to clean up.

As you saw in the example above the modifier for the calling convention isn't used: that is because when no convention is specified, visual studio gains the calling convention from the options. - Which are "__cdecl" by default.
Now a more complete description of samplefunction would be:
GMEXPORT double __cdecl SampleFunction(double a, double b) {
	return a * b;
}
Using stdcall is neglectibly faster: it removes literary 1 command from assembly: however it makes the functions -for the compiler- more difficult. And even if you use "c" function style it will mangle the names: a function will get a preceding "_", and after the name of the function the "@" followed by the size of arguments (in bytes) are added. The sample function is renamed to: _SampleFunction@16
Bottom line: just use __cdecl, and don't bother with stdcall.

Now take a second look at the small script above, you should be able to fully understand what was previously gibberish. The GMEXPORT just contains 2 function modifiers.

DLL entry point
Just like normal programs have an entry point: the "main" functions, so do dlls. This entry point is called by default "dllmain" (and there's little reason to change). A base setup for the dll main function is as following:
#include <windows.h>
BOOL WINAPI DllMain(
  HANDLE hinstDLL, 
  DWORD dwReason, 
  LPVOID lpvReserved
)
{
	switch (dwReason)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}
First of all notice the argument types. Those aren't primitive types, they are typedefs, defined in the windows.h header. So if you wish to use the dllmain function, also include <windows.h>. The first argument is a pointer to the dll-handle. But the second argument is more important, it gives a "reason" why the dll is called. I'll summarise the reasons below:
DLL_PROCESS_ATTACH - Is called the moment the library is loaded, and only loaded the first time for that application. - In gamemaker this means the first time you call the function "external_define".
DLL_THREAD_ATTACH - Is called when the dll is attached to a specific thread, and for each thread it is attached it is called. - As gamemaker is single threaded this functionaility isn't used
DLL_THREAD_DETACH - Is called when a dll binded to a specific thread is freed (not used)
DLL_PROCESS_DETACH - Is called when the dll is freed. If the reason for freeing is an application exit, lpvReserved is non-NULL. If the library is freed because of all functions are removed, lpvReserved is NULL.

The return value should be "TRUE" when the library loaded correctly, alternativelly "FALSE" will result gamemaker in displaying the error "failed to load ....".

The dllmain function is perfect to allocate memory from the heap for global use (if necessary). Another standard use for the dllmain is to capturethe hinst of the DLL, which is used in some (GUI) libraries. However do notice that more complex things (loading other dlls, using system calls and many more) are not possible, better is to use an "init" function. Here a technical paper about what should and should not be done in dllmain can be downloaded.


Part 3 - Functions in DLLS
During this part the layout of functions will be discussed. It should already be understood what the function modifiers are and why to use them. This part will also describe many practical challenges (and ways to solve them) c++ programmes face when working for gamemaker. For the rest of this section we consider in each codepiece the <windows.h>, the <string> & the <sstream> headers to always be included - They aren't necessary for dlls (and shouldn't be in your dlls by default). However we use parts of these headers for "debug" messages. Specifically the "MessageBoxA( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);" function & stringstreams to convert numbers to c++ strings in a C++ way. (Note that in more advanced programs you're better of using boost::lexical_cast - however this requires a third party library and isn't included in this tutorial). The start of the main file will hence look like:
#include <string>
#include <sstream>
#include <windows.h>
#define GMEXPORT extern "C" __declspec (dllexport)

template <typename T>
std::string to_string(const T & value) {
    std::stringstream sstr;
    sstr << value;
    return sstr.str();
}

variable types
There are 2 types of variables gamemaker can understand: 64bit floating point precision numbers & null-terminated character arrays. - In c++ represented by "double" & "char *", in gamemaker "real" and "string". All data has to be fitted in these two types.

Passing arguments to C++
Passing doubles is very easy, just define a function to take a double as argument: SampleFunction(double a, double B)/>/>/>
Also passing character arrays is easy: SampleFunction(char *a, char *B)/>/>/> As we work with C++ I strongly urge everyone to translate the character array to a std::string immediatelly, and perform operations on that string. Two examples:
#include <windows.h>
#include <string>
#include <sstream>
#define GMEXPORT extern "C" __declspec (dllexport)


template <typename T>
std::string to_string(const T & value) {
    std::stringstream sstr;
    sstr << value;
    return sstr.str();
}



GMEXPORT double SampleFunctionNumbers(double a, double b) {
	std::string mynum(to_string(a * b));
	mynum += " is the multiple of the given arguments";
	::MessageBoxA(NULL, mynum.c_str(), "message box", MB_ICONINFORMATION);

	return 0;
}
GMEXPORT double SampleFunctionCharArrays(char * a, char * b) {
	std::string mynum(a);
	mynum += std::string(b);
	mynum += " is the given arguments combined";
	::MessageBoxA(NULL, mynum.c_str(), "message box", MB_ICONINFORMATION);

	return 0;
}
Can be found in the "tutorial3" section of the included file.

Returning numbers
Returning a type such as doubles is very easy, just specify the return type (AS DOUBLE!) - and use return ... like you would normally. You can see how to use these things in the examples above. The doubles can be used directly as numbers in gamemaker, and no conversion has to be made.

Returning strings
Strings are a bit more difficult to use than doubles. Basically what you wish to return is a character array. - So set the return type to char *. Now experienced C++ programmers will immediately see a troubling point here (which is a good reason for using std::strings instead of char * as much as possible). the "value" of char * is simply a pointer to the start of the character array. The actual data contained in the array has to be managed manually. If one would go naively and do something like:
char * myfun() {
    char * ret = "hello world";
    return ret;
}
He would have an immediate memory problem: char * points to a piece of memory which was owned by the function. So after the function is called the memory containing the string is freed.

The C-way to solve this problem is to use the heap. Which works - however one has to carefully manage the heap now, remembering to free the returned array:
char * myfun() {
    char* ret = new char[12];
    return ret;
}
Gamemaker won't free the array for you, so if you wish to use the heap you'll have to create a specific "free" function. And make sure that the free function is called each time after the function returning the array.
C++ programmers prefer to not use character arrays in such a case, and simply return a std::string object. - However gamemaker can't handle non primitive objects. So this can't be used either.

The easiest way is to simply use a globalvariable to "hold" the data. (let's call it RetString for now) - As the scope of RetString is global, it won't go out of scope unless the dll is freed. And as there is just a single variable there's no possible memory leak. Use the c_str() member function to get the internal character array of the std::string. So the code would look like:
std::string RetString; //outside function scope, global defined function.
GMEXPORT const char * SampleFunctionCharArrays(char * a, char * b) {
	std::string first(a);
	std::string second(b);

	RetString = first + second;
	return RetString.c_str();
}
The only problem with this is that RetString is not thread safe, so you can't make threaded calls to functions that return strings. (If gamemaker ever supports threads, post a question about it and I'll give a method which is thread safe).
the /tutorial_return/ part of the included zip contains examples with both types of return types.

Pointers, handles & objects
In many, many applications functions aren't so-called "monoliths". A function most of the time doesn't stand on it's own. Many times you have a function that first initializes an objects.. Than 10s of functions that will adapt & change the initialized object. And then a few functions that will read the object. The following sections will use a sample class, which should be defined as a seperate header file:
//SampleClass.h
#pragma once

class SampleClass {
public:
	SampleClass() 
	{
	}
	void SetVals(double a, double b) {
		FirstVal = a;
		SecondVal = b;
	}
	double Multiply() {
		return FirstVal * SecondVal;
	}
	double Sum() {
		return FirstVal + SecondVal;
	}
private:
	double FirstVal;
	double SecondVal;
};

Even gamemaker already does those things: think about datastructures or particles.
The syntax is always like this, and it is good practice to use the same syntax for your own dlls/engines:
-initialize function returns a handle
-Other functions take as first argument this handle.

Now the question is: what to use as a "handle". - A first and easy answer would be "a pointer". However after saying that you should immediatelly think: "but gamemaker can only support character arrays or doubles as return value". So you would have to start thinking about converting a pointer to a double. - Which isn't really recommended, nor considered good practice in a statically typed language such as C++. A better way is to prevent pointers. Below 3 manners to describe this will be presented. First I present a manner which prevents the whole pointer use, which is most compatible with standard C++. And I'll describe 2 ways you *can* use pointers. (as those are slightly faster and require much less setup/are much easier to understand).

Circumventing the need for pointers
This method is very recommended, espcially as all other methods are officially undefined behaviour. (Heck when discussing it with people who have years of C++ experience I won a bet whether the other methods actually work).
Spoiler


Using simple pointers
Spoiler


Using pointers & memcpy
Spoiler

With this you should be able to create your whole advanced DLLS, manage memory & do anything you wish in DLLS.

Part 4 - Building the GameMaker programmers API
For now The gamemaker part has been taken for granted, the included gamemaker files were just used. This part will look into the gamemaker parts of dlls. Some generally accepted "standard" will be discussed. There are some differences between GM 8.1 & GM:studio; when the difference is important a note will be made and you will see [GMS] or [GM81] around it.
First of all realize that one can only create an API if one also knows what is INSIDE the dll, how everything should be called and what the limitations are. In other words: you have to have a documentation, or source code.

Now for gamemaker there are basically three function of importance: external_define, external_call, and external_free.
external_define() is used to define a "function", you have to specify how the function works with that. external_call is then used to execute this function. And external_free is finally used to free the data used by the DLL.

When one uses DLL's it is best practice to leave the DLL in the main executable folder, as to keep all executing code together in a place. Though in very large games one might create a "system" subfolder or something. [GM81]
Note however that from GM:STUDIO onwards it is hard, near impossible to refer to the executing folder when testing the game. As such for GM:Studio the fail safe manner is to include the file. (at the resource includes simply include the dll). Of course if you build dlls for others to use you should consider making it an extension, though building an extension is outside the scope of this tutorial (it starts with the same steps described here). [GMS]


external_define
As said the external_define function is used to "tell" gamemaker that the dll you create has a certain function, the exact syntax is:
external_define(dllname, functionname, calling_convention, return_type, argument_number, arg1_type, arg2_type...)
dllname is simply the name of the dll you call (can also be a relative path). The dllhost windows service will then lookup this name inside the application folder, and if it isn't found there in the system folders.
functionname is the name of the function inside the dll. Please consider once again how name mangling works, about extern "C", and the problem with stdcall.
calling_convention is eitehr dll_cdecl or dll_stdcall. It is decided by your dll, and you should use the calling choose there (normally dll_cdecl). Notice that if you chose dll_stdcall, you will have to take care of the naming convention. (prefix _ & postfix @ + argument size in bytes).
return_type is the return type of your dll, once again make sure it matches the function. It can be either ty_real or ty_string, which corresponds with either doubles or character arrays resp.
argument_number is simply the number of arguments your function takes.
arg1_type...argN_type is for each argument the type your function takes (ty_real or ty_string).

This command returns a number which should be used as "index" for external_call.

The very first call to external_define is (as said) special: the dllmain function will also be invoked with as reason DLL_PROCESS_ATTACH, look it up at the start of the tutorial.

external_call
external_call executes a function you previously defined using external_define. The first argument is hence the value returned by the external_define option. The following functions are arguments which have to be passed to the dll.


external_free
External free frees all "data" from the dll. This function will free all memory, and removes all functions (so they have to be redefined). Recall that if your dll is unloaded during external_free, the dllmain function will be called. With as reason DLL_PROCES_DETACH, and the 3rd argument will be non NULL.

Best practices
Gamemaker is very forgiving as language, so it is hard to make it fail. However that doesn't mean there aren't good & bad ways to do something. Especially with DLLs.
First of all, never ever constantly redefine a function (and free it) when you need it. Secondly, it is a terrible habit to expose external_call to the user of your engine (even yourself at another time). Always wrap external_calls inside a script.

Basically it means each dll comes with an gmres (or other resource) file:
  • It should contain an "init" function, where all functions are defined. Best practice is to store the functions in global variables - if you write an engine prefix those global variables with your engine's. An example:
    globalvar InitFn, DeleteFn, SetFn, MultiplyFn, SumFn;InitFn = external_define("tutorial_class.dll",  "InitializeClass", dll_cdecl, ty_real, 0);DeleteFn = external_define("tutorial_class.dll",  "DeleteClass", dll_cdecl, ty_real, 1, ty_real);SetFn = external_define("tutorial_class.dll",  "SetValues", dll_cdecl, ty_real, 3, ty_real, ty_real, ty_real);MultiplyFn = external_define("tutorial_class.dll",  "Multiply", dll_cdecl, ty_real, 1, ty_real);SumFn = external_define("tutorial_class.dll",  "Sum", dll_cdecl, ty_real, 1, ty_real);
    Better would've been if I prefixed the globalvars to indicate the engine I'm creating.
  • Each "dll-function" should have it's own script. These scripts should be called so external_call doesn't have to be called. As the functions aren't displayed in the help-part of gamemaker, you will have to give some help inside the scripts. So work on a good header format which displays at least those things: -The arguments should be explained, the returned value should be explained and finally side effects/limitations should be explained-.
  • External free is a dubious thing to use. First of all, using it when ending a game is considered "bad practice" in windows: as windows will free dlls loaded by your application anyways. However secondly remember what it does: it unloads all functions. However it doesn't give any indication to the globalvars you used to index functions. The globalvars aren't freed either. So you're left with a lot of indexes pointing towards nothing.
    The question hence becomes: do you wish to support "unloading & reloading" the dll during the game? - If you really have a good reason that a dll should be unloaded halfway, be sure to implement a "free" script. If however the game doesn't make sense without the dll loaded, don't implement it(NOTE 1).
  • When your dll works and your are now developing your game, it is important to be cautious on how you terminate your game when testing it. All you developers out there have run into trouble while programming a game, errors creep in and are eventually reported through the error window. Such errors as unknown variable x or array out of bound... It is important to note that when you have a dll loaded in memory, it is best to try to exit the game normally. So when you get the error dialog box, try to ignore the error until the game continues and then hit the ESC key to quit. There has been reports on erroneous dll related errors being reported when the functions are defined after an improper shut down of the game. This will limit the problem
  • Document your interface! Your work may be useless if there is no documentation on how to use the dll so document how each function works, include relationships with other functions and provide simple 3 liner example of how each function works. This is best to do while you write your c/c++ code while you are designing your interface, document your functions in the c++ source and move the comments to the GM script when defining the GML interface or in the help file if you plan to make a gm extension.

NOTE 1:
  • That said, there are a few pointers for loading and unloading, the best place to load a dll is in a empty boot room (first room). A room only visited when the game starts (or is restarted). In such room you would drop a persistent Controller object. This object will follow you throughout the game in every room. You put the dll init function in the Game Start event of that object, followed by a room_goto_next() call. In the Game End event, there you would free the dll or call the un_init() function provided if any. The method ensures that no object will access the dll when not initialized, which can happen if you init the dll in the create of one object and use the dll features in the create of another object in the same room. Since the dll is loaded in the boot room before any of your game code executes, the functions will be defined and ready to use in the real game room.
  • The Game End is captured by the persistent object since it follows you. This is useful for games that call game_restart() or when you hit the restart menu in the debug window.

Visual studio developing with Gamemaker 8.1 (and earlier)
The following section is completely pointed at visual studio, and hence hidden from normal view. It explains what I figured as a very easy way to program a test case. The ability to test dlls using microsoft's debugging environment.
Spoiler


Visual studio developing with Gamemaker studio
The following section is completely pointed at visual studio, and hence hidden from normal view. It explains what I figured as a very easy way to program a test case. The ability to test dlls using microsoft's debugging environment. This section also uses GM:Studio, as studio has some differences (in filesystem handling) that are of importance for GM. It is mostly a copy of previous section with some differences.
Spoiler






Good luck with writing your dll, if you have any questions feel free to ask them as usual :)/>/>/>/>.


Search Tags
  • GMCTUTORIAL
  • GMCDLL

  • 6

#2 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14384 posts
  • Version:GM:Studio

Posted 28 November 2011 - 03:37 PM

Original Tutorial
Spoiler

  • 0

#3 zehevi

zehevi

    GMC Member

  • GMC Member
  • 637 posts
  • Version:GM8

Posted 29 November 2011 - 02:08 AM

This tutorial is very useful and is very well explained with details.

Many thanks for this one. :thumbsup:
  • 0

#4 NicharCZ

NicharCZ

    GMC Member

  • GMC Member
  • 10 posts
  • Version:GM8

Posted 12 January 2012 - 04:37 PM

Hello there,
tutorial is fine, but I have a problem. In debugging VS2010 shows me this:

1>------ Build started: Project: tutorial_class, Configuration: Debug Win32 ------
1>  main.cpp
1>     Creating library F:\Projects\My DLL's\Tutorials\tutorial_class\C++\Debug\tutorial_class.lib and object F:\Projects\My DLL's\Tutorials\tutorial_class\C++\Debug\tutorial_class.exp
1>     Creating library F:\Projects\My DLL's\Tutorials\tutorial_class\C++\Debug\tutorial_class.lib and object F:\Projects\My DLL's\Tutorials\tutorial_class\C++\Debug\tutorial_class.exp
1>  tutorial_class.vcxproj -> F:\Projects\My DLL's\Tutorials\tutorial_class\C++\Debug\tutorial_class.dll
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

in one of your tutorials..

Would somebody help me to solve this? I really want to create my own DLL and with this error it is impossible. Please, step-by-step tutorial. Thanks a lot!

I've tried to find something about: "Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped". But no solution was there..

Edited by NicharCZ, 12 January 2012 - 04:41 PM.

  • 0

#5 TamoNekiTipo

TamoNekiTipo

    Centurion

  • Banned Users
  • 51 posts
  • Version:GM8

Posted 12 January 2012 - 05:16 PM

Good enough.
Though I probably won't use this info much...
I'm still a noob in C++ <_<

#6 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14384 posts
  • Version:GM:Studio

Posted 12 January 2012 - 06:13 PM

Hello there,
tutorial is fine, but I have a problem. In debugging VS2010 shows me this:

1>------ Build started: Project: tutorial_class, Configuration: Debug Win32 ------
1>  main.cpp
1>     Creating library F:\Projects\My DLL's\Tutorials\tutorial_class\C++\Debug\tutorial_class.lib and object F:\Projects\My DLL's\Tutorials\tutorial_class\C++\Debug\tutorial_class.exp
1>     Creating library F:\Projects\My DLL's\Tutorials\tutorial_class\C++\Debug\tutorial_class.lib and object F:\Projects\My DLL's\Tutorials\tutorial_class\C++\Debug\tutorial_class.exp
1>  tutorial_class.vcxproj -> F:\Projects\My DLL's\Tutorials\tutorial_class\C++\Debug\tutorial_class.dll
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

in one of your tutorials..

Would somebody help me to solve this? I really want to create my own DLL and with this error it is impossible. Please, step-by-step tutorial. Thanks a lot!

I've tried to find something about: "Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped". But no solution was there..


That's not an error, that is a Successfully Compiled message, if it said 1 or more Failed then that would be a problem
  • 0

#7 NicharCZ

NicharCZ

    GMC Member

  • GMC Member
  • 10 posts
  • Version:GM8

Posted 12 January 2012 - 08:00 PM

That's not an error, that is a Successfully Compiled message, if it said 1 or more Failed then that would be a problem


Ok, I know.
But what should I do?

Edited by NicharCZ, 12 January 2012 - 08:13 PM.

  • 0

#8 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14384 posts
  • Version:GM:Studio

Posted 13 January 2012 - 01:17 AM


That's not an error, that is a Successfully Compiled message, if it said 1 or more Failed then that would be a problem


Ok, I know.
But what should I do?


grab the dll and start using it
  • 0

#9 NicharCZ

NicharCZ

    GMC Member

  • GMC Member
  • 10 posts
  • Version:GM8

Posted 13 January 2012 - 08:37 AM

Oh, solved, thanks a lot! :) I didn't know that is so easy :D I'm sorry
  • 0

#10 Debels

Debels

    GMC Member

  • GMC Member
  • 1997 posts
  • Version:GM:Studio

Posted 14 July 2012 - 02:21 PM

Going to try and make a dll for GM with this code :)
  • 0

#11 zehevi

zehevi

    GMC Member

  • GMC Member
  • 637 posts
  • Version:GM8

Posted 27 August 2012 - 12:50 AM

I'm having hard time understanding how to use a DLL for other then mathematical functions.
I'm giving my DLL variables, the DLL then calculate and return a new value,
But I want to extend GM's functionality or speed up slow functions.
Google led me to classes, and right there - I went lost.

Could you explain what it does and if it really is the way to "extend" GM's fanctionality, or atleast lead me to it?

Sorry if it was unclear.
Thanks, Zehevi.
  • 0

#12 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14384 posts
  • Version:GM:Studio

Posted 27 August 2012 - 02:38 AM

I'm having hard time understanding how to use a DLL for other then mathematical functions.
I'm giving my DLL variables, the DLL then calculate and return a new value,
But I want to extend GM's functionality or speed up slow functions.
Google led me to classes, and right there - I went lost.

Could you explain what it does and if it really is the way to "extend" GM's fanctionality, or atleast lead me to it?

Sorry if it was unclear.
Thanks, Zehevi.


You dont need a class.

from post 2, hidden original tutorial
extern "C" {

//Example passing a number
gmexport double SampleFunctionNumber(double count)
{
        //Loop to count
        //Note the cast so the compiler does not show warning, casts are resolved at compile time, 
        //they have little effect at run time
        char formated[20];
        MessageBoxA(GetActiveWindow(),"","SampleFunctionNumber"
, MB_ICONINFORMATION|MB_OK);
        for(int i = 0; i<(int)count; i++)
        {
                wsprintf(formated,"count %d",i);
                MessageBoxA(GetActiveWindow(),formated,"SampleFunctionNumber"
, MB_ICONINFORMATION|MB_OK);
        }
        return (double) 1;
}

//example passing string
gmexport double SampleFunctionString(LPCSTR string)
{
        //Show a message. GetActiveWindow always returns GM's Main window.
        return (double) MessageBoxA(GetActiveWindow(),string,"SampleFunctionString"
, MB_ICONINFORMATION|MB_OKCANCEL);
}

}

  • 1

#13 zehevi

zehevi

    GMC Member

  • GMC Member
  • 637 posts
  • Version:GM8

Posted 29 August 2012 - 05:59 AM

You dont need a class.

from post 2, hidden original tutorial

extern "C" {

//Example passing a number
gmexport double SampleFunctionNumber(double count)
{
        //Loop to count
        //Note the cast so the compiler does not show warning, casts are resolved at compile time, 
        //they have little effect at run time
        char formated[20];
        MessageBoxA(GetActiveWindow(),"","SampleFunctionNumber"
, MB_ICONINFORMATION|MB_OK);
        for(int i = 0; i<(int)count; i++)
        {
                wsprintf(formated,"count %d",i);
                MessageBoxA(GetActiveWindow(),formated,"SampleFunctionNumber"
, MB_ICONINFORMATION|MB_OK);
        }
        return (double) 1;
}

//example passing string
gmexport double SampleFunctionString(LPCSTR string)
{
        //Show a message. GetActiveWindow always returns GM's Main window.
        return (double) MessageBoxA(GetActiveWindow(),string,"SampleFunctionString"
, MB_ICONINFORMATION|MB_OKCANCEL);
}

}


Thanks Icuurd.
This is not the answer I expected to receive but, I do understand it.
Since GM cannot pass resources and object names to the DLL (DLL wouldn't know what to do with it?),
I just gonna have to satisfy with this.

After thinking this over, how do you handle graphics through the DLL?
The DLL is suppose to call, for example, OpenGL functions and communicate directly
with the graphics card and not back to the GM? (like GM>DLL>GPU and not GM>DLL>GM>GPU)

Oh 1 last question that is probably the most important one,
how can I handle arrays?
Just declare them and the data would be saved as long as the DLL is running,
or the DLL cannot save data?

Edited by zehevi, 29 August 2012 - 06:09 AM.

  • 0

#14 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14384 posts
  • Version:GM:Studio

Posted 29 August 2012 - 06:57 AM

Thanks Icuurd.
This is not the answer I expected to receive but, I do understand it.
Since GM cannot pass resources and object names to the DLL (DLL wouldn't know what to do with it?),
I just gonna have to satisfy with this.

You can get GMAPI and you will be able to access GM functions from the dll. And you will be able to pass GM resources to the dll and you can do stuff with the resource through GMAPI, like say passing a sprite and you can draw it in a loop in a function you called in the draw event of an object. You dll wouljd need a drawFunc to be called by from the GM side.

After thinking this over, how do you handle graphics through the DLL?
The DLL is suppose to call, for example, OpenGL functions and communicate directly
with the graphics card and not back to the GM? (like GM>DLL>GPU and not GM>DLL>GM>GPU)

It's complicated to have the dll draw on the graphis card without interfering with GM's drawing. Again, GMAPI would allow you to draw in the GM world.

Oh 1 last question that is probably the most important one,
how can I handle arrays?
Just declare them and the data would be saved as long as the DLL is running,
or the DLL cannot save data?

You cant pass anything but a double or a string to the dll. so you're sortof screwed. However, if you get GMAPI, you can communicate large amount of data back a forth via a ds_list
  • 1

#15 zehevi

zehevi

    GMC Member

  • GMC Member
  • 637 posts
  • Version:GM8

Posted 29 August 2012 - 11:26 AM

Okay, so the answer to my problems is GMAPI.

But let me rephrase my last question: If I declare a variable in the DLL enviroment, will it's value
exist as long as the DLL is "attached" to GM, or will the variable be destroyed once the DLL return the value (or simple end the case).
(aka: is the memory used for the DLL is reserved as long as GM is holding the DLL?).

Thanks again Icuurd.
  • 0

#16 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14384 posts
  • Version:GM:Studio

Posted 29 August 2012 - 04:25 PM

Okay, so the answer to my problems is GMAPI.

But let me rephrase my last question: If I declare a variable in the DLL enviroment, will it's value
exist as long as the DLL is "attached" to GM, or will the variable be destroyed once the DLL return the value (or simple end the case).
(aka: is the memory used for the DLL is reserved as long as GM is holding the DLL?).

Thanks again Icuurd.


Variable scope varies. it depends where you declared the variable in the c++ project

int globalVariable = 10
eport souble func()
{
int localSope = 11;

}

Of course pointers allocated with new or malloc needs to be freed manually later on.
memory should stay allocated until the program quits. Pretty sure even if you unload the dll, the memory is only freed when the program quits. It's part of the OS to rid any memory leaked by the program.
  • 1

#17 zehevi

zehevi

    GMC Member

  • GMC Member
  • 637 posts
  • Version:GM8

Posted 31 August 2012 - 02:15 PM

That was really helpful. Thanks again!
  • 0

#18 markmd76

markmd76

    GMC Member

  • GMC Member
  • 59 posts
  • Version:GM:Studio

Posted 22 December 2012 - 08:15 AM

Should this work in GM Studio? I ask as I have tried it and always get a zero returned following the example. I even tried importing the tutorial2 source provided and that too returns a zero.
  • 0

#19 icuurd12b42

icuurd12b42

    Self Formed Sentient

  • Global Moderators
  • 14384 posts
  • Version:GM:Studio

Posted 22 December 2012 - 06:02 PM

Should this work in GM Studio? I ask as I have tried it and always get a zero returned following the example. I even tried importing the tutorial2 source provided and that too returns a zero.


DLLs are still available in studio. you need to change the type in the GM definition since the tutorial was written at the time for GM6 through 8.1 and those version had a bug in the external_define which forced us to use the wrong export type...
  • 0

#20 paul23

paul23

    GMC Member

  • Global Moderators
  • 3355 posts
  • Version:GM8

Posted 23 December 2012 - 12:55 AM


Should this work in GM Studio? I ask as I have tried it and always get a zero returned following the example. I even tried importing the tutorial2 source provided and that too returns a zero.


DLLs are still available in studio. you need to change the type in the GM definition since the tutorial was written at the time for GM6 through 8.1 and those version had a bug in the external_define which forced us to use the wrong export type...


I hope you're referring to the old-tutorial here? As what the dll says still holds true: if you make a dll that uses cdecl as calling convention (default for C-style functions). GM should also have that in its definition..

calling_convention is eitehr dll_cdecl or dll_stdcall. It is decided by your dll, and you should use the calling choose there (normally dll_cdecl).


This line of the tutorial indicates the choice - normally dll_cdecl, unless you explicitly chose otherwise.

EDIT: I did some extensive testing and the return example indeed shows errors. Though I already found the culprit: some kind of silly stupidity on my part.. I honestly forgot the word "return" in the scripts. Previously gamemaker had a bug where if you forgot the return statement in a script, it would return the last calculated value.

So change the scripts "String" to:
return external_call(StringFn, argument0, argument1);
and "Real" to:
return external_call(RealFn, argument0, argument1);

I'm too tired to look through all tutorial files, will do somewhere this year (hopefully :P). But quite amazing you're the first to find this bug!
  • 0




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users