• 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.

P3D v4 How do I start making c++ GDI+ gauges?

I know I can start with the SDK building the gauges but every try it ends with P3D CTD(crash to desktop) of ucrtbase or Kernalbase. Can anyone list here a guide for P3D v4.4?

Sent from my iPhone using Tapatalk


Resource contributor
With the examples he shared, I would drop D2D, but combine the multi-thread layout with the GDI+ graphics library.
i have played a bit with the example above
compiling it in X64 with VS 2019, using the gauges header from P3d v4 SDK.
It compiles and links nicely, however i get a run time error of "access violation".
Here my code, stolen from example and de-templatized for simplicity:
BTW: also the original code gets the same error on the same line.
void FSAPI Callback(GAUGEHDR* gau, int serviceId, unsigned) {
    switch (serviceId)    {
        // when the panel service starts, we create the C++ gauge object
        // that we'll store as an opaque pointer in user_data
        assert(gau->user_data == 0);
        gau->user_data = reinterpret_cast<UINT32>(new TransparentGauge);
        PELEMENT_STATIC_IMAGE    mCanvas = reinterpret_cast<PELEMENT_STATIC_IMAGE>(gau->elements_list[0]);
        reinterpret_cast<TransparentGauge*>(gau->user_data)->mCanvas = mCanvas; // offending line
// continue
Any hint will be really appreciated.
Last edited:
The gauge callback variable list is defined differently for x64 than it is for x32.

Also... don't release gauges with assert calls... they're fine for testing while developing... but should never be in released code, it doesn't "clean up" and crashes the bazinga out of the sim if triggered.

I don't know why you rewrote the if (gau->user_data == 0) statement to an assert... but... I guess if you hate if statements... LOL

Without seeing the declaration of mCanvas in your TransparentGauge class... couldn't tell you what the problem is. I suspect mCanvas is defined as a protected variable... which can't be accessed the way you're trying to access it.
Last edited:
Thanks for replay,
however i'm a bit confused, not sure to have understood since the gauge callback signature is exactly what is defined in x64 gauges.h:
typedef void FSAPI GAUGE_CALLBACK( PGAUGEHDR pgauge, SINT32 service_id, UINT_PTR extra_data );
May you be a bit more precise (language barrier, i guess)
The thrid variable passed you have defined as unsigned, whereas the variable is actually a UINT_PTR. UINT_PTR is an unsigned __int64 and you declared the third parameter as simply unsigned. While that's not the source of your error, it IS an incorrect definition.
gau->user_data = reinterpret_cast<UINT32>(new TransparentGauge);

You are casting a 64 bit pointer to a 32 bit uint, truncating it, and then,

reinterpret_cast<TransparentGauge*>(gau->user_data)->mCanvas = mCanvas; // offending line

converting it back to a 64 bit pointer, which won't work. In V4 the user_data field is a UNIT_PTR (uintptr_t), so use that in your initial cast instead to avoid the truncation.


reinterpret_cast is dangerous in general, don't use it unless you absolutely have to. There are better ways to store truly global data than in the GAUGEHDR, like a singleton.
This line:
gau->user_data = reinterpret_cast<UINT32>(new TransparentGauge);
should be:
gau->user_data = reinterpret_cast<UINT_PTR>(new TransparentGauge);
I disagree regarding reinterpret_cast... it's no more dangerous than a direct typecast. The gauge object is NOT a global data... so it doesn't need to be a singleton. There are lots and lots of reasons for it to not ever be a singleton but I can't fathom a single reason for it needing to be a singleton.
Well, you're entitled to your opinion. The general opinion of the C++ community is that casting is bad, and reinterpret_casts can almost always be avoided. In my ~15 million LOC project at work, our static analysis bans reinterpret_cast. When I see a pointer being cast to an integer, I can't help but think there is a better way to handle that design. To me, this includes the tradeoffs of introducing managed global state. If you see some specific reasons for this object to not be a singleton, like needing explicit memory management/instantiation, more than one instance, support for subclassing, mocking for testing, etc, what approach would you take? DI?

Last edited:
Not sure I'd listen to the advice of someone who does this:
for (int m = 1; m <= 12; ++m)
Just really bad choice there... really bad. I'm not talking about the 12 either.


Resource contributor
I would also point out that trying to use the invisible systems gauge template to do a glass display is a bad idea. I would rather copy the unique snippets of the invisible systems gauge template to the ESP GDI+ template if I needed it. Rather start with the template designed to be a glass display.