I'm using an unmanaged DLL with a function that throws std::exception.
I'm using a .NET DLL wrapper so it can be distributed to use in .NET programs.
I would like to be able catch the message from the native exception but all I'm getting is System.Runtime.InteropServices.SEHException("External component has thrown an exception.")
Is there any way to propagate the exception details? Maybe I should export a custom exception from the native DLL? How would I do this?
Thanks
native DLL:
__declspec(dllexport) void __stdcall W32DLLFunc(int param) {
if (param < 0) {
throw new exception("Error: param must be > 0");
}
...
}
.net DLL:
[DllImport("nw32.dll", CharSet = CharSet::Auto)]
static void W32DLLFunc(int param);
vb.net program:
Try
W32DLLFunc(-1)
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
Native C++ exceptions are native C++ exceptions. They don't work with things that aren't C++. They even don't work between different versions of C++.
Native C++ exceptions are not good for interoperability.
The standard mechanism for returning error information from DLLs is to return a value to indicate success or failure and to use SetLastError and GetLastError to communicate an error code if the function failed.
If you want a standard mechanism to return more information than an error code you need to look at COM, which has IErrorInfo.
But it would be simpler just to define some error codes.
If possible, I would alter the original DLL so that it doesn't leak exceptions at all. If this is impossible because existing C++ clients depends on the current design then I would add a parallel API: a new version of each exported function that calls the original, catches exceptions and returns error codes. This is all boilerplate that can be hidden in macros. If you can't touch the DLL I would add a native wrapper to translate the exceptions.
Update
As suggested by IInspectable, another option is to create a mixed-mode assembly.
Add a .Net class to your existing C++ library. This should provide a wrapper for each API function that calls the original API, catches any exception, copies the details into a .Net exception and throws the .Net exception.
By keeping all the native exception handling within the DLL this avoids the problems with C++ exceptions crossing DLL boundaries. The wrapped exceptions can be consumed in any .Net language without the need to create declarations for the exported functions. The only disadvantage is that you need an additional API for interoperability with other native code.
Related
I have watched Mike Acton's talk on Data-Oriented Design and C++.
As he stated at 8min30sec that they don't use exceptions, they cannot force it to be off on third party libraries. Therefore they "sandbox around" these libraries.
My questions are:
1.
What is exactly meant by "sandboxing around" libraries using exceptions, when my codebase runs with disabled exceptions and how does it work and how do I do that? (are there differences on platforms (Win/Linux/Mac, consoles or mobile?)
2. When using the standard library (which uses exceptions, as in new and the alikes) how I "sandbox" them - or is it the same principle as in 1.?
You can sandbox the exception by just caching it in a wrapper. Let's suppose you have:
a third party library T that DOES use exceptions
your application A that DOES NOT use exceptions.
You then create a wrapper W (compiled WITH exceptions enabled) that will wrap T but catch all exceptions wherever it exists and, for example, replace by and error code, like this:
// this one comes from the original library T that throws exceptions
void someFunctionInT();
// this will be your wrapper around the function above
int someFunctionInW()
{
try
{
someFunctionInT();
}
catch (...)
{
return -1;
}
return 0;
}
So, this way, your wrapper W should NOT throw any exception and you can use safely link against your application A.
Obviously, this is just a simple example. But you can do something a little "fancier" once at this time you already "paid" for the exception handling. For example, you can test the exception and return different error codes or prepare an error message to be retrieved from another function, etc, etc... up to your creativity. :-)
My COM server implemented in Visual C++ uses a ton of other C++ code. That other C++ code sometimes wraps code in __try-__except and translates structured exceptions into custom C++ exceptions. This part I cannot change.
No method of my COM server should let those exceptions propagate through the COM boundary so it has to catch and translate them into HRESULTs. Those custom C++ exceptions contain the original error code which was obtained during translation - it's something like EXCEPTION_ACCESS_VIOLATION. The question is how I craft an appropriate HRESULT value so that the client has as much information as possible as to what happened (and perhaps decide to restart the server (and itself in case of inproc) after seeing an access violation).
Suppose it was EXCEPTION_ACCESS_VIOLATION which is defined in WinBase.h
#define EXCEPTION_ACCESS_VIOLATION STATUS_ACCESS_VIOLATION
and the latter is defined in WinNT.h
#define STATUS_ACCESS_VIOLATION ((DWORD)0xC0000005L)
I could use HRESULT_FROM_WIN32() to translate that code into HRESULT assuming that it was a Win32 error in the first place.
Do I use HRESULT_FROM_WIN32() here or do I use any other way to do the translation?
You are supposed to return HRESULT code, where you choose appropriate code to indicate status of operation. It does not have to be a failure code, but you typically want to show something that satisfies FAILED(...) macro, e.g. E_FAIL, or DISP_E_EXCEPTION or HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION).
It is highly unlikely that callers compare against specific exception-related HRESULT, so specific failure code makes sense rather for diagnostic. Also, as you complete handling the exception before exiting from COM method, there is no need to return specific HRESULT code because no additional actions are required or necessary.
To provide additional information, it is possible to use ISupportErrorInfo, IErrorInfo and friends. Callers can retrieve free text description and many popular environments to this automatically, so for example .NET caller will have this additional information on exception message rather than standard message generated from HRESULT code.
ATL offers AtlReportError to wrap SetErrorInfo API, which also suggests on generating HRESULT code:
... If hRes is zero, then the first four versions of AtlReportError return DISP_E_EXCEPTION. The last two versions return the result of the macro MAKE_HRESULT( 1, FACILITY_ITF, nID ).
When we write a program in C, it is possible that we will call some libraries that were wrote in C++ but had a C interface. Then it may happen that when we call these libraries, a C++ exception will occur. So my question is how we can handle this situation. I am more interested in this problem from a C++ developer's perspective. Suppose I am developing a C++ library that will be invoked by a C program, should I stop using exception and instead return error codes? Another situation is that if I have already a fully developed C++ library that uses exception, how can I transfer this library in a quick way that will only use error returning method?
You have to catch all exceptions on the C++ side and convert them to appropriate error returns in C, which may include specific error codes where appropriate. This doesn't mean that you stop using exceptions — you can still use them in C++ — but you can't expose them to C, they become an implementation detail.
A typical wrapper can be structured as follows:
// thingy-wrapper.h, typically included from C code:
#ifdef __cplusplus
extern "C" {
#endif
// create a typedef visible to C that doesn't expose the layout of
// the implementation.
typedef void *thingy_t;
// wrapper for std::string Thingy::get_name()
char *thingy_get_name(thingy_t t);
#ifdef __cplusplus
}
#endif
// thingy-wrapper.cpp, implements the wrapper, compiled with C++:
#include <thingy-wrapper.h>
#include <thingy.hpp> // declares Thingy class
char *thingy_get_name(thingy_t t_)
{
try {
Thingy& t = *static_cast<Thingy*>(t_);
std::string name = t.get_name();
return strdup(name.c_str());
}
catch(...) {
return NULL;
}
}
In this simple example, the caller of thingy_get_name can detect that an error occurred, but cannot find out the details of the error. A more realistic example would catch specific exceptions, and set a last_error variable to an error code or message before returning NULL. ... would only be caught as a last resort, and would set last_error to a generic UNKNOWN_ERROR value. A separate API for querying the last error, such as thingy_last_error(), would be available for the more careful callers of thingy_get_name().
The separation between error and non-error results enables code that doesn't care about the cause of errors to simply check if it received NULL, while allowing more conscientious code to properly propagate or report the error. If your library is multi-threaded, make sure that last_error uses thread-local storage.
Then it may happen that when we call these libraries, a C++ exception will occur. So my question is how we can handle this situation.
The C code cannot handle this situation. C code cannot deal with C++ exceptions.
Suppose I am developing a C++ library that will be invoked by a C program, should I stop using exception and instead return error codes?
No. If you want the C++ library to be consumed by C++ code you should use native C++ error handling. Which means exceptions.
However, the interface that you expose to the C code must not throw exceptions. Typically this means writing an adaptor layer that catches exceptions raised by the C++ library, and converts them into error codes to be consumed by the C code.
Another situation is that if I have already a fully developed C++ library that uses exception, how can I transfer this library in a quick way that will only use error returning method?
There's really no shortcut here. You have to write the adaptor that converts C++ exceptions into C error codes. You'll be writing an adaptor anyway if you want the library to expose interfaces for both C and C++ consumers. So the aspect of error handling is just another thing to take care of with this adaptor.
Exception are not caught in C so if you want to catch them then in your C code or your C++ code you have to write the wrappers very carefully.
Also make sure that in your C++ functions you have your functions declared as:
extern "C"
You may also check How to mix C and C++
I have inherited an old MFC/Win32 C++ application who's source code I am not supposed to edit.
This MFC application needs to host an old MFC/Win32 C++ DLL. This DLL also tries to make function calls through a Mixed-mode wrapper to a managed C++/CLI DLL. I know it sounds a little confusing, so here's a diagram of what I mean:
Old MFC/Win32 Application (NO CLR)
---> Hosting old MFC/Win32 DLL (NO CLR)
---> Making function calls to Mixed-Mode wrapper (CLR)
---> Sending function calls to C++/CLI DLL (CLR)
My problem currently is that when I try to mount an object of the C++/CLR wrapper class let's say WrapperClass WC;, the MFC/Win32 application encounters an "Unhandled exception."
I have a feeling that I may need to somehow host the CLR in a separate process in order to be able to make the object. Is this the right idea? Or am I completely out of whack here?
The code compiles time and this only occurs at run-time.
Any ideas?
Here is an example of the code I am trying to run:
MFC/Win32 DLL
#include "WrapperClass.h"
BOOL Test::bTest() //This function is called elsewhere within MFC/Win32 DLL
{
DWORD dwTest;
WrapperClass WC; //Unhandled exception here!
return WC.FunctionToCall(dwTest); //FunctionToCall is a BOOL
}
Mixed-Mode Wrapper Class
BOOL WrapperClass::FunctionToCall(DWORD dw)
{
GCHandle h = GCHandle::FromIntPtr(IntPtr(m_impl)); //m_impl def'd as void *
CPPCLIClass^ CCC = safe_cast<CPPCLIClass^>(h.Target);
return (BOOL)CCC->FunctionToCall(dw);
}
C++/CLI DLL
bool CPPCLIClass::FunctionToCall(UInt32 ui32)
{
if (ui32 == 42)
{
return true;
}
}
UPDATE:
I've managed to coax a real exception out of the program. I am now receiving a System.IO.FileNotFound exception with additional information stating:
An unhandled exception of type 'System.IO.FileNotFoundException' occured in
Unknown Module.
Additional information: Could not load file or assembly 'CPPCLIProject,
Version=1.0.4351.29839, Culture=neutral, PublicKeyToken=null' or one of its
dependencies. The system cannot find the file specified.
Does this mean anything? I understand that it apparently cannot find CPPCLIProject (Note: this is not the wrapper project), but then if I'm linking on the .lib file from the Mixed-mode wrapper, how would I not receive linker errors then?
Are you sure that the implementation of WrapperClass::FunctionToCall() isn't throwing the exception? It looks like you're caching the memory location of a CLR object, and then trying to call one of its members. I think the CLR is free to move objects around, so it's possible that you're trying to use an object that has moved.
If you change the implementation of WrapperClass::FunctionToCall() to something simple (i.e. create a CLR object, call a member function), do you still get the same unhandled exception?
UPDATE: Which class is in CPPCLIProject - CPPCLIClass? Assuming this project represents the C++/CLI DLL, it sounds like it just can't find the assembly to load it when it needs to call your class.
Where is this assembly on disk relative to the rest of the application? If your root EXE is unmanaged (which it sounds like it is, since it is MFC/Win32), then the CLR looks in the EXE's directory and the GAC in order to load assemblies (I don't think it looks in the Path, but I'm not positive on that).
So if the CPPCLIProject isn't in the same directory as the EXE, that could be your problem.
Your best bet is to
run under a debugger (add the additional DLLs with debug information to the debug session)
enable break on all (first-chance) exceptions
trace/assert all HRESULT codes
in general try to catch
C++ exceptions (try/catch...)
Windows Structured Exceptions (IIRC _try/_catch, but see MSDN)
The idea is to convert the 'unkown exception' into 'known exception'.
Normally speaking there is no need to host the CLR part out of process. This is what a mixed-mode DLL is about. Then again, if this is legacy code, you might be running into complicated mixes of runtime dependencies that, shall we say, could clash.
Further thoughts:
If I understand correctly, you have the option to recompile all sources (just not touch the legacy codebase?). If so, make sure all code is compiled against the same version (think Service Packs) and type (think Static vs Dynamic/Multithread/Debug) of the runtime libraries.
While you are checking additional pathways, keep an eye on potentially conflicting dependencies on
ATL server libs
MFC libs (again static vs dynamic/Multithread/Debug flavours).
I am compiling my program with a 3rd party library. That library contains an error callback if an error occurs internally. Inside that error callback I am throwing an exception and I have a unit test to verify that when I do something invalid that the exception is thrown. This all works beautifully in Windows, but when I test this in linux (fedora) I am getting an abort from an uncaught exception.
I tried wrapping my call directly with a try-catch block but no luck. ( Also, all my code is running within the google test framework which also normally catches exceptions ). The only thing that seems to catch the exception is if I wrap the throw statement in a try block directly within the error callback.
Does anyone have any idea why this would happen and if there is a way to catch the exception?
When you interface with third-party libraries you usually have to catch all exception on the border between your code and their code:
int yourCallback( params )
{
try {
doStuff( params );
return Okay;
} catch (...) {
return Error;
}
}
The reason is you can't be sure that library is written in C++ or it uses the very same version of C++ runtime as your code uses.
Unless you're completely sure that code can deal with your exceptions you can't propagate exceptions to third-party code. The extreme example is COM where both your code and "other code" can be in whatever language and with whatever runtime and you are not allowed to let exceptions propagate through COM boundary.
Usually you should not throw exceptions "through" code you do not know anything about. It might be C code, which will not even clean up after itself.
How to deal with your concrete problem would require concrete information about the 3rd-party library you are interfacing with. What is that callback there for? To give you a chance to fix stuff? To inform you that an error occurred? Can you cancel whatever operation it is called from?
One way to deal with such a scenario is to store some information somewhere when the callback is called and check for that information when the actual processing finishes from your function that calls into that library.