【windows核心编程】DLL相关(1)

 

DLL相关的东西

 

1、DLL的加载方式

隐式:

#pragma comment(lib, "XX.lib");

编译器去查找名为XX.dll的DLL,除了名字相同,该DLL和该LIB的GUID也相同。

 

显式:

HINSTANCE   hInst = LoadLibrary(TEXT("XX.dll"));

if(NULL == hInst)  retrun;

 

HINSTANCE hInst = LoadLibrary(TEXT("XX.dll"), NULL,   FLAGS);

第三个参数为一些标志,详见核心编程。

 

2、DLL的入口函数

原型

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
);

其中hModule为该DLL在当前进程地址空间中的位置,ul_reason_for_call为调用此函数的原因,lpReserved如果为显式加载则为0,如果为隐式加载则为非0。

详细说一下ul_reason_for_call

复制代码
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
#ifdef _DEBUG

    switch (ul_reason_for_call)
    {
        //只在该DLL第一次加载进地址空间的时候被调用一次
        //此后当该DLL被加载进不同进程的地址空间时让然不调用
        //当主调线程通过该reason调用DllMain函数时,主调线程不会收到DLL_THREAD_ATTACH通知
    case DLL_PROCESS_ATTACH:
        OutputDebugString(TEXT("\r\n***********B Dll DLL_PROCESS_ATTACH ***********\r\n"));
        break;

        //当该DLL已经被加载到进程地址空间后,当有新【线程创建】的时候,新创建的线程先执行此段代码
        //然后再去执行线程本身的代码
        //不管有几个DLL,每个DLL的此处代码都会被新线程执行
    case DLL_THREAD_ATTACH:
        OutputDebugString(TEXT("\r\n***********B Dll DLL_THREAD_ATTACH ***********\r\n"));
        break;

        //当该DLL已经被加载到进程地址空间后,当有【线程结束】的时候,该线程结束之前会调用此处代码
        //然后才返回
        //不管有几个DLL,每个DLL的此处代码都会被执行
    case DLL_THREAD_DETACH:
        OutputDebugString(TEXT("\r\n***********B Dll DLL_THREAD_DETACH *********** \r\n"));
        break;

        //只有在该DLL从内存中卸载的时候才会被调用一次 
    case  DLL_PROCESS_DETACH:
        OutputDebugString(TEXT("\r\n***********B Dll DLL_PROCESS_DETACH ***********\r\n"));
        break;
    }

#endif

    return TRUE;
}
复制代码

 

 

demo

有两个DLL, ADll.dll 和 BDll.dll

在程序中加载这个两个DLL, 然后创建一个新线程,看输出

复制代码
//加载DLL
void CUseABDllDlg::OnBnClickedBtnLoadlib()
{
    HINSTANCE hInstA = LoadLibrary(_T("ADll.dll"));
    HINSTANCE HIntB = LoadLibrary(_T("BDll.dll"));
    if (NULL == hInstA || NULL == HIntB)
    {
        AfxMessageBox(_T("加载DLL失败"));
        return;
    }  
}

//线程函数
UINT WINAPI WorkThread(LPVOID lpParam)
{
#ifdef _DEBUG
    OutputDebugString(TEXT("\r\n&&&&&&&&&&&&&&&&&&线程被创建&&&&&&&&&&&&&&&&&\r\n"));
#endif

    return 0U;
}

//创建线程
void CUseABDllDlg::OnBnClickedButton2()
{
    HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, WorkThread, NULL, 0, NULL);
    if (NULL == hThread || INVALID_HANDLE_VALUE == hThread)
    {
        AfxMessageBox(_T("创建线程失败"));
    }
}
复制代码

 

 

输出

 

结论:

每创建一个新线程,当前进程地址空间中的DLL的DllMain函数都会通过DLL_THREAD_ATTACH通知被调用一次

每结束一个线程,进程地址空间空的DLL的DllMain函数都会通过DLL_THREAD_DETACH通知被调用一次。

 

 

posted on   崔好好  阅读(270)  评论(0编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示