gpt4 book ai didi

c++ - Visual Studio代码二进制兼容性

转载 作者:行者123 更新时间:2023-11-28 05:42:44 25 4
gpt4 key购买 nike

假设我们有一个.exe和多个.dll,它们都使用不同的Visual Studio版本以C / C ++编写。

.exe和.dll可能具有大量的第三方静态库,并且很难升级整个项目(C ++或DLL)以使用较新的Visual Studio。

除了二进制兼容性可能会因Visual Studio切换而提高:
Visual studio 2015 run-time dependencies or how to get rid of Universal CRT?

可能还会有更多问题-如调试等:
https://randomascii.wordpress.com/2013/09/11/debugging-optimized-codenew-in-visual-studio-2012/

正如我在较旧的Visual Studio中所了解的那样,C ++函数名称改写也存在问题(从Visual Studio更改为Visual Studio)
移植解决方案会造成更多问题。


分配/免费问题


据我了解,在.exe上下文中执行p = malloc()(exe是用vs2010编译的),然后在.dll上下文中执行free(p)(dll是用vs2013编译的)会导致应用程序崩溃。

我猜想一种方法是根本不使用CRT分配功能(不使用malloc,免费,新建等),而是直接使用Windows api(LocalAlloc等),这样代码就可以在Visual Studio的不同版本中工作,但是用您自己的分配方案覆盖所有分配过程听起来像是繁琐的任务。

您知道使vs版本混合成为可能的其他方法吗?


我还测试了C ++函数处理的功能在Visual Studio 2010和2013之间兼容,但是现在它已成为标准-在下一个Visual Studio版本中是否会以不兼容的方式更改?

最佳答案

提出这样的问题有时真的很有用-从评论您的人那里可以获得真正有用​​的链接。

我想在此处复制粘贴该链接:
http://siomsystems.com/mixing-visual-studio-versions/

这是技术背景/问题的描述。

我自己试图为问题#1建立某种解决方案的原型。 (问题2对我来说仍然不清楚)

主要问题本身来自dll的MSVCR100.DLL / MSVCR120.DLL / ...趋向于使用自己的内存管理例程,而不是尝试具有一些共同点-例如依靠Windows API。据我所知,可能是vs2015中引入的通用CRT试图摆脱这个问题-但是-不确定-需要更深入的研究。

我自己想-“好吧,如果我们正在加载MSVCR120.DLL-为什么我们不能拦截malloc / realloc / free函数并路由到我们的CRT”。此解决方案适用于使用“单线程DLL”或“多线程DLL”运行时库的exe和dll。

我已经从这个网站上摘了Minhooks:

http://www.codeproject.com/Articles/44326/MinHook-The-Minimalistic-x-x-API-Hooking-Libra

并编写如下代码片段:

crtCompatibility.cpp:

#include <Windows.h>
#include "MinHook.h" //MH_Initialize
#include <map>
#include <vector>
#include <atlstr.h> //CStringW
#include <Psapi.h> //EnumProcessModules

using namespace std;

map<CStringW, bool> g_dlls; // dll file path (lowercased) for all loaded .dll's within process.
map<CStringW, vector<void*> > g_mapHooks; // dll file path to hooks accosiated with given dll.
map<CStringW, bool> g_myCrtDlls; // filenames only of all crt's which we enabled by default.
CRITICAL_SECTION g_dllCheck;
bool g_executingInCrt = false; // true if executing in dll's crt, don't reroute such mallocs then, otherwise crt gets angry to you.

DWORD g_monitorThread = (DWORD) -1;

#define CRTS_TO_HOOK 10 // Maximum CRT's to hook
bool hookIsFree[CRTS_TO_HOOK] = { true, true, true, true, true, true, true, true, true, true };

//-------------------------------------------
// malloc rerouter.
//-------------------------------------------

