I noticed on the MSDN website that it used a catch all exception handler. I.e.:
try {
MSXML2::IXMLDOMDocumentPtr docPtr;
MSXML2::IXMLDOMNodePtr DOMNodePtr;
// init
TESTHR(CoInitialize(NULL));
TESTHR(docPtr.CreateInstance("Msxml2.DOMDocument.6.0"));
VARIANT vtTemp;
vtTemp.vt=VT_I2;
vtTemp.iVal = 1; //NODE_ELEMENT
// load a document
_variant_t varXml("c:\\Temp\\books.xml");
_variant_t varOut((bool)TRUE);
varOut = docPtr->load(varXml);
if ((bool)varOut == FALSE)
throw(0);
MessageBox(NULL, _bstr_t(docPtr->xml), _T("Original Document"), MB_OK);
DOMNodePtr = docPtr->createNode(vtTemp, "VIDEOS", "");
docPtr->documentElement->appendChild(DOMNodePtr);
MessageBox(NULL, _bstr_t(docPtr->xml), _T("New Document"), MB_OK);
} catch(...)
{
MessageBox(NULL, _T("Exception occurred"), _T("Error"), MB_OK);
}
Now my question is, does the function calls to the MSXML2::IXMLDOMDocumentPtr object throw any exceptions? I wasn't aware that a COM object could throw a C++ exception due to the COM boundary can interface with any language.
If it is just to catch the throw(0), then why not catch on an int? I know a little theory on COM but my knowledge is a little limited.
Related
I am trying to use the COM-based Windows Firewall API for traversing the existing Firewall rules and find out if one specific rule exists among them.
Currently I have difficulties with understanding what is going on in the Cleanup part of this example (https://learn.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;
}
In particular, these lines confuse me:
// Release pFwRule
if (pFwRule != NULL)
{
pFwRule->Release();
}
The pFwRule pointer gets overwritten on each iteration, so here we are explicitly Releaseing only the last Rule that was obtained via QueryInterface in the while loop above.
Releaseing a pointer obtained from a successfull call to QueryInterface is logical, because QueryInterface calls AddRef before returning (which is explicitly stated in the documentation).
But what I cannot understand, is:
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?
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)?
The same question about the pEnumerator pointer obtained from get__NewEnum: why don't we Release this one as well?
The code is indeed leaking COM memory.
Every call to an interface's AddRef() method must have a matching call to its Release() method. ANY function call that outputs an interface pointer must call AddRef() on it before exit, and the caller must then call Release() on it afterwards.
The general rule is, for any function that allocates and returns memory to the caller, the caller must free it when done using it.
So, to answer your questions:
Yes, there are missing calls to Release() in this code, so there are COM interfaces being leaked - specifically: pFwRules, pEnumerator, and pFwRule are not being Release()'d properly.
DumpFWRulesInCollection() is also leaking COM memory as well. It is not freeing any of the BSTR strings that are output by FwRule's methods.
And also, when it calls SafeArrayGetElement() in a loop, it is not clearing the InterfaceString on each iteration.
No, QueryInterface() does not implicitly Release() a non-null pointer. Just as SafeArrayGetElement() does not clear the element being written to.
It's a reasonable reaction to be confused when studying the sample code. It does indeed leak resources.
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?
No. QueryInterface unconditionally overwrites the value pointed to by its ppvObject argument, either with a NULL pointer, if the COM object does not implement the requested interface, or with a pointer to the requested interface. Not calling Release is a resource leak.
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)?
Correct again. get_Rules returns a resource the caller is responsible for. Not calling Release on the returned interface is a resource leak.
The same question about the pEnumerator pointer obtained from get__NewEnum: why don't we Release this one as well?
The same rules apply here as well: The caller is responsible for cleaning up the iterator it received. This, too, is a resource leak.
Special note on MSDN samples: Although they are tagged "C++", the majority of code samples for COM are actually written in C. Unlike C++, C doesn't have much to offer with respect to automatic resource management.
If you are using C++, you can take advantage of automatic resource management, and employ one of the provided smart pointer types (e.g. ATL's CComPtr, or Visual C++' _com_ptr_t).
I am using below code to manipulate visual studio.
Everything worked fine except that smart pointers did not release properly before function exit: it threw exception upon release.:exception 0xc0000005, access violation when write to 0xfeeefeee.
the running stop at the bold part below.
public:
typedef T _PtrClass;
~CComPtrBase() throw()
{
if (p)
p->Release();
}
Could any one give me some hint?
int myAutomateVS()
{
CoInitialize(NULL);
CComPtr<IRunningObjectTable> pTable=NULL;
GetRunningObjectTable(0, &pTable);
CComPtr<IEnumMoniker> pEnumMoniker=NULL;;
pTable->EnumRunning(&pEnumMoniker);
CComPtr<IMoniker> pMoniker=NULL;
ULONG nMoniker=0;
CComPtr<IUnknown> pRunningObj=NULL;
while(pEnumMoniker->Next(1, &pMoniker, &nMoniker) == S_OK)
{
CComPtr<IBindCtx> pCtx=NULL;
CreateBindCtx(0, &pCtx);
LPOLESTR pwszName;
pMoniker->GetDisplayName(pCtx, NULL, &pwszName);
pTable->GetObject(pMoniker, &pRunningObj);
if(_tcsstr(pwszName, L"VisualStudio.DTE.8") != NULL)
break;
}
CComPtr<EnvDTE::_DTE> pDTE;
pDTE = pRunningObj;
CComPtr<EnvDTE::_Solution> solution;
pDTE->get_Solution(&solution);
CComBSTR fullName;
solution->get_FullName(&fullName);
CoUninitialize();
return 0;
}
I have two win32 programs, and they communicate with one DLL via IPC. I use MSVC ++ so it makes sense that I use CreateFileMapping and MapViewOfFile. I already have some codes done, the problem is that when my second program starts and loads a DLL and starts to work with DLLs, everything is fine and IPC communication is going well. When the first program starts and loads a DLL and starts to work with DLLs and the second program starts and loads a DLL and starts to work with DLLs, second program stops communicating with DLL via IPC. The IPC communication is probably running but probably in wrong way. It obtains the size and capacity, but the content is junk. The content of the shared memory is vector of pointers! What am I doing wrong? Is there anything I need to know to get those work fine?
//The sender writer
MainWindow::~MainWindow()
{
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
}
void MainWindow::SetData(vector <SubWindow*> &iMainWin)
{
hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(vector <SubWindow*>), TEXT("MyMappedMem"));
if (hMapFile == NULL)
return;
vector <SubWindow*> *pBuf = reinterpret_cast<std::vector<SubWindow*>*> (MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(vector <SubWindow*>)));
if (pBuf == NULL)
{
CloseHandle(hMapFile);
return;
}
memcpy(pBuf, &iMainWin, sizeof(iMainWin));
return;
}
//The reciever reader
void DataWork()
{
do{
if (strcmp(pe32.szExeFile, "Win32Project.exe") == 0)
{
bProcessFound = true;
break;
}
} while (Process32Next(hProcessSnap, &pe32));
if (!bProcessFound)
{
MessageBox(NULL, TEXT("ERROR Process32Next Openning DLL"), TEXT("ERROR"), MB_OK);
return;
}
else
{
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, TEXT("MyMappedMem"));
if (hMapFile == NULL)
{
MessageBox(NULL, TEXT("ERROR OpenFileMapping DLL"), TEXT("ERROR"), MB_OK);
return;
}
vector<SubWindow*> *pBuf = reinterpret_cast<std::vector<SubWindow*>*> (MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(vector <SubWindow*>)));
if (pBuf == NULL)
{
MessageBox(NULL, TEXT("ERROR MapViewOfFile DLL"), TEXT("ERROR"), MB_OK);
CloseHandle(hMapFile);
return;
}
for (int i = 0; i < buf->size(); i++)
(*buf)[i]->value_access;
UnmapViewOfFile(pBuf);
CloseHandle(hMapFile);
}
}
Pointers in the first program are useless in the second program, and vice versa. You cannot use a pointer from program A in program B because they have different virtual memory spaces.
Attempting to share a std::vector using shared memory is almost certainly wrong. The std::vector will allocate memory from the calling process, which will not be accessible via any other process. Furthermore, pointers pointing to local addresses will not be accessible any other process.
Additionally, you would need to coordinate access to the std::vector using a mutex, which you aren't doing.
In this situation, I can't see any reason why using a text file wouldn't be sufficient.
According to this, there's no way to convert a HRESULT error code into a Win32 error code. Therefore (at least to my understanding), my use of FormatMessage in order to generate error messages (i.e.
std::wstring Exception::GetWideMessage() const
{
using std::tr1::shared_ptr;
shared_ptr<void> buff;
LPWSTR buffPtr;
DWORD bufferLength = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetErrorCode(),
0,
reinterpret_cast<LPWSTR>(&buffPtr),
0,
NULL);
buff.reset(buffPtr, LocalFreeHelper());
return std::wstring(buffPtr, bufferLength);
}
) does not work for HRESULTs.
How do I generate these kinds of system-specific error strings for HRESULTs?
This answer incorporates Raymond Chen's ideas, and correctly discerns the incoming HRESULT, and returns an error string using the correct facility to obtain the error message:
/////////////////////////////
// ComException
CString FormatMessage(HRESULT result)
{
CString strMessage;
WORD facility = HRESULT_FACILITY(result);
CComPtr<IErrorInfo> iei;
if (S_OK == GetErrorInfo(0, &iei) && iei)
{
// get the error description from the IErrorInfo
BSTR bstr = NULL;
if (SUCCEEDED(iei->GetDescription(&bstr)))
{
// append the description to our label
strMessage.Append(bstr);
// done with BSTR, do manual cleanup
SysFreeString(bstr);
}
}
else if (facility == FACILITY_ITF)
{
// interface specific - no standard mapping available
strMessage.Append(_T("FACILITY_ITF - This error is interface specific. No further information is available."));
}
else
{
// attempt to treat as a standard, system error, and ask FormatMessage to explain it
CString error;
CErrorMessage::FormatMessage(error, result); // <- This is just a wrapper for ::FormatMessage, left to reader as an exercise :)
if (!error.IsEmpty())
strMessage.Append(error);
}
return strMessage;
}
I am working with a webbrowser host on c++, I managed to sink event and I am running this void on DISPID_DOCUMENTCOMPLETE:
void DocumentComplete(LPDISPATCH pDisp, VARIANT *url)
{
READYSTATE rState;
iBrowser->get_ReadyState(&rState);
if(rState == READYSTATE_COMPLETE)
{
HRESULT hr;
IDispatch *pHtmlDoc = NULL;
IHTMLDocument2 *pDocument = NULL;
IHTMLElement *pBody = NULL;
IHTMLElement *lpParentElm = NULL;
BSTR bstrHTMLText;
hr = iBrowser->get_Document(&pHtmlDoc);
hr = pHtmlDoc->QueryInterface(IID_IHTMLDocument2, (void**)&pDocument);
if( (FAILED(hr)) || !pDocument)
{
MessageBox(NULL, "QueryInterface failed", "WebBrowser", MB_OK);
}
hr = pDocument->get_body( &pBody );
if( (!SUCCEEDED(hr)) || !pBody)
{
MessageBox(NULL, "get_body failed", "WebBrowser", MB_OK);
}
pBody->get_parentElement(&lpParentElm);
lpParentElm->get_outerHTML(&bstrHTMLText);
_bstr_t bstr_t(bstrHTMLText);
std::string sTemp(bstr_t);
MessageBox(NULL, sTemp.c_str(), "WebBrowser", MB_OK);
}
}
I don't much about c++, I built this code from watching other codes in google. Now I know I have to use ->Release, but do I have to use all these?:
pHtmlDoc->Release();
pDocument->Release();
pBody->Release();
lpParentElm->Release();
iBrowser->Release();
Because on the examples I used to build my code it was using it only for the IHTMLElement(s).
Yes, you do have to call Release() on those pointers, otherwise objects will leak. The same goes for BSTRs.
You'll be much better off if you use smart pointers for that - ATL::CComPtr/ATL::CComBSTR or _com_ptr_t/_bstr_t.
You should wrap those objects into a CComPtr or one of its variants. That will handle the release for you. It goes with the concept of RAII.
Yes you do. But not on the iBrowser, you didn't acquire that pointer inside this code.
Beware that your error checking isn't sufficient, your code will bomb when get_Document() fails. Same for get_parentElement(). And after the message box is dismissed.