How should I check that [out] params in COM can be used? - com

Officially one should not use [out] parameters from COM functions unless the function succeeded this means that there are (at least) three ways to see if an [out] parameter can be used.
Consider the following interface
interface IFoo : IUnknown {
HRESULT GetOtherFoo([out] IFoo** ppFoo);
HRESULT Bar();
};
Which of the following ways would you recommend on using it?
1. Check return value
CComPtr<IFoo> other;
HRESULT hr = foo->GetOtherFoo(&other);
if (SUCCEEDED(hr))
other->Bar();
This makes me a bit nervous since a bug in IFoo could cause a NULL pointer dereferencing.
2. Check the output parameter
This depends on the fact that if a method fails it mustn't change any of the [out] parameters (if the parameter changed <==> it's safe to use it).
CComPtr<IFoo> other;
foo->GetOtherFoo(&other);
if (other)
other->Bar();
Note that this sort of happens anyway, CComPtr's destructor will call Release if the pointer isn't NULL so it can't be garbage.
3. The paranoid way, check both
CComPtr<IFoo> other;
HRESULT hr = foo->GetOtherFoo(&other);
if (SUCCEEDED(hr) && other)
other->Bar();
This is a bit verbose in my opinion.
P.S. See related question.

If you are willing to write more checks and make code a bit slower for making it more reliable option 3 is for you. Since you expect that there are bugs in the COM server it is quite reasonable to check against them.

COM server methods that return a success HRESULT, yet set some of their output parameters to NULL are not very common. There are a few cases (IClientSecurity::QueryBlanket comes to mind) where this is used, but usually the client may expect all output parameters to be non-NULL if the method returned successfully.
It is, after all, a matter of how the method is documented. In the default case, however, I would consider 1. to be a safe way to go.

Related

Why Microsoft CRT is so permissive regarding a BSTR double free

This is a simplified question for the one I asked here. I'm using VS2010 (CRT v100) and it doesn't complain, in any way ever, when i double free a BSTR.
BSTR s1=SysAllocString(L"test");
SysFreeString(s1);
SysFreeString(s1);
Ok, the question is highly hypothetical (actually, the answer is :).
SysFreeString takes a BSTR, which is a pointer, which actually is a number which has a specific semantic. This means that you can provide any value as an argument to the function, not just a valid BSTR or a BSTR which was valid moments ago. In order for SysFreeString to recognize invalid values, it would need to know all the valid BSTRs and to check against all of them. You can imagine the price of that.
Besides, it is consistent with other C, C++, COM or Windows APIs: free, delete, CloseHandle, IUnknown::Release... all of them expect YOU to know whether the argument is eligible for releasing.
In a nutshell your question is: "I am calling SysFreeString with an invalid argument. Why compiler allows me this".
Visual C++ compiler allows the call and does not issue a warning because the call itself is valid: there is a match of argument type, the API function is good, this can be converted to binary code that executes. The compiler has no knowledge whether your argument is valid or not, you are responsible to track this yourselves.
The API function on the other hand expects that you pass valid argument. It might or might not check its validity. Documentation says about the argument: "The previously allocated string". So the value is okay for the first call, but afterward the pointer value is no longer a valid argument for the second call and behavior is basically undefined.
Nothing to do with the CRT, this is a winapi function. Which is C based, a language that has always given programmers enough lengths of rope to hang themselves by invoking UB with the slightest mistake. Fast and easy-to-port has forever been at odds with safe and secure.
SysFreeString() doesn't win any prizes, clearly it should have had a BOOL return type. But it can't, the IMalloc::Free() interface function was fumbled a long time ago. Nothing you can't fix yourself:
BOOL SafeSysFreeString(BSTR* str) {
if (str == NULL) {
SetLastError(ERROR_INVALID_ARGUMENT);
return FALSE;
}
SysFreeString(*str);
*str = NULL;
return TRUE;
}
Don't hesitate to yell louder, RaiseException() gives a pretty good bang that is hard to ignore. But writing COM code in C is cruel and unusual punishment, outlawed by the Geneva Convention on Programmers Rights. Use the _bstr_t or CComBSTR C++ wrapper types instead.
But do watch out when you slice the BSTR out of them, they can't help when you don't or can't use them consistently. Which is how you got into trouble with that VARIANT. Always pay extra attention when you have to leave the safety of the wrapper, there are C sharks out there.
See this quote from MSDN:
Automation may cache the space allocated for BSTRs. This speeds up
the SysAllocString/SysFreeString sequence.
(...)if the application allocates a BSTR and frees it, the free block
of memory is put into the BSTR cache by Automation(...)
This may explain why calling SysFreeString(...) twice with the same pointer does not produce a crash,since the memory is still available (kind of).

