• Which the release of FS2020 we see an explosition of activity on the forun and of course we are very happy to see this. But having all questions about FS2020 in one forum becomes a bit messy. So therefore we would like to ask you all to use the following guidelines when posting your questions:

    • Tag FS2020 specific questions with the MSFS2020 tag.
    • Questions about making 3D assets can be posted in the 3D asset design forum. Either post them in the subforum of the modelling tool you use or in the general forum if they are general.
    • Questions about aircraft design can be posted in the Aircraft design forum
    • Questions about airport design can be posted in the FS2020 airport design forum. Once airport development tools have been updated for FS2020 you can post tool speciifc questions in the subforums of those tools as well of course.
    • Questions about terrain design can be posted in the FS2020 terrain design forum.
    • Questions about SimConnect can be posted in the SimConnect forum.

    Any other question that is not specific to an aspect of development or tool can be posted in the General chat forum.

    By following these guidelines we make sure that the forums remain easy to read for everybody and also that the right people can find your post to answer it.

FSXA Adding a menu in FSX

Messages
64
Country
france
Bonjour,

Il y a un moment, caKus m'a aidé à introduire un menu dans FSX grâce à une dll de son cru.
Je souhaiterais modifié celle ci pour lancer une application à partir d'une variable string en lieu et place d'une chaîne statique comme:
Code:
TCHAR pg[] = L"D:\\Program Files (x86)\\Concorde Performance System\\CDC.EXE\0" ;
Comment faire pour un ignorant en C++comme moi.

Merci de ton aide
Cordialement
Pierre
 
Hi Doug,

Thanks for reply.

The way you show me needs to change a lot of code in the concerned dll.
Unfortunately, I'm a very new bie in C++ and unable to do this way.

Is there a mean to convert a string variable into PCHAR?

Thanks for help

Pierre
 
Try converting youe ASCII string to a Unicode string as shown in the snippet:


Code:
char AsciiString[] = "Hello World";
wchar_t UnicodeString[MAX_PATH];

MultiByteToWideChar(CP_ACP, 0, AsciiString, -1, UnicodeString, MAX_PATH);
 
Thanks for reply,

What I want to do is using the TCHAR with a string variable read in a text file.
The variable contains the path of application to launch which could change from one user to another.

I cant pass through a static text string.

Thanks for help

Cheers
Pierre
 
My snippet converts an Ansi String (char*) variable to a Unicode String(wchar_t*) variable.

There is no such thing as a TCHAR variable. TCHAR is a macro defined by:

Code:
#ifdef _UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif

This defines TCHAR as wchar-t for a UNICODE build and char otherwise.
 
Thanks Mgh,

Maybe you know another way do do that without a TCHAR?

Thanks for your help

Cheers
Pierre
 
Can you explain exactly what you want to do?

EDIT

Do you want to click on the same menu item but have a different name for the .exe file depending on some flag/trigger?
 
Last edited:
Hello Doug and Mgh,

Thanks for your patience.
In fact it is quite simple but my explanation isn't.

That I want to do is launching an .exe from the fsx menu.

This .exe is located in my application installation folder which may have different path depending of installation folder selected by each user.

This location even may be on network computer linked to another with FSX.

I would to have a little text file included in fsx main folder containing the path to my application and read by the dll and concatenated with .exe name launch the .exe file through the menu.

Hope to be clear

Thanks again for help
Pierre
 
The following snippet reads a single file path into the variable ExeFileName[] from a text file called MyTestFile.txt. It then converts it as UNICODE to the artDLL variable pg[].


Code:
FILE *fp;  // File pointer
char ExeFileName[MAX_PATH + 1];  // exe file name
char FileName[MAX_PATH + 1];  // source file name
char ch;  // ANSI character
int c = 0;// Counter

// Set file name
strcpy(FileName, "C:\\...\\MyTestFile.txt");

// Open ANSI text file for read only
fp = fopen(FileName, "r");

// Read all characters up to but excluding EOF
while ((ch = fgetc(fp)) != EOF)
    {
    // Build file name
    ExeFileName[c] = ch;
    ++c;
    }

// Close the file
fclose(fp);

// Add terminating zero ('\0')
ExeFileName[c] = '\0';

