//------------------------------------------------------------------------------
// File: Validate.h
//
// Desc: DirectShow sample code - parameter validation macros.
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------


// Summary:
//
// V_INAME(interfacename)                - Set the interface name for error display
// V_STRUCTPTR_READ(ptr,type)            - A dwSize struct which we will read
// V_STRUCTPTR_WRITE(ptr,type)           - A dwSize struct which we will read/write
// V_PTR_READ(ptr,type)                  - A typed ptr w/o a dwSize which we will read
// V_PTR_WRITE(ptr,type)                 - A typed ptr w/o a dwSize which we will read/write
// V_PTR_WRITE_OPT(ptr,type)             - An optional typed ptr w/o a dwSize which we will read/write
// V_BUFPTR_READ(ptr,size)               - A variable-size buffer that we will read
// V_BUFPTR_READ_OPT(ptr,size)			 - An optional variable-size buffer that we will read
// V_BUFPTR_WRITE(ptr,size)              - A variable-size buffer that we will read/write
// V_BUFPTR_WRITE_OPT(ptr,size)          - An optional variable-size buffer that we will read/write
// V_PTRPTR_WRITE(ptrptr)                - A pointer to a pointer to write to
// V_PTRPTR_WRITE_OPT(ptrptr)            - A pointer to a pointer to write to that is optional
// V_PUNKOUTER(punk)                     - A pointer to a controlling unknown, aggregation supported
// V_PUNKOUTER_NOADD(punk)               - A pointer to a controlling unknown, aggregation not supported
// V_INTERFACE(ptr)                      - A pointer to a COM interface
// V_INTERFACE_OPT(ptr)                  - An optional pointer to a COM interface
// V_REFGUID(ref)                        - A reference to a GUID (type REFGUID)
// V_HWND(hwnd)							 - A window handle
// V_HWNDOPT(hwnd)						 - An optional window handle
//
// For handling different versions of structures:
// 
// V_STRUCTPTR_READ_VER(ptr,ver)         - Begin a struct version block for read access
//                                         At the end, 'ver' will contain the 
//                                         discovered version of the struct
// V_STRUCTPTR_READ_VER_CASE(base,ver)   - Test struct against version ver of
//                                         type 'base'. 
// V_STRUCTPTR_READ_VER_END(base,ptr)    - End a struct version block
//
// V_STRUCTPTR_WRITE_VER(ptr,ver)        - Struct version block for write access
// V_STRUCTPTR_WRITE_VER_CASE(base,ver)
// V_STRUCTPTR_WRITE_VER_END(base,ptr)
//
// The struct version block expects type names of a base type followed by a 
// numeric version, such as
//
// typedef struct { } FOO7;
// typedef struct { } FOO8;
//
// In the header FOO and LPFOO are conditionally typedef'd based on a version
// #define. The DLL will be compiled with the latest version number and hence
// the largest version of the struct.
//
// Since Windows headers are compiled by default with 8-byte alignment, adding
// one DWORD may not cause the size of the structure to change. If this happens
// you will get a 'case label already used' error on one of the VER_CASE macros.
// If this happens, you can get around it by adding a dwReserved field to the 
// end of the struct to force the padding.
//
// 'optional' means the pointer is allowed to be NULL by the interface specification.
//
// Sample usage:
//
// int IDirectMusic::SetFooBarInterface(
//     LPDMUS_REQUESTED_CAPS    pCaps,          // Caps w/ dwSize (read-only)
//     LPVOID                   pBuffer,        // Buffer we will fill in
//     DWORD                    cbSize,         // Size of the buffer
//     PDIRECTMUSICBAR          pBar)           // Callback interface for bar on this buffer
// {
//     V_INTERFACE(IDirectMusic::SetFooBarInterface);
//     V_BUFPTR_WRITE(pBuffer, cbSize);
//     V_INTERFACE(pBar);
//     DWORD dwCapsVer;                         // Must be a DWORD!!!
//
//     V_STRUCTPTR_READ_VER(pCaps, dwCapsVer);
//     V_STRUCTPTR_READ_VER_CASE(DMUS_REQUESTED_CAPS, 7);
//     V_STRUCTPTR_READ_VER_CASE(DMUS_REQUESTED_CAPS, 8);
//     V_STRUCTPTR_READ_VER_END_(DMUS_REQUESTED_CAPS, pCaps);
//
//     // At this point, if we are still in the function we have a valid pCaps
//     // pointer and dwCapsVer is either 7 or 8, indicating the version of
//     // the struct passed in.
//
//     ...
// }
//
#ifndef _VALIDATE_H_
#define _VALIDATE_H_


#ifdef DBG
#include <stddef.h>

#include "debug.h"

// To turn on DebugBreak on parameter error, use the following or -DRIP_BREAK in the build:
//
//#define RIP_BREAK 1

#ifdef RIP_BREAK
#define _RIP_BREAK DebugBreak();
#else
#define _RIP_BREAK 
#endif

