Code snippets
| Favourite C definitions | ||
Microsoft C favourites |
||
| _countof() | #define _countof(array)
(sizeof(array)/sizeof(array[0])) /* lifted from afximpl.h */ |
Gives the number of elements in an given array. Aka. ELEMENTS. Useful everywhere, especially in these times of Unicode. |
| EXTERN_C | #ifndef EXTERN_C #ifdef __cplusplus #define EXTERN_C extern "C" #else #define EXTERN_C extern #endif #endif /* lifted from objbase.h */ |
For prototyping functions. Useful when mixing C and C++ code - indispensable actually. |
| offsetof | #define offsetof(s,m)\ (size_t)&(((s *)0)->m) /* resides in stddef.h */ |
Sadly overlooked gem. |
Homegrown favourites (C + MFC) |
||
| SafeStrlen() | #define SafeStrlen(lpsz) ( (
(LPCTSTR)(lpsz) == NULL) ? 0 : lstrlen(lpsz) ) /* clone of CString::SafeStrlen. Cast ensures safe SafeStrlen(CString) - and SafeStrlen(std::string.c_str() :) ) */ |
To some, it's cleaner to pass around NULL than ubiquitous _T("")
- that is, a pointer to just _T('\0'), allocated for in the static data 'segment'. Anyway, this macro's safe and typesafe (yields warning if pointer isn't LPCTSTR)! |
| __argc, __argv |
#ifdef _MSDOS extern int __argc; extern TCHAR** __argv; #endif |
__argc and __argv isn't prototyped anywhere in antiquated Microsoft compilers. They're practical to have in scope, also outside of main(). |
| ParamStr() | #define ParamStr(a) (__argc > a) ? __argv[a] : _T("") | It's safer! |
| IsWinNT() | #ifdef _WIN32 #ifdef _UNICODE #define IsWinNT() TRUE #else #define IsWinNT() ((GetVersion() & 0xC0000000) == 0) #endif #else /* Win16 */ #define WF_WINNT 0x4000 #define IsWinNT() ((GetWinFlags() & WF_WINNT) == WF_WINNT) #endif |
How to detect NT, also from within Win16 apps. |
| DECLARE_ CONSOLEAPP (MFC) |
#ifdef _CONSOLE #define DECLARE_CONSOLEAPP \ extern int AFXAPI\ AfxWinMain(HINSTANCE hInstance,\ HINSTANCE hPrevInstance,\ LPTSTR lpCmdLine, int nCmdShow);\ extern "C" int _tmain( int /*argc*/,\ TCHAR** /*argv*/, TCHAR** /*envp*/)\ {\ return AfxWinMain(\ GetModuleHandle(NULL), NULL,\ GetCommandLine(), SW_SHOW);\ } // remember to instantiate app class #endif // _CONSOLE |
How to use CCommandLine and CWinApp in console apps. See Console app. |
| Program samples | |
| MFC: Console app |
- using CCommandLine and CWinApp in console apps. |
| C: Safer strncpy/cat |
- get them now, or suffer later. strncpy/cat doesn't always zero-terminate strings! |
| Borland Pascal: File object |
- Encapsulation of System.Assign/System.Reset/System.Rewrite/System.IOResult. (I haven't tested it with Delphi, and I don't know whether Delphi offers something similar.) |
EXTERN_C char*
strncatz( char* strDest, const char* strSource, size_t count);
EXTERN_C char*
strncpyz( char* strDest, const char* strSource, size_t count);
#ifdef _WIN32
EXTERN_C wchar_t*
wcsncatz( wchar_t* strDest, const wchar_t* strSource, size_t count);
EXTERN_C wchar_t*
wcsncpyz( wchar_t* strDest, const wchar_t* strSource, size_t count);
#endif
#ifdef _UNICODE
#define _tcsncatz wcscatz
#define _tcsncpyz wcscpyz
#else
#define _tcsncatz strcatz
#define _tcsncpyz strcpyz
#endif
char* strncatz( char* strDest, const char* strSource,
size_t count)
/* safe strcat (always zero-terminated ) */
{
const int length = strlen(strDest);
char* lpsz = strncat(strDest, strSource, count - length - 1);
lpsz[count - 1] = _T('\0');
return lpsz;
}
char* strncpyz( char* strDest, const char* strSource,
size_t count)
/* safe strcpy (always zero-terminated ) */
{
char* lpsz = strncpy(strDest, strSource, min(strlen(strSource) + 1, count) );
lpsz[count - 1] = _T('\0');
return lpsz;
}
#ifdef _WIN32
wchar_t* wcsncatz( wchar_t* strDest, const wchar_t* strSource, size_t count)
/* safe strcat (always zero-terminated ) */
{
const int length = wcslen(strDest);
wchar_t* lpsz = wcsncat(strDest, strSource, count - length - 1);
lpsz[count - 1] = _T('\0');
return lpsz;
}
wchar_t* wcsncpyz( wchar_t* strDest, const wchar_t* strSource, size_t count)
/* safe strncpy (always zero-terminated ) */
{
wchar_t* lpsz = wcsncpy(strDest, strSource, min(wcslen(strSource) + 1, count) );
lpsz[count - 1] = _T('\0');
return lpsz;
}
#endif
Unit TFileUnt;
Interface
Uses
Objects, Dos, Crt;
Type
ByteFile = File Of Byte;
PDateTime = ^DateTime;
Str2 = String[2];
Str80 = String[80];
sz3 = Array [0.. 2] Of Char;
sz512 = Array [0.. 511] Of Char;
Const
FILE_MODE_CREATE = 1;
FILE_MODE_READ = 2;
FILE_MODE_WRITE = 4;
FILE_MODE_READWRITE = 6;
FILE_SEEK_END = 1; { End of file }
FILE_SEEK_CUR = 2; { Current position of file pointer }
FILE_SEEK_SET = 3; { Beginning of file }
Type
TFile = Object(TObject)
private
m_hfile : ByteFile;
m_bOpen : Boolean;
m_bFromHandle : Boolean;
m_sFileName : PathStr;
m_nOpenFlags : Word;
m_nError : Integer;
m_bIOError : Boolean; { IOResult or DOSError error }
public
{ Construction / Destruction }
Constructor Init;
Destructor Done; Virtual;
Function Open (sFileName : PathStr; nOpenFlags : Word) : Boolean; Virtual;
Function Close : Boolean;
{ I/O }
Function Read (pDest : Pointer; nByteCount : Word) : Word;
Function Write (pSrc : Pointer; nByteCount : Word) : Word; Virtual;
{ Misc. helpers }
Function Remove : Boolean;
Function Rename(sNewName : PathStr) : Boolean;
Function Seek(nByteCount : LongInt; nOffset : Integer) : Boolean;
Function GetSize : LongInt;
Function GetPos : LongInt;
Function SetTime(dt : DateTime ) : Boolean;
Function GetTime(pDest : PDateTime) : Boolean;
Function GetName : PathStr;
Function GetDrive : Char;
Function IsError : Boolean;
Function GetError : Integer;
private
Function SetError(bIOError : Boolean) : Boolean;
End;
TIncFile = Object(TFile)
private
m_nInc : LongInt;
public
Constructor Init(nInc : LongInt);
Function Open(sFileName : PathStr; nOpenFlags : Word) : Boolean; Virtual;
Function Write(pSrc : Pointer; nByteCount : Word) : Word; Virtual;
Function GetInc : LongInt;
Procedure DoIncrement;
End;
TTextFile = Object(TFile)
public
Procedure WriteStr(s : String);
Procedure WriteLn (s : String);
Procedure WriteStrZ(psz : PChar);
Procedure WriteLnZ (psz : PChar);
Function ReadLn : Str80;
End;
Function IsDrivePresent (d : char) : Boolean;
Function IsDriveReadOnly(d : char) : Boolean;
Function GetDriveFree (d : char) : LongInt; { d = #0 -> default drive }
Function RemoveFile(sFileName : PathStr) : Boolean; { wildcards ok }
Function CopyFile(sDest, sSrc : PathStr) : Boolean;
Function MoveFile(sDest, sSrc : PathStr) : Boolean; { smart - renames if on same drive }
Function SetFileTime(sFileName : PathStr; dt : DateTime ) : Boolean;
Function GetFileTime(sFileName : PathStr; pDest : PDateTime) : Boolean;
Function FileExists(sFileName : PathStr): Boolean;
{ helpers }
Function memset(pDest : Pointer; c : Integer; nCount : Word) : Pointer;
Function memmove(pDest, pSrc : Pointer; nCount : Word) : Pointer;
Function UpperCaseStr(s : Str80) : Str80;
Function UpperCaseChar(c : char) : char;
Implementation
Uses Strings;
Const PASCAL_FILE_MODE_READ = 0; PASCAL_FILE_MODE_WRITE = 1; PASCAL_FILE_MODE_READWRITE = 2; PASCAL_FILE_MODE_DEFAULT = PASCAL_FILE_MODE_READWRITE;
Constructor TFile.Init;
Begin
TObject.Init;
m_bOpen := FALSE;
m_bFromHandle := FALSE;
m_sFileName := '';
m_nOpenFlags := 0;
m_nError := 0;
DOS.DOSError := 0;
End;
Destructor TFile.Done;
Begin
if((m_bOpen) And (Not m_bFromHandle)) then
Close;
TObject.Done;
End;
Function TFile.IsError : Boolean;
Begin
IsError := m_nError <> 0;
End;
Function TFile.GetError : Integer;
Begin
GetError := m_nError;
End;
Function TFile.SetError(bIOError : Boolean) : Boolean; { called after *every* Pascal IO operation }
Begin
if(bIOError) then
m_nError := System.IOResult { Pascal does only return the last error *once* }
else
m_nError := DOS.DOSError;
DOS.DOSError := 0;
SetError := IsError;
End;
Function TFile.Open(sFileName : PathStr; nOpenFlags : Word) : Boolean;
Begin
Open := FALSE;
m_sFileName := UpperCaseStr(FExpand(sFileName)); { make sure that it's a full path }
m_nOpenFlags := nOpenFlags;
if(((nOpenFlags And FILE_MODE_CREATE) = 0) And
(Not FileExists(m_sFileName))) Then
exit;
System.Assign(m_hfile, m_sFileName);
if(SetError(TRUE)) then
exit;
if( ((nOpenFlags And FILE_MODE_CREATE) <> 0) ) then
{$I-} System.Rewrite(m_hfile) {$I+}
else
Begin
if(((nOpenFlags And FILE_MODE_READ ) <> 0) And
((nOpenFlags And FILE_MODE_WRITE) <> 0)) then
System.FileMode := PASCAL_FILE_MODE_READWRITE
else
if((nOpenFlags And FILE_MODE_READ ) <> 0) then
System.FileMode := PASCAL_FILE_MODE_READ
else
System.FileMode := PASCAL_FILE_MODE_WRITE;
System.Reset(m_hfile);
System.FileMode := PASCAL_FILE_MODE_DEFAULT;
End;
if(SetError(TRUE)) then
exit;
m_bOpen := TRUE;
Open := TRUE;
End;
Function TFile.GetName : PathStr;
Begin
GetName := m_sFileName;
End;
Function TFile.GetDrive : Char;
Begin
GetDrive := m_sFileName[1];
End;
Function TFile.Close : Boolean;
Begin
Close := FALSE;
if(m_bOpen) then
Begin
System.Close(m_hfile);
Close := Not SetError(TRUE);
End;
m_bOpen := FALSE;
End;
Function TFile.Read (pDest : Pointer; nByteCount : Word) : Word;
Var
result : Word;
Begin
System.BlockRead(File(m_hfile), pDest^, nByteCount * SizeOf(Byte), result);
Read := result;
End;
Function TFile.Write(pSrc : Pointer; nByteCount : Word) : Word;
Var
result : Word;
Begin
System.BlockWrite(File(m_hfile), pSrc^, nByteCount * SizeOf(Byte), result);
Write := result;
End;
Function TFile.Remove : Boolean;
Begin
Remove := FALSE;
if(m_bOpen) then
if(Not Close) then
exit;
System.Erase(m_hfile);
Remove := Not SetError(TRUE);
End;
Function TFile.Rename(sNewName : PathStr) : Boolean;
Begin
Rename := FALSE;
if(Not Close) then
exit;
m_sFileName := UpperCaseStr(FExpand(sNewName)); { make sure that it's a full path }
System.Rename(m_hfile, m_sFileName);
Rename := Not SetError(TRUE);
End;
Function TFile.Seek(nByteCount : LongInt; nOffset : Integer) : Boolean;
Var
p : LongInt;
Begin
case(nOffset) of
FILE_SEEK_END : p := GetSize - nByteCount;
FILE_SEEK_CUR : p := GetPos + nByteCount;
FILE_SEEK_SET : p := 0 + nByteCount;
else
p := 0;
End;
System.Seek(m_hFile, p);
Seek := Not SetError(TRUE);
End;
Function TFile.GetSize : LongInt;
Begin
GetSize := System.FileSize(m_hfile);
End;
Function TFile.GetPos : LongInt;
Begin
GetPos := System.FilePos(m_hfile);
End;
Function TFile.GetTime(pDest : PDateTime) : Boolean;
Var
t : LongInt;
Begin
DOS.GetFTime(m_hfile, t);
UnpackTime(t, pDest^);
GetTime := Not SetError(FALSE); { DOSError ? }
End;
Function TFile.SetTime(dt : DateTime ) : Boolean;
Var
t : LongInt;
Begin
{ Pascal bug - 47 sec's becomes 46 !! }
PackTime(dt, t);
DOS.SetFTime(m_hfile, t);
SetTime := Not SetError(FALSE); { DOSError ? }
End;
Function IsDrivePresent(d : char) : Boolean;
Begin
IsDrivePresent := -1 <> Dos.DiskFree(Ord(UpperCaseChar(d)) - Ord('A') + 1);
End;
Function GetDriveFree(d : char) : LongInt;
Function GetDefaultDrive : Char;
Var
s : String;
Begin
GetDir(0, s);
GetDefaultDrive := s[1];
End;
Begin
If(d = #0) then
d := GetDefaultDrive
;
GetDriveFree := DiskFree(Ord(UpperCaseChar(d)) - Ord('A') + 1);
End;
Procedure TTextFile.WriteStr(s : String);
Begin
Write(@s[1], Length(s));
End;
Procedure TTextFile.WriteLn(s : String);
Begin
s := s + #13 + #10;
WriteStr(s);
End;
Procedure TTextFile.WriteStrZ(psz : PChar);
Begin
Write(psz, strlen(psz));
End;
Procedure TTextFile.WriteLnZ (psz : PChar);
Const
szCrLf : sz3 = #13 + #10;
Var
sz : sz512;
Begin
strcopy(sz, psz);
strcat(sz, szCrLf);
WriteStrZ(sz);
End;
Function TTextFile.ReadLn : Str80;
Var
p : LongInt;
len : Byte ;
s2 : str2;
s : Str80;
Begin
p := GetPos;
s2 := '12';
while((s2 <> #13 + #10) And (s2 <> #10 + #13)) do
Begin
if(2 <> Read(@s2[1], 2)) then
break;
Seek(-1, FILE_SEEK_CUR);
End;
s := '';
if((s2 = #13 + #10) Or (s2 = #10 + #13)) then
Begin
len := GetPos - p - 1;
Seek(p, FILE_SEEK_SET);
s[0] := Char(len);
Read(@s [1], len);
Read(@s2[1], 2);
End
else
Seek(p, FILE_SEEK_SET);
ReadLn := s;
End;
Constructor TIncFile.Init(nInc : LongInt);
Begin
TFile.Init;
m_nInc := nInc;
End;
Function TIncFile.Open(sFileName : PathStr; nOpenFlags : Word) : Boolean;
Begin
Open := FALSE;
if(Not TFile.Open(sFileName, nOpenFlags)) then
exit;
if(GetSize < m_nInc) then
DoIncrement;
Open := TRUE;
End;
Procedure TIncFile.DoIncrement;
Const
BLOCKSIZE = 8192;
Var
p : Pointer;
nBlockSize : Word;
nByteCount : LongInt;
nOldPos : LongInt;
Begin
nOldPos := GetPos;
nBlockSize := BLOCKSIZE;
If(nBlockSize > m_nInc) then
nBlockSize := m_nInc;
GetMem(p, nBlockSize); { get dummy data block }
memset(p, 0, nBlockSize); { zero it }
nByteCount := 0;
Seek(0, FILE_SEEK_END);
while(nByteCount < m_nInc - nBlockSize) do
begin
TFile.Write(p, nBlockSize);
inc(nByteCount, nBlockSize);
end;
nByteCount := m_nInc - nByteCount;
if(nByteCount > 0) then { write the rest }
TFile.Write(p, nByteCount);
FreeMem(p, nBlockSize);
{ close and re-open }
Close;
TFile.Open(m_sFileName, m_nOpenFlags And (Not FILE_MODE_CREATE));
Seek(nOldPos, FILE_SEEK_SET); { return pointer }
End;
Function TIncFile.Write(pSrc : Pointer; nByteCount : Word) : Word;
Begin
Write := 0;
if(GetPos + nByteCount > GetSize) then
Begin
if(GetDriveFree(GetDrive) > m_nInc) then
DoIncrement { add dummy block at end }
else
exit; { no room - exit and return 0 }
End;
Write := TFile.Write(pSrc, nByteCount);
End;
Function TIncFile.GetInc : LongInt;
Begin
GetInc := m_nInc;
End;
Function CopyFile(sDest, sSrc : PathStr) : Boolean;
Const
BLOCKSIZE = 8192;
Var
fDest, fSrc : TFile;
Procedure DoCopy;
Var
p : Pointer;
nBlockSize : Word;
dt : DateTime;
Begin
GetMem(p, nBlockSize); { get dummy data block }
nBlockSize := BLOCKSIZE;
while(nBlockSize = BLOCKSIZE) do
begin
nBlockSize := fSrc.Read(p, BLOCKSIZE);
fDest.Write(p, nBlockSize);
end;
FreeMem(p, nBlockSize);
fSrc .GetTime(@dt);
fDest.SetTime(dt);
End;
Begin
fSrc .Init;
fDest.Init;
CopyFile := FALSE;
if (fSrc .Open(sSrc , FILE_MODE_READ) And
fDest.Open(sDest, FILE_MODE_CREATE or FILE_MODE_READWRITE)) then
Begin
DoCopy;
CopyFile := TRUE;
End;
fSrc .Done;
fDest.Done;
End;
Function MoveFile(sDest, sSrc : PathStr) : Boolean;
Function IsSameDrive : Boolean;
Var
sPath1 , sPath2 : PathStr;
Begin
sPath1 := DOS.FExpand(sSrc);
sPath2 := DOS.FExpand(sDest);
IsSameDrive := UpperCaseChar(sPath1[1]) = UpperCaseChar(sPath2[1]);
End;
Function JustRename : Boolean;
Var
f : TFile;
dt : DateTime;
Begin
f.Init;
JustRename := FALSE;
if (f.Open(sSrc , FILE_MODE_READWRITE)) then
Begin
f.GetTime(@dt);
JustRename := f.Rename(sDest); { closes f }
if(f.Open(sDest, FILE_MODE_READWRITE)) then { re-open and set time }
f.SetTime(dt);
End;
f.Done;
End;
Begin
MoveFile := FALSE;
if(IsSameDrive) then { if on same drive, simple renaming will do }
Begin
MoveFile := JustRename;
exit;
End;
if(Not CopyFile(sDest, sSrc)) then
exit;
MoveFile := RemoveFile(sSrc);
End;
Function SetFileTime(sFileName : PathStr; dt : DateTime ) : Boolean;
Var
f : TFile;
Begin
SetFileTime := FALSE;
f.Init;
if(f.Open(sFileName, FILE_MODE_WRITE)) then
SetFileTime := f.SetTime(dt);
f.Done;
End;
Function GetFileTime(sFileName : PathStr; pDest : PDateTime) : Boolean;
Var
f : TFile;
Begin
GetFileTime := FALSE;
f.Init;
if(f.Open(sFileName, FILE_MODE_READ)) then
GetFileTime := f.GetTime(pDest);
f.Done;
End;
Function RemoveFile(sFileName : PathStr) : Boolean; { remove one or more files }
var
sr : SearchRec;
bOK : Boolean;
f : TFile;
sDir : DirStr;
sName : NameStr; { Not used }
sExt : ExtStr; { Not used }
Begin
f.Init;
DOS.FSplit(sFileName, sDir, sName, sExt);
DOS.FindFirst(sFileName, Archive, sr);
bOK := DosError = 0;
while(DosError = 0) do
begin
if(f.Open(sDir + sr.Name, FILE_MODE_WRITE)) then
bOK := bOK And f.Remove;
DOS.FindNext(sr);
end;
f.Done;
RemoveFile := bOK;
End;
function FileExists(sFileName : PathStr) : Boolean;
{ Boolean function that returns True if the file exists; otherwise,
it returns False. Closes the file if it exists. }
Var
f : file;
Begin
FileExists := FALSE;
if(sFileName = '') then
exit;
{$I-}
System.Assign(f, sFileName);
System.FileMode := PASCAL_FILE_MODE_READ;
System.Reset(F);
System.Close(F);
{$I+}
System.FileMode := PASCAL_FILE_MODE_DEFAULT;
FileExists := (System.IOResult = 0);
End; { FileExists }
Function IsDriveReadOnly(d : char) : Boolean;
Var
f : file;
Begin
IsDriveReadOnly := FALSE;
{$I-}
System.Assign(f, d + ':\plzualzw.azq');
System.FileMode := PASCAL_FILE_MODE_READWRITE;
System.Rewrite(f);
System.Close(f);
{$I+}
System.FileMode := PASCAL_FILE_MODE_DEFAULT;
if(System.IOResult = 0) then { was created - not read only}
Begin
System.Erase(f);
exit;
End;
IsDriveReadOnly := TRUE;
End; { FileExists }
Function UpperCaseChar(c : char) : char;
begin
Case(c) of
'' : UpperCaseChar := '';
'' : UpperCaseChar := '';
'' : UpperCaseChar := '';
else
UpperCaseChar := UpCase(c);
end;
end;
Function UpperCaseStr(s : Str80) : Str80;
Var
a:integer;
begin
For a := 1 to Length(s) do
s[a] := UpperCaseChar(s[a]);
UpperCaseStr := s;
end;
Function memset(pDest : Pointer; c : Integer; nCount : Word) : Pointer;
Begin
System.FillChar(pDest^, nCount, c);
memset := pDest;
End;
Function memmove(pDest, pSrc : Pointer; nCount : Word) : Pointer;
Begin
move(pSrc^, pDest^, nCount);
memmove := pDest;
End;
Begin end.
This page © Troels Knakkergaard. Updated march 30rd 1999