Windows 防火墙 C++ API - 如何正确清理 COM 资源?
Windows Firewall C++ API - How to correctly clean up the COM resources?
我正在尝试使用基于 COM 的 Windows 防火墙 API 来遍历现有的防火墙规则,并找出其中是否存在特定规则。
目前我很难理解这个例子的 Cleanup
部分发生了什么 (https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ics/c-enumerating-firewall-rules):
/********************************************************************++
Copyright (C) Microsoft. All Rights Reserved.
Abstract:
This C++ file includes sample code for enumerating Windows Firewall
rules using the Microsoft Windows Firewall APIs.
********************************************************************/
#include <windows.h>
#include <stdio.h>
#include <comutil.h>
#include <atlcomcli.h>
#include <netfw.h>
#pragma comment( lib, "ole32.lib" )
#pragma comment( lib, "oleaut32.lib" )
#define NET_FW_IP_PROTOCOL_TCP_NAME L"TCP"
#define NET_FW_IP_PROTOCOL_UDP_NAME L"UDP"
#define NET_FW_RULE_DIR_IN_NAME L"In"
#define NET_FW_RULE_DIR_OUT_NAME L"Out"
#define NET_FW_RULE_ACTION_BLOCK_NAME L"Block"
#define NET_FW_RULE_ACTION_ALLOW_NAME L"Allow"
#define NET_FW_RULE_ENABLE_IN_NAME L"TRUE"
#define NET_FW_RULE_DISABLE_IN_NAME L"FALSE"
// Forward declarations
void DumpFWRulesInCollection(INetFwRule* FwRule);
HRESULT WFCOMInitialize(INetFwPolicy2** ppNetFwPolicy2);
int __cdecl main()
{
HRESULT hrComInit = S_OK;
HRESULT hr = S_OK;
ULONG cFetched = 0;
CComVariant var;
IUnknown *pEnumerator;
IEnumVARIANT* pVariant = NULL;
INetFwPolicy2 *pNetFwPolicy2 = NULL;
INetFwRules *pFwRules = NULL;
INetFwRule *pFwRule = NULL;
long fwRuleCount;
// Initialize COM.
hrComInit = CoInitializeEx(
0,
COINIT_APARTMENTTHREADED
);
// Ignore RPC_E_CHANGED_MODE; this just means that COM has already been
// initialized with a different mode. Since we don't care what the mode is,
// we'll just use the existing mode.
if (hrComInit != RPC_E_CHANGED_MODE)
{
if (FAILED(hrComInit))
{
wprintf(L"CoInitializeEx failed: 0x%08lx\n", hrComInit);
goto Cleanup;
}
}
// Retrieve INetFwPolicy2
hr = WFCOMInitialize(&pNetFwPolicy2);
if (FAILED(hr))
{
goto Cleanup;
}
// Retrieve INetFwRules
hr = pNetFwPolicy2->get_Rules(&pFwRules);
if (FAILED(hr))
{
wprintf(L"get_Rules failed: 0x%08lx\n", hr);
goto Cleanup;
}
// Obtain the number of Firewall rules
hr = pFwRules->get_Count(&fwRuleCount);
if (FAILED(hr))
{
wprintf(L"get_Count failed: 0x%08lx\n", hr);
goto Cleanup;
}
wprintf(L"The number of rules in the Windows Firewall are %d\n", fwRuleCount);
// Iterate through all of the rules in pFwRules
pFwRules->get__NewEnum(&pEnumerator);
if(pEnumerator)
{
hr = pEnumerator->QueryInterface(__uuidof(IEnumVARIANT), (void **) &pVariant);
}
while(SUCCEEDED(hr) && hr != S_FALSE)
{
var.Clear();
hr = pVariant->Next(1, &var, &cFetched);
if (S_FALSE != hr)
{
if (SUCCEEDED(hr))
{
hr = var.ChangeType(VT_DISPATCH);
}
if (SUCCEEDED(hr))
{
hr = (V_DISPATCH(&var))->QueryInterface(__uuidof(INetFwRule), reinterpret_cast<void**>(&pFwRule));
}
if (SUCCEEDED(hr))
{
// Output the properties of this rule
DumpFWRulesInCollection(pFwRule);
}
}
}
Cleanup:
// Release pFwRule
if (pFwRule != NULL)
{
pFwRule->Release();
}
// Release INetFwPolicy2
if (pNetFwPolicy2 != NULL)
{
pNetFwPolicy2->Release();
}
// Uninitialize COM.
if (SUCCEEDED(hrComInit))
{
CoUninitialize();
}
return 0;
}
// Output properties of a Firewall rule
void DumpFWRulesInCollection(INetFwRule* FwRule)
{
variant_t InterfaceArray;
variant_t InterfaceString;
VARIANT_BOOL bEnabled;
BSTR bstrVal;
long lVal = 0;
long lProfileBitmask = 0;
NET_FW_RULE_DIRECTION fwDirection;
NET_FW_ACTION fwAction;
struct ProfileMapElement
{
NET_FW_PROFILE_TYPE2 Id;
LPCWSTR Name;
};
ProfileMapElement ProfileMap[3];
ProfileMap[0].Id = NET_FW_PROFILE2_DOMAIN;
ProfileMap[0].Name = L"Domain";
ProfileMap[1].Id = NET_FW_PROFILE2_PRIVATE;
ProfileMap[1].Name = L"Private";
ProfileMap[2].Id = NET_FW_PROFILE2_PUBLIC;
ProfileMap[2].Name = L"Public";
wprintf(L"---------------------------------------------\n");
if (SUCCEEDED(FwRule->get_Name(&bstrVal)))
{
wprintf(L"Name: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_Description(&bstrVal)))
{
wprintf(L"Description: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_ApplicationName(&bstrVal)))
{
wprintf(L"Application Name: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_ServiceName(&bstrVal)))
{
wprintf(L"Service Name: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_Protocol(&lVal)))
{
switch(lVal)
{
case NET_FW_IP_PROTOCOL_TCP:
wprintf(L"IP Protocol: %s\n", NET_FW_IP_PROTOCOL_TCP_NAME);
break;
case NET_FW_IP_PROTOCOL_UDP:
wprintf(L"IP Protocol: %s\n", NET_FW_IP_PROTOCOL_UDP_NAME);
break;
default:
break;
}
if(lVal != NET_FW_IP_VERSION_V4 && lVal != NET_FW_IP_VERSION_V6)
{
if (SUCCEEDED(FwRule->get_LocalPorts(&bstrVal)))
{
wprintf(L"Local Ports: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_RemotePorts(&bstrVal)))
{
wprintf(L"Remote Ports: %s\n", bstrVal);
}
}
else
{
if (SUCCEEDED(FwRule->get_IcmpTypesAndCodes(&bstrVal)))
{
wprintf(L"ICMP TypeCode: %s\n", bstrVal);
}
}
}
if (SUCCEEDED(FwRule->get_LocalAddresses(&bstrVal)))
{
wprintf(L"LocalAddresses: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_RemoteAddresses(&bstrVal)))
{
wprintf(L"RemoteAddresses: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_Profiles(&lProfileBitmask)))
{
// The returned bitmask can have more than 1 bit set if multiple profiles
// are active or current at the same time
for (int i=0; i<3; i++)
{
if ( lProfileBitmask & ProfileMap[i].Id )
{
wprintf(L"Profile: %s\n", ProfileMap[i].Name);
}
}
}
if (SUCCEEDED(FwRule->get_Direction(&fwDirection)))
{
switch(fwDirection)
{
case NET_FW_RULE_DIR_IN:
wprintf(L"Direction: %s\n", NET_FW_RULE_DIR_IN_NAME);
break;
case NET_FW_RULE_DIR_OUT:
wprintf(L"Direction: %s\n", NET_FW_RULE_DIR_OUT_NAME);
break;
default:
break;
}
}
if (SUCCEEDED(FwRule->get_Action(&fwAction)))
{
switch(fwAction)
{
case NET_FW_ACTION_BLOCK:
wprintf(L"Action: %s\n", NET_FW_RULE_ACTION_BLOCK_NAME);
break;
case NET_FW_ACTION_ALLOW:
wprintf(L"Action: %s\n", NET_FW_RULE_ACTION_ALLOW_NAME);
break;
default:
break;
}
}
if (SUCCEEDED(FwRule->get_Interfaces(&InterfaceArray)))
{
if(InterfaceArray.vt != VT_EMPTY)
{
SAFEARRAY *pSa = NULL;
pSa = InterfaceArray.parray;
for(long index= pSa->rgsabound->lLbound; index < (long)pSa->rgsabound->cElements; index++)
{
SafeArrayGetElement(pSa, &index, &InterfaceString);
wprintf(L"Interfaces: %s\n", (BSTR)InterfaceString.bstrVal);
}
}
}
if (SUCCEEDED(FwRule->get_InterfaceTypes(&bstrVal)))
{
wprintf(L"Interface Types: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_Enabled(&bEnabled)))
{
if (bEnabled)
{
wprintf(L"Enabled: %s\n", NET_FW_RULE_ENABLE_IN_NAME);
}
else
{
wprintf(L"Enabled: %s\n", NET_FW_RULE_DISABLE_IN_NAME);
}
}
if (SUCCEEDED(FwRule->get_Grouping(&bstrVal)))
{
wprintf(L"Grouping: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_EdgeTraversal(&bEnabled)))
{
if (bEnabled)
{
wprintf(L"Edge Traversal: %s\n", NET_FW_RULE_ENABLE_IN_NAME);
}
else
{
wprintf(L"Edge Traversal: %s\n", NET_FW_RULE_DISABLE_IN_NAME);
}
}
}
// Instantiate INetFwPolicy2
HRESULT WFCOMInitialize(INetFwPolicy2** ppNetFwPolicy2)
{
HRESULT hr = S_OK;
hr = CoCreateInstance(
__uuidof(NetFwPolicy2),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(INetFwPolicy2),
(void**)ppNetFwPolicy2);
if (FAILED(hr))
{
wprintf(L"CoCreateInstance for INetFwPolicy2 failed: 0x%08lx\n", hr);
goto Cleanup;
}
Cleanup:
return hr;
}
尤其是,这些行让我感到困惑:
// Release pFwRule
if (pFwRule != NULL)
{
pFwRule->Release();
}
pFwRule
指针在每次迭代时都会被覆盖,所以这里我们明确地 Release
仅使用 last 规则,该规则是通过 QueryInterface
在上面的 while
循环中。
Release
从成功调用 QueryInterface
获得的指针是合乎逻辑的,因为 QueryInterface
在返回之前调用 AddRef
(这在文档中有明确说明) .
但我无法理解的是:
为什么我们不在循环中查询下一个规则之前 Release
所有先前遍历的规则?它们是否已在某处隐式释放? QueryInterface
是否调用 Release
undercover 以防传递给它的非空指针?
我们为什么不在 pFwRules
上调用 Release
? INetFwPolicy2::get_Rules
函数不是给我们一个指向 COM 对象的新指针吗,它在返回给我们之前被 AddRef
编辑过(因此必须由调用者在结束)?
关于从 get__NewEnum
获得的 pEnumerator
指针的相同问题:我们为什么不 Release
这个也一样?
该代码确实在泄漏 COM 内存。
对接口的 AddRef()
方法的每次调用都必须有对其 Release()
方法的匹配调用。任何输出接口指针的函数调用都必须在退出前对其调用 AddRef()
,然后调用者必须随后对其调用 Release()
。
一般规则是,对于任何向调用者分配和 returns 内存的函数,调用者必须在使用完后释放它。
所以,回答你的问题:
是的,此代码中缺少对 Release()
的调用,因此存在 COM 接口泄漏 - 具体而言:pFwRules
、pEnumerator
和 pFwRule
没有被正确 Release()
。
DumpFWRulesInCollection()
也在泄漏 COM 内存。它不会释放 FwRule
方法输出的任何 BSTR
字符串。
而且,当它在循环中调用 SafeArrayGetElement()
时,它不会在每次迭代时清除 InterfaceString
。
不,QueryInterface()
不会隐式 Release()
一个非空指针。正如SafeArrayGetElement()
不清除正在写入的元素。
研究示例代码时感到困惑是一种合理的反应。它确实会泄漏资源。
Why don't we Release
all the previously traversed Rules before querying a next one in the loop? Have they been released implicitly somewhere? Does QueryInterface
call Release
undercover in case a non-null pointer is passed to it?
没有。 QueryInterface
无条件覆盖其 ppvObject
参数指向的值,如果 COM 对象未实现所请求的接口,则使用 NULL 指针,或者使用指向所请求接口的指针。不调用 Release
是资源泄漏。
Why don't we call Release
on pFwRules
? Doesn't the INetFwPolicy2::get_Rules
function give us a new pointer to a COM object, which is AddRef
'ed before being returned to us (and thus must be Released
by the caller in the end)?
再次更正。 get_Rules
returns 调用者负责的资源。不在返回的接口上调用 Release
是资源泄漏。
The same question about the pEnumerator
pointer obtained from get__NewEnum
: why don't we Release
this one as well?
相同的规则也适用于此:调用者负责清理它收到的迭代器。这也是资源泄漏。
关于 MSDN 示例的特别说明:虽然它们被标记为 "C++",但大多数 COM 代码示例实际上是用 C 编写的。与 C++ 不同,C 不在自动资源管理方面没有什么可以提供的。
如果您使用的是 C++,则可以利用自动资源管理,并使用其中一种提供的智能指针类型(例如 ATL 的 CComPtr, or Visual C++' _com_ptr_t)。
我正在尝试使用基于 COM 的 Windows 防火墙 API 来遍历现有的防火墙规则,并找出其中是否存在特定规则。
目前我很难理解这个例子的 Cleanup
部分发生了什么 (https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ics/c-enumerating-firewall-rules):
/********************************************************************++
Copyright (C) Microsoft. All Rights Reserved.
Abstract:
This C++ file includes sample code for enumerating Windows Firewall
rules using the Microsoft Windows Firewall APIs.
********************************************************************/
#include <windows.h>
#include <stdio.h>
#include <comutil.h>
#include <atlcomcli.h>
#include <netfw.h>
#pragma comment( lib, "ole32.lib" )
#pragma comment( lib, "oleaut32.lib" )
#define NET_FW_IP_PROTOCOL_TCP_NAME L"TCP"
#define NET_FW_IP_PROTOCOL_UDP_NAME L"UDP"
#define NET_FW_RULE_DIR_IN_NAME L"In"
#define NET_FW_RULE_DIR_OUT_NAME L"Out"
#define NET_FW_RULE_ACTION_BLOCK_NAME L"Block"
#define NET_FW_RULE_ACTION_ALLOW_NAME L"Allow"
#define NET_FW_RULE_ENABLE_IN_NAME L"TRUE"
#define NET_FW_RULE_DISABLE_IN_NAME L"FALSE"
// Forward declarations
void DumpFWRulesInCollection(INetFwRule* FwRule);
HRESULT WFCOMInitialize(INetFwPolicy2** ppNetFwPolicy2);
int __cdecl main()
{
HRESULT hrComInit = S_OK;
HRESULT hr = S_OK;
ULONG cFetched = 0;
CComVariant var;
IUnknown *pEnumerator;
IEnumVARIANT* pVariant = NULL;
INetFwPolicy2 *pNetFwPolicy2 = NULL;
INetFwRules *pFwRules = NULL;
INetFwRule *pFwRule = NULL;
long fwRuleCount;
// Initialize COM.
hrComInit = CoInitializeEx(
0,
COINIT_APARTMENTTHREADED
);
// Ignore RPC_E_CHANGED_MODE; this just means that COM has already been
// initialized with a different mode. Since we don't care what the mode is,
// we'll just use the existing mode.
if (hrComInit != RPC_E_CHANGED_MODE)
{
if (FAILED(hrComInit))
{
wprintf(L"CoInitializeEx failed: 0x%08lx\n", hrComInit);
goto Cleanup;
}
}
// Retrieve INetFwPolicy2
hr = WFCOMInitialize(&pNetFwPolicy2);
if (FAILED(hr))
{
goto Cleanup;
}
// Retrieve INetFwRules
hr = pNetFwPolicy2->get_Rules(&pFwRules);
if (FAILED(hr))
{
wprintf(L"get_Rules failed: 0x%08lx\n", hr);
goto Cleanup;
}
// Obtain the number of Firewall rules
hr = pFwRules->get_Count(&fwRuleCount);
if (FAILED(hr))
{
wprintf(L"get_Count failed: 0x%08lx\n", hr);
goto Cleanup;
}
wprintf(L"The number of rules in the Windows Firewall are %d\n", fwRuleCount);
// Iterate through all of the rules in pFwRules
pFwRules->get__NewEnum(&pEnumerator);
if(pEnumerator)
{
hr = pEnumerator->QueryInterface(__uuidof(IEnumVARIANT), (void **) &pVariant);
}
while(SUCCEEDED(hr) && hr != S_FALSE)
{
var.Clear();
hr = pVariant->Next(1, &var, &cFetched);
if (S_FALSE != hr)
{
if (SUCCEEDED(hr))
{
hr = var.ChangeType(VT_DISPATCH);
}
if (SUCCEEDED(hr))
{
hr = (V_DISPATCH(&var))->QueryInterface(__uuidof(INetFwRule), reinterpret_cast<void**>(&pFwRule));
}
if (SUCCEEDED(hr))
{
// Output the properties of this rule
DumpFWRulesInCollection(pFwRule);
}
}
}
Cleanup:
// Release pFwRule
if (pFwRule != NULL)
{
pFwRule->Release();
}
// Release INetFwPolicy2
if (pNetFwPolicy2 != NULL)
{
pNetFwPolicy2->Release();
}
// Uninitialize COM.
if (SUCCEEDED(hrComInit))
{
CoUninitialize();
}
return 0;
}
// Output properties of a Firewall rule
void DumpFWRulesInCollection(INetFwRule* FwRule)
{
variant_t InterfaceArray;
variant_t InterfaceString;
VARIANT_BOOL bEnabled;
BSTR bstrVal;
long lVal = 0;
long lProfileBitmask = 0;
NET_FW_RULE_DIRECTION fwDirection;
NET_FW_ACTION fwAction;
struct ProfileMapElement
{
NET_FW_PROFILE_TYPE2 Id;
LPCWSTR Name;
};
ProfileMapElement ProfileMap[3];
ProfileMap[0].Id = NET_FW_PROFILE2_DOMAIN;
ProfileMap[0].Name = L"Domain";
ProfileMap[1].Id = NET_FW_PROFILE2_PRIVATE;
ProfileMap[1].Name = L"Private";
ProfileMap[2].Id = NET_FW_PROFILE2_PUBLIC;
ProfileMap[2].Name = L"Public";
wprintf(L"---------------------------------------------\n");
if (SUCCEEDED(FwRule->get_Name(&bstrVal)))
{
wprintf(L"Name: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_Description(&bstrVal)))
{
wprintf(L"Description: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_ApplicationName(&bstrVal)))
{
wprintf(L"Application Name: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_ServiceName(&bstrVal)))
{
wprintf(L"Service Name: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_Protocol(&lVal)))
{
switch(lVal)
{
case NET_FW_IP_PROTOCOL_TCP:
wprintf(L"IP Protocol: %s\n", NET_FW_IP_PROTOCOL_TCP_NAME);
break;
case NET_FW_IP_PROTOCOL_UDP:
wprintf(L"IP Protocol: %s\n", NET_FW_IP_PROTOCOL_UDP_NAME);
break;
default:
break;
}
if(lVal != NET_FW_IP_VERSION_V4 && lVal != NET_FW_IP_VERSION_V6)
{
if (SUCCEEDED(FwRule->get_LocalPorts(&bstrVal)))
{
wprintf(L"Local Ports: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_RemotePorts(&bstrVal)))
{
wprintf(L"Remote Ports: %s\n", bstrVal);
}
}
else
{
if (SUCCEEDED(FwRule->get_IcmpTypesAndCodes(&bstrVal)))
{
wprintf(L"ICMP TypeCode: %s\n", bstrVal);
}
}
}
if (SUCCEEDED(FwRule->get_LocalAddresses(&bstrVal)))
{
wprintf(L"LocalAddresses: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_RemoteAddresses(&bstrVal)))
{
wprintf(L"RemoteAddresses: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_Profiles(&lProfileBitmask)))
{
// The returned bitmask can have more than 1 bit set if multiple profiles
// are active or current at the same time
for (int i=0; i<3; i++)
{
if ( lProfileBitmask & ProfileMap[i].Id )
{
wprintf(L"Profile: %s\n", ProfileMap[i].Name);
}
}
}
if (SUCCEEDED(FwRule->get_Direction(&fwDirection)))
{
switch(fwDirection)
{
case NET_FW_RULE_DIR_IN:
wprintf(L"Direction: %s\n", NET_FW_RULE_DIR_IN_NAME);
break;
case NET_FW_RULE_DIR_OUT:
wprintf(L"Direction: %s\n", NET_FW_RULE_DIR_OUT_NAME);
break;
default:
break;
}
}
if (SUCCEEDED(FwRule->get_Action(&fwAction)))
{
switch(fwAction)
{
case NET_FW_ACTION_BLOCK:
wprintf(L"Action: %s\n", NET_FW_RULE_ACTION_BLOCK_NAME);
break;
case NET_FW_ACTION_ALLOW:
wprintf(L"Action: %s\n", NET_FW_RULE_ACTION_ALLOW_NAME);
break;
default:
break;
}
}
if (SUCCEEDED(FwRule->get_Interfaces(&InterfaceArray)))
{
if(InterfaceArray.vt != VT_EMPTY)
{
SAFEARRAY *pSa = NULL;
pSa = InterfaceArray.parray;
for(long index= pSa->rgsabound->lLbound; index < (long)pSa->rgsabound->cElements; index++)
{
SafeArrayGetElement(pSa, &index, &InterfaceString);
wprintf(L"Interfaces: %s\n", (BSTR)InterfaceString.bstrVal);
}
}
}
if (SUCCEEDED(FwRule->get_InterfaceTypes(&bstrVal)))
{
wprintf(L"Interface Types: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_Enabled(&bEnabled)))
{
if (bEnabled)
{
wprintf(L"Enabled: %s\n", NET_FW_RULE_ENABLE_IN_NAME);
}
else
{
wprintf(L"Enabled: %s\n", NET_FW_RULE_DISABLE_IN_NAME);
}
}
if (SUCCEEDED(FwRule->get_Grouping(&bstrVal)))
{
wprintf(L"Grouping: %s\n", bstrVal);
}
if (SUCCEEDED(FwRule->get_EdgeTraversal(&bEnabled)))
{
if (bEnabled)
{
wprintf(L"Edge Traversal: %s\n", NET_FW_RULE_ENABLE_IN_NAME);
}
else
{
wprintf(L"Edge Traversal: %s\n", NET_FW_RULE_DISABLE_IN_NAME);
}
}
}
// Instantiate INetFwPolicy2
HRESULT WFCOMInitialize(INetFwPolicy2** ppNetFwPolicy2)
{
HRESULT hr = S_OK;
hr = CoCreateInstance(
__uuidof(NetFwPolicy2),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(INetFwPolicy2),
(void**)ppNetFwPolicy2);
if (FAILED(hr))
{
wprintf(L"CoCreateInstance for INetFwPolicy2 failed: 0x%08lx\n", hr);
goto Cleanup;
}
Cleanup:
return hr;
}
尤其是,这些行让我感到困惑:
// Release pFwRule
if (pFwRule != NULL)
{
pFwRule->Release();
}
pFwRule
指针在每次迭代时都会被覆盖,所以这里我们明确地 Release
仅使用 last 规则,该规则是通过 QueryInterface
在上面的 while
循环中。
Release
从成功调用 QueryInterface
获得的指针是合乎逻辑的,因为 QueryInterface
在返回之前调用 AddRef
(这在文档中有明确说明) .
但我无法理解的是:
为什么我们不在循环中查询下一个规则之前
Release
所有先前遍历的规则?它们是否已在某处隐式释放?QueryInterface
是否调用Release
undercover 以防传递给它的非空指针?我们为什么不在
pFwRules
上调用Release
?INetFwPolicy2::get_Rules
函数不是给我们一个指向 COM 对象的新指针吗,它在返回给我们之前被AddRef
编辑过(因此必须由调用者在结束)?关于从
get__NewEnum
获得的pEnumerator
指针的相同问题:我们为什么不Release
这个也一样?
该代码确实在泄漏 COM 内存。
对接口的 AddRef()
方法的每次调用都必须有对其 Release()
方法的匹配调用。任何输出接口指针的函数调用都必须在退出前对其调用 AddRef()
,然后调用者必须随后对其调用 Release()
。
一般规则是,对于任何向调用者分配和 returns 内存的函数,调用者必须在使用完后释放它。
所以,回答你的问题:
是的,此代码中缺少对
Release()
的调用,因此存在 COM 接口泄漏 - 具体而言:pFwRules
、pEnumerator
和pFwRule
没有被正确Release()
。DumpFWRulesInCollection()
也在泄漏 COM 内存。它不会释放FwRule
方法输出的任何BSTR
字符串。 而且,当它在循环中调用SafeArrayGetElement()
时,它不会在每次迭代时清除InterfaceString
。不,
QueryInterface()
不会隐式Release()
一个非空指针。正如SafeArrayGetElement()
不清除正在写入的元素。
研究示例代码时感到困惑是一种合理的反应。它确实会泄漏资源。
Why don't we
Release
all the previously traversed Rules before querying a next one in the loop? Have they been released implicitly somewhere? DoesQueryInterface
callRelease
undercover in case a non-null pointer is passed to it?
没有。 QueryInterface
无条件覆盖其 ppvObject
参数指向的值,如果 COM 对象未实现所请求的接口,则使用 NULL 指针,或者使用指向所请求接口的指针。不调用 Release
是资源泄漏。
Why don't we call
Release
onpFwRules
? Doesn't theINetFwPolicy2::get_Rules
function give us a new pointer to a COM object, which isAddRef
'ed before being returned to us (and thus must beReleased
by the caller in the end)?
再次更正。 get_Rules
returns 调用者负责的资源。不在返回的接口上调用 Release
是资源泄漏。
The same question about the
pEnumerator
pointer obtained fromget__NewEnum
: why don't weRelease
this one as well?
相同的规则也适用于此:调用者负责清理它收到的迭代器。这也是资源泄漏。
关于 MSDN 示例的特别说明:虽然它们被标记为 "C++",但大多数 COM 代码示例实际上是用 C 编写的。与 C++ 不同,C 不在自动资源管理方面没有什么可以提供的。
如果您使用的是 C++,则可以利用自动资源管理,并使用其中一种提供的智能指针类型(例如 ATL 的 CComPtr, or Visual C++' _com_ptr_t)。