windows 如何检查dll的引用计数?如何知道dll被加载到哪里?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3553231/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 15:06:20  来源:igfitidea点击:

How to check dll's reference count? How to know where the dll was loaded?

c++windows

提问by ldlchina

As you know, if the call to LoadLibrary specifies a DLL module already mapped into the address space of the calling process, the function simply returns a handle of the DLL and increments the module's reference count.

如您所知,如果对 LoadLibrary 的调用指定了一个已映射到调用进程地址空间的 DLL 模块,则该函数仅返回 DLL 的句柄并增加模块的引用计数。

Somewhere, I need to get the reference count of a dll. How to get the dll's reference count? How to know where the dll was loaded? Thanks.

在某个地方,我需要获取 dll 的引用计数。如何获取dll的引用计数?如何知道dll被加载到哪里?谢谢。

回答by C Johnson

I googled it, and found this articlewhich claims to provide the answer. Sorry I couldn't be more helpful:

我用谷歌搜索,发现这篇文章声称提供了答案。抱歉,我帮不上忙了:

回答by Chubsdad

If it is a non programmatic way (thanks to C.Johnson for giving that perspective), WinDBG could be helpful

如果它是一种非程序化的方式(感谢 C.Johnson 提供这种观点),WinDBG 可能会有所帮助

http://windbg.info/doc/1-common-cmds.html#10_modules

http://windbg.info/doc/1-common-cmds.html#10_modules

Look at !dlls and it's variants.

看看 !dlls 和它的变体。

!dll - all loaded modules with load count

!dll - 带有加载计数的所有加载模块

EDIT 2:

编辑2:

If you want to know from where all the DLL is being loaded from the process, there are two ways:

如果您想知道从进程中加载​​所有 DLL 的位置,有两种方法:

a. Look at the command

一种。看命令

