Jump to content
vugie

DLL Error Handling

Recommended Posts

I'm making wrappers for certain open source library (DLL). It has its own error handling: each time when an error appears special Error() function is called with error number and message as arguments. It looks like that:

void Error(int errnum, char *msg){  if (custom error handler defined) custom_error_handler(errnum, msg)	else DisplayDialog(errnum,msg);  abort();}

The problem is that LabVIEW crashes each time library calls Error() function. At first dialog with error number appears (as it should), then after clicking OK windows dialog "application unexpectly quit", everything hangs for a while and LV IDE disappears... I think that abort() is not LV friendly.

I compile the library myself, but due to maintenance and licensing issue I don't want to modify its code. I would rather like to create my custom error handler (there is a SetErrorHandler function which gets a pointer to it), which would exit somehow more softly. I'm already able to build another DLL with error handler an to provide its pointer to SetErrorHandler (using GetProcAddress function of Kernel32.dll). But how to exit softly and provide error information up to LV wrapper?

Maybe is there a way to compile DLL in such way that abort is more LV friendly? (MSVC Express)

Share this post


Link to post
Share on other sites

I'm making wrappers for certain open source library (DLL). It has its own error handling: each time when an error appears special Error() function is called with error number and message as arguments. It looks like that:

void Error(int errnum, char *msg){  if (custom error handler defined) custom_error_handler(errnum, msg)	else DisplayDialog(errnum,msg);  abort();}

The problem is that LabVIEW crashes each time library calls Error() function. At first dialog with error number appears (as it should), then after clicking OK windows dialog "application unexpectly quit", everything hangs for a while and LV IDE disappears... I think that abort() is not LV friendly.

I compile the library myself, but due to maintenance and licensing issue I don't want to modify its code. I would rather like to create my custom error handler (there is a SetErrorHandler function which gets a pointer to it), which would exit somehow more softly. I'm already able to build another DLL with error handler an to provide its pointer to SetErrorHandler (using GetProcAddress function of Kernel32.dll). But how to exit softly and provide error information up to LV wrapper?

Maybe is there a way to compile DLL in such way that abort is more LV friendly? (MSVC Express)

You're going to need to modify that code even if you put in a custom error handler - currently it calls abort() regardless of whether it calls the custom error handler or not. How often does your library generate an error? Do you expect your code to be able to recover from the error? You could write your own custom error handler that posts a user event to LabVIEW, or sets an occurrence; I've never done it, but I've seen sample code. You would need a function to register the occurrence or event refnum before running any functions from the DLL that crashes.

