|

|
Looking for a Way to Automate Your Visual Studio Builds?
Try the Programmers' Canvas Toolkit
Debugger Output Formatting
- Read the page Symbols for Watch Variables
for some enlightening information about the debugger's Variables and Watch
windows
- For example, to view the contents
of a BSTR, enter the name of the variable followed by
,su
in the watch window:
bstrVariableName,su
Crypto API
See here
Command Line Parser
See
here
SetFilePointer doesn't work on large files when FILE_END
is used. A "large" file is one that is larger than 2^32 bytes which is
roughly 4 gigabytes. Instead, you must call GetFileSize and
then call SetFilePointer with the file's size and
FILE_BEGIN.
std::min and std::max
Microsoft's #defines for min and max conflict with the functions from the
standard C++ library. Since #defines and macros can not be corralled into
namespaces (a serious deficiency in C++ namespaces), std::min and
std::max can only be used in Windows-specific applications if you
#define NOMINMAX in stdafx.h
warning LNK4098: defaultlib "MSVCRTD" conflicts with use of
other libs; use /NODEFAULTLIB:library
mfcs42.lib(dllmodul.obj) : warning LNK4006: _DllMain@12 already defined in MSVCRT.lib(dllmain.obj); second definition ignored
- MFC libraries must be linked before CRT libraries
- Open the Project Settings dialog box by clicking Settings on the Build
menu.
- In the Settings For view, select (highlight) the project configuration
that's getting the link errors.
- Click the Link tab.
- Select INPUT in the Category combo box.
- In the Libraries to Ignore edit box, insert the library names (for
example,
msvctrt.lib msvcrt.lib) NOTE: The linker command line is equivalent to /NOD:<library name>
- In the Object/library Modules edit box, insert the library names. You must
ensure that these are listed in order and as the first two libraries in the
line (for example, mfcs42.lib MSVCRT.lib).
LIBCMT.lib (crt0.obj) : error LNK2001: unresolved external symbol _main
If you get this error when building the release build, click on the
C/C++ tab in Project->Settings and select Preprocessor from the Category
drop-down box. Remove the ATL_MIN_CRT flag in the Preprocessor
definitions box. By default, ATL release builds use a tiny version of
the C runtime library (CRT). However, this tiny CRT does not include
certain functions which your ATL project may use. Therefore, you will
get unresolved symbols. By removing this flag, you will tell the linker
to link in full-size version of the CRT. However, this only
increases your DLL by ~30KB.
- Code should port to 64 bits easily
- Don't convert pointers to integers and vice-versa (e.g., user data in GUI
objects)
- Use the new types as described in Microsoft's
recommendations
Exceptions should not be thrown out of COM methods - the way to guarantee this is to
have a try/catch block around methods that might receive exceptions.
Turn on exception handling via Project Settings/C++/C++ Language/Enable Exception
Handling.
If you get the following warning:
warning C4530: C++ exception handler used, but unwind semantics are not enabled.
Specify -GX
Your code will GPF (or worse) if an exception is ever thrown.
Exception handling results in robust code. Put clean up logic in
destructors.
For instance, it's better for code to use exception handling to deal with errors that
occur deep down on the stack rather than relying on code checking the return value and
returning E_FAIL through multiple layers of internal calls -- instead, the code just
throws an HRESULT.
- Non-MFC projects should call
_beginthread or _beginthreadex,
and call _endthreadex upon completion. Failure
to call _endthreadex causes the HANDLE returned by _beginthread
to be invalid.
- MFC projects should call
AfxBeginThread
- Worker threads should always end on their own. Never stop a thread by calling the
various "end thread" methods - they can leak memory and leave mutexes
permanently locked (e.g., those for the C runtime).
- See NT Synchronization Primitives
- Every thread should have an outer try-catch block to avoid GPFs in the release build
MFC Version:
STDAPI ThreadFunction(LPVOID)
{
try
{
...
}
catch(CException *pE) // MFC catcher
{
// Avoid memory leak
pE->Delete();
}
catch(...)
{
}
return 0;
}
Non-MFC version:
STDAPI ThreadFunction(LPVOID)
{
try
{
...
}
catch(...)
{
}
return 0;
}
For the following hypothetical method parameters:
// Parameters:
// [in] CComPtr<IXMLDOMNode>& pParentNode
// [in] BSTR bsName
// [in] unsigned long* pULValue
Alternatively this method could take an IXMLDOMNode * for four reasons:
- Callers may not be using
CComPtr. Sounds crazy but it could happen - how about if
someone used CComQIPtr - you have to create a temporary CComPtr just to call the method
which also causes AddRef and Release to be called
- Conceptually there's no difference between
IXMLDOMNode * and
CComPtr<> because the
method doesn't need to modify the smart pointer and the smart pointer doesn't provide any
additional value
CComPtr automatically converts itself to a pointer to the template class -
IXMLDOMNode *, so existing code that uses CComPtr and calls this method would still work
- Since you're passing this in by reference, the code could inadvertently change the smart
pointer to point to something else
To summarize, pass parameters as raw interface pointers instead of smart pointers.
See also string conversion information
Item 1: Using CComBSTR in STL containers
Use CAdapt which is in <atlbase.h>. Failure
to do so may result in memory leaks ???
std::vector< CAdapt <CComBSTR> > vect;
Item 2: Excessive string copies
Don't convert BSTRs to LPTSTRs for no reason. Code that converts to
LPTSTR just
to convert it back to a BSTR wastes memory and CPU. Make sure you have a good reason
for converting to LPTSTR. std::basic_string has some useful methods but
if the wstr functions can suffice, they are much faster because they don't require a string
copy.
Use CComBSTR::Attach to attach a CComBSTR to an existing
BSTR without copying it. CComBSTR has many useful methods such as
+=, so sometimes
std::basic_string
can be avoided.
When returning BSTRs from methods, use Detach() on
CComBSTR:
*pOut = CComBSTR(L"hello there").Detach();
Item 3: Passing strings as input parameters to C++ functions and methods
There are many options, which are listed below. I find the most
efficient and flexible approach is this:
- Pass in a
LPOLESTR along with int stringLength.
In other words, callers must provide the length of the input string
- Create an identical method whose name has BSTR appended to it.
This method takes a BSTR which internally calls SysStringLen and passes
it to the method that takes
LPOLESTR str, int length
- This way you satisfy both types of callers -- those who have a string
and those who have a BSTR, and you've accounted for the case in which
the length of a BSTR can be determined very quickly.
- The only problem with this is you can't use specialized BSTR and
CComBSTR functions, but I typically don't need them -- if I do, I write
the method using BSTR
- Generally, I prefer creating methods that take LPOLESTR and no string
length to methods that take BSTR
2) Take LPOLESTR
This is the most common usage. This will accept a string created with
L"This
is a string" or CComBSTR(L"This is a string").
The advantage is some flexibility and avoiding common bugs when users pass a
L"foo" string instead of a real CComBSTR.
3) Take BSTR
The advantage of taking BSTR instead of LPOLESTR is that determining the length of a
BSTR is much faster. SysStringLen looks at the four bytes in front of the pointer to
determine the length which is faster than scanning the entire string until a null
character is found. Another advantage is that the parameter can be passed directly
to a COM method that expects a BSTR, so an extra string copy is avoided.
The disadvantage to passing BSTRs is that callers may pass in strings
as L"foo" instead of CComBSTR(L"foo"), and although
the compiler won't complain, SysStringLen will return a bogus
value.
I don't recommend that you provide a BSTR method without providing an
identical LPOLESTR method.
4) Take CComBSTR& / _bstr_t&
Not much advantage of this over #3 - CComBSTR may have some needed methods.
It's easy to create a CComBSTR from a BSTR without copying the data -- use
Attach() but don't forget to use Detach().
5) Take LPTSTR
LPTSTR is passed when the code is not dealing with UNICODE strings, which is rare since
all COM methods only deal with BSTRs. This will cause gratuitious string copies.
It's generally preferable to use #2 or #3.
6) Take std::basic_string<>
I generally disapprove of this convention as it assumes that the caller
uses std::basic_string<>, and most code doesn't use it
Item 4: String output parameters
There are many ways to do this which are listed below. I prefer to
pass a BSTR * and let the system allocate memory for me. For really
time-sensitive code you have to pass fixed-length arrays, but those cases are
very rare.
1) Take LPOLESTR
This is not optimal as it requires the string length to have a predetermined maximum.
In many cases the function will need to return a variable-length string.
2) Take LPOLESTR * / LPTSTR *
Supports returning variable-length strings.
"new" allocates the string and the caller to use delete[] to
delete it. Not ideal if the caller really needs a BSTR to be returned since the
LPOLESTR has to be converted into a BSTR, which involves reallocating the string so the
4-byte length can be put in front of the string.
3) Take BSTR *
This is the most common usage. It supports returning variable-length
strings. Ideal if caller needs BSTR to be returned.
For most strings you can take a BSTR * to modify a string and avoid an
extra string copy. You can't do this with input strings passed to
methods, however (what if the IDL specifies [in, out] ?).
Methods like foo(BSTR in, BSTR *out) typically result in an extra
string copy when the string needs to be modified and you don't need the
original value. The alternative is to add a falg, as in foo(BSTR in,
BSTR *out, bool & bOutContainsValidData); however, this results in a
very difficult to use function.
A method like foo(BSTR *inAndOut) can avoid an extra string copy.
The string is both an input and an output. The passed-in string must
be Free'd by the called method.
Warning! This will cause memory leaks unless callers
are very careful. If you pass a pointer to a BSTR that contains data, the previous
data won't get free'd. This happens even if a CComBSTR is used because
operator
&() does not free the string before returning the pointer to the BSTR data
member (m_str).
4) Take CComBSTR& / _bstr_t &
Advantage is that CComBSTR::Empty() can be called implicitly inside the
method. Memory leaks are less possible. Downside is that
wrapper must be created by the caller.
- For UNICODE configurations, define
UNICODE and _UNICODE
- All code should, generally, build and work in UNICODE and non-UNICODE environments
- This means using "tchar.h", USES_CONVERSION, and the various _tcs macros.
See also
- Non-UNICODE code can take the performance hit required by converting to/from UNICODE.
- All projects should have "Win32 Unicode Debug" and "Win32 Unicode
Release" configurations
For Exes and Libs...
- Choose Configurations from the Build menu
- Add "Unicode Debug" - Copy Win32 Debug
- Add "Unicode Release" - Copy Win32 Release
- Click OK
- Choose Settings from the Project menu
- Go to the General tab
- For "Win32 Unicode Debug" set Intermediate Files and Output Files to DebugU
- For "Win32 Unicode Release", set Intermediate Files and Output Files to
ReleaseU
- Go to the C++ tab
- Choose "Preprocessor" from the combo-box
- For "Win32 Unicode Debug" and "Win32 Unicode Release", add _UNICODE
and UNICODE to the list of preprocessor variables
- Go to the Link tab
- Choose "Output" from the combo-box
- The MIDL step seems to be missing from the newly created
configurations. Go to the project settings for the IDL file and
copy the Debug settings to the two Unicode configurations.
Projects should have an additional configuration that adds debug symbols to
"Release" builds. This will be helpful for debugging release builds.
- Choose Configurations from the Build menu
- Add "Release Symbols" - Copy "Win32 Release MinSize"
- Add "Unicode Release Symbols" - Copy "Win32 Release Unicode
MinSize"
- Click OK
- Select Settings from the Project menu
- Select "Multiple Configurations" in the "Settings For" dropdown
- Select "Win32 Release Symbols" and "Win32 Unicode Release Symbols"
- Click OK
- In Intermediate Files and Output Files, enter "ReleaseSymbols"
- Click the link tab
- Click on Generate Debug Info
- Click the C++ tab
- Select "Program Database for Edit and Continue" in the Debug Info combo box
- Select "Disable (Debug)" in the Optimizations combo box
- Select the "Win32 Unicode Release Symbols" configuration
- Click the General tab
- Set Intermediate Files and Output Files to "ReleaseUSymbols"
|