Jump to content

What is the best way to handle errors for external DLL/C code,


Recommended Posts

Hello all,

background

A while ago I wrote a dll wrapper to get data from dedicated test-hardware. For this hardware there is a dll available.  To use this dll you need to use an callback function. If you need to use a dll in Labview which needs a callback you need to write the callback in C code and use a Labview C lib to get the data into Labview, basically my dll takes care of this problem .

Enhance the code and way of working more efficient

The code I wrote code works fine. However I did not use / test it for hours on a row. And sometimes If I make a mistake when changing the code I run into problems. I was able to fix all the errors and problems however it was not the most efficient way of working. For instance If there  is a serious error in the code the dll I need for the hardware can crash, it can make my wrapper crash, which will in return make Labview crash. This makes the way of coding inefficiënt and it will not enable me to read my C++ book etc etc.

Example pseudo code

I did try to extract some main points of intrest from the code, I did add some comment in the code. I hope someone can give me some general guideline or advice what to read.

At this time I am reading:

Unhandled C++ exceptions

I think I might be able to use it, I did not use this before so advice would be welcome.

 

Thank you all for the help !

 

// ..
// This is a part of the code only intended to have a talk about how to catch errors 
// It is not complete and intended as Pseudo code
// ..


int __stdcall MyDriverLoads(const int NumberToInitialise,const bool enableAnotherOption , const char *const path , LVUserEventRef *rwer)
    {
		
	// loads the dll 
	// Q -- I can check if (F_functionname !=0), if it is not 0 then I can proceed if the function returns 0 I can return an error to Labview
	Extern_dll =	Load_Extern_dll();
		
	// Get the functions I need from the dll
	p2_ExternDriver F_ExternDriver = 	(p2_ExternDriver)GetProcAddress(Extern_dll, "_ExternDriver");
	
	p2_Initialise F_Initialise = 		(p2_Initialise)GetProcAddress(Extern_dll, "_Initialise");	
	p2_AdapterCount F_AdapterCount = 	(p2_AdapterCount)GetProcAddress(Extern_dll, "_AdapterCount");	
	p2_StartLogging F_StartLogging = 	(p2_StartLogging)GetProcAddress(Extern_dll, "_StartLogging");	

	p2_StopLogging F_StopLogging = 		(p2_StopLogging)GetProcAddress(Extern_dll, "_StopLogging");	
	
	
	// When there are adapters found I tell it in Labview
	adapterCount = F_AdapterCount();
	
	
	// Get the callback function adress, if adress == 0 I can see something is wrong
	void(__stdcall *p2_CallbackFunction_t) (pt2Obj , const LogData & ) = &CallbackFunction;
	
	
	// Q -- I could use try {} and catch {} however I do not have acces to the source code of the dll so,
	//		I do not know if they used 
	//			trow "The Blue error" , or
	//			trow "The Red error"
	
	ret = F_ExternDriver(adapterCount, enableLogFile, path, *p2_CallbackFunction_t, pt2Obj);
	
	// This tels the dll to start logging before this command you will not receive data in Labview
	ret F_StartLogging(1,2,3);
	
	// Labview is receiving data thats great !!!
	
	return ret;
	// Q -- Labview has been receiving data for a hour what happens if the used extern dll crashes ??
	//		If the dll crashes when return is passed I can not use try() and catch anymore.
	// 
		
	}
	
	


	extern void __stdcall CallbackFunction(pt2Obj , const LogData & )
		{
		// There is a lot to do here 
		// do stuff!!
		// I did not write everything here
		
		err = PostLVUserEvent(*event_ref, &tmpEvent);
	
		}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Link to comment

