SDK Question Coding for Beta plus Backwards Compatibility to O2010

ADSWNJ

Scientist
Addon Developer
Joined
Aug 5, 2011
Messages
1,667
Reaction score
3
Points
38
What's the general advice on coding for the beta plus Orbiter 2010? I would like to take advantage of the new elevation model, but have the code gracefully fall back to the default model for previous versions.

What I want to understand is how to determine the Orbiter version at runtime, and how to ensure that code calling the new functions can be compiled into a single DLL but still be able to be run (with unresolved dependencies) on the older Orbiter as well.

Any thoughts?
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
What's the general advice on coding for the beta plus Orbiter 2010? I would like to take advantage of the new elevation model, but have the code gracefully fall back to the default model for previous versions.

What I want to understand is how to determine the Orbiter version at runtime, and how to ensure that code calling the new functions can be compiled into a single DLL but still be able to be run (with unresolved dependencies) on the older Orbiter as well.

Any thoughts?

I tried to take the beta plugins and put them into 2010P1 and observed that they still work. So it should be possible to compile and link a beta addon that e.g. prints the current altitude to oapiDebugString() with the proper function according to the detected oapiGetOrbiterVersion(), which then also runs in 2010P1. I think the best bet is to just try it out.
 

ADSWNJ

Scientist
Addon Developer
Joined
Aug 5, 2011
Messages
1,667
Reaction score
3
Points
38
It fails, unfortunately.

Here's a code snippet from an update PursuitMFD that I am looking at following Turtle91's request. As Brighton Beach is now in a 2.5km crater, it's important to correct for surface elevation if you want to do a precision landing.

Here's what I have:

Code:
#ifdef USING_ORBITER2016
	double surfElev = oapiSurfaceElevation(href,blon,blat);
	pos.y = length(Rpos)-bradius-surfElev-vclass->GetCOG_elev();
#else
	pos.y = length(Rpos)-bradius-vclass->GetCOG_elev();
#endif

i.e. my altitude is my distance to the center of the body, minus the average body radius, minus the vessel's COG elevation, minus the surface elevation (if used).

I have a temporary #define USING_ORBITER2016 to fence out the code, but this means I need to do 2 packages and releases. What I really want is to have a single DLL where at execution time I call the right calculation depending on oapiGetOrbiterVersion(). However ... if you have a call in your code (even if conditional), to oapiSurfaceElevation() then on launch on Orbiter 2010 you get an Orbiter Launchpad: orbiter.exe - Entry Point Not Found ... because the dynamic linking of oapiGetSurfaceElevation() cannot be resolved.

Maybe it's a question for @Martins? Can we have a new patch of Orbiter 2010 with null functions for all the new things in Orbiter 2016, so we can release code for both versions in one DLL?

(I.e. specifically for this case ... can we have a null function in Orbiter 2010 for oapiSurfaceElevation(), so the DLl can load.)

Or is there a more elegant solution to override the DLL instantiator's default behavior and say "it's cool, I know I don't have that function, but I know what I'm doing".
 

turtle91

Active member
Joined
Nov 1, 2010
Messages
319
Reaction score
7
Points
33
So you are using the surface elevation only for the base coordinates, and not realtime-surface-data during the entire aoproach(which I have tried, but this gave too much pitch oscillations, which ruined the final aproach) ? That's clever :)

I mean, I have no clue about C++ developement, but...what about trying this the other way around ?:

I.e.

#ifdnef USING_ORBITER2015

?
Maybe the same result...but just an idea...
 

ADSWNJ

Scientist
Addon Developer
Joined
Aug 5, 2011
Messages
1,667
Reaction score
3
Points
38
Maybe the same result...but just an idea...

Yeah it'll be the same result, just by thinking though the steps that the compiler would take. If it sees some code with an entry not defined in an older Orbiter, it'll fail to load the DLL. I don't know how to make a single DLL that uses a function if there, but stubs it out if not, and do that at runtime.

@turtle - I'll email you privately with the new PursuitMFD and some instructions how to set up your VC++ 2010 environment.
 

Hielor

Defender of Truth
Donator
Beta Tester
Joined
May 30, 2008
Messages
5,580
Reaction score
2
Points
0
Basically what you need is something like reverse delay-loading. I don't know if that exists...
 

kamaz

Unicorn hunter
Addon Developer
Joined
Mar 31, 2012
Messages
2,298
Reaction score
4
Points
0
Yeah it'll be the same result, just by thinking though the steps that the compiler would take. If it sees some code with an entry not defined in an older Orbiter, it'll fail to load the DLL. I don't know how to make a single DLL that uses a function if there, but stubs it out if not, and do that at runtime.