typedef void* (__cdecl *pFuncMalloc) (size_t size);
pFuncMalloc porigMalloc[CRTS_TO_HOOK] = { 0 };
map<CStringW, int> g_AllocationId;

template <int i>
void* __cdecl _thisCrtMalloc( size_t size )
{
if( !g_executingInCrt && g_monitorThread == GetCurrentThreadId() )
return malloc( size );

return porigMalloc[i]( size );
}

pFuncMalloc thisCrtMalloc[CRTS_TO_HOOK] =
{
_thisCrtMalloc<0>, _thisCrtMalloc<1>, _thisCrtMalloc<2>, _thisCrtMalloc<3>, _thisCrtMalloc<4>,
_thisCrtMalloc<5>, _thisCrtMalloc<6>, _thisCrtMalloc<7>, _thisCrtMalloc<8>, _thisCrtMalloc<9>
};

//-------------------------------------------
// realloc rerouter.
//-------------------------------------------

typedef void* (__cdecl *pFuncRealloc) (void* p, size_t size);
pFuncRealloc porigRealloc[CRTS_TO_HOOK] = { 0 };

template <int i>
void* __cdecl _thisCrtRealloc( void* p, size_t size )
{
if( !g_executingInCrt && g_monitorThread == GetCurrentThreadId() )
return realloc( p, size );

return porigRealloc[i]( p, size );
}

pFuncRealloc thisCrtRealloc[CRTS_TO_HOOK] =
{
_thisCrtRealloc<0>, _thisCrtRealloc<1>, _thisCrtRealloc<2>, _thisCrtRealloc<3>, _thisCrtRealloc<4>,
_thisCrtRealloc<5>, _thisCrtRealloc<6>, _thisCrtRealloc<7>, _thisCrtRealloc<8>, _thisCrtRealloc<9>
};

//-------------------------------------------
// free rerouter.
//-------------------------------------------

typedef void( __cdecl *pFuncFree ) (void*);
pFuncFree porigFree[CRTS_TO_HOOK] = { 0 };

template <int i>
void __cdecl _thisCrtFree( void* p )
{
if( !g_executingInCrt && g_monitorThread == GetCurrentThreadId() )
return free( p );

porigFree[i]( p );
}

pFuncFree thisCrtFree[CRTS_TO_HOOK] =
{
_thisCrtFree<0>, _thisCrtFree<1>, _thisCrtFree<2>, _thisCrtFree<3>, _thisCrtFree<4>,
_thisCrtFree<5>, _thisCrtFree<6>, _thisCrtFree<7>, _thisCrtFree<8>, _thisCrtFree<9>
};


//
// Normally we could just return true here. But just to minimize amount of hooks
// enabled accross whole process, we know which plugins are using which visual studio
// crt.
//
bool CrtNeedsToBeHooked( const wchar_t* pDll )
{
if( wcsicmp( pDll, L"msvcr120.dll") == 0 )
return true;

return false;
}