// Convert to UNICODE (wchar_t)
MultiByteToWideChar(CP_ACP, 0, ExeFileName, -1, pg, MAX_PATH);

[COLOR="Silver"]// Concat path + program name in startFLL
wcscat(szFolderPath, pg) ;
[/COLOR]

EDIT

I just noticed that in StartDLL szFolderPath contains the path to the "Program Files folder. A typical path is C:\Program Files." Is that what you want?
 
Last edited:
Hi Mgh,

I will try your solution today and then back to give you the result.

Thanks so much to take your time send me reply.

Best Regards
Pierre
 
Hi Mgh,

I've tried unsuccessfully your solution.
Menu are added in FSX, but exe's files not launched when click on them.
May be I've forgot something:

Here is the complete code I use:
Code:
// CPSModulesStartDll.cpp*: définit les fonctions exportées pour l'application DLL.
//
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <Shlobj.h>
#include <string.h>
#include "SimConnect.h"
#include <wchar.h>



static enum GROUP_ID {
	GROUP_MENU
};

static enum EVENT_ID {
	EVENT_MENU_START,
 	EVENT_SUBMENU_START,
    EVENT_SUBMENU1_START
};


FILE *fp;
char ExeFileName[MAX_PATH + 1];
char FileName[MAX_PATH + 1];
char ch;
int c = 0;
wchar_t pg[MAX_PATH];

HANDLE  hSimConnect = NULL;
static int quit = 0 ;


void CALLBACK MyDispatchProcMI(SIMCONNECT_RECV* pData, DWORD cbData, void *pContext)
{
	//HRESULT hr;
	switch(pData->dwID)
	{
	case SIMCONNECT_RECV_ID_EVENT:
		{
			SIMCONNECT_RECV_EVENT *evt = (SIMCONNECT_RECV_EVENT*)pData;

			switch(evt->uEventID)
			{
			case EVENT_SUBMENU_START:
				{
					STARTUPINFO si = { 0 };
					si.cb = sizeof(si);
					si.dwFlags = STARTF_USESHOWWINDOW;
					si.wShowWindow = SW_SHOWNORMAL  ;

					PROCESS_INFORMATION pi = { 0 };
					
					
					strcpy(FileName, "XCPS1.dat"); // Contains: d:\Program Files (x86)\Concorde Performance System\CFM.exe
					fp = fopen(FileName, "r");
					while ((ch = fgetc(fp)) != EOF)
					{
						ExeFileName[c] =ch;
						++c;
					}
					fclose(fp);
					ExeFileName[c] = '\0';
					MultiByteToWideChar(CP_ACP, 0, ExeFileName, -1, pg, MAX_PATH);
																
					::CreateProcess(NULL, pg , NULL, NULL, FALSE, 
						NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) ;

					break;
				}
              case EVENT_SUBMENU1_START:
				  {
					  STARTUPINFO si = { 0 };
					si.cb = sizeof(si);
					si.dwFlags = STARTF_USESHOWWINDOW;
					si.wShowWindow = SW_SHOWNORMAL  ;

					PROCESS_INFORMATION pi = { 0 };
	

					strcpy(FileName, "XCPS2.dat"); //Contains: d:\Program Files (x86)\Concorde Performance System\CDC.exe
					fp = fopen(FileName, "r");
					while ((ch = fgetc(fp)) != EOF)
					{
						ExeFileName[c] =ch;
						++c;
					}
					fclose(fp);
					ExeFileName[c] = '\0';
					MultiByteToWideChar(CP_ACP, 0, ExeFileName, -1, pg, MAX_PATH);
				
					::CreateProcess(NULL, pg , NULL, NULL, FALSE, 
						NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) ;

					break;
				  }

			default:
				break;
			}
			break;
		}

	case SIMCONNECT_RECV_ID_QUIT:
		{
			quit = 1;
			break;
		}

	default:
		printf("Received ID: %d", pData->dwID);
		break;
	}
}

