Apparently I have a fundamental misunderstanding of how macros work.  I thought a macro just caused the preprocessor to replace the @defined macro with the replacement text.  But apparently that's not always the case.  My code follows:
TstBasInc.h
#pragma once
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <time.h>
//  copy macro
#define Cpy(ToVar, FrmVar) do {                                                                                 \
                                errno = strncpy_s(ToVar, sizeof(ToVar), FrmVar, _TRUNCATE);                     \
                                    if (errno == STRUNCATE)                                                     \
                                        fprintf(stderr, "string '%s' was truncated to '%s'\n", FrmVar, ToVar);  \
                            } while(0);
//  clear numeric array macro
#define ClrNumArr(ArrNam, ArrCnt)           \
            for (s = 0; s < ArrCnt; s++)    \
                ArrNam[s] = 0;
uint32_t    s;          //  subscript
typedef struct {
    short   C;
    short   YY;
    short   MM;
    short   DD;
} SysDat;
TstMacCmpErr:
#include "stdafx.h"
#include "TstBasInc.h"      //  test basic include file
#define ARRCNT 3
int main()
{
    char    Cnd = 'E';      //  define to use 'else' path
    char    ToVar[7 + 1];   //  Cpy To-Variable
    int     IntArr[ARRCNT]; //  integer array
    Cpy(ToVar, "short")                     //  compiles with or without the semi-colon
    if (Cnd != 'E')
//      Cpy(ToVar, "short string");         //  won't compile:  illegal else without matching if
        Cpy(ToVar, "short string")          //  will compile
    else
        Cpy(ToVar, "extra long string");    //  compiles with or without the semi-colon
//  the following code shows how I thought the macro would expand to    
    {                                                                                   \
        errno = strncpy_s(ToVar, sizeof(ToVar), "short str", _TRUNCATE);                \
        if (errno == STRUNCATE)                                                         \
            fprintf(stderr, "string '%s' was truncated to '%s'\n", "short str", ToVar);;    \
    }
    if (Cnd == 'E') {
        ClrNumArr(IntArr, ARRCNT)               //  compiles with or without the semi-colon
            printf("intarr[0] = %d\n", IntArr[0]);
    }
    else
        printf("intarr[0] is garbage\n");
    return 0;
}
The results follow:
string 'extra long string' was truncated to 'extra l'
string 'short str' was truncated to 'short s'
intarr[0] = 0;
As the comments say, when I have a semi-colon after Cpy(ToVar, "short string"); it won't even compile because I get an "C2181   illegal else without matching if" error.  As you see, I tried adding a do-while in the macro as suggested in this post, but it didn't make any difference.  When the macro code is copied directly (even without the do-while) that code works okay.  I would have thought just adding the braces in the macro would have fixed the problem but it didn't.  It must have something to do with the Cpy ending in an if because the ClrNumArr macro compiles with or without the semi-colon.  So can someone tell me why the Cpy macro doesn't just replace the text?  I must be missing something simple.
I'm using VS 2015 Community Edition Update 1.
Edit:  I documented the problem and isolated it to (I think) the if statement in the Cpy macro.  Still nobody has explained why the macro didn't expand the way I thought it should.  That should have been the title of the post, as that's my question more than the problem with the semi-colon, which I now have a solution for.
 
     
     
     
    