callExtension: Difference between revisions

From Bohemia Interactive Community
Jump to navigation Jump to search
m (Text replacement - "_{10,} " to "")
m (Text replacement - "[[server.cfg#" to "[[Arma 3: Server Config File#")
 
(78 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{Command|Comments=
{{RV|type=command


| arma2oa |Game name=
|game1= arma2oa
|version1= 1.60


|1.60|= Game version
|game2= tkoh
|version2= 1.00


|gr1= System |GROUP1=
|game3= arma3
|version3= 0.50


| Calls custom .dll also known as [[Extensions|Extension]]. The name of the extension is the name of the extension .dll without ".dll" part (or without "_x64.dll" part on 64-bit Arma). For example if the file is 'myExtension.dll' the name of the extension will be "myExtension". For 64-bit extensions, the name of the extension doesn't need to change and is still "myExtension". The game will automatically look for 'myExtension_x64.dll' when you use 64-bit Arma exe.
|gr1= System
 
|descr= Calls custom .dll also known as [[Extensions|Extension]]. The name of the extension is the name of the extension .dll without ".dll" part (or without "_x64.dll" part on 64-bit Arma). For example if the file is 'myExtension.dll' the name of the extension will be "myExtension". For 64-bit extensions, the name of the extension doesn't need to change and is still "myExtension". The game will automatically look for 'myExtension_x64.dll' when you use 64-bit Arma exe.
<br><br>
<br><br>
This command is blocking, meaning that the game will wait for the extension to return before continuing. This may cause FPS drop if extension is not optimised. If extension takes too long, consider making asynchronous extension, where the result of the work of the extension is collected in a separate call.
This command is blocking, meaning that the game will wait for the extension to return before continuing. This may cause FPS drop if extension is not optimised. If extension takes too long, consider making asynchronous extension, where the result of the work of the extension is collected in a separate call.
<br><br>
<br><br>
Currently there is no limit how much data you can send to the extension. However there is a limit on how much data you can return from extension in one call. The limit is known to the extension and is passed in <tt> int outputSize</tt>. The limit may or may not change in the future and is currently 10240 bytes. It is up to extension designer to handle multipart results if returned data exceeds output limit.
Currently there is no limit how much data you can send to the extension. However there is a limit on how much data you can return from extension in one call. The limit is known to the extension and is passed in {{hl| int outputSize}}. The limit may or may not change in the future and is currently 10240 bytes. It is up to extension designer to handle multipart results if returned data exceeds output limit.
<br><br>
<br><br>
Since Arma 3 v1.67 it is possible to pass array of arguments to extensions. The array of arguments could be anything and all elements will be converted to strings, however you might want to only send simple types like [[Boolean]]s, [[String]]s, [[Number]]s and [[Array]]s of all of the above. There is currently a limit on how many arguments can be sent and it is 2048 (since Arma 3 v1.92; previous limit: 1024). However an argument could be an [[Array]] itself, in this case extension maker will have to provide additional methods for parsing such arguments.
Since {{arma3}} v1.68 it is possible to pass array of arguments to extensions. The array of arguments could be anything and all elements will be converted to strings, however you might want to only send simple types like [[Boolean]]s, [[String]]s, [[Number]]s and [[Array]]s of all of the above. There is currently a limit on how many arguments can be sent and it is 2048 (since Arma 3 v1.92; previous limit: 1024). However an argument could be an [[Array]] itself, in this case extension maker will have to provide additional methods for parsing such arguments.
<br><br>
<br><br>
Possible error codes:
Possible error codes:
Line 22: Line 27:
Each error will have entry in .rpt file with more details.<br><br>
Each error will have entry in .rpt file with more details.<br><br>


The extension execution timeout, after which <tt>301: EXECUTION_WARNING_TAKES_TOO_LONG</tt> warning is issued, is hardcoded on clients and is 1000.0 milliseconds (1 second). On the server the default limit is also 1 second, however it is possible to set custom limit with <tt>callExtReportLimit</tt> param (see [[server.cfg#Server_Options | Server Options]]).
The extension execution timeout, after which {{hl|301: EXECUTION_WARNING_TAKES_TOO_LONG}} warning is issued, is hardcoded on clients and is 1000.0 milliseconds (1 second). On the server the default limit is also 1 second, however it is possible to set custom limit with {{hl|callExtReportLimit}} param (see [[Arma 3: Server Config File#Server_Options | Server Options]]).
<br><br>
<br><br>
If an extension with the given name can't be found (or it is found but doesn't implement the required interface properly / at all) the following error will be written into the RPT (In this example the given dll-name was "MyExtension"):
If an extension with the given name cannot be found (or it is found but doesn't implement the required interface properly / at all) the following error will be written into the RPT (In this example the given dll-name was "MyExtension"):
<code>14:27:07 CallExtension 'MyExtension' could not be found</code>
<code style="display: block">14:27:07 CallExtension 'MyExtension' could not be found</code>
<br>
<br>
If an extension is not whitelisted with BattlEye (see [[Extensions]] for more info) it will be blocked on clients running with enabled BattlEye protection. RPT message outputted however is a little obscure:
If an extension is not whitelisted with BattlEye (see [[Extensions]] for more info) it will be blocked on clients running with enabled BattlEye protection. RPT message outputted however is a little obscure:
<code>21:35:04 Call extension 'MyExtension' could not be loaded: Insufficient system resources exist to complete the requested service</code>
<code style="display: block">21:35:04 Call extension 'MyExtension' could not be loaded: Insufficient system resources exist to complete the requested service</code>
<br>
<br>
Since Arma 3 v1.69, <tt>RVExtensionVersion</tt> interface (see source code example below) has been added, which is called by the engine on extension load and expects extension version. This interface is designed to work with both, Linux and Windows. The max buffer size is 32 bytes. The version information will then appear in .[[rpt]] file like so:
Since {{arma3}} v1.70, {{hl|RVExtensionVersion}} interface (see source code example below) has been added, which is called by the engine on extension load and expects extension version. This interface is designed to work with both, Linux and Windows. The max buffer size is 32 bytes. The version information will then appear in .[[rpt]] file like so:
<code>19:06:36 CallExtension loaded: test_extension (.\test_extension.dll) [1.0.0.1]</code>
<code style="display: block">19:06:36 CallExtension loaded: test_extension (.\test_extension.dll) [1.0.0.1]</code>
<br>
<br>
For more information see [[Extensions]].
For more information see [[Extensions]].
<br><br>
<br><br>
<u>Linux specific</u><br>
<u>Linux specific</u><br>
While on Windows the extension name is case-insensitive, on Linux the extension name is case-sensitive and should match the name of the .so file exactly (minus ".so" part). Currently only 32-bit extensions are supported on Linux.<br><br>
While on Windows the extension name is case-insensitive, on Linux the extension name is case-sensitive and should match the name of the .so file exactly (minus ".so" part).<br><br>


{{Important | If a user has anti-virus software running, this could cause brand new extension to stutter the game and return with <tt>EXECUTION_WARNING_TAKES_TOO_LONG</tt> when executed for the first time, because of the AV software scanning. After the extension is whitelisted by AV this should go away until a new version of the extension is installed. Perhaps a dummy call to the extension on init should be considered as a feature of implementation to account for that}}
{{Feature|important|
If a user has '''anti-virus software real time protection running''', this could cause brand new extension to stutter the game and return with {{hl|EXECUTION_WARNING_TAKES_TOO_LONG}} when executed for the first time, because of the AV software scanning.
After the extension is whitelisted by AV this should go away until a new version of the extension is installed.
Perhaps a dummy call to the extension on init should be considered as a feature of implementation to account for that.
}}
<br>
<br>


'''<u>Extension Callback</u>'''
'''<u>Extension Context</u>'''
 
Since {{arma3}} v2.12 the engine will call the {{hl|RVExtensionContext}} method (if it exists, see {{Link|#Example 4}}) and pass the following data:
* {{hl|steamID}} of the client calling extension [[getPlayerUID]] or "0"
* {{hl|fileSource}} from which the extension was executed or "" if done on the fly
* {{hl|missionName}} [[missionNameSource]]
* {{hl|serverName}} [[serverName]]


Since Arma 3 v1.95 it is possible to call the game directly from the extension via function pointer provided when extension is called for the first time (assuming the extension implements at least one of the <tt>RVExtension</tt> or <tt>RVExtensionArgs</tt> methods). The function pointer passed over to <tt>RVExtensionRegisterCallback</tt> method is of the following signature (see Example 4):
|s1= extension [[callExtension]] function
<syntaxhighlight lang=cpp>int(*callbackProc)(char const *name, char const *function, char const *data)</syntaxhighlight>
Calling this function pointer from extension will trigger [[Arma_3:_Event_Handlers/addMissionEventHandler#ExtensionCallback | "ExtensionCallback"]] mission event handler with 3 user supplied params. The params are
* <tt>name</tt> - make it unique name, for example the extension name, so that other modders can quickly filter out calls from own extensions
* <tt>function</tt> - make it name of the function the extension sends the result to. (Note: The returned function is just a STRING! So [[compile]] is needed, before using [[call]] or [[spawn]], to execute it)
* <tt>data</tt> - make it the actual result. You can also format it as an array so it could be parsed by [[parseSimpleArray]]
Calling function pointer returns an <tt>int</tt>. This is the number of available slots in the input buffer left for this frame after your call and can range from 99 to -1. The buffer is processed and cleared every frame and the maximum number of slots that can be filled per frame is 100. If you are planning to call back with more than 100 results per frame, make sure your extension retries if it receives negative <tt>int</tt>, which means the buffer was full and your call did not succeed. Ideally suited for callbacks from different threads, but if callback is initiated from the calling thread, the EH will fire on the next frame of game simulation. Needless to say, the "ExtensionCallback" event handler needs to exist before any callbacks, otherwise the data will just stay in the buffer. Removing all "ExtensionCallback" EHs clears the buffer as well as mission restart. |DESCRIPTION=


| extension '''callExtension''' function |SYNTAX1=
|p1= extension: [[String]] - extension name
|p2= function: [[String]] - data sent to the extension


|p1= extension: [[String]] - extension name |PARAMETER1=
|r1= [[String]] - data sent back from extension; If the extensiion was not found an empty String will be returned
|p2= function: [[String]] - data sent to the extension |PARAMETER2=


| [[String]] - data sent back from extension; If the extensiion wasn't found an empty String will be returned |RETURNVALUE1=
|s2= extension [[callExtension]] [function, arguments]


| s2= extension '''callExtension''' [function, arguments] {{Since|arma3|1.67|y}} |SYNTAX2=
|s2since= arma3 1.68


|p21= extension: [[String]] - extension name |PARAMETER21=
|p21= extension: [[String]] - extension name


|p22= function: [[String]] - extension function identifier |PARAMETER22=
|p22= function: [[String]] - extension function identifier


|p23= arguments: [[Array]] - function arguments. Could be array of [[Anything]], each element will be converted to [[String]] automatically. Current allowed max length of this array is 2048 (since Arma 3 v1.92; previous limit: 1024) |PARAMETER23=
|p23= arguments: [[Array]] - function arguments. Could be array of [[Anything]], each element will be converted to [[String]] automatically. Current allowed max length of this array is 2048 (since Arma 3 v1.92; previous limit: 1024)


| r2= [[Array]] in format [result, returnCode, errorCode], where:
|r2= [[Array]] in format [result, returnCode, errorCode], where:
* result: [[String]] - data sent back from extension. It is up to extension maker what it is.
* result: [[String]] - data sent back from extension. It is up to extension maker what it is.
* returnCode: [[Number]] - integer return from extension method. It is up to extension maker to define it.
* returnCode: [[Number]] - integer return from extension method. It is up to extension maker to define it.
* errorCode: [[Number]] - error code in case of command error (see description). 0 means no errors. |RETURNVALUE2=
* errorCode: [[Number]] - error code in case of command error (see description). 0 means no errors.


|x1= <code>_return = "myExtension" [[callExtension]] "stringToBeParsed";</code>|EXAMPLE1=
|x1= <sqf>_return = "myExtension" callExtension "stringToBeParsed";</sqf>


|x2= <code>_result = "test_extension" [[callExtension]] [[str]] [[weapons]] [[player]];
|x2= <sqf>
_result = "test_extension" [[callExtension]] ["fnc1", [[getUnitLoadout]] [[player]]];
_result = "test_extension" callExtension str weapons player;
_result = "test_extension" [[callExtension]] ["fnc2", [[magazinesAmmoFull]] [[player]]];
_result = "test_extension" callExtension ["fnc1", getUnitLoadout player];
_result = "test_extension" [[callExtension]] ["fnc1", [<nowiki/>[[weapons]] [[player]], [[magazines]] [[player]]]];</code>|EXAMPLE2=
_result = "test_extension" callExtension ["fnc2", magazinesAmmoFull player];
_result = "test_extension" callExtension ["fnc1", [weapons player, magazines player]];
</sqf>


|x3= <code>_result = "test_extension" [[callExtension]] ["fnc1", [1,"two",[[true]],[4,"five",[[false]]]]];
|x3= <sqf>
[[parseSimpleArray]] (_result [[select]] 0) [[params]] ["_number","_string","_boolean","_array"];
_result = "test_extension" callExtension ["fnc1", [1, "two", true, [4, "five", false]]];
[[systemChat]] [[str]] [_number,_string,_boolean,_array];</code>
parseSimpleArray (_result select 0) params ["_number","_string","_boolean","_array"];
systemChat str [_number,_string,_boolean,_array];
</sqf>
<br>
<br>
<u>Source Code</u> ([http://data.bistudio.com/a3data/test_extension.zip Download .dll])<br><br>
<u>Source Code</u> ({{Link|link= http://data.bistudio.com/a3data/test_extension.zip|text= Download .dll}})
<spoiler>
This is an example of an extension compatible with both syntaxes. When using 1st syntax, the data is just copied from input to output. When using alt syntax, the arguments are parsed and then assembled back into string array in 2 ways: fnc1 and fnc2. fnc1 is a fraction faster.
This is an example of an extension compatible with both syntaxes. When using 1st syntax, the data is just copied from input to output. When using alt syntax, the arguments are parsed and then assembled back into string array in 2 ways: fnc1 and fnc2. fnc1 is a fraction faster.
<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
#include <string>
#include <string>
#include <vector>
#include <vector>
Line 94: Line 108:
extern "C"
extern "C"
{
{
//--- Engine called on extension load  
//--- Engine called on extension load
__declspec (dllexport) void __stdcall RVExtensionVersion(char *output, int outputSize);
__declspec (dllexport) void __stdcall RVExtensionVersion(char *output, int outputSize);
//--- STRING callExtension STRING
//--- STRING callExtension STRING
Line 170: Line 184:
}
}
}
}
</syntaxhighlight>|EXAMPLE3=
</syntaxhighlight>
</spoiler>


|x4= <code>fncToExecute_1 = { [[hint]] [[format]] ["Extension Result 1: %1", [[_this]]] };
|x4= Since {{arma3}} v2.12: <sqf>hint ("myExtContext" callExtension "");</sqf>
fncToExecute_2 = { [[hint]] [[format]] ["Extension Result 2: %1", [[_this]]] };
Here is a minimal example: <spoiler>
fncToExecute_3 = { [[hint]] [[format]] ["Extension Result 3: %1", [[_this]]] };
<syntaxhighlight lang="cpp">
#include <string>
#include <vector>
#include <iterator>
#include <sstream>
#include <iomanip>


[[addMissionEventHandler]] ["ExtensionCallback",  
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
{
[[params]] ["_name", "_function", "_data"];
switch (ul_reason_for_call)
[[if]] (_name [[isEqualTo]] "test_callback") [[then]]
{
{  
case DLL_PROCESS_ATTACH:
[[parseSimpleArray]] _data [[call]] ([[missionNamespace]] [[getVariable]] [_function,
case DLL_THREAD_ATTACH:
{
case DLL_THREAD_DETACH:
[[hint]] "Function does not exist!"
case DLL_PROCESS_DETACH:
}]);  
break;
};
}
}];
return TRUE;
}


"test_callback" [[callExtension]] [[str]] "test data";</code>
std::vector<std::string> contextInfo;
<br>
Here is a minimal example of an extension utilising [[Arma_3:_Event_Handlers/addMissionEventHandler#ExtensionCallback | extension callback]] (don't actually do it like this). ''fncToExecute_X'' function is called from "ExtensionCallback" event handler when it is triggered after 2 seconds of the extension call.
<syntaxhighlight lang=cpp>
#include <thread>
#include <string>
#include <chrono>


extern "C"
extern "C"
{
{
__declspec (dllexport) void __stdcall RVExtensionRegisterCallback(int(*callbackProc)(char const *name, char const *function, char const *data));
//--- User entry point
__declspec (dllexport) void __stdcall RVExtension(char *output, int outputSize, const char *function);
__declspec (dllexport) void __stdcall RVExtension(char *output, int outputSize, const char *function);
//--- Engine passed context
__declspec (dllexport) void __stdcall RVExtensionContext(const char **args, int argsCnt);
}
}


int(*callbackPtr)(char const *name, char const *function, char const *data) = nullptr;
//--- name callExtension function
 
void __stdcall RVExtension(char *output, int outputSize, const char *function)
void __stdcall RVExtensionRegisterCallback(int(*callbackProc)(char const *name, char const *function, char const *data))
{
{
callbackPtr = callbackProc;
//--- Not used here
}
(void)function;


void __stdcall RVExtension(char *output, int outputSize, const char *function)
if (!contextInfo.empty())
{
{
if (!callbackPtr)
std::ostringstream oss;
return;
const char qt = '"';


std::thread ([](std::string fnc)
for (auto it = contextInfo.begin(); it != contextInfo.end() - 1; ++it)
{
oss << std::quoted(*it, qt, qt) << ",";
using namespace std::chrono_literals;
oss << std::quoted(contextInfo.back(), qt, qt);
fnc = "[1,2,3," + fnc + "]";


for (int i = 1; i < 4; ++i) // run 3 times
//--- Send context info back
{
strncpy_s(output, outputSize, ("[" + oss.str() + "]").c_str(), _TRUNCATE);
std::this_thread::sleep_for(2s); // sleep for 2 seconds
}
callbackPtr("test_callback", ("fncToExecute_" + std::to_string(i)).c_str(), fnc.c_str());
}
}


}, function).detach();
//--- Context is executed first, copy it
void __stdcall RVExtensionContext(const char **args, int argsCnt)
{
contextInfo.assign(args, std::next(args, argsCnt));
}
}
</syntaxhighlight>|EXAMPLE4=
</syntaxhighlight>
</spoiler>


| [[call]], [[compile]], [[parseSimpleArray]], [[Extensions]] |SEEALSO=
|seealso= [[freeExtension]] [[call]] [[compile]] [[parseSimpleArray]] [[Extensions]]
}}
}}
<h3 style='display:none'>Notes</h3>
<dl class='command_description'>
<!-- Note Section BEGIN -->
<!-- Note Section END -->
</dl>
<h3 style='display:none'>Bottom Section</h3>
{{GameCategory|arma2|Scripting Commands}}
{{GameCategory|arma3|Scripting Commands}}
{{GameCategory|tkoh|Scripting Commands}}

Latest revision as of 13:40, 17 May 2024

Hover & click on the images for description

Description

Description:
Calls custom .dll also known as Extension. The name of the extension is the name of the extension .dll without ".dll" part (or without "_x64.dll" part on 64-bit Arma). For example if the file is 'myExtension.dll' the name of the extension will be "myExtension". For 64-bit extensions, the name of the extension doesn't need to change and is still "myExtension". The game will automatically look for 'myExtension_x64.dll' when you use 64-bit Arma exe.

This command is blocking, meaning that the game will wait for the extension to return before continuing. This may cause FPS drop if extension is not optimised. If extension takes too long, consider making asynchronous extension, where the result of the work of the extension is collected in a separate call.

Currently there is no limit how much data you can send to the extension. However there is a limit on how much data you can return from extension in one call. The limit is known to the extension and is passed in int outputSize. The limit may or may not change in the future and is currently 10240 bytes. It is up to extension designer to handle multipart results if returned data exceeds output limit.

Since Arma 3 v1.68 it is possible to pass array of arguments to extensions. The array of arguments could be anything and all elements will be converted to strings, however you might want to only send simple types like Booleans, Strings, Numbers and Arrays of all of the above. There is currently a limit on how many arguments can be sent and it is 2048 (since Arma 3 v1.92; previous limit: 1024). However an argument could be an Array itself, in this case extension maker will have to provide additional methods for parsing such arguments.

Possible error codes:
  • 101: SYNTAX_ERROR_WRONG_PARAMS_SIZE
  • 102: SYNTAX_ERROR_WRONG_PARAMS_TYPE
  • 201: PARAMS_ERROR_TOO_MANY_ARGS
  • 301: EXECUTION_WARNING_TAKES_TOO_LONG
Each error will have entry in .rpt file with more details.

The extension execution timeout, after which 301: EXECUTION_WARNING_TAKES_TOO_LONG warning is issued, is hardcoded on clients and is 1000.0 milliseconds (1 second). On the server the default limit is also 1 second, however it is possible to set custom limit with callExtReportLimit param (see Server Options).

If an extension with the given name cannot be found (or it is found but doesn't implement the required interface properly / at all) the following error will be written into the RPT (In this example the given dll-name was "MyExtension"): 14:27:07 CallExtension 'MyExtension' could not be found
If an extension is not whitelisted with BattlEye (see Extensions for more info) it will be blocked on clients running with enabled BattlEye protection. RPT message outputted however is a little obscure: 21:35:04 Call extension 'MyExtension' could not be loaded: Insufficient system resources exist to complete the requested service
Since Arma 3 v1.70, RVExtensionVersion interface (see source code example below) has been added, which is called by the engine on extension load and expects extension version. This interface is designed to work with both, Linux and Windows. The max buffer size is 32 bytes. The version information will then appear in .rpt file like so: 19:06:36 CallExtension loaded: test_extension (.\test_extension.dll) [1.0.0.1]
For more information see Extensions.

Linux specific
While on Windows the extension name is case-insensitive, on Linux the extension name is case-sensitive and should match the name of the .so file exactly (minus ".so" part).

If a user has anti-virus software real time protection running, this could cause brand new extension to stutter the game and return with EXECUTION_WARNING_TAKES_TOO_LONG when executed for the first time, because of the AV software scanning.

After the extension is whitelisted by AV this should go away until a new version of the extension is installed.

Perhaps a dummy call to the extension on init should be considered as a feature of implementation to account for that.


Extension Context

Since Arma 3 v2.12 the engine will call the RVExtensionContext method (if it exists, see Example 4) and pass the following data:

Groups:
System

Syntax

Syntax:
extension callExtension function
Parameters:
extension: String - extension name
function: String - data sent to the extension
Return Value:
String - data sent back from extension; If the extensiion was not found an empty String will be returned

Alternative Syntax

Syntax:
extension callExtension [function, arguments]
Parameters:
extension: String - extension name
function: String - extension function identifier
arguments: Array - function arguments. Could be array of Anything, each element will be converted to String automatically. Current allowed max length of this array is 2048 (since Arma 3 v1.92; previous limit: 1024)
Return Value:
Array in format [result, returnCode, errorCode], where:
  • result: String - data sent back from extension. It is up to extension maker what it is.
  • returnCode: Number - integer return from extension method. It is up to extension maker to define it.
  • errorCode: Number - error code in case of command error (see description). 0 means no errors.

Examples

Example 1:
_return = "myExtension" callExtension "stringToBeParsed";
Example 2:
_result = "test_extension" callExtension str weapons player; _result = "test_extension" callExtension ["fnc1", getUnitLoadout player]; _result = "test_extension" callExtension ["fnc2", magazinesAmmoFull player]; _result = "test_extension" callExtension ["fnc1", [weapons player, magazines player]];
Example 3:
_result = "test_extension" callExtension ["fnc1", [1, "two", true, [4, "five", false]]]; parseSimpleArray (_result select 0) params ["_number","_string","_boolean","_array"]; systemChat str [_number,_string,_boolean,_array];

Source Code (Download .dll)

This is an example of an extension compatible with both syntaxes. When using 1st syntax, the data is just copied from input to output. When using alt syntax, the arguments are parsed and then assembled back into string array in 2 ways: fnc1 and fnc2. fnc1 is a fraction faster.

#include <string>
#include <vector>
#include <iterator>
#include <sstream>

#define CURRENT_VERSION "1.0.0.1"

extern "C"
{
	//--- Engine called on extension load
	__declspec (dllexport) void __stdcall RVExtensionVersion(char *output, int outputSize);
	//--- STRING callExtension STRING
	__declspec (dllexport) void __stdcall RVExtension(char *output, int outputSize, const char *function);
	//--- STRING callExtension ARRAY
	__declspec (dllexport) int __stdcall RVExtensionArgs(char *output, int outputSize, const char *function, const char **args, int argsCnt);
}

//--- Extension version information shown in .rpt file
void __stdcall RVExtensionVersion(char *output, int outputSize)
{
	//--- max outputSize is 32 bytes
	strncpy_s(output, outputSize, CURRENT_VERSION, _TRUNCATE);
}

//--- name callExtension function
void __stdcall RVExtension(char *output, int outputSize, const char *function)
{
	std::string str = function;
	strncpy_s(output, outputSize, ("Input Was: " + str).c_str(), _TRUNCATE);
}

//--- name callExtension [function, args]
int __stdcall RVExtensionArgs(char *output, int outputSize, const char *function, const char **args, int argsCnt)
{
	if (strcmp(function, "fnc1") == 0)
	{
		//--- Manually assemble output array
		int i = 0;
		std::string str = "[";

		//--- Each argument can be accessed via args[n]
		if (argsCnt > 0)
			str += args[i++];

		while (i < argsCnt)
		{
			str += ",";
			str += args[i++];
		}

		str += "]";

		//--- Extension result
		strncpy_s(output, outputSize, str.c_str(), _TRUNCATE);

		//--- Extension return code
		return 100;
	}

	else if (strcmp(function, "fnc2") == 0)
	{
		//--- Parse args into vector
		std::vector<std::string> vec(args, std::next(args, argsCnt));

		std::ostringstream oss;
		if (!vec.empty())
		{
			//--- Assemble output array
			std::copy(vec.begin(), vec.end() - 1, std::ostream_iterator<std::string>(oss, ","));
			oss << vec.back();
		}

		//--- Extension result
		strncpy_s(output, outputSize, ("[" + oss.str() + "]").c_str(), _TRUNCATE);

		//--- Extension return code
		return 200;
	}

	else
	{
		strncpy_s(output, outputSize, "Avaliable Functions: fnc1, fnc2", outputSize - 1);
		return -1;
	}
}
↑ Back to spoiler's top
Example 4:
Since Arma 3 v2.12:
hint ("myExtContext" callExtension "");
Here is a minimal example:
#include <string>
#include <vector>
#include <iterator>
#include <sstream>
#include <iomanip>

BOOL APIENTRY DllMain(	HMODULE hModule,
						DWORD ul_reason_for_call,
						LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
	}
	return TRUE;
}

std::vector<std::string> contextInfo;

extern "C"
{
	//--- User entry point
	__declspec (dllexport) void __stdcall RVExtension(char *output, int outputSize, const char *function);
	//--- Engine passed context
	__declspec (dllexport) void __stdcall RVExtensionContext(const char **args, int argsCnt);
}

//--- name callExtension function
void __stdcall RVExtension(char *output, int outputSize, const char *function)
{
	//--- Not used here
	(void)function;

	if (!contextInfo.empty())
	{
		std::ostringstream oss;
		const char qt = '"';

		for (auto it = contextInfo.begin(); it != contextInfo.end() - 1; ++it)
			oss << std::quoted(*it, qt, qt) << ",";
		oss << std::quoted(contextInfo.back(), qt, qt);

		//--- Send context info back
		strncpy_s(output, outputSize, ("[" + oss.str() + "]").c_str(), _TRUNCATE);
	}
}

//--- Context is executed first, copy it
void __stdcall RVExtensionContext(const char **args, int argsCnt)
{
	contextInfo.assign(args, std::next(args, argsCnt));
}
↑ Back to spoiler's top

Additional Information

See also:
freeExtension call compile parseSimpleArray Extensions

Notes

Report bugs on the Feedback Tracker and/or discuss them on the Arma Discord or on the Forums.
Only post proven facts here! Add Note