int __stdcall DLLStart(void)
{
	HRESULT hr;
	// Place all initialization code for the client in this function
	if (SUCCEEDED(SimConnect_Open(&hSimConnect, "StartModules", NULL, 0, 0, 0)))
	{
		// Create some private events
		hr = SimConnect_MapClientEventToSimEvent(hSimConnect, EVENT_MENU_START);
		hr = SimConnect_MapClientEventToSimEvent(hSimConnect, EVENT_SUBMENU_START); 		
		hr = SimConnect_MapClientEventToSimEvent(hSimConnect, EVENT_SUBMENU1_START);

		hr = SimConnect_MenuAddItem(hSimConnect, "CPS", EVENT_MENU_START, 12345);
		hr = SimConnect_MenuAddSubItem(hSimConnect, EVENT_MENU_START, "FUEL MANAGEMENT",EVENT_SUBMENU_START, 234567);
        hr = SimConnect_MenuAddSubItem(hSimConnect, EVENT_MENU_START, "DESCENT CALCULATOR",EVENT_SUBMENU1_START, 345678);

		// Sign up for the notifications
		hr = SimConnect_AddClientEventToNotificationGroup(hSimConnect, GROUP_MENU, EVENT_MENU_START);

		hr = SimConnect_SetNotificationGroupPriority(hSimConnect, GROUP_MENU, SIMCONNECT_GROUP_PRIORITY_HIGHEST);
		SimConnect_CallDispatch(hSimConnect, MyDispatchProcMI, NULL);
	}
	return 0;
}

//
// The DLLStop function must be present.
//
void __stdcall DLLStop(void)
{
	// Close the client
	if (hSimConnect != NULL)
		HRESULT hr = SimConnect_Close(hSimConnect);
}

Could you, please tell me what I did wrong?

Thanks so much
Pierre
 
you need to give strcpy() the full name of the file, including its path otherwise fopen() can't find it.
 
Thanks for help Mgh,

The problem is the dll is in the same folder than the XCPS1.dat and XCPS2.dat folder (FSX main folder) which may be variable depending of the user settings.

How can I manage this path?

Best Regards
Pierre
 
Thanks for help Mgh,
The problem is the dll is in the same folder than the XCPS1.dat and XCPS2.dat folder (FSX main folder) which may be variable depending of the user settings.
How can I manage this path?
Best Regards
Pierre

You can use these functions:
GetModuleFileName
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683197(v=vs.85).aspx

PathRemoveFileSpec
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773748(v=vs.85).aspx

If you provide a null handle to GetModuleFileName, it will return the fully qualified path to the FSX.EXE file. You can then use PathRemoveFileSpec to remove "fsx.exe" from that path to get just the path name.

Doug
 
Pierre,

Keep in mind that almost every Windows API function returns a value. It is good practice to check this value. When things aren't working as you expect, it is usually the first thing you should review.
Also, have a look on MSDN at the GetLastError function. If the return value for your Windows API has indicated a failure, you can usually get more useful information by calling GetLastError.

Doug
 