//
// Loading one dll might load another (dependent) dll as well.
// Same is with FreeLibrary. We keep here record of which dll's are loaded
// to compare with previous state.
//
void EnumDlls( bool bCheckNew )
{
EnterCriticalSection( &g_dllCheck);
HMODULE dlls[1024] = { 0 };
DWORD nItems = 0;
wchar_t path[MAX_PATH];
HANDLE hProcess = GetCurrentProcess();

if( !EnumProcessModules( hProcess, dlls, sizeof( dlls ), &nItems ) )
{
LeaveCriticalSection( &g_dllCheck);
return;
}

// Not visited.
for( auto it = g_dlls.begin(); it != g_dlls.end(); it++ )
it->second = false;

nItems /= sizeof( HMODULE );
for( unsigned int i = 0; i < nItems; i++ )
{
path[0] = 0;
if( !GetModuleFileNameExW( hProcess, dlls[i], path, sizeof( path ) / sizeof( path[0] ) ) )
continue;

_wcslwr_s( path, MAX_PATH );

auto it = g_dlls.find( path );
if( it != g_dlls.end() )
{
// Visited.
it->second = true;
continue;
}

g_dlls[path] = true;

if( !bCheckNew )
continue;

wchar_t* pDll = wcsrchr( path, L'\\' );
if( pDll ) pDll++;

//
// MSVCRxxx.dll (For example MSVCR100.DLL) is loading, let's hook it's memory allocation routines
// and route them to our CRT.
//
// (P.S. this might be different .dll name for vs2015, haven't tested)
//
if( _wcsnicmp( pDll, L"MSVCR", 5 ) == 0 && CrtNeedsToBeHooked(pDll) && g_myCrtDlls.find( pDll ) == g_myCrtDlls.end() )
{
// While we are executing our code in hookLoadLibrary, we can execute GetProcLibrary
// functions, and it's possible to get dead lock because of this.
// kernel32.dll is waiting for LoadLibrary to complete, but we are waiting for GetProcLibrary
// to complete.
LeaveCriticalSection( &g_dllCheck);
void* f = GetProcAddress( dlls[i], "malloc" );
void* f2 = GetProcAddress( dlls[i], "realloc" );
void* f3 = GetProcAddress( dlls[i], "free" );
EnterCriticalSection( &g_dllCheck);

int FoundFreeSlot = -1;

for( int freeSlot = 0; freeSlot < CRTS_TO_HOOK; freeSlot++ )
if( hookIsFree[freeSlot] == true )
{
FoundFreeSlot = freeSlot;
break;
}

if( FoundFreeSlot != -1 )
{
vector<void*> vecTargets;

// Hook malloc, realloc, free functions CRT compatibility.
vecTargets.push_back( f );
MH_CreateHook( f, thisCrtMalloc[FoundFreeSlot], (void**)&porigMalloc[FoundFreeSlot] );

vecTargets.push_back( f2 );
MH_CreateHook( f2, thisCrtRealloc[FoundFreeSlot], (void**)&porigRealloc[FoundFreeSlot] );

vecTargets.push_back( f3 );
MH_CreateHook( f3, thisCrtFree[FoundFreeSlot], (void**)&porigFree[FoundFreeSlot] );

g_mapHooks[path] = vecTargets;
MH_EnableHook( MH_ALL_HOOKS );
g_AllocationId[path] = FoundFreeSlot;
hookIsFree[FoundFreeSlot] = false;
}
}
} //for

//
// Check if .dll's were freed.
//
for( auto it = g_dlls.begin(); it != g_dlls.end(); )
{
if( !it->second )
{
// Release trampolines.
auto hooks = g_mapHooks.find( it->first );
if( hooks != g_mapHooks.end() )
{
// Release allocation slot.
int allocSlot = g_AllocationId[ it->first ];
if( allocSlot < CRTS_TO_HOOK )
hookIsFree[allocSlot] = true;

vector<void*>& vec = hooks->second;
for( size_t i = 0; i < vec.size(); i++ )
MH_RemoveHook2( vec[i], false );
}

// Dll was freed.
g_dlls.erase( it++ );
continue;
}
it++;
} //for

if( !bCheckNew )
{
// Collect CRT names upon which we are running at. .NET might try to draw multiple CRTs.
for( auto it = g_dlls.begin(); it != g_dlls.end(); it++ )
{
CStringW path = it->first;
wchar_t* pPath = path.GetBuffer( MAX_PATH );
_wcslwr_s( pPath, MAX_PATH );

wchar_t* pDll = wcsrchr( pPath, L'\\' );
if( pDll ) pDll++;

if( _wcsnicmp( pDll, L"MSVCR", 5 ) == 0 )
g_myCrtDlls[pDll] = true;
}
}

LeaveCriticalSection( &g_dllCheck );
} //EnumDlls

//-------------------------------------------
// Intercepts LoadLibraryW
//-------------------------------------------

