Jump to content
madrussian

More flexibility with callExtension

Recommended Posts

I'm writing an A3/C++ interface for my (crazy) AI mod, using callExtension.

 

Noticed something that's slowing down my dev effort, namely that as soon as you callExtension on a particular dll file, from that point on that dll file is frozen until the game itself is exited.  Meaning that called dll can't be deleted (or thus rapidly replaced).  So every time you make a C++ change, unless you go to some extra lengths, you must restart the game to try out changes to your dll.

 

It is of course possible to keep copy/pasting in your new dlls and renaming them (while keeping the game open), and correspondingly change your callExtension calls within A3.  Anyway that's super finicky, and I'm wondering if there's a better way.  Question #1:

 

Is it possible to tell A3 to release control of a previously called dll? (without exiting game)

 

Meanwhile, I had the idea of having my very basic callExtension dll just call another dll with all my important code in it.  My idea is, even if A3 locks down the callExtension dll, maybe other dlls called by that dll remain free for deletion/replacement, etc.  I read up on run-time based dlls and set up 3 test projects in VS:  One exe and two dlls.  Basically my test exe gains access to my 1st dll's function (during runtime).  That 1st dll's function then gains access to my 2nd dll's function (during runtime), which returns a result.  So on my test exe's console, I see the correct result from my 2nd dll, proving that this mini chain works as expected. (in my test environment anyhow).  In this working example, my exe is analogous to A3, my 1st dll is analogous to callExtension dll (which remains locked upon calling), and my 2nd dll is analogous to the dll that we wish to remain unlocked (and thus deletable).

 

Spoiler

Btw, I did all this with LoadLibrary, GetProcAddress, extern "C", __declspec (dllexport), etc in C++ (completely apart from anything A3 or callExtension related).  If anyone is curious, I'll post my code.

 

Armed with this small victory, I tried to duplicate this result with a new A3 callExtension dll and a separate dll (both containing minimal test code like before).  I did everything the exact same way as before (for all practical purposes).  Fired up the game and called callExtension.  But unfortunately so far, it doesn't work.  I can tell based on the callExtension return values (which I set up for various outcomes).  Upon investigation, my LoadLibrary call (in my callExtension dll) to my separate dll returns NULL pointer, so I'm stuck there.  Question #2:

 

Any particular reason calling LoadLibrary (inside your callExtension dll) to a separate dll shouldn't work?

 

Otherwise, maybe I'm just doing this wrong.  In that, case maybe I'll post my simple test code and see if someone can point out the flaw.  Question #3:

 

If this idea should work, anyone know if that separate dll will end up "locked" (until game exit) as well, making all this a fool's errand?

 

Anyhow, thanks for reading and thanks in advance for help. 🙂

 

Spoiler

Btw - I'll probably settle on a more proper Intercept-based interface eventually, but for now just using my own callExtension calls / dlls to figure out how all this works, etc.  Also more importantly trying (again) to eliminate having to restart the game ad-nauseum every time C++ code changes during dev (which afaik Intercept doesn't easily allow).

 

Share this post


Link to post
Share on other sites

>Is it possible to tell A3 to release control of a previously called dll? (without exiting game)

no - this is security feature

>Any particular reason calling LoadLibrary (inside your callExtension dll) to a separate dll shouldn't work?~
>If this idea should work, anyone know if that separate dll will end up "locked" (until game exit) as well, making all this a fool's errand?


It can be done, for example http://killzonekid.com/arma-3-extension-tester-callextension-exe-callextension_x64-exe/ has

freeExtension “<name>”

command

  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, killzone_kid said:

no - this is security feature

 

Thanks, very good to know this definitively. 🙂

 

1 hour ago, killzone_kid said:

It can be done, for example http://killzonekid.com/arma-3-extension-tester-callextension-exe-callextension_x64-exe/ has

freeExtension “<name>”

command

 

I checked out your blog entry.  From entry:

 

Quote

callExtension.exe and callExtension_x64.exe are 32 and 64 bit executables to call 32 and 64 bit Arma 3 extensions accordingly, as if they were called from the game itself.

 

Quote

I needed this many times while writing my extensions, since it is quite a pain in the butt to restart Arma 3 every time you want to test some changes in the extension code.

 

I'm definitely experiencing pain in the butt from restarting A3 over and over.  Your tool dlls look extremely useful, but probably won't help me much in this case, because I'm in-game visually analyzing a bunch of 3D markers generated by my C++ code.  So I do need the game up & running, and the actual A3 callExtension dll connected up.

 

Also as you point out in your blog, freeExtension is a command you wish were included in the actual game (but unfortunately isn't).

 

Anyhow, my last two questions stand.  I'll re-phrase them a bit to be more specific:

 

2. If one needs/has actual A3 running and calls callExtension dll (via callExtension command) from A3, is there any particular reason calling LoadLibrary (from within aforementioned callExtension dll C++ code) to a separate dll shouldn't work?

 

^ Specifically, I'm trying to figure out why my callExtension dll's LoadLibrary call is returning a NULL pointer.  (Whereas this very thing works perfectly in my exe -> dll -> dll test code.)  Again, I'll post my A3 callExtension code and seperate dll code (and/or my test code too) if someone cares to take a look.

 

3. If one managed to make this work (see #2, again running actual A3 calling callExtension dll via callExtension command which then calls a 2nd dll via LoadLibrary and GetProcAddress C++ calls), anyone know if that 2nd dll will simply end up "locked" as well?  (like callExtension dll always ends up "locked" upon being called 1st time by A3)  By "locked" I mean unable to be deleted/replaced until game exit.

 

If that 2nd dll can indeed be called, but is destined to directly end up "locked" too (unable to be deleted/replaced), I might as well just give up on this idea now and try something completely different.  My pal @johnnyboy recommended a queue [callExtension dll communicating to/from another app like an exe (console or similar)].

 

Thanks again for the help.  Just trying to figure out how all this works.  Also, thanks @killzone_kid generally for your blog.  It's helped unstuck me more times than I can count.

 

  • Like 1

Share this post


Link to post
Share on other sites

Yes, having freeExtension command would have been ideal...this is why I'm going to add one and limit it to DEV branch.

  • Like 1
  • Thanks 2

Share this post


Link to post
Share on other sites
9 hours ago, killzone_kid said:

Yes, having freeExtension command would have been ideal...this is why I'm going to add one and limit it to DEV branch.

 

That sounds splendid, many thanks. 🙂

Share this post


Link to post
Share on other sites
Quote

every time you make a C++ change, unless you go to some extra lengths, you must restart the game to try out changes to your dll.

FYI, atleast when you're on windows (you're talking about DLL so you are), you can use Visual Studio's "Edit and Continue" feature.

That can live-edit C++ and C# DLL's while the game is running, without the need to unload/reload it.

 

Quote

Any particular reason calling LoadLibrary (inside your callExtension dll) to a separate dll shouldn't work?

LoadLibrary searches for the DLL in configured search paths, which will NOT include your @mod folder. So most likely your DLL simply is not found, thus it fails to load.

You need to pass the full path to the DLL to be loaded, you could use something like this to find where your current DLL is at: https://github.com/dedmen/PboExplorer/blob/master/src/Util.ixx#L324

 

Quote

If this idea should work, anyone know if that separate dll will end up "locked" (until game exit) as well, making all this a fool's errand?

Well it won't if you unload it with FreeLibrary.

Which you might need to do in a destructor, or just implement your own command in your wrapper DLL to unload the sub-DLL.

  • Like 1

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×