230 likes | 487 Views
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 *()
E N D
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 0xC00000FDbecause it allocates from the stack 2 x strlen(narrow_str) • operator const char *() constoperator 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 *() constoperator 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 depracatedsee Visual Studio .NET
Dangers of CComBSTR • What is wrong with the following member functions?operator BSTR() constBSTR* operator&() • int a;int * p = &a;
CComPtr<T> CComQIPtr<T> • T** operator&() • Gives access to internal datacan cause a leak (if not NULL) • T& operator*() • Does not access to IUnknownoperator->() 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
How to choose ? • Consistency is best • Consistently use _bstr_t or CComBSTR • Consistently use CComPtr or _com_ptr_tIf 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 implementationImplement the raw_ functions. • Read the .tlh filesBe 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> orstruct • { • 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 synchronoussee 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 ofbad 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.