typedef HMODULE( WINAPI *pFuncLoadLibraryW )(const wchar_t* file);
pFuncLoadLibraryW g_origLoadLibraryW = NULL;

HMODULE WINAPI hook_LoadLibraryW( const wchar_t* file )
{
bool bUpdateLock = g_monitorThread == GetCurrentThreadId();
if( bUpdateLock )
g_executingInCrt = true;

HMODULE h = g_origLoadLibraryW( file );

if( bUpdateLock )
g_executingInCrt = false;

if( !h )
return h;

EnumDlls( true );
return h;
} //hook_LoadLibraryW

//-------------------------------------------
// Intercepts LoadLibraryA
//-------------------------------------------

typedef HMODULE( WINAPI *pFuncLoadLibraryA )(const char* file);
pFuncLoadLibraryA g_origLoadLibraryA = NULL;

HMODULE WINAPI hook_LoadLibraryA( const char* file )
{
bool bUpdateLock = g_monitorThread == GetCurrentThreadId();
if( bUpdateLock )
g_executingInCrt = true;

HMODULE h = g_origLoadLibraryA( file );

if( bUpdateLock )
g_executingInCrt = false;

if( !h )
return h;

EnumDlls( true );
return h;
} //hook_LoadLibraryW

//-------------------------------------------
// Intercepts FreeLibrary
//-------------------------------------------

typedef BOOL( WINAPI *pFuncFreeLibrary ) (HMODULE h);
pFuncFreeLibrary g_origFreeLibrary;

BOOL WINAPI hook_FreeLibrary( HMODULE h )
{
bool bUpdateLock = g_monitorThread == GetCurrentThreadId();
if( bUpdateLock )
g_executingInCrt = true;

BOOL b = g_origFreeLibrary( h );

if( bUpdateLock )
g_executingInCrt = false;

if( !b )
return b;

EnumDlls( true );
return b;
} //hook_FreeLibrary

//
// This function intercepts and starts monitor what new dll's gets loaded and freed.
// If there is loaded MSVCRxxx.DLL different CRT run-time than we're running in - we intercepts
// it's memory allocation routines, so allocation in .dll would work identically to allocation in main .exe
//
void EnableCrtMonitor(void)
{
EnumDlls( false );
MH_Initialize();
MH_CreateHookApi( L"kernel32.dll", "LoadLibraryW", hook_LoadLibraryW, (void**)&g_origLoadLibraryW );
MH_CreateHookApi( L"kernel32.dll", "LoadLibraryA", hook_LoadLibraryA, (void**)&g_origLoadLibraryA );
MH_CreateHookApi( L"kernel32.dll", "FreeLibrary", hook_FreeLibrary, (void**)&g_origFreeLibrary );
MH_EnableHook( MH_ALL_HOOKS );
}

class CCrtCompatibilityEnabler
{
public:
CCrtCompatibilityEnabler()
{
InitializeCriticalSection( &g_dllCheck);
EnableCrtMonitor();
}

~CCrtCompatibilityEnabler()
{
MH_DisableHook( MH_ALL_HOOKS );
MH_Uninitialize();
DeleteCriticalSection(&g_dllCheck);
}
} g_CheckCrtShutdown;

//
// This function enables or disables CRT compatibility hooks.
//
// Enabling can be done like this:
// EnableDisableCrtCompatibility( GetCurrentThreadId() );
// and disabling - running without any argument.
//
// When hooks are enabled - for thread which is monitored - all memory allocations
// will be performed using main .exe CRT.
//
void EnableDisableCrtCompatibility( DWORD monitorThread = (DWORD) -1)
{
g_monitorThread = monitorThread;
}


但是,我在编写此代码时遇到了多个问题,因此,如果存在一些问题-不一定很容易解决-但是它可以与我自己的代码一起使用(这是相对较大的代码库,具有超过3-4个CRT,迷上了)。

