Jump to content

Need help with plugin programming..


Recommended Posts

Hi!

I have started working on my first Euroscope-plugin and I need some help... The plugin runs a simple Winsock server that exchanges data with an external software. The plugin loads and starts fine, but after I while (a few houndred sent messges), I get this error:

 

Windows has triggered a breakpoint in EuroScope.exe.

This may be due to a corruption of the heap, which indicates a bug in EuroScope.exe or any of the DLLs it has loaded.

This may also be due to the user pressing F12 while EuroScope.exe has focus.

The output window may have more diagnostic information.

 

I then press contunie and get this:

Unhandled exception at 0x77d7e6c3 in EuroScope.exe: 0xC0000374: A heap has been corrupted.

 

If I press contunie now, the plugin contunies to run like normal and with no more errors. When I load and run the plugin the normal way (not via Visual Studio), it runs fine for a while before ES crashes.

 

EDIT: char* msg and char* tmp are the private data members of the CEsEFSproxy cl[Mod - Happy Thoughts]. Here's the CEsEFSproxy constructor:

CEsEFSproxy::CEsEFSproxy(void) 
: CPlugIn (EuroScopePlugIn::COMPATIBILITY_CODE,
"EFS Proxy",
"0.1.0",
"Even Rognlien",
"Only for use with ENGM EFS" )
{
msg = new char();
tmp = new char();
}

 

EDIT: And destructor

CEsEFSproxy::~CEsEFSproxy(void)
{
delete msg;
delete tmp;
}

 

This is the code that builds up the data to send and sends it to the socket. msg and tmp are [Mod - Happy Thoughts]igned different strings each time there is a position update.

void CEsEFSproxy :: OnAircraftPositionUpdate (EuroScopePlugIn :: CAircraft Aircraft)
{	

//      msg-format:	@P:CALLSIGN:LATITUDE:LONGITUDE:ALTITUDE

if(ClientSocket != INVALID_SOCKET)
{
	strcpy(msg, "@P:");                       // Beginning of position-message
	strcat(msg, Aircraft.GetCallsign());
	strcat(msg, ":");
	sprintf(tmp, "%f", Aircraft.GetPosition().GetPosition().m_Latitude);
	strcat(msg, tmp);
	strcat(msg, ":");
	sprintf(tmp,"%f", Aircraft.GetPosition().GetPosition().m_Longitude);
	strcat(msg, tmp);
	strcat(msg, ":");
	strcat(msg, itoa(Aircraft.GetPosition().GetPressureAltitude(), tmp, 10));
	strcat(msg, "\0");

	send(ClientSocket, msg, strlen(msg), 0);	// Send to client
}
}

 

Server code: http://pastebin.com/AHZhCJex

 

What's wrong? Could it be that I'm using the same char* several times?

Link to post
Share on other sites

I also get these..

Unhandled exception at 0x069e2799 in EuroScope.exe: 0xC0000005: Access violation writing location 0x052d9000.

 

It's strange, because it seems to happen quite randomly... sometimes it can process data for a long time, but when theres much data to send, the compiler stops faster.

Another tought; can the reason for this be that the two char pointers are allocated on another thread than the thread using it?

 

Full code:

EsEFSproxy2.cpp: -

EsEFSproxy2.h: -

 

Hope someone can help me...

Edited by Guest
Link to post
Share on other sites

Didn't get to see how you declared the variables, but you need to allocate memory to the pointer before trying to copy things into it. When you first declare it with:

 

char* msg;

 

all you are doing is saying that you have a pointer. You haven't told the system what the pointer is pointing at. Normally when you do

 

msg = "test 123\0";

 