Code:
double getSurfaceElevation(void *href, double blon, double blat)
{
  // FIXME: as the function entry point does not change, do this only once at startup
  double (*my_oapiSurfaceElevation)(void *, double, double);
  my_oapiSurfaceElevation = GetProcAddress(GetModuleHandle("orbiter.exe"), "oapiSurfaceElevation");

  if ( my_oapiSurfaceElevation ) {
   return my_oapiSurfaceElevation(href, blat, blon);
  } else {
   return 0;
  }

Written from memory and without testing so beware of the bugs, but that's the general idea.

Use GetProcAddress to get pointer to the function at runtime and then call the function via that pointer. (The pointer type must match the function prototype.) If the function is not defined in the specified module/dll, GetProcAddress() will return NULL.

Since you are resolving the function name at runtime via GetProcAddress() then the linker never sees it and cannot complain :)

---------- Post added at 11:47 PM ---------- Previous post was at 11:34 PM ----------

And to check if you are running under orbiter.exe or orbiter_ng.exe:

HANDLE hOrbiter = GetModuleHandle("orbiter.exe");
if ( hOrbiter == NULL ) hOrbiter = GetModuleHandle("orbiter_ng.exe");
if ( hOrbiter == NULL ) ERROR("Dude, this is an orbiter plugin...");

...

fFunc = GetProcAddress(hOrbiter, "whateverfunctionyouneed");

---------- Post added 01-27-16 at 12:03 AM ---------- Previous post was 01-26-16 at 11:47 PM ----------

Another way of doing this, possibly cleaner:

double stub_getSurfaceElevation(void *href, double blon, double blat)
{
return 0;
}
double (*my_oapiSurfaceElevation)(void *, double, double);

// STARTUP:
my_oapiSurfaceElevation = GetProcAddress(hOrbiter, "oapiSurfaceElevation");
if ( ! my_oapiSurfaceElevation ) my_oapiSurfaceElevation = stub_oapiSurfaceElevation;

// RUNTIME:
my_oapiSurfaceElevation(....)
 
Last edited:

ADSWNJ

Scientist
Addon Developer
Joined
Aug 5, 2011
Messages
1,667
Reaction score
3
Points
38
kamaz ... I have to say, this is a beautiful thing!

I'll set this up as an OrbiterCompatibility.h and OrbiterCompatibility.c to see if it can be generally useful for others / and extended with whatever functions need stubbing.

---------- Post added 01-27-16 at 06:34 PM ---------- Previous post was 01-26-16 at 11:49 PM ----------

So ... good news / bad news:

The good: the GetProcAddress works perfectly, and allows you to make a runtime decision on using a function.

The bad: it looks like there's a lot deeper a set of interop decisions than just the missing functions. Compiling for O2010 or O2016 now works fine (with the GetProcAddress in there), and it runs on the respective builds, but I cannot run the compiled DLL from one build on the other build, due to access violations in oapi::Sketchpad::Text. (And I assume fixing this would find the next and so on...). So I think that this is basically a non-starter, and 2 separate packages will be the way to go (sadly).
 

kamaz

Unicorn hunter
Addon Developer
Joined
Mar 31, 2012
Messages
2,298
Reaction score
4
Points
0
It's actually strange that a DLL built for 2010 does not run under 2016. If the API is supposed to be backwards compatible, this is a bug. Maybe ask martins...?

If the number of conflicts is low, you can work around that one as well. Steps:

1. Make a DLL which wraps oapi::Sketchpad into a C interface, i.e. make functions like _oapi_Sketchpad_dothis(). Compile this DLL for both 2010 and 2016. Say one is named compat2010.dll and the other one compat2016.dll.

2. In the add-on, detect if you are running under 2010 or 2016, then
HANDLE *hCompat = LoadLibrary("compat2010.dll")
or
HANDLE *hCompat = LoadLibrary("compat2016.dll")
depending on Orbiter version.

3. GetProcAddress(hCompat, "_oapi_Sketchpad_dothis") and call via pointer.

4. Make a wrapper function for steps 2-3 to hide your machinations :)

NB see also this thread: http://www.orbiter-forum.com/showthread.php?t=35969 meson800 was using python to automatically generate wrapper classes for all of the API. So using his approach you could wrap the whole API (or large parts thereof) into compat DLLs automatically...
 
Last edited:
Top