调试时,我从CRT未分配的内存中获得了无内存的回调_thisCrtFree-我怀疑MSVCRxxx.DLL可以在内部分配内存,并且仍然可以对已分配的内存进行免费调用-我决定不与.NET线程打架(我使用的是混合模式C ++代码)-但是要指定执行分配/重新分配/空闲的确切线程。

启用CRT兼容模式可以这样完成:

EnableDisableCrtCompatibility( GetCurrentThreadId() );

<call to dll>
p = malloc(1024);
<back to exe>

EnableDisableCrtCompatibility( );

free( p );


因此,现在可以跨.dll / .exe / vs版本边界分配和释放内存。

但是请注意,不能保证更高级别的类(例如vector / string)在Visual Studio版本之间是向后兼容的,因此您需要单独进行测试。但是,如果您有自己的字符串/数组类,则可以自己控制代码二进制兼容性。

另请注意,如果您有一些在.dll启动期间初始化的全局变量-则不会挂接内存管理,并且仍在使用旧的CRT malloc / realloc / free。 (请参见代码-g_executingInCrt)

请随时使用此代码,如果有任何疑问,请问我。

在下面可以找到Minhooks的略微修改版本:

http://www.saunalahti.fi/~tarmpika/codesamples/MinHook.zip

更新1.5.2016上面的方法不适用于延迟加载的dll,因为.dll是在某些特定的函数调用处加载的(不在LoadLibrary中),并且crt初始化又一次无效。我已经更新了代码,以便.dll负责启用或禁用crt兼容性。但是代码几乎和我上面写的一样。如果您还需要检查构造函数/析构函数的状态-用范围运算符包装代码会很有意义。例如-这样:

void UseExeCrt( bool bExesCrt )
{
if( bExesCrt )
EnableDisableCrtCompatibility( GetCurrentThreadId() );
else
EnableDisableCrtCompatibility( );
}

... class implemented in .exe, declaration for .dll:

class __declspec(dllimport) CMyOwnClass
{
public:
void GetComplexData();

...
};

... data resides in .dll:

vector<String> g_someDllsData;


... function resides in .dll:

...
UseExeCrt( true ); // Use exe's CRT for allocating CMyOwnClass.
{
CMyOwnClass tempClass;
tempClass.GetComplexData(); // Since function resides in .exe - we use .exe's CRT.

UseExeCrt( false ); // Now we use some data which resides in .dll, and we must use .dll's crt.

g_someDllsData.resize( ... );


UseExeCrt( true ); // Before deleting CMyOwnClass - we must use again .exe's CRT
} //~CMyOwnClass is called.

UseExeCrt( false ); // Now back to .dll's CRT. (Just to be safe).


但是,基本原则是尽量减少主要执行时间-为安全起见。

18.5.2016更新事实证明,当加载复杂的应用程序并且连接了许多CRT时,LoadLibrary + GetProcAddress中的钩子可以挂在死锁中。主要问题是,如果您已钩住LoadLibrary,则不应在临界区中使用类似api的函数。 kernel32.dll在一个线程中等待您,而您在另一个线程中等待kernel32.dll。现在,通过仅钩住我需要的内容(请参见函数CrtNeedsToBeHooked)并通过在GetProcAddress调用期间释放关键部分,使潜在的死锁情况最小化。

但这通常很常见,如果您已经连接了一些api,则需要照顾多状态/多线程状态机,这是api和api挂钩所引入的-问题再现起来可能并非易事(我的快速PC甚至在虚拟机问题上不会发生,但一些较慢的PC会出现此问题)。

从理论上讲,您可以假设您完全控制应用程序正在加载什么.dll,但这并非完全正确。例如-存在一些第三方应用程序-例如tortoise svn,该应用程序作为资源管理器扩展加载,每当您打开浏览对话框时,该应用程序就会依次显示。

在一台计算机上安装了该.dll,而另一台则未安装。问题可能仅在第一台机器上出现。

关于c++ - Visual Studio代码二进制兼容性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36811679/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com