2
0
mirror of https://github.com/rehlds/metamod-r.git synced 2025-02-06 18:42:18 +03:00
metamod-r/doc/txt/windows_notes.txt
2016-07-04 12:07:29 +06:00

111 lines
5.3 KiB
Plaintext

# vi: set ts=4 sw=4 :
# vim: set tw=75 :
After writing and testing under linux, I then tried to move the code to
windows, and being unfamiliar with windows coding, I ran into several
issues I wasn't aware of, which I describe here for posterity:
- Apparently the GiveFnptrsToDll() routine has to be declared "WINAPI",
which is a macro for "__stdcall". Without this, the behavior I observed
was that the routine would be called, and would return seemingly okay,
but during the following call to GetNewDLLFunctions, the application
would crash.
This was especially confusing because, using SDK2.1 source for
reference, the use of EXPORT and DLLEXPORT macros seemed inconsistent.
Doing a simple grep for "define.*EXPORT" turns up these definitions:
cl_dll/cl_dll.h: #define EXPORT _declspec( dllexport )
cl_dll/hud_iface.h: #define EXPORT _declspec( dllexport )
dlls/cbase.h: #define EXPORT _declspec( dllexport )
cl_dll/in_defs.h: #define DLLEXPORT __declspec( dllexport )
engine/eiface.h: #define DLLEXPORT __stdcall
Between "EXPORT", "DLLEXPORT", and "dllexport", and then "_declspec" and
"__declspec", they all seemed the same to me. Of course, they aren't.
It seems "__declspec(dllexport)" (I'm still unsure about the single vs
double underscore) simply says that the routine should be visible
externally, outside the DLL. The "__stdcall" specifier, however,
changes the way the stack is cleaned up when the routine returns, and no
doubt having the routine itself using a different convention than the
caller (the engine) caused memory corruption and thus application
crashes. The specifier doesn't seem, to me, to be particularly relative
to DLL exporting per se, so I'd say the macro name was unfortunate and
confusing.
The other confusion was that GiveFnptrsToDll is apparently the _only_
function that needs to be declared __stdcall; declaring the other
external functions (GetEntityAPI, etc) produced MSVC errors and other
problems (as might be expected).
Also, it seems "__declspec" has to be placed _before_ the return type,
whereas "__stdcall" is placed _after_ the return type. Well, at least
in MSVC; mingw appears to be looser.
Further complicating this, the __stdcall generally causes the function
to be given an internal symbol with a trailing "@" and digits specifying
the number of bytes in the function arguments (in this case, "8"). At
least, this is true under the mingw GNU compiler; I couldn't tell if
MSVC was the same. In any case, by default then, the function is
exported as "GiveFnptrsToDll@8()", and the engine can't resolve the name
properly.
In mingw you can apparently alias this to the non-@ name via ".def"
files, but it looked like that if I have a .def file, I'd also have to
list all the entities in linkfunc.cpp (which would be a pain to
maintain). Under MSVC, this didn't appear to be a problem, as both the
SDK source and adminmod source use a ".def" file, but still export all
the other functions okay. I'm not sure why the difference; I may be
missing a mingw link parameter/option.
There are, however, mingw link options (--add-stdcall-alias, --kill-at)
to handle the problem (the first appears to do the job; I'm unsure about
the second), while still exporting all the other necessary functions.
Now, reading MSDN:
http://msdn.microsoft.com/library/devprods/vs6/visualc/vccore/_core_determine_which_exporting_method_to_use.htm
there's apparently an issue of an "export ordinal" and the order of the
list of exported functions, which is solved by using a .def file.
Perhaps this is why the SDK uses a .def file, in which case I may have
problems if I don't specify that GiveFnptrsToDll is the first function
(as the SDK .def file does). Although, perhaps this isn't even an issue
given that the DLL functions are called by name explicitly
(dlsym/GetFuncPointer), rather than being resolved to an library offset
at link time.
In any case, apparently using the conventions in the SDK, and including
the same headers in the same order produces the correct result, but it
wasn't at all clear to me, when looking at the source.
This one was hard for me to track down, and I also found this page to
be helpful:
http://www.geocities.com/Tokyo/Towers/6162/win32/dll/make.html
- Linux appears to be either (a) much more forgiving about improper
pointer references in memcpy, or (b) laying out its memory in a manner
that hides problems with that. In my case, I had:
DLL_FUNCTIONS *dllapi_table;
dllapi_table=malloc(...)
memcpy(pFunctionTable, &dllapi_table, sizeof(DLL_FUNCTIONS));
Since the argument is already a pointer, the extra "&" is improper, and
should instead be:
memcpy(pFunctionTable, dllapi_table, sizeof(DLL_FUNCTIONS));
Under linux, it didn't seem to be a problem, and program operation was
(as far as I could tell) correct. Under windows, though, I got program
crashes after calling the 4th or 5th entity in linkfunc.cpp. It
wasn't at all obvious what the problem was, and took quite a while to
track down.
- missing functions (strtok_r, snprintf)
- missing macros (PATH_MAX, NAME_MAX)
- dlerror, getlasterror
- LoadLibrary returning 0 on failure (vs dlclose returning 0 on success)
- limits.h under mingw