Convert C ++ managed extension projects from pure intermediate language to hybrid mode
Compilation: Northtibet
Original article: Converting Managed Extensions for C ++ Projects from Pure Intermediate Language to Mixed Mode
The C ++ managed Extension Project that creates DLL contains MSIL (Microsoft intermediate language) Code by default. This code is not consistent with the C Runtime Library (CRT ), the C/C ++ Library link of the local machine such as ATL or MFC does not use any static variables. Its code is only applicable to runtime in public languages.
This is because the link with an entry point leads to the running of managed code in DllMain, Which is insecure (see the relevant documentation of DllMain to learn about what you cannot do during its execution ).
Static variables cannot be initialized for DLL without entry points, except for simple types such as integer types. Generally, you cannot have any static variables in/noentry dll.
The ATL, MFC, and CRT libraries depend on static variables, so you cannot use these libraries in the DLL.
If your mixed-mode DLL must use static variables or libraries that depend on static variables (such as ATL, MFC, or CRT), you must modify your DLL to have an external entry point.
To do this, you must convert the hosted DLL to the hybrid mode. So,
How do I convert a hosted DLL to a hybrid mode?
- Use the/noentry link: in solution manager, right-click the project node and select "properties ". In the Properties dialog box, select "linker" and select "command line ". Add a switch to the "Additional Options" edit box.
- Link msvcrt. Lib: Select "connector" in the properties page dialog box of the project, and then select "input ". Add msvcrt. lib to "add dependency ".
- Delete nochkclr. OBJ: delete nochkclr. OBJ from the attached dependency attribute on the "input" Page (same as the previous step.
- Link to CRT: on the "input" Page (the same page as the previous step), add _ dllmaincrtstartup @ 12 to the "Force symbol reference" attribute.
Modify the DLL code for manual initialization.
After converting to the mixed mode, you must modify the DLL code and perform manual initialization based on your DLL implementation:
- Your DLL uses _ declspec (dllexport) output, and the link between the DLL user and the DLL is static or dynamic, so the DLL user cannot use managed code.
- Your DLL is a com-Based DLL.
- The caller of your dll can use the hosted code, and your dll contains the DLL output or managed entry point.
Modify the DLL output with _ declspec (dllexport) and the caller cannot use the hosted code:
- Add two new outputs to the DLL:
// Init. cpp
// Add these header files before the using namespace System command header,
// Or add them to the. cpp file without the using namespace System Command Header
# Include <windows. h>
# Include <_ vcclrit. h>
// Call this function before you call anything in this DLL.
// It is safe to call from multiple threads, not to reference security, but to re-import security
Extern "C" _ declspec (dllexport) void _ stdcall DllEnsureInit (void)
{
// Do not do anything here. If you need additional initialization steps,
// Create a static object with a constructor and complete initialization in the constructor.
_ Crt_dll_initialize ();
// Do not do anything here.
}
// Call the function after the entire process completely calls the DLL. It is safe to call from multiple threads.
// Security is not referenced, but security is re-imported. The first call will be terminated.
Extern "C" _ declspec (dllexport) void _ stdcall DllForceTerm (void)
{
// Do not do anything here. If you need additional termination steps,
// Do nothing else here. If you need extra terminate steps,
// Use atexit.
_ Crt_dll_terminate ();
// Do not do anything here.
}
Add the following code to the "exports" section of the DLL. def file:
DllEnsureInit PRIVATE
DllForceTerm PRIVATE
If you do not have these two lines, The Link error will occur in the application linked to the DLL when both of the DLL outputs the function. A typical error is that the output function name is the same.
When there are multiple DLL callers, each caller can perform a static or dynamic link with your DLL.
- Your dll can have multiple callers
- If the caller is statically linked to the DLL, add the following call before using your DLL or anything dependent on it in the application for the first time:
// Code snippet 1
Typedef void (_ stdcall * pfnEnsureInit) (void );
Typedef void (_ stdcall * pfnForceTerm) (void );
{
//... Initialization code
HMODULE hDll =: GetModuleHandle ("mydll. dll ");
If (! HDll)
{
// Exit and return. Nothing else to do
}
PfnEnsureInit pfnDll = (pfnEnsureInit): GetProcAddress (hDll,
"DllEnsureInit ");
If (! PfnDll)
{
// Exit and return. Nothing else to do
}
PfnDll ();
//... More initialization code
}
- After the application uses the DLL for the last time, add the following code:
// Code segment 2
{
//... Termination code
HMODULE hDll =: GetModuleHandle ("mydll. dll ");
If (! HDll)
{
// Exit and return. Nothing else to do
}
PfnForceTerm pfnDll = (pfnForceTerm): GetProcAddress (hDll,
"DllForceTerm ");
If (! PfnDll)
{
// Exit and return. Nothing else to do
}
PfnDll ();
//... More termination code
}
- If the caller dynamically links to this DLL, insert code snippet 1 to the first call to LoadLibrary, and insert code snippet 2 to the last call to FreeLibrary.
COM-Based DLL modification method
The following describes how to modify the output functions of DllCanUnloadNow, DllGetClassObject, DllRegisterServer, and DllUnregisterServer:
// Implement DLL output
STDAPI DllCanUnloadNow (void)
{
HRESULT hrReturn = S_FALSE;
// Function as usual
// At this point hrReturn is S_ OK if you can unload
If (hrReturn = S_ OK)
{
_ Crt_dll_terminate ();
}
Return hrReturn;
}
STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID * bp)
{
// Do not do anything here.
_ Crt_dll_initialize ();
// Continue with DllGetClassObject as before
}
STDAPI DllRegisterServer (void)
{
If (! (_ Crt_dll_initialize ()))
{
Return E_FAIL;
}
// Call the registration code here
HRESULT hr = <registration code>
_ Crt_dll_terminate ();
Return hr;
}
STDAPI DllUnregisterServer (void)
{
If (! (_ Crt_dll_initialize ()))
{
Return e_fail;
}
// Call the logout code here
Hresult hR = <unregistration code>
_ Crt_dll_terminate ();
Return hr;
}
Your DLL contains the caller. The caller uses the hosted code and DLL output or hosting entry point. The modification method is as follows:
- Implements a Managed class with static member functions. static member functions are used to initialize termination routines. Add a. cpp file to the project:
// ManagedWrapper. cpp
// This code verifies that when the/NOENTRY link option is used, DllMain is not automatically called by Loader.
// It also checks some CRT initialization functions.
# Include <windows. h>
# Include <stdio. h>
# Include <string. h>
# Include <stdlib. h>
# Include <math. h>
# Include "_ vcclrit. h"
# Using <mscorlib. dll>
Using namespace System;
Public _ gc class ManagedWrapper {
Public:
Static int minitialize (){
Int retval = 0;
Try {
_ Crt_dll_initialize ();
} Catch (System: exception * E ){
Console: writeline (E );
Retval = 1;
}
Return retval;
}
Static int mterminate (){
Int retval = 0;
Try {
_ Crt_dll_terminate ();
} Catch (System: exception * E ){
Console: writeline (E );
Retval = 1;
}
Return retval;
}
};
Bool winapi dllmain (hinstance hmodule, DWORD dwreason, lpvoid
Lpvreserved ){
Console: writeline (S "dllmain is called ...");
Return true;
}/* Dllmain */
- Before and after the DLL is referenced and used, call these initialization routines and termination routine member functions in main:
#using <mscorlib.dll>
#using "ijwdll.dll"
using namespace System;
int main() {
int retval = 0;
retval += ManagedWrapper::minitialize();
retval += ManagedWrapper::mterminate();
return retval;
}