You cannot pass a null pointer as an argument (a.k.a. top-level pointer), as they're [ref]
by default.
But you may pass a null pointer, in or out, for non-top-level pointers.
The IDL method definition goes like this:
HRESULT GetStuffAB([in] long countA,
[in] long countB,
[out, size_is(, *pItemsA)] long **pA,
[out, size_is(, *pItemsB)] long **pB,
[out] long *pItemsA,
[out] long *pItemsB);
The C++ implementation:
HRESULT CMyClass::GetStuffAB(long countA,
long countB,
long **pA,
long **pB,
long *pItemsA,
long *pItemsB)
{
// COM stubs will check this for you
// However, you should (must?) manually check in same apartment calls
if (!pA) return E_POINTER;
if (!pB) return E_POINTER;
if (!pItemsA) return E_POINTER;
if (!pitemsB) return E_POINTER;
*pA = nullptr;
*pB = nullptr;
*pItemsA = 0L;
*pItemsB = 0L;
if (countA < 0) return E_INVALIDARG;
if (countB < 0) return E_INVALIDARG;
// Get amount of As into *pItemsA if countA > 0
// Get amount of Bs into *pItemsB if countB > 0
if (*pItemsA < 0) return E_FAIL;
if (*pItemsB < 0) return E_FAIL;
if (*pItemsA > 0)
{
*pA = CoTaskMemAlloc(sizeof(long) * *pItemsA);
if (!*pA) return E_OUTOFMEMORY;
}
if (*pItemsB > 0)
{
*pB = CoTaskMemAlloc(sizeof(long) * *pItemsB);
if (!*pB)
{
if (*pA)
{
// You should not assume the memory will be freed by the caller
// in such drastic situations, so free and clear *pA here before returning
CoTaskMemFree(*pA);
*pA = nullptr;
}
return E_OUTOFMEMORY;
}
}
// Get As into *pA and Bs into *pB
// Or just copy them if getting the amounts implied getting the items
// You could just as well always return S_OK
return (*pItemsA > 0 || *pItemsB > 0) ? S_OK : S_FALSE;
}
Not shown is whatever other code you must implement yourself to get the amount of A
s and the amount of B
s, and the A
s and B
s themselves.
If you must get the items to know the amounts, and you're not using RAII, you should free those resources manually before returning.
As an alternative to using CoTaskMemAlloc
and CoTaskMemFree
, you may want to use ATL's CComHeapPtr
, which will automatically free memory RAII-style, thus simplifying your code. Just make sure you call Detach
into *pA
and *pB
before returning successfully.