Encountering errors when using C++ interop on old MFC/Win32 application - 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).

Related

DLL fails to load if unused ref class is removed

I'm running into a very strange problem trying to compile and use a windows runtime component within an UWP application (VS2017 community 15.9.13 with NetCore.UniversalWindowsPlatform 6.2.8, compiled without /clr but with /ZW).
It is basically something like the Grayscaletransform. The runtime component is actually working as expected, now I wanted to remove some unused code. However, as soon as I remove it from a particular file and recompile, it indeed compiles, links, but the DLL does not load any more.
Here's some example code that I have to put in:
ref class DummyU sealed
{
public:
DummyU() {}
};
DummyU^ CreateDummyU()
{
return ref new DummyU();
}
The code just makes it work, although it is a) not referenced at all and b) does not do anything useful.
The result of removing it:
Exception thrown at 0x0EFF322F (vccorlib140d_app.dll) in TestAppUWP.exe: 0xC0000005: Access violation reading location 0x00000000.
in
STDAPI DllGetActivationFactory(_In_ HSTRING activatibleClassId, _Deref_out_ IActivationFactory** ppFactory)
{
return Platform::Details::GetActivationFactory(Microsoft::WRL::Details::ModuleBase::module_, activatibleClassId, ppFactory);
}
function in dllexports.cpp which is part of VS. The module_ becomes NULL.
Does anyone have an idea if there are any known bugs with respect to the windows runtime not being initialized/used properly if there is no explicit instantiation of a ref class in a file?
EDIT 1:
Here's the link to the full source code:
What's happening here is that you're mixing modes a bit. Since you've compiled your C++ code with the /CX flag, you've told the compiler to enable winrt extensions to produce a WinRT DLL. In practice though, none of your code is actually using the CX extensions to implement classes. You're using WRL and standards C++. The compiler looks at the DLL, finds no CX-style WinRT classes, and doesn't set up the module, accordingly.
This is basically an untested & unsupported case, since you've chosen to say that you want to expose ref classes by picking a UWP component library project type, but then didn't actually provide any ref classes. It happens that under the hood, /CX effectively uses WRL, so you can nudge it along and initialize the state to work correctly, but you're kinda hacking the implementation details of the system.
There are two options I would recommend, either works: just make the project a non-CX Win32 DLL and init the module as described above. Or, better yet, flip over to C++ /WinRT, which will give you better support for the WinRT types than /CX and allow you to more easily mix in the classic COM types in your implementation. You can get started by just turning off the /CX flag in the compiler switches, then start updating the code accordingly.
Ben
You might have wrong .winmd file for your component. WinRT components made in C++ produce two outputs, dll and winmd. Both must match. It's possible you have them from different builds.
Another possible reason is error in manifest. The manifest of the app must include all referenced runtime components.
BTW, for native DLLs written in classic C++ and exposing C API, deployment is simpler, you include a DLL in the package and they just work, with [DllImport] if you're consuming them from C#.
Update: You can replace that ref class with the following code, works on my PC.
struct ModuleStaticInitialize
{
ModuleStaticInitialize()
{
Microsoft::WRL::Module<Microsoft::WRL::InProc>::GetModule();
}
};
static ModuleStaticInitialize s_moduleInit;
Probably a bug in Microsoft's runtime somewhere.

Exception Handling in /clr MFC Application (compiled with /EHa)

We have a large MFC application that is in the process of being updated. It has been modified to add in some .NET components (some of the DLLs now have managed & native classes), the idea is that the old code will be phased out eventually.
The app has many DLLs with native C++ classes exported, and used by the application.
On trying to test the app we now find ANY exception seems to cause the app to crash, even though these exceptions are in theory being caught in the same function.
example:
CString AddressClass::GetPostalAddress()
{
CString address;
try {
address = (LPCSTR)(_bstr_t)m_pCommand->GetParameters()->Item["PostalAddress"]->Value;
}
catch ( _com_error& )//exception occurs if postal address is NULL
{
address = _T("");
}
return address;
}
The _com_error is not being caught when compiling with /clr and /EHa (Vs2015 update 3). Without /clr it works fine and has been operational for years. From the documentation I have read my understanding is this should work, but clearly I am mistaken.
The code responsible for generating the error is in comutil.h:
inline void CheckError(HRESULT hr)
{
if (FAILED(hr)) {
_com_issue_error(hr);
}
}
The information from the debugger is:
An exception of type 'System.Runtime.InteropServices.SEHException' occurred in XXX.dll and wasn't handled before a managed/native boundary
Additional information: External component has thrown an exception.
Is there anyway to get this working without rewriting huge amounts of code?
Thanks.
Answer from short comment, since it seems to have helped:
Generally speaking: You should not compile these with /clr. Separate your old code vs your new CLR-using code, just compile new code with /clr, compile old code/files directly native. You can still link everything together in a clr-enabled DLL.
What we do is to compile all the native stuff in separate static LIB projects, and then link these into /clr enabled projects - having separate compiler switches for individual source files inside one project is always a tad confusing. But then, it may still be the way to go for you. Depends on the mixture and calling patterns, I guess.
I'll add that I view /clr mostly as a rather powerful glue system, than something you should target for the full app stack.