the system will find a spot in the memory (let's say address 1234) and stores the string there. It will then store 1234 to "msg". Next time you use msg, it will see that msg is pointing to address 1234 and find your string at address 1234. The problem you have is that you are trying to copy straight into the pointer. The system says, okay let's copy this string into the place that the pointer "msg" is pointing at and finds that it doesn't actually point anywhere - and hence you have an access violation.

 

The solution is to either use a fixed width array like you have done, or you can allocate the number of bits you need use malloc (http://www.cplusplus.com/reference/cstdlib/malloc/)

 

malloc basically returns a void of the size (in bytes) that you specify. You then cast it into whatever type you need it as (e.g. char) For example, let's do:

 

(char*) malloc(1024)

 

to crease a 1024 byte buffer of type char at address 9876. If we [Mod - Happy Thoughts]ign this to our original variable

 

msg = (char*) malloc(1024);

 

then msg now points at address 9876, which is an empty buffer of length 1024 bytes. Now when we run strcpy, it will look at the address that msg points at (address 9876) and copy the string into that spot.

 

Now, if you were thinking of using char* as a way of avoiding having to declare the size of your variable (like I have tried to do many times coming from Python which doesn't require this, each time resulting in a painful experience that has also taught me all about pointers, memory and how much I hate C/C++) then you are out of luck. Unfortunately in C/C++, you have to tell the system pretty much everything in advance.

 

[Edit] Compiler error: missing semicolons :p

David Zhong

Link to post
Share on other sites
Now, if you were thinking of using char* as a way of avoiding having to declare the size of your variable (like I have tried to do many times coming from Python which doesn't require this, each time resulting in a painful experience that has also taught me all about pointers, memory and how much I hate C/C++) then you are out of luck. Unfortunately in C/C++, you have to tell the system pretty much everything in advance.

 

Thanks for a good explanation David! Always something to learn about C++. Yes I declared it like that first (char* msg), and it worked until the strings grew too big..

Link to post
Share on other sites
  • 2 weeks later...

Pointers is actually quite easy. But you have to understand how they work. Otherwise it's segmentation fault or the like. You always have to allocate as much memory as you need. A byte more and odd things might happen. If you use char[1024] arrays and the like, you're preallocating that much memory already. That's why it "works easily". However, you won't be able to free that memory. All depends on how your program works.

Miguel Frias

Senior Instructor (I3) & Certified Pilot (P4), ZLA I-11 graduate

Portugal vACC Training Director (ACCPT2), VATEUD Operations Director (VATEUD8)

Portugal vACC, VATEUD, VATSIM

1107190.png1107190.png

Link to post
Share on other sites
  • 4 weeks later...

Oh sorry.. will look more into it later.. Thanks for your reply!

 

New problem..

 

Function declarations in EsEFSproxy2.h:

	inline  CPlugIn * GetPlugIn ( void ) ;   // From EuroScopePlugin.h
static void on_receive(char * rxTxt);
static void serverStart(void * arg);

From EsEFSproxy2.cpp:

// Called when server receives data
void CEsEFSproxy :: on_receive( char * rxDta )
{
GetPlugIn()->AircraftSelect(rxDta) ;
}

Gives me these errors:

- error C2352: 'CEsEFSproxy::GetPlugIn' : illegal call of non-static member function

- error C2227: left of '->AircraftSelect' must point to cl[Mod - Happy Thoughts]/struct/union/generic type

- IntelliSense: a nonstatic member reference must be relative to a specific object

 

Removing "static" before "on_receive()" and "serverStart()" eliminates these errors, but then there is a new error when I try to call "serverStart" on a new thread:

bool    CEsEFSproxy :: OnCompileCommand ( const char * sCommandLine )
{
if ( ! strncmp ( sCommandLine, ".efsstart", 9 ))  {
	_beginthread(serverStart, 0, NULL);
}
  //...
}

- error C3867: 'CEsEFSproxy::serverStart': function call missing argument list; use '&CEsEFSproxy::serverStart' to create a pointer to member

- IntelliSense: argument of type "void (CEsEFSproxy::*)(void *arg)" is incompatible with parameter of type "void (__cdecl *)(void *)"

 

I have tried to change "serverStart" back to static, but then I can't call "on_receive" from "serverStart", because both must be static. Then I'm right back where I started...

 

Any ideas? :/

Link to post
Share on other sites
  • 3 months later...

A static function in a cl[Mod - Happy Thoughts] is essentially the same as putting a function in a namespace. The static function doesn't know about cl[Mod - Happy Thoughts] members. If you want to be accessing cl[Mod - Happy Thoughts] members of an instance, you don't want to be using static functions. On the other hand, if you are using the Singleton Cl[Mod - Happy Thoughts] Pattern, then that's a different story. In that case you WILL want static functions and the way you call them is by CEsEFSproxy::on_receive(...)

 

In the next scenario, you have two problems:

 

First, you have to realise that functions are not first-cl[Mod - Happy Thoughts] citizens in C++. They can't be moved or copied as your code tries to do. You have to p[Mod - Happy Thoughts] a pointer to where that function is stored. As the error suggests, you have to do something like '&serverStart' to get the pointer.

 

I don't know what _beginthread does or what the prototype for it looks like, but according to this error message, it is expecting the first argument to be of type 'void (*) (void*)'. This is a horrible C-style function pointer. It means a function that expects 'void*' as the argument (in the second parenthesis) and returns 'void' (the first void). The '*' in the first parenthesis means that this is a pointer (you can ignore the __cdecl part: that is Visual C++ specific stuff that doesn't make sense to play with unless you're writing a game or a server or something that requires highly-optimized code). Now you're thinking, "but looking at the prototype for 'startServer' isn't that correct"? When 'startServer' was static you were correct. But the error says that you are p[Mod - Happy Thoughts]ing a function of type 'void (CEsEFSproxy::*) (void*)'. Because the function is not static, it is a proper member function and normal member functions have a little hidden secret: they are all p[Mod - Happy Thoughts]ed a variable called 'this' which is a pointer to the object instance. So a function of type 'void (CEsEFSproxy::*) (void*)' could also be said (not accurately, but you get the meaning) 'void (*) (CEsEFSproxy*, void*)'. Why did the code work when the function was static? Because a static function works like a regular function outside of a cl[Mod - Happy Thoughts]. It doesn't get the hidden 'this' variable, so it works fine.

 

Now how to fix your problem: if you didn't write _beginthread, then you might be in trouble. If you did I would rework it to accept a function template. Even better, stop thinking about _beginthread and use the standard thread library instead: http://www.cplusplus.com/reference/thread/thread/

 

Having seen some of your code, you are using a lot of C-style programming which makes life more difficult than it has to be. Visual C++ has pretty much all the features of C++03 and the latest version has almost all the headline features and libraries of C++11. I'd suggest you go online and spend a few days just learning about the great things that you can do in C++ that make it look like a modern language instead of slogging away at C-style code. It's not your fault that you write code like this - the problem is that too many people write code like this; if I was to revamp the Euroscope SDK, the first thing I'd do is get rid of all of the char pointers everywhere and replace them with proper strings (you know, like other languages have )

David Zhong

Link to post
Share on other sites
  • 4 months later...

Finally got some more time for this plugin!

 

I'm sorry for the very late reply, David, but thanks alot for your explanation - that made things a lot clearer to me, and problem is now solved

 

Just a quick question, how can I tell whether a SECTOR_ELEMENT_SIDS_STARS is a SID or a STAR?

	// loop and find all SIDs and STARs for ENGM
for (sid_star = SectorFileElementSelectFirst(EuroScopePlugIn::SECTOR_ELEMENT_SIDS_STARS); 
       sid_star.IsValid(); 
       sid_star = SectorFileElementSelectNext(sid_star, EuroScopePlugIn::SECTOR_ELEMENT_SIDS_STARS))
{
	if (strcmp("ENGM", sid_star.GetAirportName()) == 0)	{
		strcat(sidStars, sid_star.GetRunwayName(0));
		strcat(sidStars, "*");
		strcat(sidStars, sid_star.GetName());
		strcat(sidStars, "|");
	}
}

Link to post
Share on other sites

Ok.. I found one way around. If all SIDs are listed before the STARs and the lists are sorted on runway ID, I can save all procedures from the beginning as SIDs, and when I reach the first runway for the second time, I [Mod - Happy Thoughts]ume the rest of the procedures are STARs.

 

Questions:

1. Is it possible to set the "Clearence received flag" somehow? I only found the GetClearenceFlag(...)

2. How can I ask ES to generate a squawk automatically? The SetSquawk command allows only manual input.

Link to post
Share on other sites
  • 1 month later...

Hi again!

 

Four more questions:

1. Is there any way to get the CRect that surrounds the visible tag items? I want to make something like this: http://i.gyazo.com/39fbbc10ba17f4fefbd7fb360be96144.png

 

---------------------------------------------------------------------------------------------------------------------------------

 

2. Is there an easy way to get the history trail coordinates?

 

EDIT:

Solution:

	EuroScopePlugIn::CRadarTargetPositionData history[10];
	for (int i = 0; i <= 9; i++)
	{
		if (i == 0)
			history[0] = rt.GetPosition();
		else
			history[i] = rt.GetPreviousPosition(history[i - 1]);
	}

---------------------------------------------------------------------------------------------------------------------------------

 

3. I have created a custom tag item that is listed under "Registered tag item types". However it's not showing up in the dropdown list in the tag editor.

Screenshot 1: http://i.gyazo.com/c3ea5d6e992376e6fca31c2c9e5f2763.png

Screenshot 2: http://i.gyazo.com/158925fea042e41330bbceda9c40f398.png

 

Here's my (simplified) code for the tag:

CNatcon :: CNatcon(void): CPlugIn ( EuroScopePlugIn :: COMPATIBILITY_CODE, ..., ..., ..., ... )
{	
RegisterTagItemType("Test item", TAG_ITEM_DEST_2);
}


void CNatcon :: OnGetTagItem ( EuroScopePlugIn :: CFlightPlan FlightPlan , .... ,,,,)
if (!FlightPlan.IsValid())
	return;
if (ItemCode == TAG_ITEM_DEST_2)
{
               char* txt = "Test";
	strcpy_s(sItemString, strlen(txt), txt);
}
}

 

What am I doing wrong? Do I need to register a function for the tag item if I only want to show some static text?

 

---------------------------------------------------------------------------------------------------------------------------------

 

4. How can I create multiple instances of a window? This is the constructor of my plugin:

 

CNatcon :: CNatcon(void) : CPlugIn ( EuroScopePlugIn::COMPATIBILITY_CODE,
						"NATCON",
						"1.0.0",
						"Even Rognlien",
						"-" )
{
RegisterDisplayType("ILS view 1",
	false,
	false,
	true,
	true);

RegisterDisplayType("ILS view 2",
	false,
	false,
	true,
	true);
}

 

Both windows appear under "Allowed to draw on types", but only one window is visible..

Is it possible to create more than one radar view per plugin?

Link to post
Share on other sites

1. Is there any way to get the CRect that surrounds the visible tag items?

 

3. I have created a custom tag item that is listed under "Registered tag item types". However it's not showing up in the dropdown list in the tag editor.

What am I doing wrong? Do I need to register a function for the tag item if I only want to show some static text?

 

4. How can I create multiple instances of a window? This is the constructor of my plugin:

...

Both windows appear under "Allowed to draw on types", but only one window is visible..

Is it possible to create more than one radar view per plugin?

1: Unfortunately not (I'd like to have those available as well...)

 

3: Plugin created tag items are shown in the list as " \ "

 

4: If the display types appear in the "Allowed to draw on types", I'd say it's possible to create more than one radar view per plugin (I've never tried that though). However, one ASR can only display one Display Type, so you wouldn't be able to show both of them simultaneously on a single ASR.

Link to post
Share on other sites
  • 2 weeks later...

I'm now working on a flight strip bay and I need a way to synchronize the flight strip bays of the different controllers. Will changes done to the 9 FlightStripAnnotation fields be visible to other controllers, or just me? Is there a smarter way to do this? I don't want to use the scratch pad field.

 

Edit1: Another question;

How can I select an aircraft so the callsign and flight info appears on bottom bar of Euroscope, like it does when clicking on a radar tag? I have tried to use SetASELAircraft(rt) and RadarTargetSelectASEL() after the callsign-field on a strip is clicked (of course with a valid and correct radartarget "rt") but the only thing that shows up on the bottom bar is the "message" from the clicked area/item.

 

Edit2: One more question;

The code below opens the scratch pad editor. The "Area" is 15 high and 300 wide. When the text box appears, the height seem to be right, but the width is only 40 or 50. I would like the text box to be as wide as the Area. Is that possible?

 

StartTagFunction(sObjectId,
NULL,
EuroScopePlugIn::CTR_DATA_TYPE_SCRATCH_PAD_STRING,
GetPlugIn()->RadarTargetSelect(sObjectId).GetCorrelatedFlightPlan().GetController[Mod - Happy Thoughts]ignedData().GetScratchPadString(),
NULL,
EuroScopePlugIn::TAG_ITEM_FUNCTION_EDIT_SCRATCH_PAD,
Pt,
Area);

Link to post
Share on other sites

The changes to the 9 fields will only be shown to others if the strip is pushed to them (either manually or automatically during handoff). If you're not publishing a lot of information, you could probably just as well use the scratchpad in the same way EuroScope sets direct-to clearances, [Mod - Happy Thoughts]igned headings etc. unless there's a reason you're avoiding the scratchpad completely. This method is to update the scratchpad with your message and then immediately update it again with the previous (or empty) string. Just be sure to format the messages in a way that they won't be recognized by EuroScope as a clearance, or by other plugins that are using the same technique.

 

The scratchpad strings used by EuroScope are listed here.

 

How to avoid confusing other plugins with your messages (and to avoid your plugin being confused by others) is of course guesswork, but prefixing the messages with some characters could work (for example send "ABC!text" instead of just "text").

 

If you need to put something on a strip owned by another controller, the scratchpad and the 9 fields can't be used. For this case another solution must be found.

 

For the flight info, you'll probably have to write it as the message, at least I don't remember any way to display it automatically.

 

For the text box, instead of StartTagFunction, use OpenPopUpEdit. It will set the text box area correctly. Note that it will still put all text on one row no matter how high the area is, so if you need multi-line edit boxes you'll have to write your own code.

Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
×
×
  • Create New...