NULL in/out parameter in COM

My COM object has a method, in IDL defined as:
HRESULT _stdcall my_method( [in] long value, [in, out] IAnotherObject **result );
Is the caller allowed to call this method like so:
ptr->my_method(1234, NULL);
or would the caller be violating the COM specification in doing so?
In other words, should my code which implements this function check result != NULL before proceeding; and if so, does the COM spec require that I return E_INVALIDARG or E_POINTER or something; or would it be acceptable for my function to continue on and return 0 without allocating an AnotherObject ?
My object is intended to be Automation-compatible; and it uses standard marshaling.
Note: Question edited since my original text. After posting this question I discovered that optional should only be used for VARIANT, and an [in, out] parameter where result != NULL but *result == NULL should be treated like an out parameter, and I must allocate an object.
The Rules of the Component Object Model say:
The in-out parameters are initially allocated by the caller, then freed and re-allocated by the callee if necessary. As with out parameters, the caller is responsible for freeing the final returned value. The standard COM memory allocator must be used.
So, passing NULL is a violation. You can see several violations of COM rules even in Microsoft's own interfaces, such as IDispatch, where a few [out] parameters accept NULL, but that's because they have remote interface methods (see [local] and [call_as]) that most probably allocate the needed memory when crossing apartments, or otherwise perform custom marshaling.
EDIT: To further answer your questions.
I recommend you check for NULL [out] (or [in, out]) arguments and return E_POINTER when you find one. This will allow you to catch/detect most common errors early instead of raising an access violation.
Yes, you should check for argument validity.
If the client is in-process (and same apartment, etc.) with the server, there's nothing (no proxy, no stub) to protect your code from being called with a NULL.
So you're the only one left there to enforce any COM rule, whether that's considered to be a "violation" or not.
PS: defining in+out (w/o using VARIANTs) for Automation clients seems a bit unusual IMHO. I'm not sure all Automation clients can use this (VBScript?)

Should I enforce out-parameters being non-NULL?

