Traps and pitfalls for VC6, ATL, C++ Mark Bartosik

Download Report

Transcript Traps and pitfalls for VC6, ATL, C++ Mark Bartosik

Traps and pitfalls for VC6, ATL, C++
• May be obvious ?
• May be not ?
Mark Bartosik
Danagers of _bstr_t
• _bstr_t::_bstr_t(const char * narrow_str)
– Unhandled exception 0xC00000FD
because it allocates from the stack 2 x strlen(narrow_str)
• operator const char *() const
operator char *()
– both return a pointer to PRIVATE data
const char * p = get_str();
unsigned len = strlen(p); // bang ???#! error ??
Danagers of _bstr_t
• operator const wchar_t *() const
operator wchar_t *()
– both return a pointer to PRIVATE data
BSTR p = get_str();
unsigned len = SysStringLen(p); // bang ???#! err??
Alternatives with _bstr_t
• const wchar_t * GetWString() const
• const char * GetString() const
• But not realistic. Once the extraction operators exist, you cannot
avoid them.
• #pragma depracated
see Visual Studio .NET
Dangers of CComBSTR
• What is wrong with the following member
functions?
operator BSTR() const
BSTR* operator&()
• int a;
int * p = &a;
CComPtr<T>
CComQIPtr<T>
• T** operator&()
– Gives access to internal data
can cause a leak (if not NULL)
• T& operator*()
– Does not access to IUnknown
operator->() returns proxy object with private
IUnknown
• operator T*()
– Gives implicit access to raw pointer
_com_ptr_t<T>
• T** operator&()
• T* operator->()
Exposes Iunknown, unlike CComPtr & CComQIPtr
which return a proxy object
• Behaves like CComQIPtr but uses exceptions for error
reporting.
Auto declared by #import
• variant_t : public VARIANT
How to choose ?
• Consistency is best
• Consistently use _bstr_t or CComBSTR
• Consistently use CComPtr or _com_ptr_t
If using #import prefer _com_ptr_t
• Does the rest of your code use exceptions or magic
return values?
• Write your own? (based on CComPtr)
collections
std::map<unsigned, employee_t>
std::map<unsigned, CComPtr<IEmployee> >
std::map<unsigned, CAdapt<CComPtr<IEmployee> > >
Also when erasing elements, don’t saw off the branch that you are
sitting on. (General STL rule)
Store objects NOT dumb pointers (smart pointers are objects)
Remember that my_collection.erase(iter) invalidates iter.
use
iter = my_collection.erase(iter)
Don’t forget about the algorithms #include <algorithm>
#import
•
Can use instead of .h, even for implementation
Implement the raw_ functions.
•
Read the .tlh files
Be careful some functions will return IDispatchPtr others will return
IDispatch *. It depends on the IDL.
Reading the .tlh is a thinking aid, keep it open in a window.
Debugging
• Prefer __stdcall over __thiscall
• Optimizer off
• Consider releasing with optimizer off
(except allow inlines for likes of STL)
• Symbol files on (PDB not PDB for edit continue),
Link with debug info,
Do not separate types
• Debug with Visual Studio .NET
(more) lifetime management
CObj::foo()
{
CSLock __anonymous__(m_critsec);
m_sub_object.Release();
} // Bang!
CObj::foo()
{
IUnknownPtr __anonymous__(this);
CSLock __anonymous__(m_critsec);
m_sub_object.Release();
}
(more) lifetime management
• Do not mix strong and weak pointers
• What if you need a C++ pointer to a CComObject derived
object?
std::pair<COurClass*, UnknownPtr>
or
struct
• {
•
UnknownPtr strong_ref;
COurClass * weak_ref;
• }
Patterns
• Avoid Singleton anti-pattern
• Once per what?
Per network? Per sub-net? Per machine? Per apartment ? Per security
context ? Per process? Per thread? Per transaction? Per user ? Etc.
The ATL singleton is once per process.
• Use a more natural language idiom
–
–
–
–
C++ globals are naturally per process.
C variables in a shared data seg are naturally per machine
VB module variables are naturally per thread (apartment)
But avoid global pointers to COM objects (issues with CRT and COM
initialization order)
Cyclic references & Connection points
• Wizard generated code is just bad (has some bugs)
Also it is synchronous
see www.cuj.com (search for Bartosik)
• Lack of type safety (IDispatch)
Build consideration
• Prefer dynamic link CRT
(but there are install issues)
• Avoid creating COM components within a
group
Libraries
• STL
• WTL
• boost.org
Leak Browser
• Much of COM is about ownership / lifetime
management, but poor C++ language binding, thus
the bugs.
•
•
•
•
Will find all your leaks
Soon will find all your references to deleted objects
Soon will find all use of uninitialized memory
Contact Bartosik for updates / latest build.
casts
reinterpret_cast<TO>(from)
reinterpret the bit pattern (unsafe and forceful)
dynamic_cast<TO>(from)
runtime check (from must have at least one virtual function). Often a sign of
bad design. Why don’t you know the type. dynamic_cast<derived*>(base*)
static_cast<TO>(from)
convert only if reasonable
const_cast<TO>(from)
remove either const or volatile qualifiers
(TO)
c-style cast -- avoid it, like static_cast OR reinterpret_cast
optionally combined with const_cast, and can be more forceful than
reinterpret_cast.
Compiler settings
(use PDB for all)
Linker settings
(include debug info)
Linker settings
(do not separate types)