使用DnsGetCacheDataTable时出现内存泄漏问题

4
以下代码显示DNS客户端中缓存的域名。当它执行到int stat = DnsGetCacheDataTable(pEntry);这一行时,能否有人帮我找出内存泄漏的原因?
PS:编译代码时请使用DNSAPI.lib。
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <WinDNS.h>
#include <stdarg.h>
typedef struct _DNS_CACHE_ENTRY {
    struct _DNS_CACHE_ENTRY* pNext; // Pointer to next entry
    PWSTR pszName; // DNS Record Name
    unsigned short wType; // DNS Record Type
    unsigned short wDataLength; // Not referenced
    unsigned long dwFlags; // DNS Record FlagsB
} DNSCACHEENTRY, *PDNSCACHEENTRY;

typedef int(WINAPI *DNS_GET_CACHE_DATA_TABLE)(PDNSCACHEENTRY);
void UpdateDNS(void)
{

    PDNSCACHEENTRY pEntry = (PDNSCACHEENTRY) malloc(sizeof(DNSCACHEENTRY));
    // Loading DLL
    HINSTANCE hLib = LoadLibrary(TEXT("DNSAPI.dll"));
    // Get function address
    DNS_GET_CACHE_DATA_TABLE DnsGetCacheDataTable = (DNS_GET_CACHE_DATA_TABLE) GetProcAddress(hLib, "DnsGetCacheDataTable");
    int stat = DnsGetCacheDataTable(pEntry);
    printf("stat = %d\n", stat);
    pEntry = pEntry->pNext;
    while (pEntry) {
        wprintf(L"%s : %d \n", (pEntry->pszName), (pEntry->wType));
        pEntry = pEntry->pNext;
    }
    free(pEntry);
}

int main(int argc, char **argv) {

    while (TRUE)
    {
        Sleep(100);
        UpdateDNS();
    }   
    return 0;
}
2个回答

4

这段代码存在几个问题。

首先,在开头调用 LoadLibrary,却没有在末尾调用 FreeLibrary。虽然不算是内存泄漏,但这不是一个很明智的做法...

其次,在循环前直接跳到 pEntry->pNext,导致跳过了一个条目。你的内存泄漏出现在同一行代码中,当你将 malloc 返回的值赋值时:

PDNSCACHEENTRY pEntry = (PDNSCACHEENTRY) malloc(sizeof(DNSCACHEENTRY));
/* ... */
pEntry = pEntry->pNext;

你甚至不需要使用malloc,但更糟的是,你只应该释放由malloc返回的值,这使得这个错误:
free(pEntry);

事实上,对于这个问题,您不仅不需要使用 malloc(或 free),而实际上需要使用的是DnsRecordListFree

以下是您的代码应该如何编写:

PDNS_RECORD entry;
HINSTANCE hLib = LoadLibrary(TEXT("DNSAPI.dll"));
DNS_GET_CACHE_DATA_TABLE DnsGetCacheDataTable = (DNS_GET_CACHE_DATA_TABLE) GetProcAddress(hLib, "DnsGetCacheDataTable");
int stat = DnsGetCacheDataTable(&entry);
printf("stat = %d\n", stat);
for (DNSCACHEENTRY *pTemp = &entry; pTemp; pTemp = pTemp->pNext) {
    wprintf(L"%s : %d \n", pTemp->pszName, pTemp->wType);
}
DnsRecordListFree(entry, DnsFreeRecordList);

int stat = DnsGetCacheDataTable(pEntry); 出现错误:"从“DNSCACHEENTRY”到“PVOID”的转换函数不适用"。而 DnsRecordListFree(entry, DnsFreeRecordList); 出现错误:"从“DNSCACHEENTRY”到“PDNSCACHEENTRY”的转换函数不适用"。 - mazkopolo
对于最后一行(DnsRecordListFree(entry, DnsFreeRecordList);),会显示“不存在从“DNSCACHEENTRY”到“PVOID”的适当转换函数”。 - mazkopolo
看起来 "DnsGetCacheDataTable" 只接受 ->PDNSCACHEENTRY 而不是 DNSCACHEENTRY。 - mazkopolo
@mazkopolo PDNSCACHEENTRYDNSCACHEENTRY *... 你能告诉我 * 的意思,以及为什么要添加 P 来表示它吗? - autistic
我的错...试试那个 ;) - autistic
显示剩余3条评论

2

因为代码一开始看起来很不错,所以尝试使用Deleaker进行调试:leaks

然后开始调试...结果当然是!你释放的不是原始的pEntry,而是修改过的。

这里是已修正的代码:

void UpdateDNS(void)
{

    PDNSCACHEENTRY pEntry = (PDNSCACHEENTRY) malloc(sizeof(DNSCACHEENTRY) + 0x10000);
    PDNSCACHEENTRY pFirstEntry = pEntry;
    // Loading DLL
    HINSTANCE hLib = LoadLibrary(TEXT("DNSAPI.dll"));
    // Get function address
    DNS_GET_CACHE_DATA_TABLE DnsGetCacheDataTable = (DNS_GET_CACHE_DATA_TABLE) GetProcAddress(hLib, "DnsGetCacheDataTable");
    int stat = DnsGetCacheDataTable(pEntry);
    printf("stat = %d\n", stat);
    pEntry = pEntry->pNext;
    while (pEntry) {
        wprintf(L"%s : %d \n", (pEntry->pszName), (pEntry->wType));
        pEntry = pEntry->pNext;
    }
    free(pFirstEntry);
}

更新:实际上,您不需要分配任何内存,因为DnsGetCacheDataTable已经为其分配了内存。尝试使用DnsRecordListFree释放内存,但似乎无效。仍然存在泄漏:

仍有泄漏

最终,我得到了没有泄漏的代码:

typedef int(WINAPI *DNS_GET_CACHE_DATA_TABLE)(PDNSCACHEENTRY*);

typedef void (WINAPI *P_DnsApiFree)(PVOID pData);

void UpdateDNS(void)
{
    PDNSCACHEENTRY pEntry = NULL;
    // Loading DLL
    HINSTANCE hLib = LoadLibrary(TEXT("DNSAPI.dll"));
    // Get function address
    DNS_GET_CACHE_DATA_TABLE DnsGetCacheDataTable = (DNS_GET_CACHE_DATA_TABLE)GetProcAddress(hLib, "DnsGetCacheDataTable");
    P_DnsApiFree pDnsApiFree = (P_DnsApiFree)GetProcAddress(hLib, "DnsApiFree");
    int stat = DnsGetCacheDataTable(&pEntry);
    PVOID pFirstEntry = pEntry;
    printf("stat = %d\n", stat);
    pEntry = pEntry->pNext;
    while (pEntry) {
        wprintf(L"%s : %d \n", (pEntry->pszName), (pEntry->wType));
        pDnsApiFree(pEntry->pszName);
        PVOID p = pEntry;
        pEntry = pEntry->pNext;
        pDnsApiFree(p);
    }
}

@mazkopolo 你说得对,我刚刚更新了答案。 - Artem Razin
你需要释放pFirstEntry。此外,你可以使用DnsFree(ptr, DnsFreeFlat)代替pDnsApiFree(ptr); - user1438233

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接