If and how the DLL uses exceptions is completely up to the DLL and there are actually several way this could work but only the DLL writer can tell you (if it is not explained in the documentation). Windows has a low level exception handling mechanisme that can be used from the Win32 API. It is however not very likely that a DLL would make use of that. Then you have the structured exception handling or its namesakes from different C++ compilers. And here things get interesting since each compiler builder was traditionally very protective about their own implementation and were very trigger happy about suing anyone trying to infringe on the related patents. It means that GCC for a very long time could not use the Microsoft SEH mechanism and therefore developed their own method that was avoiding Microsoft patents. So if your DLL uses exceptions, and doesn't handle them before returning from a function call to the caller, you might be actually in a bit of a bind as you really need to know what sort of exception handling was used. And if you use a different compiler than what was used for the DLL, you will most likely be unable to properly catch those exceptions anyhow, causing even more problems.

Basically, a DLL interface is either C++ oriented and then needs to be interfaced by the same compiler that was used for the DLL itself anyhow, or it is a standard C interface and then it should NEVER pass unhandled exceptions to the caller since that caller has potentially no way to properly catch them. One exception are Win32 exceptions that the LabVIEW Call Library Node actually is prepared to catch and turn into the well feared 1097 error everybody likes so much, unless you disable the error handling level completely in the Call Library Node configuration dialog. 😁

Your example code, while probably condensed and not the whole thing, does however ignore very basic error handling that comes into play long before you even get into potential exceptions. There is no check for the Load_Extern_DLL() to return a valid HANDLE. Neither do you check for the function pointers you get from that DLL to be all valid.

p2_CallbackFunction_t is rather superfluous and a bit misleading. The _t ending indicates it to be a type definition but in fact you simply declare a stack variable and assign the reference to the CallBack function to it. Luckily you then pass the contents of that variable to the function, so the fact that that storage is on the stack and will disappear from memory as soon as your function terminates is of no further consequence. But you could just as well simply pass the CallBack function itself to that function and completely forget about the p2_CallbackFunction_t variable declaration.

Once your function returns, you have indeed no way to detect exceptions from the DLL anymore as there is no stack or call chain on which such an exception could be passed up. The way this should be done is by the DLL handling all exceptions internally and passing an according error indication through the CallBack function in an error variable. It can't use the CallBack function to pass up exceptions either since the CallBack function is called by the DLL, so the exception handling can't go from the callback to LabVIEW but only from the callback to the caller, which is indeed yes ... big drumrolls ... the actual DLL.

If your DLL doesn't catch all exceptions properly and handles them by translating them to an error code of some sort and passing that through the callback or some other means to the calling application, then it is not finished in terms of asynchronous operation through callbacks. Exceptions only can pass up through the call chain, but if there is no call chain such as with a callback function, there is no path to pass up exceptions either.

Edited by Rolf Kalbermatter
Link to comment

Hello Rolf, thanks again for the great help!

It's kind off busy here so I was not able to reply earlier..

On 4/2/2024 at 1:56 PM, Rolf Kalbermatter said:

If and how the DLL uses exceptions is completely up to the DLL and there are actually several way this could work but only the DLL writer can tell you (if it is not explained in the documentation). Windows has a low level exception handling mechanisme that can be used from the Win32 API. It is however not very likely that a DLL would make use of that. Then you have the structured exception handling or its namesakes from different C++ compilers. And here things get interesting since each compiler builder was traditionally very protective about their own implementation and were very trigger happy about suing anyone trying to infringe on the related patents. It means that GCC for a very long time could not use the Microsoft SEH mechanism and therefore developed their own method that was avoiding Microsoft patents. So if your DLL uses exceptions, and doesn't handle them before returning from a function call to the caller, you might be actually in a bit of a bind as you really need to know what sort of exception handling was used. And if you use a different compiler than what was used for the DLL, you will most likely be unable to properly catch those exceptions anyhow, causing even more problems.

Well I am in a bit of luck this time I did find some documentation and the compiler used is "MSVC2019".

It is written in C++ and it: "exports a pure C interface", I like the word pure as it reminds me of clean air, oceans without plastic and being young, in this context however I can not really understand what is meant. maybe you can give a global idea?

