From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

unsigned char array to callbackdata problem with memset

Solved!
Go to solution

hello everyone, i'm using CVI 2012 SP1 and i have this problem.

i have a function that load panel and assign an unsigned char array as callback data to the panel and to the timer.

when i use the array with the timer everything seems ok, when i close the panel and make a memset it's seem that the function works but after i find the value in the array.

can you check my code?

i have deleted not necessary code.

p.s. i know i don't need to memset because when i close the panel and the function quit the variable will be deleted from memory but i didn't understand why the callback with memset seems to work but if i check the memory is not reset.

 

 

void LoadAlarmPage(void)
{
	int panelAHandle = {0};
	unsigned char byte_all[16] = {0};
	
	panelAHandle = LoadPanel(0, "allarmi.uir", PANEL_ALL);
	if (panelAHandle < 0)
	{
		return;
	}

	SetCtrlAttribute (panelAHandle, PANEL_ALL_TIMER, ATTR_CALLBACK_DATA, byte_all);
	SetPanelAttribute (panelAHandle, ATTR_CALLBACK_DATA, byte_all);

	if (InstallPopup(panelAHandle) == 0)
	{
		RunUserInterface();
		RemovePopup(0);
	}
	DiscardPanel(panelAHandle);
	return;
}

 

 

int CVICALLBACK PanelAlarmCallback(int panel, int event, void *callbackData, int eventData1, int eventData2)
{
	unsigned char *byte_all = callbackData;
	
	switch(event)
	{
		case EVENT_CLOSE:
			memset (byte_all, 0, 16);
			QuitUserInterface(0);
			break;
	}
	return 0;
}

 

 

int CVICALLBACK Gest_All(int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
	unsigned char *byte_all = callbackData;
	union S_DB10 *DB10 = {0};
	int i = {0};
	
	switch (event)
	{
		case EVENT_TIMER_TICK:
			switch (control)
			{
				case PANEL_ALL_TIMER: //
					
					DB10 = GetPointerToDB10();
					if(memcmp(DB10->byte_all, byte_all, 16) != 0)
					{
						memcpy (byte_all, DB10->byte_all, 16);
} ReleasePointerToDB10(); break; } break; } return 0; }

 

Davide Vittorio G. - TLGB S.R.L.
Italian SW Developer
0 Kudos
Message 1 of 11
(4,660 Views)
Solution
Accepted by topic author holly7787

I made some test adding your code to an application of mine and all works well: at program end the memory is cleared.

How exactly you find the memory has not been cleared in your app? If you happen to have some breakpoint elsewhere in your code and examine the memory while suspended, you are consuming time in this task and so some pending timer event may be fired between the call to memset and the actual program termination.

I suggest you to remove every breakpoint you may have and add this line after every manipulation of byte_all and examine the results in debug output window after the program terminates: 

DebugPrintf ("%s: %s\n", __FUNCTION__, byte_all);

This way you may know if some function is called after PanelAlarmCallback that fills the memory in again (I suppose you are not using that memory blok only in these 3 functions you have shown us).

 

Having said this, what is puzzling me in your code is the use of RunUserInterface: you should have only one call of this function throughout the whole application and I suspect that you already have one before these functions are called; calling RunUserInterface more than once may results in conflicts on who is actually processing system events. Your system will remain functional even without that RunUserInterface, as long as the panel is alive thus keeping the memory allocated so you have no need of calling it there if already have called it elsewhere.

 



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 2 of 11
(4,646 Views)

Thanks Roberto, i was using breakpoint and probably it's like you explained even if byte_all is used only in this 3 function

 

i have changed your code with this to see the result in HEX and it's working correctly.

DebugPrintf ("%s: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", __FUNCTION__, byte_all[0], byte_all[1], byte_all[2], byte_all[3], byte_all[4], byte_all[5], byte_all[6], byte_all[7], byte_all[8], byte_all[9], byte_all[10], byte_all[11], byte_all[12], byte_all[13], byte_all[14], byte_all[15]);

very nice this function i didn't knew it!

 

you are right about this is a subpanel but can you explain me better this "RunUserInterface" problem? if i don't put it in my code the panel will close immediatly after it's shown. how can i keep the panel open witout calling "RunUserInterface"?

Davide Vittorio G. - TLGB S.R.L.
Italian SW Developer
0 Kudos
Message 3 of 11
(4,640 Views)

The basic code structure can be the following:

void LoadAlarmPage(void)
{
	LoadPanel ();
	InstallPopup ();
	return;
}
int CVICALLBACK PanelAlarmCallback(int panel, int event, void *callbackData, int eventData1, int eventData2)
{
	switch(event)
	{
		case EVENT_CLOSE:
			DiscardPanel ();
			break;
	}
	return 0;
}

The first function exits and the panel remains visbile and active (with InstallPopup the panel will remain on top until discarded).

DiscardPanel in the second function automatically removes the popup panel before discarding the panel.

 

If you want to display in hex, you can do the following:

Fmt (msg, "%s: 0x%*d[zb1w2p0r16j1] \n", __FUNCTION__, 16, byte_all);
DebugPrintf (msg);