Implementing the CLR into old MFC/Win32 DLL and hosting DLL into MFC/Win32 Application

I have an old MFC application which I currently import an old MFC/Win32 DLL into.
In this old DLL, I have been tasked with writing a bunch of Multi-Threaded code which I was planning on writing using the .NET framework (hence needing the CLR).
I've made cracks at this before, and even if I can get the project to compile properly with the CLR, I find that as soon as I try to use the DLL's user interface (written in MFC) after loading the DLL into the MFC/Win32 application, the application will crash pointing to problems with the user interface.
This DLL has always worked without the CLR, so I know that it is not broken.
What is the best way of implementing the CLR in my project, even if it is only for one class?
EDIT: I currently can get the code to build with the CLR only on the one class I need it in, but the application I load the DLL into still crashes upon trying to load the user interface contained in the DLL.
EDIT2: I have figured out that it is failing an assertion on afxCurrentResourceHandle in afxwin1.inl. After doing more reading, I have a feeling that this has to do with MFC being in a "shared DLL" instead of "static DLL." Is there a workaround for this assertion?
_AFXWIN_INLINE HINSTANCE AFXAPI AfxGetResourceHandle()
{ ASSERT(afxCurrentResourceHandle != NULL);
return afxCurrentResourceHandle; }
EDIT3: I have made progress, but am still failing assertions! Apparently before you create the objects of the pages within the user interface, you must use the AFX_MANAGE_STATE macro to have afxCurrentResourceHandle be defined!
Here's an example of what I mean:
CPropertySheet Sheet("Config"); //Assume this is defined
AFX_MANAGE_STATE(AfxGetStaticModuleStatus());
CConfigPage ConfigPage;
CTestPage TestPage;
//Now I am failing an assertion when trying to run the following code
if (Sheet.DoModal() == ID_OK)
{
//Do stuff...
}
The assertion currently failing now is:
CObject* AFX_CDECL AfxStaticDownCast(CRunTimeClass* pClass, CObject* pObject)
{
ASSERT(pObject == NULL || pObject->IsKindOf(pClass));
return pObject
}
pObject certainly isn't null: pObject: 0x043fd4fc {CWnd hWnd=0x002c0abe}
Combining MFC and .NET/CLR is unlikely to work, unless you can recompile the old MFC application with CLR support. Even then, I would strongly discourage it; the two frameworks are not intended to be used simultaneously. Keep in mind that you can only use one GUI thread even if you don't use MFC.
A better solution would be to use the standard MFC threading mechanisms. It's really not hard; just make a new class with a member function like this:
static UINT Go(LPVOID pParam);
Then call AfxBeginThread(Go, this) from somewhere else in your class. Recast pParam to pointer to your class and then start calling functions on it. You don't have to use anything fancy to make that strategy work in MFC, and you've got all the standard Win32/MFC/C++ resources available. Tell me what you need from .NET and I bet I can find a way to do the same thing in C++ or through COM.

Win32 DLL importing issues (DllMain)