On 4/2/2024 at 1:56 PM, Rolf Kalbermatter said:

Basically, a DLL interface is either C++ oriented and then needs to be interfaced by the same compiler that was used for the DLL itself anyhow, or it is a standard C interface and then it should NEVER pass unhandled exceptions to the caller since that caller has potentially no way to properly catch them. One exception are Win32 exceptions that the LabVIEW Call Library Node actually is prepared to catch and turn into the well feared 1097 error everybody likes so much, unless you disable the error handling level completely in the Call Library Node configuration dialog. 😁

If I read this text I think it might be wise to turn error handling off? At this time I did set it at maximum, hoping to get some information about things that might went wrong.

 

image.png.0884eba95a3bdb34c3b62b73b45efaa4.png

 

 

On 4/2/2024 at 1:56 PM, Rolf Kalbermatter said:

Your example code, while probably condensed and not the whole thing, does however ignore very basic error handling that comes into play long before you even get into potential exceptions. There is no check for the Load_Extern_DLL() to return a valid HANDLE. Neither do you check for the function pointers you get from that DLL to be all valid.

Thank you for the remark, In the real code I do check if the handle is not 0 if it is zero I return a error code to Labview. If the pointer is 0 I also return a error code to Labview

 

On 4/2/2024 at 1:56 PM, Rolf Kalbermatter said:

p2_CallbackFunction_t is rather superfluous and a bit misleading. The _t ending indicates it to be a type definition but in fact you simply declare a stack variable and assign the reference to the CallBack function to it. Luckily you then pass the contents of that variable to the function, so the fact that that storage is on the stack and will disappear from memory as soon as your function terminates is of no further consequence. But you could just as well simply pass the CallBack function itself to that function and completely forget about the p2_CallbackFunction_t variable declaration.

Thank you for the remark. I need to read this remark again when it is not too late, my energy level is kind of low now at the end of the day. I will try to find the example I started with to go with your remark.

 

On 4/2/2024 at 1:56 PM, Rolf Kalbermatter said:

Once your function returns, you have indeed no way to detect exceptions from the DLL anymore as there is no stack or call chain on which such an exception could be passed up. The way this should be done is by the DLL handling all exceptions internally and passing an according error indication through the CallBack function in an error variable. It can't use the CallBack function to pass up exceptions either since the CallBack function is called by the DLL, so the exception handling can't go from the callback to LabVIEW but only from the callback to the caller, which is indeed yes ... big drumrolls ... the actual DLL.

If your DLL doesn't catch all exceptions properly and handles them by translating them to an error code of some sort and passing that through the callback or some other means to the calling application, then it is not finished in terms of asynchronous operation through callbacks. Exceptions only can pass up through the call chain, but if there is no call chain such as with a callback function, there is no path to pass up exceptions either.

 

I think I understand what you write. The dll I wrap in my own dll uses a callback to send error and status massages. I can see them in Labview now 🙂 .  I think however it is better to use this info in the C code eventually e.g automatic stop logging when there is an error related to the logging. I need to find out the best way to do this though. 

And even if I can use the error and status messages it would be really helpful if there is something going horrible wrong with the dll, something I did not think about, the dll will not be able to make Labview crash, I rather would have a message telling something like:  "Unknown error at dll, program halted.." ,  better to have a ugly way of telling something went wrong than a crash-ing way which closes down Labview. If this would be possible please let me know.  Maybe I can at least use some kind of exception handling when loading / running the functions of the dll I try to wrap?

Thanx for the help!

 

 

 

 

 

Link to comment
Posted (edited)
On 4/2/2024 at 3:32 PM, ShaunR said:

I'm a simple programmer.

Catch them all where possible (try { /* */ } catch (...)) and pass errors back as a return value.

That's my tuppence.

Thank you Shaun for the reply 🙂 Today the best of the day is gone, I hope I will have time soon to give it a try.

Edited by Neon_Light
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.