SDK Question Unloading via Modules list/Unloading on Orbiter Exit

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
More catch-ctd questions...

Is there a difference in how Orbiter deals with modules when you uncheck them in Orbiter versus on Orbiter exit?

I have the following module proxy class:
Code:
class ModuleProxy : public oapi::Module
{
public:
	ModuleProxy(HINSTANCE hDLL, oapi::Module* pModule);
	~ModuleProxy();
private:
	oapi::Module* actualModule;
};

ModuleProxy::ModuleProxy(HINSTANCE hDLL, oapi::Module* pModule) : oapi::Module(hDLL)
{
	actualModule = pModule;
}
ModuleProxy::~ModuleProxy()
{
	delete actualModule;
}
It looks pretty simple....


I hook oapiRegisterModule to do this:
Code:
void  MyRegisterModule(oapi::Module* module)
{
    ModuleProxy* newModule = new ModuleProxy(lastLoadedDLL, module);
   pRegisterModule(newModule);
}
Where lastLoadedDLL is an HINSTANCE set by my hook in LoadLibraryA.

I'm having an issue with Orbiter crashing on exit, but not on unchecking a module.

Normally, ModuleProxy would forward on calls to the "actualModule" pointer, but I removed all that code, and it still crashes.

In this case, ModuleProxy is just being a container for the real module.

Using Rcontrol as my test module (the only built-in Orbiter plugin that uses the non-deprecated module system), everything happily works. Checking Rcontrol calls my ModuleProxy constructor, and unchecking Rcontrol calls my ModuleProxy deconstructor, which destroys the "real" Rcontrol.

However, exiting out of the launchpad with Rcontrol still checked, Orbiter crashes with an access violation. My deconstructor is never called in this case.

---------- Post added at 12:32 PM ---------- Previous post was at 12:17 PM ----------

Ok, I rebuilt Rcontrol from the samples, so I would have debug symbols.

The same access violation occurs on exit, but the crash isn't occurring inside Rcontrol, as it just breaks into assembly.
 

orb

New member
News Reporter
Joined
Oct 30, 2009
Messages
14,020
Reaction score
4
Points
0
My guess is that when you close the launchpad modules aren't unloaded by Orbiter's code, but as a side effect of terminating the process, so not really by the same routine. Additionally, if TerminateProcess is used instead of ExitProcess the DllMain of the loaded modules won't be called but simply the DLL unloaded / memory freed. However to be sure, I'll need to test it in a debugger - I can do that in a few hours.

In what module does the exception occur, in orbiter.exe or some other?
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
Unhandled exception at 0x00415BAD in orbiter.exe: 0xC0000005: Access violation reading location 0x0FA6063C.
I thought the exception location was interesting (BAD), but looking at the disassembly there, it looks like legitimate code.

---------- Post added at 01:01 PM ---------- Previous post was at 12:58 PM ----------

My guess is that when you close the launchpad modules aren't unloaded by Orbiter's code, but as a side effect of terminating the process, so not really by the same routine. Additionally, if TerminateProcess is used instead of ExitProcess the DllMain of the loaded modules won't be called but simply the DLL unloaded / memory freed. However to be sure, I'll need to test it in a debugger - I can do that in a few hours.