One thing I did in a similar situation (an ActiveX component that crashed routinely) was put that component into a separate LabVIEW application and call it using VI server. I could detect when the component crashed (VI server returned an error) and restart that component application using System Exec, then reconnect with VI server. The users saw an extra item in the task bar (wasn't important to hide it), and an error message and a slight delay when it crashed, but it allowed the main application to run reliably.

Share this post


Link to post
Share on other sites

The most common cause (I've found) of this behaviour is that memory allocated from the Labview (i.e outside the DLL) is freed inside the DLL. When the function returns, the original pointer Labview used for allocation is non-existent.. If the DLL does not return an error exit code, Labview assumes everything was ok and attempts to use it again (i think). A normal app would show you a GPF, but Labview is a bit more robust than that (usually) and normally gives an error. But it depends how catastrophic it was.

You probably need exception handling for your dll, so that any GPF's or nasty C stuff, that breaks your DLL, still cleanly returns to labview. this is usually done in the DLL_PROCESS_DETACH of DllMain. This will mean that at least Labview will stay around for you to debug the DLL to find the root cause.

However. If the error affects the program pointer. Then nothing short of fixing the problem will suffice.

Rolf is the expert on this kind of stuff.

Share this post


Link to post
Share on other sites

You're going to need to modify that code even if you put in a custom error handler - currently it calls abort() regardless of whether it calls the custom error handler or not. How often does your library generate an error? Do you expect your code to be able to recover from the error? You could write your own custom error handler that posts a user event to LabVIEW, or sets an occurrence; I've never done it, but I've seen sample code. You would need a function to register the occurrence or event refnum before running any functions from the DLL that crashes.

Yes, I found no other way than commenting out abort(), to avoid LV crash. I currently have custom error handler which records an error within a static variable and additional GetLastError function which helps to read it with LV. It works more or less - there are some strange issues but it's not the time to carry about them. But I wonder what is going on in the memory when abort() is not called... I suspect that from time to time I'll have LV crashed. And errors appears pretty often when using this library - it's a simulation framework, so it's quite normal that sometimes everything explodes - it's a sign that you have to change some simulation parameters or restitution factors.

OK, so I modified the code of the library (the modification is inserting "//" in 3 places...). The library is dual-licensed (LGPL-BSD). No clean rules how to choose one. In the worst case I'll have to include its whole code...

Share this post


Link to post
Share on other sites

Yes, I found no other way than commenting out abort(), to avoid LV crash.

That is logical. abort() is similar to exit() which is simply a C runtime function to - yes indeed - abort the current process. Exit does try to do some extra clean up before returning to the OS, but abort simply yanks out the floor under the feet of a process and hopes that the OS will be able to clean up the mess that remains.

I currently have custom error handler which records an error within a static variable and additional GetLastError function which helps to read it with LV. It works more or less - there are some strange issues but it's not the time to carry about them. But I wonder what is going on in the memory when abort() is not called... I suspect that from time to time I'll have LV crashed. And errors appears pretty often when using this library - it's a simulation framework, so it's quite normal that sometimes everything explodes - it's a sign that you have to change some simulation parameters or restitution factors.

Your GetLastError function might have threading issues. Unless you configured your Call Library Node to execute the DLL function in the UI thread LabVIEW will call that function in any of the threads in the current subsystem and that can be in modern LabVIEW versions up to 8 threads or so. So there might be a problem in storing the error code and trying to retrieve it with another function, as that second function may be called from a different thread. If you store the error in a thread local storage, as Windows does with its GetLastError() you have a very small chance to retrieve the same error value than what was written by the previous function call, and unless you force data flow strictly chances are completely off.

Personally I would add an error handler that takes the current parameters and a LabVIEW error cluster as parameters and stuffs the first into the second and also returns an error code (the same stuffed into the cluster) which you can return from the function call to LabVIEW. Every DLL call gets an extra parameter that receives the error cluster and this parameter gets passed to the error handler function whenever it is called.

OK, so I modified the code of the library (the modification is inserting "//" in 3 places...). The library is dual-licensed (LGPL-BSD). No clean rules how to choose one. In the worst case I'll have to include its whole code...

Not knowing the library nor its circumstances I do not know, but if it is truely dual licensed you would be free to choose the license you like for your distribution. The dual licensing here was likely done so that (L)GPL projects could use it, since especially the GPL is considered by some almost incompatible to anything but itself. BSD would seem to me so liberal that it should be easily usable by GPL program, but I believe some GPL critics think that that is not the case.

Share this post


Link to post
Share on other sites

What about using a second application instance? I haven't done this, so I'm not sure how easy this would be.

But then the 2nd App.Instance would crash and your main App.Instance would get errors when calling it, which you can deal with (recover).

Felix

Share this post


Link to post
Share on other sites

What about using a second application instance? I haven't done this, so I'm not sure how easy this would be.

But then the 2nd App.Instance would crash and your main App.Instance would get errors when calling it, which you can deal with (recover).

Felix

While this would likely work I consider it a rather complicated solution which makes the whole deployment of the library more complicated. Writing a wrapper interface for the DLL (to add the extra error cluster parameter to all functions) is indeed not a trivial exercise (mainly in terms of time, not so much in terms of complication). Especially with open source it also poses the question if you want to modify the original sources or add a separate wrapper. However this is work that has to be done once at creation time and won't bother the end user with complicated setups and non-intuitive usage limitations.

If it is only a for a solution for a build once and forget application then it's feasable, otherwise I would definitely try to invest a little more time upfront in order to make it more usable in the long term.

Share this post


Link to post
Share on other sites

Thank you Rolf for bringing this thread up. Although current solution works, I'm not much satisfied with it. BTW, now I can say that it is for lvODE library I published recently.

This is how the error handling is currently implemented:

The C part first. Error handler (this function is passed to main DLL to be called in case of error) and GetLastError function:

DLL void lvodeError (int errnum, const char *msg, va_list ap) {	lasterrnum=errnum;	vsprintf_s (lastmsg,256, msg,ap);}DLL int lvodeGetLastError (char *msg){	int temperr;	if (lasterrnum==0) 	{		return 0;	}	else	{		strcpy_s(msg, 256, lastmsg);		temperr=lasterrnum;		lasterrnum=0;		return temperr;	}}

lasterrnum and lastmsg are previously mentioned statics. Abort() is commented out in main DLL.

During Initialization I get address of error hadler and I pass it to main library (ode.dll):

post-7450-0-96988600-1292232961_thumb.pn

LabVIEW part of GetLastError - not much complicated:

post-7450-0-69758000-1292233248_thumb.pn

Typical call to main DLL:

post-7450-0-89030300-1292233698_thumb.pn

So GetLastError should be called after each CLFN. As I'm still testing it, it is not put there now.

But as abort() is commented out (and I guess it is a must) and parts of code may run concurrently, I have doubts whether GetLastError will always get the error in right place...

Share this post


Link to post
Share on other sites

Personally I would add an error handler that takes the current parameters and a LabVIEW error cluster as parameters and stuffs the first into the second and also returns an error code (the same stuffed into the cluster) which you can return from the function call to LabVIEW. Every DLL call gets an extra parameter that receives the error cluster and this parameter gets passed to the error handler function whenever it is called.

Hi,

I am working in the Error Handling too I would be very interested in know how to do it the way you explain. Because I could not find anything that explains me how to implement the Error Handling and Checking for a DLL that gets called from a CLFN.

Actually, I would be very happy just if I could associate an error number and message to the "error in" input and "error out" output that the CLFN shows if you select the Default level of Error checking.

Thanks.

Share this post


Link to post
Share on other sites

Hi,

I am working in the Error Handling too I would be very interested in know how to do it the way you explain. Because I could not find anything that explains me how to implement the Error Handling and Checking for a DLL that gets called from a CLFN.

Actually, I would be very happy just if I could associate an error number and message to the "error in" input and "error out" output that the CLFN shows if you select the Default level of Error checking.

You can't to that! The error cluster of the CLFN is for reporting runtime errors from LabVIEW in trying to load the DLL and execute the function, including possible exceptions when you have configured the CLFN to use high error reporting.

If you want to pass error information from your function to the LabVIEW diagram you have to do it through a function parameter or function return value. I have done both.

Method1: When an error code is returned by all functions I have a common error handler VI that is placed after each CLFN and converts this error code into a LabVIEW error cluster.

Method2: Or you can pass the Error Cluster as extra parameter:

#pragma pack(1)typedef struct {    	LVBoolean status;    	int32 code;    	LStrHandle message;} ErrorCluster;#pragma pack()static MgErr FillErrorCluster(MgErr err, char *message, ErrorCluster *error){    	if (err)    	{            	int32 len = StrLen(message);            	error->status = LVBooleanTrue;            	error->code = err;                err = NumericArrayResize(uB, 1, (UHandle*)&(error->message), len);                if (!err)                {                    	MoveBlock(message, LStrBuf(*error->message), len);                    	LStrLen(*error->message) = len;                }     	}   	return err;}MgErr MyFunction(......, ErrorCluster *error){    	MgErr err = error->code;    	if (!error->status)    	{                err = CallAPIFunction(.....);                FillErrorCluster(err, "Error here!!!", error);    	}    	return err;}

I then use this error cluster to feed through the VI, not the error cluster from the CLFN itself. The CLFN error is useful during development and debugging to see possible errors and understand why it wouldn't work, but for most use cases, once the CLFN call has been tested and debugged, the node should not return any runtime error anymore.The function call however might, otherwise the whole exercise to pass the error cluster as parameter is quite senseless :rolleyes:.

Share this post


Link to post
Share on other sites

Join the conversation

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

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