获取.Net Runtime源码(2021.05.19写),看到"Always call CloseHandle in AsmMan::EmitManifest (#50372)",处理文件句柄泄露的代码.通过这个问题,说明即使在在C/C 开发的老司机也会在使用资源的,造成资源泄露的情况.
源码修复文件句柄泄露在SourceTree看到,代码只是将CloseHandle移到更大的作用域.修复文件句柄泄露.
避免文件句柄泄露
代码(部分代码):
m_dwMResSizeTotal = 0;
m_dwMResNum = 0;
for (i = 0; (pManRes = m_ManResLst.PEEK(i)); i )
{
BOOL fOK = TRUE;
mdToken tkImplementation = mdFileNil;
if (!pManRes->m_fNew) continue;
pManRes->m_fNew = FALSE;
WszMultiByteToWideChar(g_uCodePage, 0, pManRes->szAlias, -1, wzUniBuf, dwUniBuf);
if (pManRes->szAsmRefName)
{
tkImplementation = GetAsmRefTokByName(pManRes->szAsmRefName);
if (RidFromToken(tkImplementation) == 0)
{
report->error("Undefined AssemblyRef '%s' in MResource '%s'\n", pManRes->szAsmRefName, pManRes->szName);
fOK = FALSE;
}
}
else if (pManRes->szFileName)
{
tkImplementation = GetFileTokByName(pManRes->szFileName);
if (RidFromToken(tkImplementation) == 0)
{
report->error("Undefined File '%s' in MResource '%s'\n", pManRes->szFileName, pManRes->szName);
fOK = FALSE;
}
}
else // embedded mgd.resource, go after the file
{
HANDLE hFile = INVALID_HANDLE_VALUE;
int j;
WCHAR wzFileName[2048];
WCHAR* pwz;
pManRes->ulOffset = m_dwMResSizeTotal;
for (j = 0; (hFile == INVALID_HANDLE_VALUE) && (pwzInputFiles[j] != NULL); j )
{
wcscpy_s(wzFileName, 2048, pwzInputFiles[j]);
pwz = wcsrchr(wzFileName, DIRECTORY_SEPARATOR_CHAR_A);
#ifdef TARGET_WINDOWS
if (pwz == NULL) pwz = wcsrchr(wzFileName, ':');
#endif
if (pwz == NULL) pwz = &wzFileName[0];
else pwz ;
wcscpy_s(pwz, 2048 - (pwz - wzFileName), wzUniBuf);
hFile = WszCreateFile(wzFileName, GENERIC_READ, FILE_SHARE_READ,
0, OPEN_EXISTING, 0, 0); //打开文件
}
if (hFile == INVALID_HANDLE_VALUE)
{
report->error("Failed to open managed resource file '%s'\n", pManRes->szAlias);
fOK = FALSE;
}
else
{
if (m_dwMResNum >= MAX_MANIFEST_RESOURCES) //MAX_MANIFEST_RESOURCES 为1024
{
report->error("Too many resources (implementation limit: %d); skipping file '%s'\n", MAX_MANIFEST_RESOURCES, pManRes->szAlias);
fOK = FALSE;
}
else
{
m_dwMResSize[m_dwMResNum] = SafeGetFileSize(hFile, NULL);
if (m_dwMResSize[m_dwMResNum] == 0xFFFFFFFF)
{
report->error("Failed to get size of managed resource file '%s'\n", pManRes->szAlias);
fOK = FALSE;
}
else
{
m_dwMResSizeTotal = m_dwMResSize[m_dwMResNum] sizeof(DWORD);
m_wzMResName[m_dwMResNum] = new WCHAR[wcslen(wzFileName) 1];
wcscpy_s(m_wzMResName[m_dwMResNum], wcslen(wzFileName) 1, wzFileName);
m_fMResNew[m_dwMResNum] = TRUE;
m_dwMResNum ;
}
//CloseHandle(hFile);
//原先在这里使用CloseHandle 关闭打开的文件句柄,在读取的文件数量小于1024时,是不会出现文件句柄泄露的情况
//在打开文件数量大于的时,代码不会在else作用域执行,才会出现文件句柄泄露
//所以将CloseHandle的作用域放大,就能避免文件句柄泄露
}
CloseHandle(hFile);
}
}
if (fOK || ((Assembler*)m_pAssembler)->OnErrGo)
{
WszMultiByteToWideChar(g_uCodePage, 0, pManRes->szName, -1, wzUniBuf, dwUniBuf);
hr = m_pAsmEmitter->DefineManifestResource( // S_OK or error.
(LPCWSTR)wzUniBuf, // [IN] Name of the resource.
tkImplementation, // [IN] mdFile or mdAssemblyRef that provides the resource.
pManRes->ulOffset, // [IN] Offset to the beginning of the resource within the file.
pManRes->dwAttr, // [IN] Flags.
(mdManifestResource*)&(pManRes->tkTok)); // [OUT] Returned ManifestResource token.
if (FAILED(hr))
report->error("Failed to define manifest resource '%s': 0xX\n", pManRes->szName, hr);
}
}
具体代码在Runtime/src/coreclr/ilasm/asmman.cpp文件EmitManifest方法中.
注意事项在Windows平台,调用函数返回HANDLE/HMODULE等,在不需要的时候,一定要使用CloseHandle进行关闭,避免资源泄露.
在Linux平台,返回文件描述符的时候,在不使用的时候,记得使用close关闭.
在C语言,使用标准库,如fopen时,要用fclose进行关闭.
不管句柄还是文件描述符的,一定要成对使用.如open/close.
个人能力有限,如果您发现有什么不对,请私信我
如果您觉得对您有用的话,可以点个赞或者加个关注,欢迎大家一起进行技术交流