it works for every string, provided you change the string lenght (16 in your case).



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 4 of 11
(4,629 Views)

Never used in this way Smiley Embarassed

 

now my callbackdata isn't working because when the LoadAlarmPage() finish the byte_all became freed so i think i will do something like this.

 

using static unsigned char byte_all[16] in the timer callback and doing memset when discarding the timer, or do you think there is a better option? 

 

 

void LoadAlarmPage(void)
{
	int panelAHandle = {0};
	panelAHandle = LoadPanel(0, "allarmi.uir", PANEL_ALL);
	if (panelAHandle < 0)
	{
		return;
	}
	
	// do something

	if (InstallPopup(panelAHandle) < 0)
	{
		DiscardPanel(panelAHandle);
	}
	return;
}
int CVICALLBACK PanelAllarmiCallback(int panel, int event, void *callbackData, int eventData1, int eventData2)
{
	switch(event)
	{
		case EVENT_CLOSE:
			DiscardPanel(panel);
			break;
	}
	return 0;
}
int CVICALLBACK Gest_All(int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
	static unsigned char byte_all[16] = {0};
	union S_DB10 *DB10 = {0};
	int i = {0};
	
	switch (event)
	{			
		case EVENT_TIMER_TICK:
			switch (control)
			{
				case PANEL_ALL_TIMER: //
					
					DB10 = GetPointerToDB10();
					if(memcmp(DB10->byte_all, byte_all, sizeof(byte_all)/sizeof(unsigned char)) != 0)
					{
						memcpy (byte_all, DB10->byte_all, sizeof(byte_all)/sizeof(unsigned char));

						DebugPrintf ("%s: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", __FUNCTION__, byte_all[0], byte_all[1], byte_all[2], byte_all[3], byte_all[4], byte_all[5], byte_all[6], byte_all[7], byte_all[8], byte_all[9], byte_all[10], byte_all[11], byte_all[12], byte_all[13], byte_all[14], byte_all[15]);
					}
					ReleasePointerToDB10();
					break;
			}
			break;
			
		case EVENT_DISCARD:
			memset(byte_all, 0, sizeof(byte_all)/sizeof(unsigned char));
			break;
	}
	return 0;
}

and Smiley Surprised i think this will be too hard to debug in case of problem but thanks a lot!

Fmt (msg, "%s: 0x%*d[zb1w2p0r16j1] \n", __FUNCTION__, 16, byte_all);
DebugPrintf (msg);

 

 P.S. i think Mr Robert developing level is over 9000 Smiley Very Happy

Davide Vittorio G. - TLGB S.R.L.
Italian SW Developer
0 Kudos
Message 5 of 11
(4,618 Views)

Define the variable as static in LoadAlarmPage instead, so it is retained and will be valid for other functions as well.

I made up a little example for you to see, more or less in the line of what you already developed.

 

With reference to that little tip about displaying in hex values, I suggest you to read Fmt documentation: Fmt is a powerful instruction that can do a lot of things. In my line I told the command to read a string, elaborate it as a byte array, consider individual bytes and write each of them in hex format in 2-characters fields padded with zeroes, separated by a space, passing the actual string lenght as a parameter to make it universal Smiley Surprised Nice job for a single instruction, isn't it? Smiley Wink



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 6 of 11
(4,604 Views)

ok, i think we've reached the destination.

thank you for your support, like always!

Davide Vittorio G. - TLGB S.R.L.
Italian SW Developer
0 Kudos
Message 7 of 11
(4,599 Views)

Just another question, you said "Define the variable as static in LoadAlarmPage instead, so it is retained and will be valid for other functions as well.".

 

why don't use a static global variable instead of a static local variable passed as callback data? are there any performance improve or better memory management?

Davide Vittorio G. - TLGB S.R.L.
Italian SW Developer
0 Kudos
Message 8 of 11
(4,584 Views)

There may be a misunderstandinf on my part. YOU were using the callbackData mechanism so I answered  considering to address that scenario and giving you some hints on how to make it work. Smiley Wink

If you were using a global variable, then using callbackData is not needed any more: simply manipulate variable value where appropriate.

 

When passing to use globals, though, please consider that they are listed among bad programming practices since they increase program complexity exactly for them being potentially modifiable everywhere in the code: you need to protect yourself from multiple unwanted access and you may find the variable has been modified unexpectedly. All these events need to be considered in developing the application so the use of locals may be preferrable since they are more limited in scope and more controllable.



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 9 of 11
(4,580 Views)

no misunderstanding at all, i was just wondering why is better to use a static local variable passed as callbackdata and not a static global. usually when i create a new panel i create a .uir file and a .c and .h file with the function to work with it. if i put the static in the .c that handle the function of that panel the complexity of the software is not so bad, certainly not the best.

 

also for someone that doesn't know how to use the callback data the code is complex than one with a static global variable, obviosly my thread was about this argument so is correct to work on a "higher" complexity knowledge.

 

Davide Vittorio G. - TLGB S.R.L.
Italian SW Developer
0 Kudos
Message 10 of 11
(4,576 Views)