I have a native DLL that is a plug-in to a different application (one that I have essentially zero control of). Everything works just great until I link with an additional .lib file (links my DLL to another DLL named ABQSMABasCoreUtils.dll). This file contains some additional API from the parent application that I would like to utilize. I haven't even written any code to use any of the functions exported but just linking in this new DLL is causing problems. Specifically, I get the following error when I attempt to run the program:
The application failed to initialize properly (0xc0000025). Click on OK to terminate the application.
I believe I have read somewhere that this is typically due to a DllMain function returning FALSE. Also, the following message is written to the standard output:
ERROR: Memory allocation attempted before component initialization
I am almost 100% sure this error message is coming from the application and is not some type of Windows error.
Looking into this a little more (aka flailing around and flipping every switch I know of) I linked with /MAP turned on and found this in the resulting .map file:
0001:000af220 ??3#YAXPEAX#Z 00000001800b0220 f ABQSMABasCoreUtils_import:ABQSMABasCoreUtils.dll
0001:000af226 ??2#YAPEAX_K#Z 00000001800b0226 f ABQSMABasCoreUtils_import:ABQSMABasCoreUtils.dll
0001:000af22c ??_U#YAPEAX_K#Z 00000001800b022c f ABQSMABasCoreUtils_import:ABQSMABasCoreUtils.dll
0001:000af232 ??_V#YAXPEAX#Z 00000001800b0232 f ABQSMABasCoreUtils_import:ABQSMABasCoreUtils.dll
If I undecorate those names using "undname" they give the following (same order):
void __cdecl operator delete(void * __ptr64)
void * __ptr64 __cdecl operator new(unsigned __int64)
void * __ptr64 __cdecl operator new[](unsigned __int64)
void __cdecl operator delete[](void * __ptr64)
I am not sure I understand how anything from ABQSMABasCoreUtils.dll can exist within this .map file or why my DLL is even attempting to load ABQSMABasCoreUtils.dll if I don't have any code that references this DLL. Can anyone help me put this information together and find out why this isn't working? For what it's worth I have confirmed via "dumpbin" that the parent application imports ABQSMABasCoreUtils.dll, so it is being loaded no matter what. I have also tried delay loading this DLL in my DLL but that did not change the results.
EDIT
I have double checked and all files involved are 64 bit.
I just had exactly the same problem. This is an issue with the Abaqus API rather than with the loading of DLLS.
I think it is because the Abaqus API overrides the new and delete functions (as you seem to have noticed). If you call new or delete in your program before initializing the Abaqus API, such as by calling odb_initializeAPI(); then you get the
ERROR: Memory allocation attempted before component initialization
error message and the program crashes.
In my program, calling odb_initializeAPI(); before the first new resolved the problem.
Well, sure you'll reference the imports of that library. Hard to write a C++ program without using the new or delete operator. Dealing with 3rd party software that thinks it needs to override the CRT version of those operators is hard enough, impossible when it won't allow you to call them until it thinks the time is right. Abandon all hope or seek help from the vendor.
One of the possible reason of an error during loading of ABQSMABasCoreUtils.dll is that some dependency module (inclusive delayed load DLLs) could not be found. Use Dependency Walker (see http://www.dependencywalker.com/) to examine all dependencies of ABQSMABasCoreUtils.dll.
I have two suggestions:
Verify that you can load ABQSMABasCoreUtils.dll with respect of LoadLibrary. You don't need call any function from ABQSMABasCoreUtils.dll. Usage of LoadLibrary I don't see as the end solution. It' s only a diagnostic test. With the test you can verify either you have some general problem of loading ABQSMABasCoreUtils.dll in your program or you have some kind of process initialization problem.
If loading of ABQSMABasCoreUtils.dll with respect of LoadLibrary will failed, then use profiling feature of Dependency Walker to protocol of all calls done during loading of ABQSMABasCoreUtils.dll. One other way would be usage of Process Monitor (see http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx) to trace what file and registry operations will be done during loading of ABQSMABasCoreUtils.dll.
If LoadLibrary is not failed, then you have really an initialization problem of DLLs. Typically the problem exist if a DLL inside of DllMain try use a function from another DLL which is not yet initialized (not yet returns from DllMain). Before one start diagnostic of this problem, we should try to exclude a more simple problems with LoadLibrary.
The ABQSMABasCoreUtils.dll looks like it's importing 64-bit functions. Is your dll also 64-bit? If not, then that's the problem - you cannot mix DLLs compiled for different architectures in the same process.

Exceptions on Linux from a shared object (.so)

I have a test program called ftest. It loads .so files that contain tests and runs the tests that it finds in there. One of these tests loads and runs a .so that contains a Postgres database driver for our O/RM.
When the Postgres driver throws an exception which is defined in that .so file (or one that it links to, but ftest does not link to) and is caught by the test framework the exception destructor triggers a segfault.
This segfault happens whenever the compiled exception is in a .so that has been dynamically loaded (using dload).
This sort of thing works fine in Windows which has the same architecture. We don't really want to restrict ourselves to only use exceptions that are from the core libraries -- add-ins should be free to create their own exception classes and have them handled normally.
The exceptions are sub-classes of std::exception. Sometimes the exceptions may be defined in libraries (such as libpqxx) which means that exceptions are sometimes out of our control too.
Exceptions are thrown using something like:
throw exception_class( exception_arguments );
And are caught using:
catch ( std::exception &e ) {
// handler code
}
Is there some special compiler option needed to get this working? Do we need to switch to throw exceptions via throw new exception_class( args ) (we don't really want to do this)?
Assuming your using gcc -
Append -Wl,-E when you build the executable calling dlload(). This exports all type info symbols from the executable, which should allow the RTTI (when catching the exception) to work properly.
VC++ uses string compares to match typeinfo, results in slower dynamic_cast<> etc but smaller binaries. g++ uses pointer compares.
I encountered the same problem when attempting to use pure virtual interfaces classes implemented in a run-time loaded .so.
There are a few articles relating to the subject floating around on the net as well.
hope that helps,
Hayman.