- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
序言:这个问题仅与通过/MD
使用的动态CRT的行为有关。它不质疑任何其他建议的有效性。 DllMain
。
作为we've been told :(参见:Dynamic-Link库最佳实践,MSDN,2006年5月17日)
您永远不要在DllMain中执行以下任务:
...
使用动态C运行时(CRT)中的内存管理功能。如果未初始化CRT DLL,则对这些函数的调用会导致进程崩溃。
...
其他人已经have questioned this(如:对参数的有效性提出质疑),并且由于我们在那里有帮助地得到了答案,因此我们可以清楚地看到一个相当简单的情况,其中可以potentially cause troubles:
您是基于DLL的入口点始终为_DllMainCRTStartup的假设进行工作的。事实并非如此,这只是链接器的默认设置。它可以是程序员希望通过链接器的/ ENTRYPOINT选项快速,轻松地进行更改的任何内容。 Microsoft无法采取任何措施来防止这种情况。
因此,这些是此问题的要素:
链接/MD
而不提供自定义/ENTRYPOINT
时,是否还有其他情况需要动态初始化CRT?
具体来说,如果所有DLL加载仅通过“静态依赖项”完成,即根本没有任何显式的LoadLibrary
调用,则只需链接时DLL依赖项即可。
奖励:MS文档专门称呼“内存管理功能”,但据我所知,如果未初始化CRT,则任何CRT功能都可能不安全。为什么要用这种方式调出内存管理功能?
第三名:
Wrt。到自定义ENTRYPOINT
的方式:我不太清楚这怎么可能是一个如此重要的情况,以至于需要将其包含在not-do-in-DllMain列表中而无需进一步的限定。 IFF我提供了一个自定义的入口点,我负责正确初始化CRT,否则CRT不能在程序的任何地方正常工作,而不仅仅是DllMain。为什么要专门调出DllMain部分?
这使我回到Q.1,即如果这是唯一对动态CRT有问题的情况。对于这对于DllMain比DLL其他部分更重要的原因,或者我在这里可能会错过的内容,我们将不胜感激或大开眼界。
奖金链接:
When are global objects constructed and destructed by Visual C++?
DllMain : a horror story
Calling LoadLibrary from DllMain
原理:我觉得我应该在上下文中添加:我之所以这么问,是因为我们有大量的代码通过全局C ++对象构造函数来处理事务。多年来已经审查了实际发生的问题(例如并发的LoadLibrary
,线程同步等),但是所有代码都充满了std
C ++和CRT函数,在Windows XP上愉快地工作了很多年,7和Windows 10没有任何已知的问题。虽然我不是要哭“但它可以工作”,但我必须在这里进行工程判断,以尝试“解决”这一问题是否存在任何中短价值。因此,如果肥皂盒的答案可以留在他们的盒子里,我将不胜感激。
最佳答案
链接/MD
时,是否还有其他情况?
自定义/ENTRYPOINT
,动态CRT应该不完整
初始化了吗?
首先一些符号:
X具有静态导入(取决于)Y和Z:X[ Y, Z]
X入口点:X_DllMain
X_DllMain
呼叫LoadLibrary(Y)
:X<Y>
当使用/MD
时-在单独的DLL中使用crt。在这种情况下初始化意味着已调用crt DLL的入口点。因此问题可以更笼统,更清楚:
来自X[Y]
=> Y_DllMain
在X_DllMain
之前调用?
一般情况下因为Y[X]
或Y[Z[X]]
可以是循环依赖项。
最著名的示例user32[gdi32]
和gdi32[user32]
或win10中的示例取决于gdi32[gdi32full[user32]]
。因此必须先调用user32_DllMain
或gdi32_DllMain
吗?但是很明显,任何crt DLL都不依赖于我们的自定义DLL。因此,让我们排除循环依赖的情况。
当加载程序加载模块X时-加载所有它的依赖模块(并且依赖关系-这是递归过程),如果它尚未在内存中,则加载程序构建调用图,并开始调用模块入口点。很明显,如果A[B]
,则加载程序总是尝试在B_DllMain
之前调用A_DllMain
(当循环顺序未定义时,循环依赖除外)。但是调用图中将包含哪些模块?所有X依赖模块?当然不。当我们开始加载X时,其中一些模块可能已经在内存中(加载)。因此,它的入口点已经使用DLL_PROCESS_ATTACH
调用,并且现在不能再次调用。在XP,Vista,Win7中使用的这种策略:
当我们加载X时:
加载或定位所有依赖模块在内存中
仅调用新加载的模块(在X之后)的入口点。
如果A[B]
-在B_DllMain
之前调用A_DllMain
示例:已加载X[Y[W[Z]], Z]
//++begin load X
Z_DllMain
W_DllMain
Y_DllMain
X_DllMain
// --end load X
LoadLibrary
,则会发生这种情况。
X[Y<W[ Z]>, Z]
//++begin load X
Y_DllMain
//++begin load W
W_DllMain
//--end load W
Z_DllMain
X_DllMain
// --end load X
W_DllMain
,
Z_DllMain
将在
W[Z]
之前被调用。正是因为这不建议从DLL入口点调用
LoadLibrary
。
ExitThread
。在DLL分离期间退出线程可能会导致
ExitProcess
调用-挂起。因此确实会出现死锁,但不会在呼叫
ExitThread
-后者期间发生。
LoadLibrary
调用
DllMain
-user32.dll始终从其入口点为imm32.dll调用
LoadLibrary
(仍然为true,并且在win10上)
X[Y<W[Z]>, Z]
//++begin load X
Y_DllMain
//++begin load W
Z_DllMain
W_DllMain
//--end load W
X_DllMain
// -- end load X
test.exe[ kernel32, D1< D2[kernel32, msvcrt] >, msvcrt ]
SomeFunc
LoadLibraryW(L"D2")
,然后调用
D2.SomeFunc
/NODEFAULTLIB kernel32.lib msvcrt.lib
)
#include <Windows.h>
extern "C"
{
__declspec(dllimport) int __cdecl sprintf(PSTR buf, PCSTR format, ...);
}
BOOLEAN WINAPI MyEp( HMODULE , DWORD ul_reason_for_call, PVOID )
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
OutputDebugStringA("D2.DllMain\n");
}
return TRUE;
}
INT_PTR WINAPI SomeFunc()
{
__pragma(message(__FUNCDNAME__))
char buf[32];
// this is only for link to msvcrt.dll
sprintf(buf, "D2.SomeFunc\n");
OutputDebugStringA(buf);
return 0;
}
#ifdef _WIN64
#define FuncName "?SomeFunc@@YA_JXZ"
#else
#define FuncName "?SomeFunc@@YGHXZ"
#endif
__pragma(comment(linker, "/export:" FuncName ",@1,NONAME,PRIVATE"))
/NODEFAULTLIB kernel32.lib
)
#include <Windows.h>
#pragma warning(disable : 4706)
BOOLEAN WINAPI MyEp( HMODULE hmod, DWORD ul_reason_for_call, PVOID )
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
OutputDebugStringA("D1.DllMain\n");
if (hmod = LoadLibraryW(L"D2"))
{
if (FARPROC fp = GetProcAddress(hmod, (PCSTR)1))
{
fp();
}
}
}
return TRUE;
}
INT_PTR WINAPI SomeFunc()
{
__pragma(message(__FUNCDNAME__))
OutputDebugStringA("D1.SomeFunc\n");
return 0;
}
#ifdef _WIN64
#define FuncName "?SomeFunc@@YA_JXZ"
#else
#define FuncName "?SomeFunc@@YGHXZ"
#endif
__pragma(comment(linker, "/export:" FuncName ",@1,NONAME"))
/NODEFAULTLIB kernel32.lib D1.lib msvcrt.lib
)
#include <Windows.h>
extern "C"
{
__declspec(dllimport) int __cdecl sprintf(PSTR buf, PCSTR format, ...);
}
__declspec(dllimport) INT_PTR WINAPI SomeFunc();
void ep()
{
char buf[32];
// this is only for link to msvcrt.dll
sprintf(buf, "exe entry\n");
OutputDebugStringA(buf);
ExitProcess((UINT)SomeFunc());
}
LDR: D1.dll loaded - Calling init routine
D1.DllMain
Load: D2.dll
LDR: D2.dll loaded - Calling init routine
D2.DllMain
D2.SomeFunc
LDR: msvcrt.dll loaded - Calling init routine
exe entry
D1.SomeFunc
LdrpRunInitializeRoutines - INFO: Calling init routine for DLL "D1.dll"
D1.DllMain
Load: D2.dll
LdrpRunInitializeRoutines - INFO: Calling init routine for DLL "D2.DLL"
D2.DllMain
D2.SomeFunc
LdrpRunInitializeRoutines - "msvcrt.dll"
exe entry
D1.SomeFunc
D2.DllMain
,在msvcrt入口点之前调用了
D2[msvcrt]
LdrpInitializeNode - INFO: Calling init routine for DLL "D1.dll"
D1.DllMain
LdrpInitializeNode - INFO: Calling init routine for DLL "msvcrt.dll"
LdrpInitializeNode - INFO: Calling init routine for DLL "D2.DLL"
D2.DllMain
D2.SomeFunc
exe entry
D1.SomeFunc
X[Y]
时没有在内存中进行任何未初始化的Y-
Y_DllMain
将在
X_DllMain
之前调用。或者换句话说-如果没有人从DLL入口点调用
LoadLibrary(X)
(或
LoadLibrary(Z[X])
)。因此,如果您的DLL将以“正常”方式加载(而不是通过
LoadLibrary
调用
DllMain
或在某些dll加载事件中从驱动程序注入),则可以确保已调用crt入口点(已初始化crt)
X[Y]
-
Y_DllMain
将始终在
X_DllMain
之前调用。
/ENTRYPOINT
。
DllMainCRTStartup
-该模块会按名称调用函数
DllMain
(这不是入口点)。因此,在动态crt的情况下-我们实际上有2个crt部分-主要部分在单独的DLL中,它将在调用您的DLL入口点之前进行初始化(如果不是特殊情况,我将介绍更高的版本和win7,vista,xp)。和小的静态部分(模块内部的代码)。该静态部分何时被称为已满取决于您。此部分
DllMainCRTStartup
进行一些内部初始化,在代码(
initterm
)中初始化全局对象,然后调用
DllMain
,然后返回(通过DLL分离)为全局变量调用析构函数。
DllMainCRTStartup
关于winapi - 在哪种情况下,动态CRT在调用用户提供的DllMain时尚未初始化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48302191/
在为 Web 应用程序用例图建模时,为用户可以拥有的每个角色创建一个角色是否更好?或拥有一个角色、用户和一个具有特权的矩阵? guest < 用户 < 版主 < 管理员 1: guest 、用户、版主
我无法使用 Elixir 连接到 Postgres: ** (Mix) The database for PhoenixChat.Repo couldn't be created: FATAL 28P
这个问题已经有答案了: Group by field name in Java (7 个回答) 已关闭 7 年前。 我必须编写一个需要 List 的方法并返回 Map> . User包含 Person
感谢您的帮助,首先我将显示代码: $dotaz = "Select * from customers JOIN contracts where customers.user_id ='".$_SESS
我只想向所有用户中的一个用户显示一个按钮。我尝试了 orderByKey() 但没有成功! 用户模型有 id 成员,我尝试使用 orderByChild("id") 但结果相同! 我什至尝试了以下技巧
我们在工作中从 MongoDB 切换到 Postgres,我正在建立一个 BDR 组。 在这一步,我正在考虑安全性并尽可能锁定。因此,我希望设置一个 replication 用户(角色)并让 BDR
export class UserListComponent implements OnInit{ users; constructor(private userService: UserS
我可以使用 Sonata User Bundle 将 FOS 包集成到 sonata Admin 包中。我的登录功能正常。现在我想添加 FOSUserBundle 中的更改密码等功能到 sonata
在 LinkedIn 中创建新应用程序时,我得到 4 个单独的代码: API key 秘钥 OAuth 用户 token OAuth 用户密码 我在 OAuth 流程中使用前两个。 的目的是什么?最后
所以..我几乎解决了所有问题。但现在我要处理另一个问题。我使用了这个连接字符串: SqlConnection con = new SqlConnection(@"Data Source=.\SQLEX
我有一组“用户”和一组“订单”。我想列出每个 user_id 的所有 order_id。 var users = { 0: { user_id: 111, us
我已经为我的Django应用创建了一个用户模型 class User(Model): """ The Authentication model. This contains the u
我被这个问题困住了,找不到解决方案。寻找一些方向。我正在用 laravel 开发一个新的项目,目前正致力于用户认证。我正在使用 Laravels 5.8 身份验证模块。 对密码恢复 View 做了一些
安装后我正在使用ansible配置几台计算机。 为此,我在机器上本地运行 ansible。安装中的“主要”用户通常具有不同的名称。我想将该用户用于诸如 become_user 之类的变量. “主要”用
我正在尝试制作一个运行 syncdb 的批处理文件来创建一个数据库文件,然后使用用户名“admin”和密码“admin”创建一个 super 用户。 到目前为止我的代码: python manage.
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 6 年前。 Improv
我已在 Azure 数据库服务器上设置异地复制。 服务器上运行的数据库之一具有我通过 SSMS 创建的登录名和用户: https://learn.microsoft.com/en-us/azure/s
我有一个 ionic 2 应用程序,正在使用 native FB Login 来检索名称/图片并将其保存到 NativeStorage。流程是我打开WelcomePage、登录并保存数据。从那里,na
这是我的用户身份验证方法: def user_login(request): if request.method == 'POST': username = request.P
我试图获取来自特定用户的所有推文,但是当我迭代在模板中抛出推文时,我得到“User”对象不可迭代 观看次数 tweets = User.objects.get(username__iexact='us
我是一名优秀的程序员,十分优秀!