"bu kernel32!LoadLibraryExW ";as /mu ${/v:MyAlias} poi(@esp+4); .if ( $spat( \"${MyAlias}\", \"MYDLL\" ) != 0 ) { kn; } .else { g }" "

"bu kernel32!LoadLibraryExW";as /mu ${/v:MyAlias} poi(@esp+4); .if ( $spat( \"${MyAlias}\", \" MYDLL\" ) != 0 ) { kn; } .else { g }" "

in the above URL

在上面的网址

b. Run the process under WinDBG. Debug->Even Filter and select "Load Module" and set it to "Enabled" under "Execution". Under "Continue" set it to "Not Handled".

湾 在 WinDBG 下运行该进程。Debug->Even Filter 并选择“Load Module”并将其设置为“Execution”下的“Enabled”。在“继续”下将其设置为“未处理”。

One of these should help you definitely.

其中之一绝对可以帮助您。

回答by Mattias Bergmark

   typedef struct _LDR_MODULE
    {
        LIST_ENTRY InLoadOrderModuleList;
        LIST_ENTRY InMemoryOrderModuleList;
        LIST_ENTRY InInitializationOrderModuleList;
        PVOID BaseAddress;
        PVOID EntryPoint;
        ULONG SizeOfImage;
        UNICODE_STRING FullDllName;
        UNICODE_STRING BaseDllName;
        ULONG Flags;
        USHORT LoadCount;
        USHORT TlsIndex;
        LIST_ENTRY HashTableEntry;
        ULONG TimeDateStamp;
    } LDR_MODULE, *PLDR_MODULE;

    struct LDR_MODULE_COMPARE
    {
        bool operator()(CONST LDR_MODULE& L, CONST LDR_MODULE& R) CONST {
            ustring ul = L.BaseDllName.Buffer;
            ustring ur = R.BaseDllName.Buffer;
            ul.to_lower();
            ur.to_lower();
            int cmp = wcscmp(ul.c_wstr(), ur.c_wstr());
            if (cmp == 0) {
                ul = L.FullDllName.Buffer;
                ur = R.FullDllName.Buffer;
                cmp = wcscmp(ul.c_wstr(), ur.c_wstr());
            }
            return cmp < 0;
        }
    };

    typedef std::set<LDR_MODULE, LDR_MODULE_COMPARE> LDR_MODULE_SET;
    typedef std::map<ustring, LDR_MODULE, ustring::map_ustring_compare> LDR_MODULE_MAP;

DWORD get_process_id(LPCWSTR processname_z) {
        DWORD aProcesses[1024], cbNeeded, cProcesses;
        unsigned int i;
        DWORD result = 0;
        //Enumerate all processes
        if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
            return NULL;

        // Calculate how many process identifiers were returned.
        cProcesses = cbNeeded / (DWORD)sizeof(DWORD);
        ustring fullpath(processname_z);
        fullpath.to_lower();
        ustring uprocess(processname_z);
        uprocess = _UWC(uprocess.filename());
        uprocess.to_lower();
        size_t ext_pos = uprocess.find_last_of('.');
        if (ext_pos != ustring::unpos) {
            uprocess = uprocess.left(ext_pos);
        }

        TCHAR szEXEName[MAX_PATH];
        //Loop through all process to find the one that matches
        //the one we are looking for
        for (i = 0; i < cProcesses; i++) {
            // Get a handle to the process
            HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
                                          PROCESS_VM_READ, FALSE, aProcesses[i]);

            // Get the process name
            if (NULL != hProcess) {
                HMODULE hMod;
                DWORD cbNeeded;

                if (EnumProcessModules(hProcess, &hMod,
                                       sizeof(hMod), &cbNeeded)) {
                    //Get the name of the exe file
                    GetModuleBaseName(hProcess, hMod, szEXEName,
                                      sizeof(szEXEName) / sizeof(TCHAR));
                    size_t len = _tcslen(szEXEName);
                    _tcscpy(szEXEName + len - 4, TEXT("
typedef struct _PEB_LDR_DATA {
    BYTE Reserved1[8];
    PVOID Reserved2[3];
    LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
")); ustring uexename((TCHAR*)szEXEName); uexename = _UWC(uexename.filename()); uexename.to_lower(); if (uexename == uprocess) { result = aProcesses[i]; } else if (GetModuleFileNameEx(hProcess, 0, szEXEName, MAX_PATH)) { uexename = (TCHAR*)szEXEName; uexename.to_lower(); if (uexename == fullpath) { result = aProcesses[i]; } } } } CloseHandle(hProcess); if (result > 0) break; } return result; } HRESULT get_dll_references_or_count(LPCWSTR process_z, LPCWSTR dll_z, _Out_ DWORD* count_ptr, _Out_opt_ LDR_MODULE_SET* pdlls, _Out_opt_ LDR_MODULE_SET* pnew_dlls, BOOL append) { HRESULT hr = E_FAIL; PROCESS_BASIC_INFORMATION pbi; PEB peb; DWORD dwSize = 0; SIZE_T stSize = 0; DWORD process_id = 0; HANDLE hProcess = NULL; PEB_LDR_DATA peb_ldr_data; ustring udll; LDR_MODULE peb_ldr_module; void *readAddr = NULL; HMODULE hMod = NULL; typedef NTSTATUS(WINAPI* ZwQueryInformationProcess)(HANDLE, DWORD, PROCESS_BASIC_INFORMATION*, DWORD, DWORD*); ZwQueryInformationProcess MyZwQueryInformationProcess = NULL; // if (count_ptr == NULL && pdlls == NULL) return hr; if (count_ptr != NULL) *count_ptr = 0; if (pdlls != NULL && !append) pdlls->clear(); // process_id = get_process_id(process_z); if (process_id == 0) return hr; // hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process_id); if (hProcess == NULL) goto Exit; // hMod = GetModuleHandle(L"ntdll.dll"); MyZwQueryInformationProcess = (ZwQueryInformationProcess)GetProcAddress(hMod, "ZwQueryInformationProcess"); if (MyZwQueryInformationProcess == NULL) goto Exit; // if (MyZwQueryInformationProcess(hProcess, 0, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &dwSize) < 0) { goto Exit; } // if (!ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(PEB), &stSize)) goto Exit; // if (!ReadProcessMemory(hProcess, peb.Ldr, &peb_ldr_data, sizeof(peb_ldr_data), &stSize)) goto Exit; // _LIST_ENTRY* pmodule = peb_ldr_data.InMemoryOrderModuleList.Flink; _LIST_ENTRY* pstart = pmodule; readAddr = (void*)pmodule; // Go through each modules one by one in their load order. udll = dll_z; udll.to_lower(); while (ReadProcessMemory(hProcess, readAddr, &peb_ldr_module, sizeof(peb_ldr_module), &stSize)) { // Get the reference count of the DLL if (pdlls == NULL) { ustring utmp(peb_ldr_module.FullDllName.Buffer); utmp.to_lower(); if (utmp == udll) { *count_ptr = (int)(signed short)peb_ldr_module.LoadCount; break; } utmp = peb_ldr_module.BaseDllName.Buffer; utmp.to_lower(); if (utmp == udll) { *count_ptr = (int)(signed short)peb_ldr_module.LoadCount; break; } } else { if (append) { if (pdlls->find(peb_ldr_module) == pdlls->end()) { pdlls->insert(peb_ldr_module); if (pnew_dlls != NULL) { pnew_dlls->insert(peb_ldr_module); } } #ifdef _DEBUG else { ATLTRACE("%s already loaded\n", peb_ldr_module.FullDllName.Buffer); } #endif } else { pdlls->insert(peb_ldr_module); } } _LIST_ENTRY* pprevmodule = pmodule; pmodule = pmodule->Flink; if (pprevmodule == pmodule || pmodule == pstart) { break; } readAddr = (void *)(pmodule); } if (pdlls == NULL) { if (*count_ptr == 0) { hr = E_NOINTERFACE; } else { hr = S_OK; } } else { if (pdlls->size() == 0) { hr = E_NOINTERFACE; } else { if (count_ptr != NULL) { *count_ptr = (DWORD)pdlls->size(); } hr = S_OK; } } Exit: SAFE_CLOSEHANDLE(hProcess); return hr; }

This a "covert" way to get information about any proccess's loaded dlls. It does work on Windows 10. Note that ustring is my personal special string implementation, can be replaced accordingly. Worth noticing is the peb_ldr_data.InMemoryOrderModuleList.Flink. Its the linked list with all the loaded dlls. It says in MSDN documentation that when reaching the last entry it will point to itself. THAT IS NOT THE CASE. It well revert back to the first entry in the list. Atleast in Win10 Pro. LDR_MODULE::LoadCount is what you are looking for i believe.

这是获取有关任何进程加载的 dll 的信息的“隐蔽”方式。它确实适用于 Windows 10。请注意,ustring 是我个人的特殊字符串实现,可以相应地替换。值得注意的是peb_ldr_data.InMemoryOrderModuleList.Flink。它是包含所有加载的 dll 的链接列表。它在 MSDN 文档中说,当到达最后一个条目时,它将指向自己。事实并非如此。它很好地恢复到列表中的第一个条目。至少在 Win10 Pro 中。我相信 LDR_MODULE::LoadCount 就是你要找的东西。

mvh Mattias

mvh 马蒂亚斯

hmmm Hold on abit...it might not work correctly in Win10...Ill be back.

嗯,稍等一下……它在 Win10 中可能无法正常工作……我会回来的。

3 things there are relevent. And it does work in Win 10 but the LoadCount does not show ref count though. Only if its dynamic(6) or static(-1)

有3件事是相关的。它确实在 Win 10 中工作,但 LoadCount 不显示引用计数。仅当其动态(6)或静态(-1)

PEB_LDR_DATA: There are to different structures floating around. The system one:

PEB_LDR_DATA:有不同的结构在浮动。系统一:

typedef struct _PEB_LDR_DATA2
    {
        ULONG Length;
        UCHAR Initialized;
        PVOID SsHandle;
        LIST_ENTRY InLoadOrderModuleList;
        LIST_ENTRY InMemoryOrderModuleList;
        LIST_ENTRY InInitializationOrderModuleList;
        PVOID EntryInProgress;

    } PEB_LDR_DATA2, *PPEB_LDR_DATA2;

And in some user examples user defined:

在一些用户示例中,用户定义了:

#include <iostream>
#include <ntstatus.h>
#include <Windows.h>
#include <winternl.h>
#include <string>
#include <tchar.h>
#include <excpt.h>
#include <fstream>

using namespace std;

struct _PROCESS_BASIC_INFORMATION_COPY 
{
    PVOID Reserved1;
    PPEB PebBaseAddress;
    PVOID Reserved2[2];
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;
} PROCESS_BASIC_INFORMATION_COPY;


struct _LDR_MODULE_COPY
{
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID BaseAddress;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    LIST_ENTRY HashTableEntry;
    ULONG TimeDateStamp;
} LDR_MODULE_COPY , *PLDR_MODULE_COPY;


struct _PEB_LDR_DATA_COPY
{ 
    ULONG Length;
    UCHAR Initialized;
    PVOID SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID EntryInProgress;
} PEB_LDR_DATA_COPY , *PPEB_LDR_DATA_COPY;


typedef ULONG (WINAPI * ZwQueryInformationProcess)( HANDLE ProcessHandle,
                                                    ULONG  ProcessInformationClass,
                                                    PVOID  ProcessInformation,
                                                    ULONG  ProcessInformationLength,
                                                    PULONG ReturnLength );

char *w2c(char *pcstr,const wchar_t *pwstr, size_t len)
{
    int nlength=wcslen(pwstr);
    //Gets converted length
    int nbytes = WideCharToMultiByte( 0, 0, pwstr, nlength, NULL,0,NULL, NULL ); 
    if(nbytes>len)   nbytes=len;
    // Through the above obtained results, convert Unicode character for the ASCII character
    WideCharToMultiByte( 0,0, pwstr, nlength,   pcstr, nbytes, NULL,   NULL );
    return pcstr ;
}

int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {
   puts("in filter.");
   if (code == EXCEPTION_ACCESS_VIOLATION) {
      puts("caught AV as expected.");
      return EXCEPTION_EXECUTE_HANDLER;
   }
   else {
      puts("didn't catch AV, unexpected.");
      return EXCEPTION_CONTINUE_SEARCH;
   };
}

int main()
{
    _PROCESS_BASIC_INFORMATION_COPY     stProcessBasicInformation   = { 0 };
    _PEB_LDR_DATA_COPY                  peb_ldr_data                = { 0 };
    _LDR_MODULE_COPY                    peb_ldr_module              = { 0 };
    PEB                                 peb                         = { 0 };
    USHORT                              loadCount                   = 0;
    //ofstream                          outputfile;

    //outputfile.open("dllNameAndTheirCount.txt", ios::app||ios::beg);

    HMODULE hModule = LoadLibrary( (const char*)"NTDLL.dll" );

    HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); /* Get current prcess handle */

    ZwQueryInformationProcess ZwQueryInformationProcessPtr = (ZwQueryInformationProcess)GetProcAddress( hModule, "ZwQueryInformationProcess");

    if(ZwQueryInformationProcessPtr){
        ZwQueryInformationProcessPtr(hProcess, 0, &stProcessBasicInformation, sizeof(stProcessBasicInformation), 0);
    }

    DWORD dwSize = 0;
    bool bStatus;
    /* Get list of loaded DLLs from PEB. */
    bStatus = ReadProcessMemory(hProcess, stProcessBasicInformation.PebBaseAddress, &peb, sizeof(peb), &dwSize);

    bStatus = ReadProcessMemory(hProcess, peb.Ldr, &peb_ldr_data, sizeof(peb_ldr_data), &dwSize);


    void *readAddr = (void*) peb_ldr_data.InLoadOrderModuleList.Flink;

     // Go through each modules one by one in their load order.
    while( ReadProcessMemory(hProcess, readAddr, &peb_ldr_module, sizeof(peb_ldr_module), &dwSize) )
    {

        __try{
                // Get the reference count of the DLL
                loadCount = (signed short)peb_ldr_module.LoadCount;
                //outputfile << "DLL Name: " << peb_ldr_module.BaseDllName.Buffer << endl;
                //outputfile << "DLL Load Count: " << peb_ldr_module.LoadCount << endl;
                wcout << "DLL Name: " << peb_ldr_module.BaseDllName.Buffer << endl;
                cout << "DLL Load Count: " << peb_ldr_module.LoadCount << endl;
                cout << endl << endl;
            }_except(filter(GetExceptionCode(), GetExceptionInformation())){
                //outputfile << "DLL Name: " << "No Name Found" << endl;
                //outputfile << "DLL Load Count: " << peb_ldr_module.LoadCount << endl;
                readAddr = (void *) peb_ldr_module.InLoadOrderModuleList.Flink;
                continue;
        }
        readAddr = (void *) peb_ldr_module.InLoadOrderModuleList.Flink;
    }

    FreeLibrary( hModule );

    return 0;
}

This is beginning to become a mess. They both seem to work but as suspected. The memory becomes offsetted in some way. Nevertheless you can get info about a process's (arbitrary Exe program) loaded modules this way but LoadCount does not show the actual ref count in Windows > 7.

这开始变得一团糟。它们似乎都有效,但令人怀疑。内存以某种方式偏移。尽管如此,您可以通过这种方式获取有关进程(任意 Exe 程序)加载模块的信息,但 LoadCount 不会在 Windows > 7 中显示实际引用计数。

Btw i checked again use the USER defined PEB_LDR_DATA structure. The system one generates inaccuracy. Some members in LDR_MODULE becomes junk. Why? I dont know. ("Det fixar b?tklubben...").

顺便说一句,我再次检查使用用户定义的 PEB_LDR_DATA 结构。系统一产生不准确。LDR_MODULE 中的一些成员变成了垃圾。为什么?我不知道。(“Det fixar b?tklubben...”)。

To bad...

坏...

回答by paxdiablo

I'm not certain you fully understand how LoadLibrary/FreeLibraryis meant to work. You call FreeLibrarywhen you're finished with it and that decrements the reference count that was incremented when you loaded it. If some other part of your process is still using it, it's probably not your concern.

我不确定你是否完全理解它LoadLibrary/FreeLibrary是如何工作的。FreeLibrary当你完成它时调用它并减少加载它时增加的引用计数。如果您流程的其他部分仍在使用它,则您可能不关心。

The reference count may tell you how many times it's been "loaded" but won't help in figuring out who loaded it.

引用计数可能会告诉您它被“加载”了多少次,但无助于确定谁加载了它。

回答by jay.lee

You can enumerate the loaded modules in a process with Module32First()/Module32Next()and then use MODULEENTRY32.GlblcntUsageto check it's reference count. I'm not sure how reliable this is though.

您可以使用Module32First()/枚举进程中加载​​的模块Module32Next(),然后使用MODULEENTRY32.GlblcntUsage它来检查它的引用计数。我不确定这有多可靠。

回答by Vijay Kumbhani

Please shown below code. Note: I have written below code in Visual Studio 2010.

请显示下面的代码。注意:我在 Visual Studio 2010 中编写了以下代码。

#include <winternl.h>                   //PROCESS_BASIC_INFORMATION


// warning C4996: 'GetVersionExW': was declared deprecated
#pragma warning (disable : 4996)
bool IsWindows8OrGreater()
{
    OSVERSIONINFO ovi = { 0 };
    ovi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );

    GetVersionEx(&ovi);
    if( (ovi.dwMajorVersion == 6 && ovi.dwMinorVersion >= 2) || ovi.dwMajorVersion > 6 )
        return true;

    return false;
} //IsWindows8OrGreater
#pragma warning (default : 4996)



bool ReadMem( void* addr, void* buf, int size )
{
    BOOL b = ReadProcessMemory( GetCurrentProcess(), addr, buf, size, nullptr );
    return b != FALSE;
}

#ifdef _WIN64
    #define BITNESS 1
#else
    #define BITNESS 0
#endif

typedef NTSTATUS (NTAPI *pfuncNtQueryInformationProcess)(HANDLE,PROCESSINFOCLASS,PVOID,ULONG,PULONG);

//
//  Queries for .dll module load count, returns 0 if fails.
//
int GetModuleLoadCount( HMODULE hDll )
{
    // Not supported by earlier versions of windows.
    if( !IsWindows8OrGreater() )
        return 0;

    PROCESS_BASIC_INFORMATION pbi = { 0 };

    HMODULE hNtDll = LoadLibraryA("ntdll.dll");
    if( !hNtDll )
        return 0;

    pfuncNtQueryInformationProcess pNtQueryInformationProcess = (pfuncNtQueryInformationProcess)GetProcAddress( hNtDll, "NtQueryInformationProcess");
    bool b = pNtQueryInformationProcess != nullptr;
    if( b ) b = NT_SUCCESS(pNtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof( pbi ), nullptr ));
    FreeLibrary(hNtDll);

    if( !b )
        return 0;

    char* LdrDataOffset = (char*)(pbi.PebBaseAddress) + offsetof(PEB,Ldr);
    char* addr;
    PEB_LDR_DATA LdrData;

    if( !ReadMem( LdrDataOffset, &addr, sizeof( void* ) ) || !ReadMem( addr, &LdrData, sizeof( LdrData ) ) ) 
        return 0;

    LIST_ENTRY* head = LdrData.InMemoryOrderModuleList.Flink;
    LIST_ENTRY* next = head;

    do {
        LDR_DATA_TABLE_ENTRY LdrEntry;
        LDR_DATA_TABLE_ENTRY* pLdrEntry = CONTAINING_RECORD( head, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks );

        if( !ReadMem( pLdrEntry , &LdrEntry, sizeof(LdrEntry) ) )
            return 0;

        if( LdrEntry.DllBase == (void*)hDll )
        {
            //  
            //  http://www.geoffchappell.com/studies/windows/win32/ntdll/structs/ldr_data_table_entry.htm
            //
            int offDdagNode = (0x14 - BITNESS) * sizeof(void*);   // See offset on LDR_DDAG_NODE *DdagNode;

            ULONG count = 0;
            char* addrDdagNode = ((char*)pLdrEntry) + offDdagNode;

            //
            //  http://www.geoffchappell.com/studies/windows/win32/ntdll/structs/ldr_ddag_node.htm
            //  See offset on ULONG LoadCount;
            //
            if( !ReadMem(addrDdagNode, &addr, sizeof(void*) ) || !ReadMem( addr + 3 * sizeof(void*), &count, sizeof(count) ) )
                return 0;

            return (int)count;
        } //if

        head = LdrEntry.InMemoryOrderLinks.Flink;
    }while( head != next );

    return 0;
} //GetModuleLoadCount

For more info go to below url

有关更多信息,请转到以下网址

回答by TarmoPikaro

Tested on Windows 8.1. Will not guarantee that this will work on newer windows (e.g. 10, however - according to documentation should be working)

在 Windows 8.1 上测试。不能保证这将适用于较新的窗口(例如 10,但是 - 根据文档应该可以工作)

// Someone reserved us, let's force us to shutdown.
while( GetModuleLoadCount( dll ) > 1 )
    FreeLibrary(dll);

FreeLibraryAndExitThread(dll, 0);

Usage for injected .dll's:

注入 .dll 的用法:

##代码##

回答by Ana Betts

This information isn't available via a public API afaik. What's your scenario? Running AppVerifierwill catch any mistakes you've made with module (or any other) handles.

此信息无法通过公共 API afaik 获得。你的场景是什么?运行AppVerifier将捕获您使用模块(或任何其他)句柄所犯的任何错误。