I suggest you put a breakpoint in your application when you are developing on the line ::CreateProcess(... to check that values you are getting for ExeFileName and pg are what you expect.
 
Thanks so much to you both Doug and Mgh,

I'll try you tip immediately and back to inform you.

Best Regards
Pierre
 
Hello,

I'm back with unsuccessful result.

Here is my last code:
Code:
// CPSModulesStartDll.cpp*: définit les fonctions exportées pour l'application DLL.
//
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <Shlobj.h>
#include <string.h>
#include "SimConnect.h"
#include <wchar.h>
#include <WinBase.h>



static enum GROUP_ID {
	GROUP_MENU
};

static enum EVENT_ID {
	EVENT_MENU_START,
 	EVENT_SUBMENU_START,
    EVENT_SUBMENU1_START
};


FILE *fp;
char ExeFileName[MAX_PATH + 1];
char FileName[MAX_PATH + 1];
char ch;
int c = 0;
wchar_t pg[MAX_PATH];


HANDLE  hSimConnect = NULL;
static int quit = 0 ;


void CALLBACK MyDispatchProcMI(SIMCONNECT_RECV* pData, DWORD cbData, void *pContext)
{
	//HRESULT hr;
	switch(pData->dwID)
	{
	case SIMCONNECT_RECV_ID_EVENT:
		{
			SIMCONNECT_RECV_EVENT *evt = (SIMCONNECT_RECV_EVENT*)pData;

			switch(evt->uEventID)
			{
			case EVENT_SUBMENU_START:
				{
					STARTUPINFO si = { 0 };
					si.cb = sizeof(si);
					si.dwFlags = STARTF_USESHOWWINDOW;
					si.wShowWindow = SW_SHOWNORMAL  ;

					PROCESS_INFORMATION pi = { 0 };

					strcpy(FileName, "XCPS1.dat"); // Contains: d:\Program Files (x86)\Concorde Performance System\CFM.exe
					fp = fopen(FileName, "r");
					while ((ch = fgetc(fp)) != EOF)
					{
						ExeFileName[c] =ch;
						++c;
					}
					fclose(fp);
					
					ExeFileName[c] = '\0';
					[COLOR="DarkOrange"]DWORD Path = ::GetModuleFileNameA(nullptr,ExeFileName,sizeof(ExeFileName));
					MultiByteToWideChar(CP_ACP, 0, Path + ExeFileName, -1, pg, MAX_PATH);[/COLOR]
																
					::CreateProcess(NULL, pg , NULL, NULL, FALSE, 
						NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) ;

					break;
				}
              case EVENT_SUBMENU1_START:
				  {
					  STARTUPINFO si = { 0 };
					si.cb = sizeof(si);
					si.dwFlags = STARTF_USESHOWWINDOW;
					si.wShowWindow = SW_SHOWNORMAL  ;

					PROCESS_INFORMATION pi = { 0 };
	

					strcpy(FileName, "XCPS2.dat"); //Contains: d:\Program Files (x86)\Concorde Performance System\CDC.exe
					fp = fopen(FileName, "r");
					while ((ch = fgetc(fp)) != EOF)
					{
						ExeFileName[c] =ch;
						++c;
					}
					fclose(fp);
					ExeFileName[c] = '\0';
					[COLOR="DarkOrange"]DWORD Path = ::GetModuleFileNameA(nullptr,ExeFileName,sizeof(ExeFileName));
					MultiByteToWideChar(CP_ACP, 0, Path + ExeFileName, -1, pg, MAX_PATH);[/COLOR]
				
					::CreateProcess(NULL, pg , NULL, NULL, FALSE, 
						NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) ;

					break;
				  }

			default:
				break;
			}
			break;
		}

	case SIMCONNECT_RECV_ID_QUIT:
		{
			quit = 1;
			break;
		}

	default:
		printf("Received ID: %d", pData->dwID);
		break;
	}
}

int __stdcall DLLStart(void)
{
	HRESULT hr;
	// Place all initialization code for the client in this function
	if (SUCCEEDED(SimConnect_Open(&hSimConnect, "StartModules", NULL, 0, 0, 0)))
	{
		// Create some private events
		hr = SimConnect_MapClientEventToSimEvent(hSimConnect, EVENT_MENU_START);
		hr = SimConnect_MapClientEventToSimEvent(hSimConnect, EVENT_SUBMENU_START); 		
		hr = SimConnect_MapClientEventToSimEvent(hSimConnect, EVENT_SUBMENU1_START);

		hr = SimConnect_MenuAddItem(hSimConnect, "CPS", EVENT_MENU_START, 12345);
		hr = SimConnect_MenuAddSubItem(hSimConnect, EVENT_MENU_START, "FUEL MANAGEMENT",EVENT_SUBMENU_START, 234567);
        hr = SimConnect_MenuAddSubItem(hSimConnect, EVENT_MENU_START, "DESCENT CALCULATOR",EVENT_SUBMENU1_START, 345678);

		// Sign up for the notifications
		hr = SimConnect_AddClientEventToNotificationGroup(hSimConnect, GROUP_MENU, EVENT_MENU_START);

		hr = SimConnect_SetNotificationGroupPriority(hSimConnect, GROUP_MENU, SIMCONNECT_GROUP_PRIORITY_HIGHEST);
		SimConnect_CallDispatch(hSimConnect, MyDispatchProcMI, NULL);
	}
	return 0;
}

//
// The DLLStop function must be present.
//
void __stdcall DLLStop(void)
{
	// Close the client
	if (hSimConnect != NULL)
		HRESULT hr = SimConnect_Close(hSimConnect);
}

The main problem is I don't know how to debug the dll. (FSX being the .exe)

Could you help me please?

Thanks

Best Regards
Pierre
 
Last edited:
Back
Top