If I get the broad difference between DLLCLBK and OrbiterAPI.
OrbiterAPI exist in the Orbiter core for me to use from code in my.dll, DLLCLBK (something) exists in the Orbiter core, but is empty, and I can write code in my.dll which will be used?
Not entirely correct. You're right on OrbiterAPI: there are a lot of functions in the Orbiter core (orbiter.exe) which can be called from your DLL. oapiGetEmptyMass is an example. But the truth on DLLCLBK is a bit different, and maybe that's exactly what confuses you.
DLLCLBK itself is just a preprocessor macro (I guess it is defined somewhere in the Orbiter SDK header files). An example of how to use it is:
Code:
DLLCLBK void opcPreStep(double simt, double simdt, double mjd)
{
sprintf(oapiDebugString(), "Simulation time: %.2f minutes", simt/60);
}
So, it adds some flags to your function definition (or maybe it does nothing, if DLLCLBK is an empty macro). But most probably it exactly defines the
calling convention, which is a convention of how a function should be called on the machine code level. Normally, you as a C/C++ programmer don't need to worry about this, but here it could cause problems if your code is compiled with a different compiler than orbiter.exe, or even with different settings. To avoid all the trouble, you add DLLCLBK to your function prototype. This makes your function's calling convention exactly how Orbiter expects it.
In other words: you need to use DLLCLBK on all functions that need to be called by Orbiter.
That is the meaning of the word DLLCLBK. Now, it is not the case that "(something) exists in the Orbiter core, but is empty, and I can write code in my.dll which will be used". It works differently, and this is also why it's no problem for Orbiter to load multiple DLLs which contain the same function.
You know function pointers? If not, please learn about them before continue reading this. The idea is that Orbiter, for every DLL it loads, it remembers some data, e.g. in a struct or a class (and there probably is an array containing all the DLL data as objects). For instance, Orbiter could remember the name of each DLL it loads. But, more importantly, it remembers some function pointers.
Initially, these function pointers point to nothing (e.g. they could be NULL). But when Orbiter continues loading the DLL, it uses some windows system calls to scan the DLL for certain function names. Windows has a function that accepts a DLL handle, the name of a function as a string, and returns a pointer to that function. So, in each DLL, Orbiter searches for function names like "opcPreStep", and it remembers them as function pointers. Then, whenever these functions need to be called, Orbiter uses these function pointers to call these functions within each DLL.
Was this too complicated / confusing?