#define V_INAME(x) \
    static const char __szValidateInterfaceName[] = #x;                       

#define RIP_E_POINTER(ptr) \
{   Trace(-1, "%s: Invalid pointer " #ptr "\n", __szValidateInterfaceName); \
    _RIP_BREAK \
    return E_POINTER; }

#define RIP_E_INVALIDARG(ptr) \
{   Trace(-1, "%s: Invalid argument " #ptr "\n", __szValidateInterfaceName); \
    _RIP_BREAK \
    return E_INVALIDARG; }

#define RIP_E_HANDLE(h) \
{	Trace(-1, "%s: Invalid handle " #h "\n", __szValidateInterfaceName); \
    _RIP_BREAK \
	return E_HANDLE; }
    
#define RIP_W_INVALIDSIZE(ptr) \
{   Trace(-1, "%s: " #ptr "->dwSize matches no known structure size. Defaulting to oldest structure.\n", \
    __szValidateInterfaceName); \
    _RIP_BREAK \
    }
    
#define RIP_E_INVALIDSIZE(ptr) \
{   Trace(-1, "%s: " #ptr "->dwSize is too small\n", __szValidateInterfaceName); \
    _RIP_BREAK \
    return E_INVALIDARG; }
    
#define RIP_E_BLOCKVSDWSIZE(ptr) \
{   Trace(-1, "%s: " #ptr " does not point to as much memory as " #ptr "->dwSize indicates\n", \
    __szValidateInterfaceName); \
    _RIP_BREAK \
    return E_INVALIDARG; }    

// NOTE: The DebugBreak() not in #ifdef is intentional - this is something that
// must be fixed in our code, not an app-generated error.
//
#define V_ASSERT(exp) \
{   if (!(exp)) { \
        Trace(-1, "%s@%s: %s\n", __FILE__, __LINE__, #exp); \
        DebugBreak(); }}

#else

#define V_INAME(x)
#define RIP_E_POINTER(ptr)          { return E_POINTER; }
#define RIP_E_INVALIDARG(ptr)       { return E_INVALIDARG; }
#define RIP_E_HANDLE(h)	            { return E_HANDLE; }
#define RIP_E_BLOCKVSDWSIZE(ptr)    { return E_INVALIDARG; }
#define RIP_W_INVALIDSIZE(ptr)
#define RIP_E_INVALIDSIZE(ptr)      { return E_INVALIDARG; }
#define V_ASSERT(exp)

#endif          // DBG

// A passed struct we will only read from or may write to. Must be a struct
// with a dwSize.
//
// int foo(CFoo *pFoo)
// ...
// V_STRUCTPTR_READ(pFoo, CFoo);
// V_STRUCTPTR_WRITE(pFoo, CFoo);
//
// Use _PTR_ variants for structs w/o a dwSize
//
#define V_STRUCTPTR_READ(ptr,type) \
{   V_ASSERT(offsetof(type, dwSize) == 0); \
    if (IsBadReadPtr(ptr, sizeof(DWORD)))               RIP_E_BLOCKVSDWSIZE(ptr); \
	if (ptr->dwSize < sizeof(type))						RIP_E_INVALIDSIZE(ptr); \
    if (IsBadReadPtr(ptr, (ptr)->dwSize))               RIP_E_BLOCKVSDWSIZE(ptr); }

#define V_STRUCTPTR_WRITE(ptr,type) \
{   V_ASSERT(offsetof(type, dwSize) == 0); \
    if (IsBadReadPtr(ptr, sizeof(DWORD)))               RIP_E_BLOCKVSDWSIZE(ptr); \
	if (ptr->dwSize < sizeof(type))						RIP_E_INVALIDSIZE(ptr); \
    if (IsBadWritePtr(ptr, (ptr)->dwSize))              RIP_E_BLOCKVSDWSIZE(ptr); }

#define V_PTR_READ(ptr,type) \
{ if (IsBadReadPtr(ptr, sizeof(type)))                  RIP_E_POINTER(ptr); }

#define V_PTR_WRITE(ptr,type) \
{ if (IsBadWritePtr(ptr, sizeof(type)))                 RIP_E_POINTER(ptr); }

#define V_PTR_WRITE_OPT(ptr,type) \
{ if (ptr) if (IsBadWritePtr(ptr, sizeof(type)))        RIP_E_POINTER(ptr); }

// A buffer pointer with separate length (not defined by the pointer type) we will only
// read from or may write to.
//
// int foo(LPVOID *pBuffer, DWORD cbBuffer)
// ...
// V_BUFPTR_READ(pBuffer, cbBuffer);
// V_BUFPTR_WRITE(pBuffer, cbBuffer);
//
#define V_BUFPTR_READ(ptr,len) \
{   if (IsBadReadPtr(ptr, len))                         RIP_E_POINTER(ptr); }

#define V_BUFPTR_READ_OPT(ptr,len) \
{	if (ptr) V_BUFPTR_READ(ptr,len); }

#define V_BUFPTR_WRITE(ptr,len) \
{   if (IsBadWritePtr(ptr, len))                        RIP_E_POINTER(ptr); }

#define V_BUFPTR_WRITE_OPT(ptr,len) \
{	if (ptr) V_BUFPTR_WRITE(ptr,len); }

// A pointer to a pointer (such as a pointer to an interface pointer) to return
//
// int foo(IReturnMe **ppRet)
// ...
// V_PTRPTR_WRITE(ppRet);
// V_PTRPTR_WRITE_OPT(ppRet);
//
#define V_PTRPTR_WRITE(ptr) \
{   if (IsBadWritePtr(ptr, sizeof(void*)))              RIP_E_POINTER(ptr); }

#define V_PTRPTR_WRITE_OPT(ptr) \
{   if (ptr) if (IsBadWritePtr(ptr, sizeof(void*)))     RIP_E_POINTER(ptr); }

// A pointer to a controlling unknown
//
#define V_PUNKOUTER(punk) \
{   if (punk && IsBadCodePtr(punk))                     RIP_E_POINTER(ptr); }

// A pointer to a controlling unknown for which we don't support aggregation
//
#define V_PUNKOUTER_NOAGG(punk) \
{   if (punk && IsBadReadPtr(punk, sizeof(IUnknown)))   RIP_E_POINTER(ptr); \
    if (punk) return CLASS_E_NOAGGREGATION; }

// Validate an incoming interface pointer. 
//
struct _V_GENERIC_INTERFACE
{
    FARPROC *(__vptr[1]);
};

#define V_INTERFACE(ptr) \
{   if (IsBadReadPtr(ptr, sizeof(_V_GENERIC_INTERFACE)))                              RIP_E_POINTER(ptr); \
    if (IsBadReadPtr(*reinterpret_cast<_V_GENERIC_INTERFACE*>(ptr)->__vptr, sizeof(FARPROC))) \
                                                                                      RIP_E_POINTER(ptr); \
    if (IsBadCodePtr(*(reinterpret_cast<_V_GENERIC_INTERFACE*>(ptr)->__vptr)[0]))     RIP_E_POINTER(ptr); }

#define V_INTERFACE_OPT(ptr) \
{   if (ptr) V_INTERFACE(ptr); }

// Validation for a reference to a GUID, which we only ever read. 
//
#define V_REFGUID(ref) \
{   if (IsBadReadPtr((void*)&ref, sizeof(GUID)))        RIP_E_POINTER((void*)&ref); }

// Validation for a window handle
//
#define V_HWND(h) \
{	if (!IsWindow(h))									RIP_E_HANDLE(h); }	

#define V_HWND_OPT(h) \
{	if (h) if (!IsWindow(h))							RIP_E_HANDLE(h); }	

// Validation for multiple sized structs based on version
//
#define V_STRUCTPTR_READ_VER(ptr,ver) \
{   ver = 7; DWORD *pdw = &ver;  \
    if (IsBadReadPtr(ptr, sizeof(DWORD)))               RIP_E_BLOCKVSDWSIZE(ptr); \
    if (IsBadReadPtr(ptr, (ptr)->dwSize))               RIP_E_BLOCKVSDWSIZE(ptr); \
    switch ((ptr)->dwSize) {
    
#define V_STRUCTPTR_READ_VER_CASE(basetype,ver) \
    case sizeof(basetype##ver) : \
    V_ASSERT(offsetof(basetype##ver, dwSize) == 0); \
    *pdw = ver; break;
    
#define V_STRUCTPTR_READ_VER_END(basetype,ptr) \
    default : if ((ptr)->dwSize > sizeof(basetype##7)) \
    { RIP_W_INVALIDSIZE(ptr); } else \
    RIP_E_INVALIDSIZE(ptr); }}


#define V_STRUCTPTR_WRITE_VER(ptr,ver) \
{   ver = 7; DWORD *pdw = &ver;  \
    if (IsBadReadPtr(ptr, sizeof(DWORD)))               RIP_E_BLOCKVSDWSIZE(ptr); \
    if (IsBadWritePtr(ptr, (ptr)->dwSize))              RIP_E_BLOCKVSDWSIZE(ptr); \
    switch ((ptr)->dwSize) {
    
#define V_STRUCTPTR_WRITE_VER_CASE(basetype,ver) \
    case sizeof(basetype##ver) : \
        V_ASSERT(offsetof(basetype##ver, dwSize) == 0); \
        *pdw = ver; break;
    
#define V_STRUCTPTR_WRITE_VER_END(basetype,ptr) \
    default : if ((ptr)->dwSize > sizeof(basetype##7)) \
    { RIP_W_INVALIDSIZE(ptr); } else \
    RIP_E_INVALIDSIZE(ptr); }}



#endif          // _VALIDATE_H_
