Coding Standards
Coding standards for WinDV – a Win32/MFC/DirectShow desktop application from 2002, maintained with dual VC6/CMake builds. Balances modern best practices with hard C++98/VC6 compatibility constraints.
Derived from the C++ Core Guidelines, adapted to the project’s legacy constraints.
Hard Constraints
These override all other guidelines:
| Constraint | Reason |
|---|---|
| C++98 only | VC6 (cl.exe 12.x) is a supported compiler |
| No C++11+ features | No auto, nullptr, enum class, constexpr, lambdas, range-for, smart pointers, override, std::string_view, uniform init {} |
_MBCS character set |
Unicode migration (WDV-2) is planned but not done |
| Win32 API XP SP3 minimum | Newer APIs only via GetProcAddress() with NULL fallback |
| Precompiled header | StdAfx.h must be first #include in every .cpp |
| Pure C modules | .c files must NOT include StdAfx.h; use extern "C" in headers; C89 only |
Philosophy
The C++ Core Guidelines principles still apply – adapted to C++98:
-
RAII where possible – Use MFC types (
CString,CArray,CEvent) and destructor cleanup. Nostd::unique_ptr, butdeletein destructors is acceptable. -
Immutability by default – Use
conston parameters, local variables, and member functions wherever possible. Noconstexpravailable; use#defineorstatic const. -
Type safety – Prefer typed structs over bare
DWORD/intbags. Use explicit casts over implicit narrowing. -
Express intent – Names and comments should communicate purpose. MFC Hungarian notation (
m_,dw,sz) is the project convention – follow it. - Minimize complexity – Keep functions short. One function, one job.
- Value semantics – Prefer stack objects. Heap allocation only when necessary (DirectShow COM objects, variable-size buffers).
Naming Conventions
WinDV uses MFC/Win32 Hungarian notation – this is the project standard:
/* Member variables: m_ prefix */
DWORD m_dwFrameCount;
CString m_captureFilename;
bool m_enableSHA256;
/* Local variables: type prefix or plain descriptive */
DWORD dwRead;
int seqCount;
char szHash[65];
/* Constants: ALL_CAPS for #define, mixed for static const */
#define BLOCKS_PER_SEQ 150
static const int kMaxRetries = 3;
/* Functions: PascalCase (MFC convention) or camelCase */
void GetMediaType(CMediaType *type);
int GetDVRecordingTime(BYTE *buf, int len);
/* Structs: PascalCase */
struct FrameErrorInfo { ... };
struct CaptureStats { ... };
/* Classes: C prefix (MFC convention) */
class CDV : public CStatic { ... };
class CCaptureCfg : public CPropertyPage { ... };
File Organization
Headers (.h)
/* Include guard -- use #ifndef/#define, not #pragma once alone (VC6 compat).
* #pragma once is acceptable as a supplement for MSVC optimization. */
#ifndef DVERROR_H
#define DVERROR_H
/* For headers that use Win32 types (DWORD, BYTE, BOOL) but might be included
* outside the PCH chain: rely on StdAfx.h being included first (project convention).
* Only add #include <windows.h> if the header is designed for standalone use. */
struct FrameErrorInfo {
DWORD dwVideoErrorBlocks;
DWORD dwAudioErrorBlocks;
};
FrameErrorInfo AnalyzeDVFrame(const BYTE *data, int len);
#endif /* DVERROR_H */
Source Files (.cpp)
#include "stdafx.h" /* MUST be first -- precompiled header */
#include "MyModule.h" /* own header */
#include "DV.h" /* project headers */
#include <stdio.h> /* system headers last */
Pure C Modules (.c)
/* NO #include "stdafx.h" -- pure C, no MFC */
#include <string.h>
#include "sha256.h"
/* C89 only: no // comments, no mixed declarations,
* no uint32_t (use unsigned int), no size_t in public API */
Pure C Headers (.h with extern “C”)
#ifndef SHA256_H
#define SHA256_H
#ifdef __cplusplus
extern "C" {
#endif
void sha256_init(SHA256_CTX *ctx);
#ifdef __cplusplus
}
#endif
#endif /* SHA256_H */
Resource Management
MFC Types as RAII
MFC types handle their own cleanup – prefer them over raw Win32 handles:
| Resource | Use | Avoid |
|---|---|---|
| Strings | CString |
char* with malloc/free |
| Arrays | CArray<T,T&> |
T* with new[]/delete[] |
| Sync | CAutoLock lock(&m_cs) |
Manual EnterCriticalSection/Leave |
| Events | CEvent |
Raw CreateEvent/CloseHandle |
| COM | CHECK_HR() macro |
Unchecked HRESULT |
Manual Cleanup Pattern
When RAII is not available (Win32 handles, COM interfaces):
HANDLE hFile = CreateFile(...);
if (hFile == INVALID_HANDLE_VALUE) return FALSE;
/* ... use hFile ... */
CloseHandle(hFile); /* cleanup before every return path */
Newer Win32 APIs – GetProcAddress Pattern
/* Load dynamically so the binary runs on older Windows versions.
* Pattern established in WinDV.cpp for SetProcessDPIAware. */
typedef BOOL (WINAPI *PFN_SetProcessDPIAware)(void);
HMODULE hUser32 = GetModuleHandle("user32.dll");
if (hUser32) {
PFN_SetProcessDPIAware pfn = (PFN_SetProcessDPIAware)
GetProcAddress(hUser32, "SetProcessDPIAware");
if (pfn) pfn();
}
For repeated use, wrap in an __inline helper in StdAfx.h (see SafeAttachConsole()).
Error Handling
WinDV uses MFC exception handling, not C++ exceptions:
/* Throwing: use CDShowException via helper */
ThrowDShowException(CDShowException::error, "Graph build failed");
/* Catching: MFC TRY/CATCH_ALL pattern */
TRY {
BuildCapturing(vsrc);
}
CATCH_ALL(e) {
Exception2Status(e);
}
END_CATCH_ALL
/* HRESULT checking: use the CHECK_HR macro */
CHECK_HR(pGraph->AddFilter(pFilter, L"MyFilter"));
Do NOT use C++ try/catch/throw – MFC exceptions are heap-allocated with b_AutoDelete.
Threading
/* Create threads via MFC (suspended, then resumed) */
m_thread = AfxBeginThread(::CapturingThread, this,
THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
m_thread->ResumeThread();
/* Synchronization: always RAII via CAutoLock */
{
CAutoLock lock(&m_cs);
m_errorStats = newStats; /* protected access */
}
/* UI notification from worker threads: PostMessage only, never SendMessage */
GetParent()->PostMessage(WM_DV_CHECK_COMPLETE, (WPARAM)result, 0);
Expressions and Statements
Initialization
/* Always initialize -- uninitialized variables are undefined behavior */
DWORD dwCount = 0;
char szHash[65] = "";
FrameErrorInfo info;
memset(&info, 0, sizeof(info)); /* acceptable for POD structs */
Casts
/* Prefer C++ casts in new code (VC6 supports them) */
UINT nFrames = (UINT)rawDelta; /* C-style: acceptable in existing patterns */
const BYTE *block = data + offset; /* pointer arithmetic: no cast needed */
/* Never cast away const */
Signed/Unsigned
/* Avoid mixing signed and unsigned in comparisons (C4018) */
UINT nFrames = 0; /* use UINT if comparing with UINT members */
long rawDelta = a - b; /* signed arithmetic in temp */
if (rawDelta < 0) rawDelta = -rawDelta;
UINT delta = (UINT)rawDelta; /* cast after ensuring non-negative */
Magic Numbers
/* Name your constants */
#define BLOCKS_PER_SEQ 150
#define BYTES_PER_BLOCK 80
#define SEQ_SIZE (BLOCKS_PER_SEQ * BYTES_PER_BLOCK)
/* Avoid */
offset = i * 150 * 80 + b * 80 + 3; /* what are 150, 80, 3? */
Console Output from GUI App
WinDV is a GUI application (/SUBSYSTEM:WINDOWS) that also supports CLI commands:
/* 1. Try inherited stdout first (works with cmd redirection and CI) */
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
/* 2. Fall back to parent console for interactive use */
if (hOut == NULL || hOut == INVALID_HANDLE_VALUE) {
SafeAttachConsole(); /* GetProcAddress wrapper in StdAfx.h */
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
}
/* 3. Write via WriteFile (not printf -- stdout may not be connected) */
if (hOut != NULL && hOut != INVALID_HANDLE_VALUE) {
DWORD written;
WriteFile(hOut, line, len, &written, NULL);
}
/* 4. Never AllocConsole -- it replaces inherited handles */
MFC ClassWizard Blocks
Preserve //{{AFX_...}} markers exactly as-is:
//{{AFX_DATA_MAP(CCaptureCfg)
DDX_Text(pDX, IDC_MAX_FRAMES, m_maxAVIFrames);
//}}AFX_DATA_MAP
/* Add new DDX calls OUTSIDE the AFX block */
DDX_Check(pDX, IDC_CHK_SHA256, m_enableSHA256);
Settings Persistence
/* Registry (normal mode) */
AfxGetApp()->WriteProfileInt("Capture", "SHA256", m_enableSHA256);
m_enableSHA256 = AfxGetApp()->GetProfileInt("Capture", "SHA256", TRUE) != 0;
/* INI file (portable mode) is handled transparently by MFC when
* CWinApp::m_pszProfileName points to a .ini file instead of registry. */
Anti-Patterns (Project-Specific)
| Avoid | Do Instead |
|---|---|
new/delete in hot paths |
Stack objects, CArray, ring buffers |
SendMessage from worker thread |
PostMessage (avoids deadlock) |
AllocConsole() |
SafeAttachConsole() + GetStdHandle |
#include <windows.h> in .h |
Rely on StdAfx.h PCH chain |
C++11 auto, nullptr, override |
Explicit types, NULL, no keyword |
std::vector, std::string |
CArray, CString (MFC types) |
using namespace |
Explicit qualification |
// comments in .c files |
/* */ only (C89) |
Quick Reference Checklist
Before marking WinDV C++ work complete:
- [ ]
StdAfx.his first include in every.cpp(not in.c) - [ ] All variables initialized at declaration
- [ ]
conston parameters and locals where possible - [ ] No C++11+ features (compiles with VC6)
- [ ] No Win32 APIs above XP SP3 without
GetProcAddressfallback - [ ] Hungarian notation follows project convention (
m_,dw,sz) - [ ]
//{{AFX_...}}blocks preserved unchanged - [ ] Thread sync via
CAutoLock/CEvent, UI viaPostMessage - [ ] No signed/unsigned comparison warnings (use matching types or explicit cast)
- [ ] HRESULT checked via
CHECK_HR()macro - [ ] Pure C modules: C89 only,
extern "C"in header, no PCH - [ ] New Resource IDs added to
Resource.hwith_APS_NEXT_CONTROL_VALUEincremented