Modul programozás Microsoft Flight Simulatorhoz
Kulcsszavak: repülés | repülőszimuláció
A Microsoft Flight Simulator hatalmas előnye, hogy szabadon fejleszthetők hozzá kiegészítők: domborzatok, városok, repülők stb. Ebben a cikkben nagyon rövide azt foglalom össze, hogy hogyan hozható létre úgynevezett modul (module), amely szinte az FS részeként fut és amelyet bármely repülőgép használni tud. Ilyen modulként fut például az ActiveSky, az Active Camera, az FS Navigator, bizonyos recorder kiegészítők, de az IvAp, Squawkbox és FSInn multiplayer kiegészítők egyes komponensei is. Az ilyen modulokat az FS Modules könyvtárába kell betenni, ahonnan a szimulátor automatikusan be fogja hívni induláskor.
A leírás angol nyelvű, mert Internetes fórumra írtam eredetileg. Olyan sok segítséget kaptam ezektől a fórumoktól, hogy szükségét éreztem, hogy én is hozzájáruljak valamit működésükhöz.
I have received several requests to provide sample code for FS module programming. I should tell you that I am not professional in this, but as I received so much help from the Internet forums I would like to return this help by sharing my experience. Please find the example code attached and feel free to mail me any comments or questions.
Gauge and module programming is basically the same - both are DLL-s. The main difference is that Gauge DLLs (GAU files) are put in the Gauges or Panel dir, module DLLs to be put in the Modules dir.
fs9gauges.h // common extended header file GaugeMain.c // main file for gauge programming ModuleMain.c // very similar for module programming Systems.c // callback functions for both gauge and module examples
The sample ModuleMain.c is not complete, but it also shows that you can define window and menu to the module as well. That is simple Windows programming.
Macros in GAUGE programming
I do NOT use the following macros, as they do not provide access to the structures I need. See the header file and my code and you will understand
GAUGE_TABLE_BEGIN()
GAUGE_TABLE_ENTRY(&gaugehdr_1)
GAUGE_TABLE_ENTRY(&gaugehdr_4)
...
GAUGE_TABLE_END()
See the file GaugeMain.c (my sample's main source code). It defines the main function and all the neccessary structures manualy. Gauge header addresses are put manualy (not with GAUGE_TABLE_ENTRY macro) to the structure.
Includes in GAUGE programming
I am also not using the #include to include all gauge files. Instead I put them in separate source files. This makes it necessary to play a bit with some variable definitions and again some macros, but you get the adventage to be able to use localy valid variables (not global ones). No detailed information is provided about these steps here...
GAUGE and Module programming
I define my own init and termination routines that FS will call. In the init routine - module_init() - I call my own more detailed initialisation code - Init() - and I set up additional call back routines that FS will call.
// this will be called 18 times per second...
chain_insert2(CHAIN_18HZ_SYNC, System_18Hz, NULL, 0);
// this will be called every second once...
chain_insert2(CHAIN_1HZ_SYNC, System_1Hz, NULL, 0);
// this will be called when customer saves flight...
chain_insert2(CHAIN_PROCESS_FILE_IO, SaveConfig, NULL, 0);
// FS will call this with every command you press (what you define in Assignments menu)
register_key_event_handler( (GAUGE_KEY_EVENT_HANDLER)KeyHandler, NULL);
You can pass a data strusture pointer and an additional integer value to these functions and you will receive those variables when FS call the callback functions as "user_buffer" and "user_param".
My Init() function is in Systems.c. In this example it only retrieves the FS internal data pointer. The structure of that area is totally undocumented, you can find some values I found in the header file, but it is better not to use it.
Questions and answers
Today I tried to compile your ModuleMain.c file with Visual Studio 2005. And I got linker error:
ModuleMain.obj : error LNK2019: unresolved external symbol _KeyHandler@12 referenced in function _module_init@0
ModuleMain.obj : error LNK2019: unresolved external symbol _System_1Hz@16 referenced in function _module_init0
ModuleMain.obj : error LNK2019: unresolved external symbol _System_18Hz@16 referenced in function _module_init@0
ModuleMain.obj : error LNK2019: unresolved external symbol _Init referenced in function _module_init@0
ModuleMain.obj : error LNK2019: unresolved external symbol _ShutDown referenced in function _module_deinit@0
ModuleMain.obj : error LNK2019: unresolved external symbol _InitInstance referenced in function _DllMain@12
ModuleMain.obj : error LNK2019: unresolved external symbol _MyRegisterClass referenced in function _DllMain@12
Debug\swlgate.dll : fatal error LNK1120: 7 unresolved externals
That is correct. These are all my functions. See, they are all defined in my sample Systems.c file. And of course you should make a header file and include it in the main file. Put the following function declarations in a header file or simply copy them to your main file:
void Init( const char *path );
void ShutDown( void );
void FSAPI System_1Hz(DWORD event_id,VOID *buffer_specific,PVOID user_buffer,DWORD user_param);
void FSAPI System_18Hz(DWORD event_id,VOID *buffer_specific,PVOID user_buffer,DWORD user_param);
void FSAPI SaveConfig(DWORD event_id,VOID *buffer_specific,PVOID user_buffer,DWORD user_param);
void FSAPI KeyHandler(ID32 event, UINT32 evdata, PVOID userdata);
Of course you should define (code) these functions in the same file or in a separate file as I did in my Systems.c file. This is pure C programming, not module programming...
In your Systems.c file you including some file:
#include "Include\resource.h"
#include "Include\test.h"
Is it containing any important info?
Yes, test.h contains the function declarations I was speaking about above. The other one is normal Windows resource definitions header file.
Some linker error stay with me (MyRegisterClass , InitInstance)
ModuleMain.obj : error LNK2019: unresolved external symbol _InitInstance referenced in function _DllMain@12
ModuleMain.obj : error LNK2019: unresolved external symbol _MyRegisterClass referenced in function _DllMain@12
Ohh, that is only something like this, only creating the window for the module:
#define WINDOW_CLASS_NAME "MyWinClassName"
#define WINDOW_TITLE "My Module Window Name"
// FUNCTION: MyRegisterClass( HINSTANCE)
// PURPOSE: Registers the modules window class
BOOL MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, (LPCTSTR)IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW););
wcex.lpszMenuName = NULL;
wcex.lpszClassName = WINDOW_CLASS_NAME;
wcex.hIconSm = NULL;
return RegisterClassEx(&wcex) ? TRUE : FALSE;
}
// FUNCTION: InitInstance(HANDLE, int)
// PURPOSE: Saves instance handle and creates main window
// COMMENTS:
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
HWND InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
// Create the window. It will be hidden. Later you can use ShowWindow() to display it.
// You might even add a menu handler, but I define it as NULL here...
hwnd = CreateWindow(WINDOW_CLASS_NAME, WINDOW_TITLE, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, 770, 530, NULL, NULL, hInstance, NULL);
if (!hWnd)
return NULL;
return hWnd;
}
The created window is hidden. You should open it by a trigger. It is either a key that user presses in FS and you capture it via on of the callback functions, or it is a sign at the appropriate FSUIPC offset showing that your menu - created through FSUIPC - was selected. Use ShowWindow() to display the module window in this case.
