pingServer() fails with non-default locale
I'm a C++ beginner so maybe I'm doing something wrong but WASimClient fails to do pingServer() if previously std::locale::global(std::locale("")); has been executed. Here are the details:
- I'm using easylogging++ for logging.
- To activate Unicode support I need to call the
START_EASYLOGGINGPPmacro (see here). - Because I previously defined ELFF_UNICODE, one of the things the macro does is to execute
std::locale::global(std::locale("")). - If later I do
const uint32_t wasimversion = wasimclient->pingServer();, no matter how long I set the timeout, it returns 0. - Even if I don't call
START_EASYLOGGINGPP, but I executestd::locale::global(std::locale(""))before pingServer(), it will time out and return 0.
If I remove the std::locale::global(std::locale("")) call, WASimClient instantly connects to MSFS, so the problem is clearly reproducible by adding that line.
Is this something that should be corrected in WASimClient? Or can you suggest a workaround.
Hi, thanks for writing. This is a new one for me!
Just checking, std::locale::global(std::locale("")) is supposed to set the C++ locale to the current system/user locale, is that right?
I'm not clear on where it times out. Can you call connectSimulator() successfully? That sets up the base SimConnect session, which pingServer() needs.
If connectSimulator() works, does connectServer() after that? Wondering if this is something specific to the ping or systemic.
If those fail, what HRESULT do they return?
If SimConnect is failing then it becomes a different kind of issue. Have you tried a direct SimConnect connection (with that same locale setting) by any chance?
I'm guessing it's something with WASim client or server but at this point I have no clue what it could be... The client uses std::chrono::steady_clock for timing, which shouldn't be affected by locale and I'm otherwise at a loss to figure out what in the process would be locale-dependent. I will try to recreate it here but I'm not sure I'll see the same thing with my "en_US" locale (and testing with MSFS on a "sandbox" system is problematic!).
Some other ideas....
Does the logging work in WASimClient? And/or does it report any errors (before the timeout) to the log or stdout/console? I'm just wondering if there's some issue with the locale when it tries to read the client_config.ini to get defaults.
And does it change anything if you call std::locale::global(std::locale("")) before vs. after creating new WASimClient()?
I hope we can figure this out, and thanks again for reporting. -Max
Frankly, I don't know what std::locale::global(std::locale("")) does. My Windows locale is Hungarian (I guess hu_HU).
Here is what my program does:
- First it calls the START_EASYLOGGINGPP macro.
- Then it does
if (SUCCEEDED(SimConnect_Open(&hSimConnect, "x52 msfs out client", NULL, 0, 0, 0)))which succeeds. - Then it does
which also succeeds.auto tempclient = WASimCommander::Client::WASimClient(0x52C11E47); wasimclient = &tempclient; if ((hr = wasimclient->connectSimulator()) != S_OK) { - Then it does
const uint32_t wasimversion = wasimclient->pingServer();which hangs for the amount of networkTimeout in client_conf.ini. If I set it to 1000, it hangs for 1 sec. If 6000, for 6 sec. After the hang it returns 0. - I commented out error handling here, so my program continued with
if ((hr = wasimclient->connectServer()) != S_OK) {which also hangs for the amount of networkTimeout. Looking at the result code in VS debugging it is HRESULT_FROM_WIN32(ERROR_TIMEOUT) "The operation did not finish due to exceeding the timeout." (Or something along those lines in my local language.)
My client_conf.ini is:
[logging]
; Log file path is relative to the current working directory (not necessarily executable's directory); or can be absolute.
logFilepath = .\
; Logging level names are: None, Critical, Error, Warning, Info, Debug, Trace
fileLogLevel = Debug
consoleLogLevel = None
[network]
; SimConnect.cfg index or -1 for default local connection
networkConfigId = -1
; Default timeout for network requests to server, in milliseconds.
networkTimeout = 3000
; Enable SimConnect request tracking for detailed exception messages by setting to a positive integer; Disable by setting to 0 (zero).
requestTrackingMaxRecords = 200
Contents of WASimClient.log is (when I force my program to continue after pingServer() returns 0):
03-27 20:40:19.610 [INF]: Initializing WASimClient v1.2.0.0 for client 52 C11 E47 with net config ID -1
03-27 20:40:19.612 [DBG]: Dispatch loop started.
03-27 20:40:19.625 [INF]: Connected to Simulator "KittyHawk" v11.0.282 174.999, with SimConnect v11.0.62 651.3 (Server v5)
03-27 20:40:19.640 [DBG]: Created CDA ID 5 named "WASimCommander.Command.52 C11 E47" of size 544; Total data allocation : 544
03-27 20:40:19.641 [DBG]: Created CDA ID 6 named "WASimCommander.Response.52 C11 E47" of size 544; Total data allocation : 1 088
03-27 20:40:19.641 [DBG]: Created CDA ID 7 named "WASimCommander.Data.52 C11 E47" of size 1 088; Total data allocation : 2 176
03-27 20:40:19.641 [DBG]: Created CDA ID 8 named "WASimCommander.KeyEvent.52 C11 E47" of size 32; Total data allocation : 2 208
03-27 20:40:24.477 [WRN]: Server did not respond to ping after 3 000ms
03-27 20:40:29.026 [INF]: Connecting to WASimCommander server...
03-27 20:40:32.037 [ERR]: Server connection timed out or refused after 3 000ms.
03-27 20:46:28.140 [DBG]: Shutting down...
03-27 20:46:28.140 [DBG]: Dispatch loop stopped.
03-27 20:46:28.255 [INF]: Shutdown complete, network disconnected.
If I delete client_config.ini the log contains the following, which is pretty similar, just without the debug messages and timeout defaults to 1 sec:
03-27 20:53:13.760 [INF]: Initializing WASimClient v1.2.0.0 for client 52 C11 E47 with net config ID -1
03-27 20:53:13.787 [INF]: Connected to Simulator "KittyHawk" v11.0.282 174.999, with SimConnect v11.0.62 651.3 (Server v5)
03-27 20:53:14.799 [WRN]: Server did not respond to ping after 1 000ms
03-27 20:53:14.803 [INF]: Connecting to WASimCommander server...
03-27 20:53:15.823 [ERR]: Server connection timed out or refused after 1 000ms.
03-27 20:53:16.089 [INF]: Shutdown complete, network disconnected.
Thanks for the details! That helps narrow it down.
I think the problem is where the WASimClient formats a unique name from the client ID passed in the constructor. I see from the log that the formatting adds spaces around the hex digits ("for client 52 C11 E47"). This name is actually used to set up the custom events with SimConnect which are required for connection and ping. And I'm pretty sure spaces aren't allowed in custom event names.
My previous suggestion of constructing WASimClient() before the locale change should be one workaround in this case (since the client name is only crated once at construction time).
Or I guess change the locale back to 'C' (std::locale::global(std::locale::classic())) before creating the client, then back to the std::locale(""). A bit awkward but should work.
I'm not sure but maybe there's also a way to adjust the locale formatting options globally somewhere to tell it to not split hex numbers (because how would want that anyway? :-).
The real fix will be in WASimClient::Private constructor where it creates the name. Apparently ostringstream() will use the current global locale for formatting. There's a way to force it to use 'C' locale (oss.imbue(std::locale::classic())), or figure out another way to format the name to hex digits.
Please let me know if the workarounds help and if the rest seems to work OK after that!
Thanks, -Max
Thanks for the very fast solution! I did the following:
std::locale::global(std::locale::classic());
auto tempclient = WASimCommander::Client::WASimClient(0x52C11E47);
std::locale::global(std::locale(""));
and all parts of WASimClient works again, like before. It reads the ini file correctly, sends and receives data, etc. From my point of view, you can close this issue or implement a fix in your code, as you seem fit. I'm happy to help with testing your final solution, if you need.
Oh that was quick, thanks for confirming!
I was about to say you could also probably do something like
std::setlocale(LC_NUMERIC, "C") and restore it with std::setlocale(LC_NUMERIC, std::locale("").name())
But that's not much different.
Anyway, I'll keep this open for now for anyone else and will implement a fix as well.
I realized I can set the locale manually to whatever I want anyway, it doesn't have to match my system locale. But a test of the solution in a real environment would be much appreciated! I'll post here once I have a build.
Cheers, -Max