Example method in IDL:
HRESULT _stdcall a_method( [in] long value, [out] BSTR *comment );
My function logic is that for some values, no comment is necessary. Should I throw an exception if this function is called with comment == NULL by a client? Or is it OK to be permissive and allow this case?
(I'm developing my object in C++).
My rationale for trying to be strict with parameter checking is that I'm concerned about memory leaks, and about having the client make calls that are correct according to the COM spec but my object not accepting the call.
The semantics of [out] parameters are very explicit about this.
A method that gets an [out] parameter should never - ever - look at the parameter's value until it puts something on it. It is uninitialized memory. Garbage. In fact, if your method is called via a marshalled call (inter-apartment or inter-process), garbage is exactly what you get: whatever your caller might have put there when it called your method, was discarded and ignored by the proxy/stub; you never get it.
If the client/caller puts something on the parameter before making a call to your method, it is definitely a memory leak (given that it's an allocated object like a BSTR, of course), but it's the caller's fault. It is never the responsibility of a called method to deal with it. The called method can't handle the leak even if it wanted to.
If you want to handle whatever values might be passed in by the caller, you need to use an [in, out] parameter instead of [out].
One last warning: Automation clients (VBA, VBScript, etc.) don't support [out] parameters. Automation will silently handle any [out] parameter as if it was [in, out], which puts you in an awkward position: any value placed in the parameter by the client application will be leaked, and your method can't do anything about it.
If you plan on your object being used by an automation client, don't use [out] parameters. Use [in, out] instead, and make sure to check if the caller put a value on the parameter before the call. The proxy/stub will always marshal values both ways for an [in, out] parameter. If the caller placed a value on the parameter before the call, your method is responsible for releasing that value before writing to the parameter.
Edit: Expanding on the pointer itself being NULL:
You could think about checking for NULL and return E_INVALIDARG if it's NULL, but I wouldn't recommend it.
It is illegal to pass NULL as the pointer value for an [out] parameter. Even if your code handles a NULL value, if the call is marshalled, the marshaller will hit an Access Violation. The marshaller has to access the pointed value on the way back (to store the marshalled output on it) and it will do so without checking for null.
In your specific scenario (the call semantic being that there is nothing to return in a given case), the proper process is for the caller to always provide a pointer to storage, and for the called method to set the value to NULL. Something like this:
// Caller
BSTR comment;
hr = obj->a_method( 42, &comment);
// Callee
HRESULT a_method( value, BSTR *comment )
{
if (...)
{
//... I've decided we don't need to return a comment
*comment = NULL;
}
...
}
If you really want to have the pure null pointer semantic you mentioned, you can; but you have to mark the parameter with the [ptr] attribute. As far as I know, that doesn't work very well with Automation clients, and you have to use a custom marshaller. If you don't anticipate ever using an Automation client, this is clearly an option.

Tracking down the source of E_POINTER in IMFMediaSource::ReadSample

I'm getting an E_POINTER error from the ReadSample call, and as far as I can tell, none of the pointers are invalid. See snippet below (note, it's a C++/CLI app):
IMFSample* sample = NULL;
pin_ptr<IMFSample*> pinnedSample = &sample;
LONGLONG timeStamp;
HRESULT hr = mSourceReader->ReadSample(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
NULL,
NULL,
&timeStamp,
pinnedSample
);
I suspect the problem lies in the construction of the mSourceReader (an IMFSourceReader instance, created from an IMFMediaSource). But, alas, I've no idea how to backtrack and find the source, as all the COM calls in the chain of commands that created mSourceReader returned S_OK.
Much thanks for any tips.
You don't need pin_ptr when taking the address of a local variable, since the garbage collector never moves local variables around anyway.
I'd guess that one of the other three parameters you're passing NULL to is non-optional, but I need to see what function you're calling to know for sure.
Did you create the IMFSourceReader in synchronous or asynchronous mode? The docs say:
This method can complete synchronously or asynchronously. If you provide a callback pointer when you create the source reader, the method is asynchronous. Otherwise, the method is synchronous.
I think this is your problem:
In synchronous mode:
The pdwStreamFlags and ppSample parameters cannot be NULL. Otherwise, the method returns E_POINTER.
You've passed NULL for pdwStreamFlags, which is not allowed.
Doc link: http://msdn.microsoft.com/en-us/library/dd374665.aspx

Should out params be set even if COM function fails?

When implementing a COM interface I always assign to the out parameters on success but should I do so also on error?
HRESULT CDemo::Div(/*[in]*/ LONG a, /*[in]*/LONG b, /*[out,retval]*/ LONG* pRet)
{
if (pRet == NULL)
return E_POINTER;
if (b == 0)
{
*pRet = 0; // is this redundant?
return E_INVALIDARG;
}
*pRet = a/b;
return S_OK;
}
At one time I was bit on the nose by not initializing an out parameter and assuming that if I initialized the variable it will remain that value if I don't change it inside the method. However I used this method from .NET and since the marshaller sees that this is an [out] parameter it discarded the initial value I placed on the call site and put in garbage after the function returned (it was fun debugging that, not).
Is assigning to an out param even on failure overcompensation or should I really do it?
Edit: Even though formally one should not access out params if the function failed I often see (and sometimes write) code like this (using the example from sharptooth's post):
ISmth *pSmth = NULL;
pObj->GetSmth(&pSmth); // HRES is ignored
if (pSmth) // Assumes that if GetSmth failed then pSmth is still NULL
{
pSmth->Foo();
pSmth->Release();
}
This works fine in un-marshalled code (same thread apartment) but if a marshaller is involved is it smart enough to only set the return value if the function succeeded?
While the other answers are not wrong, they miss a very important point -- a COM server that intends to return a failure HRESULT MUST set all [out] parameters to NULL. This is not merely a matter of good style, it is required by COM and not adhering to it can cause random crashes when there is marshaling involved.
That said, the *pRet = 0; in the original code is not redundant but correct and required.
The rule is that the calling party is not allowed to do anything with the out parameters value if the call fails. The server therefore should not provide valid values and should not pass ownership of any resources to the out parameters.
For example if you have
HRESULT GetSmth( [out] ISmth** );
method then it's expected that the server calls AddRef() on the ISmth** variable prior to returning. It must not call AddRef() if it is going to return a failure code because the client is not allowed to use the returned out parameter value and therefore will not call Release() and you'll get a memory leak.
I'm not sure I 100% agree with sharptooth. I certainly agree that for a failed COM call you cannot and must not assign any resource ownership to any out parameters. This includes memory allocation or AddRef'ing a COM object.
However I see nothing wrong (and in fact encourage) setting purely out parameters to empty values as long is does not transfer any resource ownership. For instance there is nothing technically illegal about your code setting pRet to point to 0. This transfers no resource ownership over to pRet and is merely a helper to some caller who did not properly check for success of the call.