If that was the case, what would be causing the access violation?
(I realize that is a really broad question that probably doesn't have an answer)
 
Last edited:

orb

New member
News Reporter
Joined
Oct 30, 2009
Messages
14,020
Reaction score
4
Points
0
what would be causing the access violation?
A region that has been freed but something used there hasn't been unregistered in Orbiter. Incompatible memory protection modes.

What was located at 0x0FA6063C?
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
Question marks in the VS disassembler display:

The whole region around it is question marks:
Code:
0FA6063B  ?? ?? 
0FA6063C  ?? ?? 
0FA6063D  ?? ?? 
0FA6063E  ?? ?? 
0FA6063F  ?? ?? 
0FA60640  ?? ??

I tried accessing from 0x0EA6063B to 0x0FF6063B and it was still question marks in either direction.

I assume question marks mean un-intitalized memory?
 
Last edited:

orb

New member
News Reporter
Joined
Oct 30, 2009
Messages
14,020
Reaction score
4
Points
0
I assume question marks mean un-intitalized memory?
Yes. Most likely something that was there has been freed, but its address was not unregistered in Orbiter, since it's still referenced.
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
My guess is that when you close the launchpad modules aren't unloaded by Orbiter's code, but as a side effect of terminating the process, so not really by the same routine. Additionally, if TerminateProcess is used instead of ExitProcess the DllMain of the loaded modules won't be called but simply the DLL unloaded / memory freed.

I checked the logs, and it appears like ExitModule is called, at least for my module.

Code:
LoadLibraryA:(libFileName:[B]Modules\Plugin\Rcontrol.dll[/B])
		oapiRegisterCustomCmd:(label:Remote Vessel Control , desc:Operate the engines of any spacecraft from a dialog box. , func:0FAF2A10 , context:00000000)
		returned:4...
		oapiRegisterModule:(module:00D15008)
		Creating module proxy		[B]Inside Module Modules\Plugin\Framerate.dll::constructor[/B]
		...
...
	oapiUnregisterLaunchpadItem:(item:003D7D28)
	returned:1...
	oapiUnregisterLaunchpadItem:(item:003DB560)
	returned:1...
	oapiUnregisterLaunchpadItem:(item:003D7CE0)
	returned:1...
Detaching hooks...
Hooks detached successfully
Detaching manual hooks...
	Detaching LoadLibraryA...
Manual hooks detached successfully

The crash occurs after catch-ctd detaches all of it's hooks.

However, looking at this now, it seems like I am creating the proxy with the wrong name...

That's strange, because here's my LoadLibrary hook:
Code:
HMODULE _stdcall MyLoadLibraryA(LPCSTR libFileName)
{
    Log::writeToLogDameon(IndentString(), "LoadLibraryA:", "(libFileName:", libFileName, ")\r\n");
    HMODULE result = pLoadLibraryA(libFileName);

    lastLoadedDLL = result;
    lastLoadedDLLName = libFileName;
--snip--


---------- Post added at 02:06 PM ---------- Previous post was at 02:03 PM ----------

Ok, that's probably the problem.

I'm setting the HINSTANCE and module name variables after the original LoadLibrary call.

I guess that means that Orbiter internally registers the module proxy with the wrong DLL.

LoadLibrary eventually calls InitModule, which calls registerModule. That means I'll have to hook InitModule to capture the HINSTANCE before passing it to my module.

I'm not sure why it works by checking/unchecking, but crashes on close.

---------- Post added at 02:08 PM ---------- Previous post was at 02:06 PM ----------

Arg, but to hook InitModule, I would have to use GetProcAddress, which is only possible after LoadLibrary exits, which is also after InitModule is called...

---------- Post added at 02:10 PM ---------- Previous post was at 02:08 PM ----------

Which means that I'll have to delay registering the proxy module until after InitModule finishes so I have the right HINSTANCE to pass to ModuleProxy...hmmm

Let me see if that works.

---------- Post added at 03:26 PM ---------- Previous post was at 02:10 PM ----------

Ok, even if I defer registration so ModuleProxy is created with the correct HINSTANCE, it still crashes in the same way.

It has the same exception location, with the same question mark access violation at 0x0F6D063C (but that address changes every time).


What does oapi::Module do with the HINSTANCE you pass it?
 

orb

New member
News Reporter
Joined
Oct 30, 2009
Messages
14,020
Reaction score
4
Points
0
What does oapi::Module do with the HINSTANCE you pass it?
It stores it in a protected variable hModule, which next can be used by GetModule method.

Some functions (mainly dialog boxes) require passing it.

The value of module's HINSTANCE / HMODULE (HINSTANCE = HMODULE) is the base address of its loaded image, so you don't really need to store it anywhere to obtain its value later. There are WinAPI functions which can return it even if you didn't store it anywhere.
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
It stores it in a protected variable hModule, which next can be used by GetModule method.

Some functions (mainly dialog boxes) require passing it.

The value of module's HINSTANCE / HMODULE (HINSTANCE = HMODULE) is the base address of its loaded image, so you don't really need to store it anywhere to obtain its value later. There are WinAPI functions which can return it even if you didn't store it anywhere.

Ok, that makes sense.

Here's what I was thinking:

oapiRegisterModule would associate the Module* passed to it with the HINSTANCE given (and stored within that Module*)

It would store those in something like a std::map<HINSTANCE,Module*>

That way, Orbiter would know which oapi::Module to delete when it unloaded a certain DLL through its HINSTANCE.

Then, when unloading normally (checking/unchecking the box), Orbiter would find my ModuleProxy I had given it, and delete it (which calls ModuleProxy::~ModuleProxy through the vftable), which would call the deconstructor normally.

When Orbiter fully exits, Orbiter may be unloading catch-ctd first. It would check it's internal module map to see if any modules are associated with catch-ctd, and find none.

Later, it would unload Rcontrol, check the map, and see that it has to delete Rcontrol's module. That works great until Rcontrol's module is really a ModuleProxy. However, when Orbiter tries to call the deconstructor through the vftable, catch-ctd, and ModuleProxy's deconstructor, aren't loaded anymore, leading to a crash.

Is that possible?

It makes sense with what's going on. The logfile shows that catch-ctd unloads normally, before any ModuleProxy's deconstructors are called.

---------- Post added at 06:35 PM ---------- Previous post was at 05:14 PM ----------

If that is true, would registering my ModuleProxy with catch-ctd's HINSTANCE work?

That way, any ModuleProxy's created would be deconstructed before catch-ctd was unloaded.

Then, in ModuleProxy's deconstructor, I could register the original module with it's own HINSTANCE, effectively undoing the proxy. As long as calling oapiRegisterModule is legitimate in a module deconstructor, that would allow the actual module to be safely deconstructed on its own.

Code:
ModuleProxy::~ModuleProxy
{
  oapiRegisterModule(actualModule);
}


---------- Post added at 06:55 PM ---------- Previous post was at 06:35 PM ----------

That solution doesn't work, I still get a crash and never reach the ModuleProxy deconstructor, even when it is created with catch-ctd's HINSTANCE.
 
Top