Code Style
This document defines the WiX code style that is adapted from original source code developed at Microsoft. This is a living document and you will find older source in the repository that do not adhere to all of the guidelines in this document. When relevant to do so please fix the code to match this document.
Do not use tabs - only spaces.
The left indent should be four spaces.
Insignificant whitespace (whitespace to the right of text) should be reduced to one space in all cases except maybe comments.
// Good
int a = 0;
DWORD dw = 0; // this exists for some good reason
// Bad
int a = 0;
DWORD dw = 0; // this exists for some good reasonControl flow operators get one space after them while functions do not.
// Good
if (x)
{
func(x, y, z);
}
do
{
// do something to change fGo eventually
} while (fGo);
// Bad
if( x )
{
func ( x, y, z );
}Braces are always left justified.
// Good
HRESULT FunctionName(int a)
{
int b = 0;
int c = 0;
if (0 == a)
{
b = 1;
c = 2;
}
}
// Bad
HRESULT FunctionName(int a) {
int a = 0;
int b = 0;
if (0 == a)
{
b = 1;
c = 2;
}
}Braces are always used, even when optional.
// Good
if (fBar)
{
a = 1;
}
// Bad
if (fBar)
a = 1;Function definitions should look something like these examples.
// Good
void FunctionName();
extern "C" HRESULT __stdcall FunctionName(
__in LPCWSTR wzFoo,
__out BOOL *pfBar
);
// Bad
void FunctionName(
);
extern "C"
HRESULT
__stdcall
FunctionName(
__in LPCWSTR szFoo,
__out BOOL *pfBar
);Use prefix addition and subtraction except where postfix addition or subtraction is absolutely required. Avoid postfix in general to avoid side effects.
// Good
for (int i = 0; i < 10; ++i)
{
}
// Bad
for (int i = 0; i < 10; i++)
{
}Infinite loops should be rare but when necessary use an empty for loop and remember the braces.
// Example infinite loop required for some reason
for (;;)
{
// eventually break
}SAL annotation is required on all function parameters.
Functions should have only one of the following return types:
void
,BOOL
, orHRESULT
.void
andBOOL
functions can never fail. If they could fail, change the return type to anHRESULT
. You will find lots of wrapper functions in dutil.lib that all returnHRESULT
s. The only exception to this rule is that "uninitialize"-type functions should returnvoid
. Work hard to remove any failure cases in the uninitialize route and, if unsuccessful, carefully consider what implications a failed uninitialize route has. Consider::RevertToSelf()
as an example.The C++ type
bool
is never used. UseBOOL
instead.Functions that are from a public SDK should be prefixed with the global namespace scope operator
::
.Use the
countof()
macro (defined in dutil.h) to get the number of elements in an array. Consider this a sibling of thesizeof()
language operator.There should only be one exit from a function. An exception to this is error checking that can circumvent significant variable initialization by existing immediately at the top of the function.
Local variables should be initialized at the top of the function. If the local variable is not initialized to zero add a comment why.
The l-value in conditional tests should be the constant value whenever possible. To some people this makes the logic look "backward" but prevents accidental assignment in native code. After a while, other ways start looking wrong.
// Example
if (1 == i)
{
++i;
}There are two "secret handshakes" in the WiX code style. First, the
ExitOnXxx()
,ExitWithXxx()
, andExitFunction()
macros are used to simplify code flow. Never use an explicitgoto
. Second, theReleaseXxx()
andReleaseNullXxx()
macros are used extensively to clean up dynamically allocated memory when the function exits.// Example
HRESULT FunctionName()
{
HRESULT hr = S_OK;
IUnknown *pObject = NULL;
LPVOID pv = NULL;
WCHAR wzPAth[MAX_PATH] = { };
pv = MemAlloc(50, TRUE);
ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate pointer to nothing.");
hr = ::CoCreateInstance(IID_IUnknown, &pObject);
ExitOnFailure(hr, "Failed to get fake COM object.");
if (!::GetTempPath(wzPath, countof(wzPath)))
{
ExitWithLastError(hr, "Failed to get TEMP path.");
}
LExit: // this is always present when control flow is used
ReleaseObject(pObject);
ReleaseMem(pv);
return hr;
}The WiX support libraries make extensive use of dynamic strings. Use these string extensively instead of constant buffers or other mechanisms. You should use Hungarian notation for dynamic strings to change from "pwz" (pointer to a null-terminated string) to "scz" (for counted null-terminated string). This will make it easier to read "pointer to dynamically-allocated string" ("pscz" instead of "ppwz").
Use Hungarian notation for native code but don't go overboard with it. Sometimes just
pProperName
is just fine for an object that you are pointing at.BOOL f = FALSE;
int i = 0;
long l = 0;
DWORD dw = 0;
LPVOID pv = NULL;
FUNCTION_PTR pfn = NULL;
HRESULT hr = S_OK;Use the COM
IUnknown
pattern for lifetime management of objects.AddRef()
andRelease()
are not perfect but they are better than any other memory tracking operations we've found have in native code.WiX code style for managed code follows StyleCop definitions. Most of the above native code has actually morphed gently to match StyleCop suggested design (i.e. no tabs, four spaces).