My Training Period: pp hours. Before you begin, read someinstruction here. Functions and structure used in the program examples were dumped at Win32 functions & structures.
The Win32 programming abilities:
A Simple Searching and Sorting
Searching and Sorting Functions
|
The bsearch() function performs a binary search of a sorted array of num elements, each of width bytes in size.
The base value is a pointer to the base of the array to be searched, and key is the value being sought.
The compare parameter is a pointer to a user-supplied routine that compares two array elements and returns a value specifying their relationship.
bsearch() calls the compare routine one or more times during the search, passing pointers to two array elements on each call.
The compare routine compares the elements, and then returns one of the following values:
Value returned by compare routine | Description |
< 0 | element1 less than element2 |
0 | element1 equal to element2 |
> 0 | element1 greater than element2 |
The following program example reads the command-line parameters, sorting them with qsort(), and then uses bsearch() to find the word "red". This program was run at the command prompt lol!
/*****program example using bsearch()*****/
/*****If it is C++, better to use STL****/
#include <search.h>
#include <string.h>
#include <stdio.h>
/* a compare function prototype */
int compare(char **arg1, char *arg2[ ]);
int main(int argc, char *argv[ ])
{
char **result;
char *key = "red";
int i;
if(argc == 1)
{
printf("Usage: %s <some strings> \n", argv[0]);
printf("Example: %s blue yellow red black white\n", argv[0]);
exit(1);
}
/* sort using qsort algorithm */
qsort((void *)argv, (size_t)argc, sizeof(char *), (int (*)(const void*, const void*))compare);
/* output the sorted list */
for(i = 0; i < argc; ++i)
printf("%s ", argv[i]);
/* find the word "cat" using a binary search algorithm */
result = (char **)bsearch((char *) &key, (char *)argv, argc, sizeof(char *), (int (*)(const void*, const void*))compare);
if(result)
printf("\n\'%s\' word found at address: %p\n", *result, result);
else
printf("\n\'%s\' word not found!\n", *result);
return 0;
}
int compare(char *arg1[ ], char *arg2[ ])
{
/* compare all of both strings */
return _strcmpi(*arg1, *arg2);
}
The output:
G:\vcnetprojek\mcruntime\Debug>mcruntime blue yellow red black white
black blue mcruntime red white yellow
'red' word found at address: 00320CF4
A screen shot when run using VC++ EE 2005.
The _ftime() and localtime() functions use the TZ, the environment time variable (look like it stands for Time Zone).
If TZ is not set, the run-time library attempts to use the time-zone information specified by the operating system specified in the Control Panel's Date/Time application. If this information is unavailable, these functions use the default value of PST8PDT, which signifies the Pacific Time zone (Daylight Saving Time – DST and three letters for daylight-saving-time zone – PDT).
Function | The Use |
asctime(), _wasctime() | Convert time from type struct tm to character string. |
clock() | Return elapsed CPU time for process. |
ctime(), _ctime64(), _wctime(), _wctime64() | Convert time from type time_t or __time64_t to character string. |
difftime() | Compute difference between two times. |
_ftime(), _ftime64() | Store current system time in variable of type struct _timeb or type struct __timeb64. |
_futime(), _futime64() | Set modification time on open file. |
gmtime(), _gmtime64() | Convert time from type time_t to struct tm or from type __time64_t to struct tm. |
localtime(), _localtime64() | Convert time from type time_t to struct tm or from type __time64_t to struct tmwith local correction. |
mktime(), _mktime64() | Convert time to calendar value. |
_strdate(), _wstrdate() | Return current system date as string. |
strftime(), wcsftime() | Format date-and-time string for international use. |
_strtime(), _wstrtime() | Return current system time as string. |
time(), _time64() | Get current system time as typetime_t or as type __time64_t. |
_tzset() | Set external time variables from environment time variable, TZ. |
_utime(), _utime64(), _wutime(), _wutime64() | Set modification time for specified file using either current time or time value stored in structure. |
Table 14: Time management functions. |
In all versions of Microsoft C/C++ except Microsoft C/C++ version 7.0, and in all versions of Microsoft Visual C++, the time() function returns the current time as the number of seconds elapsed since midnight on January 1, 1970.
In Microsoft C/C++ version 7.0, time() returned the current time as the number of seconds elapsed since midnight on December 31, 1899.
The following Table is an example of the needed information in order to use the time() and _time64() functions.
Information | Description |
The function | time(),_time64(). |
The use | Get the system time. |
The prototype | time_t time(time_t *timer); __time64_t _time64(__time64_t *timer); |
The parameters | timer – Is a pointer to the storage location for time. |
Example | __time64_t lgtime;
_time64(&lgtime); |
The return value | Return the time in elapsed seconds. There is no error return. A call to time() or _time64() can fail, however, if the date passed to the function is:
|
The header files | <time.h> |
Table 15: time(),_time64() functions information. |
The time() function returns the number of seconds elapsed since midnight (00:00:00), January 1, 1970, coordinated universal time (UTC), according to the system clock.
The return value is stored in the location given by timer. This parameter may be NULL, in which case the return value is not stored.
Other information needed is the structures used for the time functions. For this thingy, you have to find it yourself in the documentation :o).
Let try using the functions in a program example.
/* playing with time. Program example for time management functions...*/
#include <time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <string.h>
int main()
{
char buff[128], ampm[ ] = "AM";
__time64_t lgtime;
struct __timeb64 timstruct;
struct tm *today, *thegmt, xmas = {0, 0, 12, 25, 11, 90};
/* set time zone from TZ environment variable. If TZ is not set,
* the operating system is queried to obtain the default value
* for the variable.*/
_tzset();
/* get UNIX-style time and display as number and string. */
_time64(&lgtime);
printf("Time in seconds since UTC 1/1/70:\t%ld seconds\n", lgtime);
printf("UNIX time and date:\t\t\t%s", _ctime64(&lgtime));
/* display UTC. */
thegmt = _gmtime64(&lgtime);
printf("Coordinated universal time, UTC:\t%s", asctime(thegmt));
/* display operating system-style date and time. */
_strtime(buff);
printf("OS time:\t\t\t\t%s\n", buff);
_strdate(buff);
printf("OS date:\t\t\t\t%s\n", buff);
/* convert to time structure and adjust for PM if necessary. */
today = _localtime64(&lgtime);
if(today->tm_hour >= 12)
{
strcpy(ampm, "PM");
today->tm_hour -= 12;
}
/* adjust if midnight hour. */
if(today->tm_hour == 0)
today->tm_hour = 12;
/* pointer addition is used to skip the first 11
* characters and printf() is used to trim off terminating
* characters.*/
printf("12-hour time:\t\t\t\t%.8s %s\n", asctime(today) + 11, ampm);
/* print additional time information. */
_ftime64(&timstruct);
printf("Plus milliseconds:\t\t\t%u\n", timstruct.millitm);
printf("Zone difference in hours from UTC:\t%u hours\n", timstruct.timezone/60);
printf("Time zone name:\t\t\t\t%s\n", _tzname[0]);
printf("Daylight savings:\t\t\t%s\n", timstruct.dstflag ? "YES" : "NOT SET");
/* make time for noon on Christmas, 1990. */
if(_mktime64(&xmas) != (__time64_t)-1)
printf("Christmas\t\t\t\t%s", asctime(&xmas));
/* use time structure to build a customized time string. */
today = _localtime64(&lgtime);
/* use strftime to build a customized time string. */
strftime(buff, 128, "Today is %A, day %d of %B in the year %Y.\n", today);
printf(buff);
return 0;
}
|
The following Table lists the needed information in order to use the perror() function.
Information | Description |
The function | perror(). |
The use | Print an error message. |
The prototype | void perror( const char *string ); /* for wide character */ void _wperror( const wchar_t *string ); |
The parameters | string - String message to print. |
Example | perror("Using perror()"); |
The return value | void. |
The header files | <stdio.h> or <wchar.h> (for _wperror()). |
Table 16: perror() function information. |
The perror() function prints an error message to stderr. _wperror() is a wide-character version of _perror().
The string argument to _wperror() is a wide-character string. _wperror() and _perror() behave identically otherwise.
errno is set on an error in a system-level call. Because errno holds the value for the last call that set it, this value may be changed by succeeding calls.
Always check errno immediately before and after a call that may set it. All errno values, defined as manifest constants in errno.h, are UNIX-compatible. The values valid for 32-bit Windows applications are a subset of these UNIX values.
The following errno values are compatible with 32-bit Windows applications. Only ERANGE and EDOM are specified in the ANSI standard.
Constant | System error message | Value |
E2BIG | Argument list too long | 7 |
EACCES | Permission denied | 13 |
EAGAIN | No more processes or not enough memory or maximum nesting level reached | 11 |
EBADF | Bad file number | 9 |
ECHILD | No spawned processes | 10 |
EDEADLOCK | Resource deadlock would occur | 36 |
EDOM | Math argument | 33 |
EEXIST | File exists | 17 |
EINVAL | Invalid argument | 22 |
EMFILE | Too many open files | 24 |
ENOENT | No such file or directory | 2 |
ENOEXEC | Exec format error | 8 |
ENOMEM | Not enough memory | 12 |
ENOSPC | No space left on device | 28 |
ERANGE | Result too large | 34 |
EXDEV | Cross-device link | 18 |
Table 17: errno values. |
The following Table lists the needed information in order to use the strerror(), _strerror(), _wcserror(), __wcserror() functions.
Information | Description |
The function | strerror(),_strerror(),_wcserror(),__wcserror(). |
The use | Get a system error message (strerror(),_wcserror()) or prints a user-supplied error message (_strerror(),__wcserror()). |
The prototype | char *strerror(int errnum); char *_strerror(const char *strErrMsg); wchar_t * _wcserror(int errnum); wchar_t * __wcserror(const wchar_t *strErrMsg); |
The parameters | errnum - Error number. strErrMsg - User-supplied message. |
Example | strerror(errno); |
The return value | All these functions return a pointer to the error-message string. Subsequent calls can overwrite the string. |
The header files | <string.h>. |
Table 18: strerror() family functions information. |
The strerror() function maps errnum to an error-message string, returning a pointer to the string. Neither strerror() nor _strerror() actually prints the message.
For this condition, you need to call an output function such as fprintf() as shown below.
if((_access("thefile.dat", 13)) == -1)
fprintf(stderr, strerror(NULL));
If strErrMsg is passed as NULL, _strerror() returns a pointer to a string containing the system error message for the last library call that produced an error.
The error-message string is terminated by the newline character ('\n'). If strErrMsg is not equal to NULL, then _strerror() returns a pointer to a string containing (in order) your string message, a colon, a space, the system error message for the last library call producing an error, and a newline character.
Your string message can be, at most, 94 characters long. Later on, in Win32 programming you will be introduced to another function, GetLastError().
The following program example uses the error message handling functions.
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <io.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
char fname[20] = "testnofile.txt";
int fhndl;
/* try to open non-existing file */
if((fhndl = _open(fname, _O_RDONLY)) == -1)
{
/* using the possible ways to create error message */
perror("Using perror()");
printf("Using strerror(): %s\n", strerror(errno));
printf(_strerror("_strerror()"));
printf("There is some error opening %s file\n", fname);
}
else
{
printf("Open %s for reading succeeded!\n", fname);
_close(fhndl);
}
return 0;
}
A sample output:
|
Well, that all folks. Hopefully you already knew how to find the needed information in order to use functions when developing your programs for Windows. The next step, get the Visual C++/MSDN/SDK Platform documentations and start digging your own favorite and other categories of functions.
It seems that other than the Graphic User interface (GUI), just with C/C++, it is not enough to do the Windows programming that cover all aspect of the Windows OS. In the next Module we will try the Win32 application programming interface (API).
For the .Net programming, keep in mind that there are C/C++ functions (run-time libraries) and the .NET framework equivalents, so dig it yourself!
If you already have the knowledge and skills how to program in C/C++, what have been discussed in this Module is nothing new isn’t it? It should be a business as usual :o).
-------------------------------------Pre End----------------------------------------
Before we proceed to the next Module, Win32 programming, let have some thought about the following lousy search file program example. This program example run at command prompt, compiled using VC++ .Net, empty console mode application on Win Xp Pro SP2. A lot more things can be done to this program :o). Have a nice thought!
Some silly thought: The pseudo code/algorithm
Receive the filename as a command line argument, store it somewhere
First of all get all the available drives in the machine, use _getdrives()
Translate all the bitmask to the respective drive.
For every found drive search the file, call user defined FindThePath()
Strip the filename string, call the user defined TheRightSide()
Test the filename string, call the user defined TestTheStrings()
Test the wildcards/extensions if any
If it is a directory, it is current working or parent directory (. and ..)?
If it is a valid root/drive, search in the current root
If not found, construct a path to traverse to other directories in finding the file
Recurse, call FindThePath()
If found, construct the full path and display it/return.
/**************************************************
Optionally, implement other routines or function calls for the found file.
***************************************************/
If finish, no more files, no more directories in current drive, return to the main program's
loop
Repeat for other drives
Then, comes the silly program:
/***** mysearch.cpp *********/
/* compiled using VC++ 7.0/.Net, run at command prompt, tested on Win Xp Pro machine */
#include <windows.h>
#include <direct.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <io.h>
#include <tchar.h>
/* prototype for recursive function */
void FindThePath(TCHAR*, TCHAR*, TCHAR*);
/* filename stripping */
static void TheRightside(TCHAR *str, size_t num)
{
size_t i = _tcslen(str);
size_t chs = 0, chr = 0;
/* may use malloc() */
TCHAR *hld = new TCHAR[num+1];
if((i > num) || (i == num))
{
for(chs = (i-num); chs<i; chs++)
{
hld[chr] = str[chs];
++chr;
}
hld[num] = '\0';
for(chs = 0; chs < (num+1); chs++)
{
str[chs] = hld[chs];
}
}
/* don't forget to free() up if using malloc() */
delete [ ] hld;
}
/* test the searched string/filename */
static bool TestTheStrings(TCHAR *str1, TCHAR *str2)
{
char *mp,*cp;
size_t x1 = 0;
bool ret = true;
x1 = _tcslen(str1);
if(_tcslen(str2) != (unsigned)x1)
{ret = false;}
else
{
for(size_t i=0; i<x1; i++)
{
if(str1[i] != str2[i])
ret = false;
}
}
while ((*str2) && (*str1 != '*'))
{
if((*str1 != *str2) && (*str1 != '?'))
{return 0;}
str1++;
str2++;
}
while (*str2)
{
if(*str1 == '*')
{
if(!*++str1)
{return 1;}
mp = str1;
cp = str2+1;
}
else if ((*str1 == *str2) || (*str1 == '?'))
{
str1++;
str2++;
}
else
{
str1 = mp;
str2 = cp++;
}
}
while (*str1 == '*')
{str1++;}
return !*str1;
return ret;
}
/* search and construct the path */
void FindThePath(TCHAR *root, TCHAR *file, TCHAR *buffer)
{
HANDLE hFind;
BOOL ok;
WIN32_FIND_DATA fd;
TCHAR start[_MAX_PATH];
TCHAR dir[_MAX_PATH];
/* search flag */
bool found = false;
TCHAR test[_MAX_PATH];
/* using test as a temp location */
_tcscpy(test, buffer);
/* get the length of the file name */
size_t tlen = _tcslen(file);
/* strip off possible filename if it is in the buffer */
TheRightside(test, tlen);
/* test for it */
if(TestTheStrings(test, file))
/* return if it is there...no need to look further */
{return;}
/* if they passed a valid string for the root */
if(_tcslen(root) > 0)
{
/* modify it to search for all files/folders.... */
_tcscpy(start, root);
_tcscat(start, "\\*");
}
for(hFind = FindFirstFile(start, &fd), ok = 1;
hFind != INVALID_HANDLE_VALUE && ok;
ok = FindNextFile(hFind, &fd))
{
// bitwise AND to test the returned fileattributes
if(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
/* assuming it is a dir so copy over */
_tcscpy(dir, fd.cFileName);
/* make sure it is not the default parent directory...*/
if((TestTheStrings(dir, ".") == false))
{
/* or current working directory, copying this over results in infinite recursion */
if((TestTheStrings(dir, "..") == false))
{
if(found == false)
/* if we have not found the file in this current call, then make a call */
{
/* constructs the path or you can try the _fullpath() */
_tcscpy(test, root);
_tcscat(test, "\\");
_tcscat(test, dir);
/*and recurse through them.....*/
FindThePath(test, file, buffer);
}/* end found */
}/* end .. test */
}/* end . test */
}/* end dir test */
/* bitwise AND to check for file flag */
if(fd.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)
{
/* if we have a file is it the one we want? */
found = TestTheStrings(file, fd.cFileName);
if(found == true)
{
/* if it is, then create the full path name for it and copy into the buffer */
_tcscpy(buffer, root);
_tcscat(buffer, "\\");
_tcscat(buffer, fd.cFileName);
printf("%s\n", buffer);
/*-----------------------------------------------------------------*/
/* other routines can be implemented/called here for the found files */
/* such as delete, rename, replace, search the file contents, */
/* move, append, create a file, change the file attributes etc... */
/*-----------------------------------------------------------------*/
}/* end found test */
}/* end archive test */
}/* end for */
/* 18 = ERROR_NO_MORE_FILES, that is if other than no more files... */
/* Check the error if any */
if(GetLastError() != 18)
printf("FindFile() error: %d\n", GetLastError());
if(hFind != INVALID_HANDLE_VALUE)
{
BOOL ok = FindClose(hFind);
if(!ok)
/* check the last error if any, very good for troubleshooting/debug */
printf("FindClose() error: %d", GetLastError());
}
}
/* the main() program */
int main(int argc, char *argv[ ])
{
TCHAR buffer[_MAX_PATH];
TCHAR mydrives[ ] = "A:";
/* if not enough arguments supplied */
if(argc != 2)
{
printf("Example usage: %s <test.txt> or <test.*> or <*.txt> or <*.*>\n", argv[0]);
printf("It is case sensitive!\n");
/* just exit */
exit (-1);
}
/*Some prompt*/
printf("Example usage: %s <test.txt> or <test.*> or <*.txt> or <*.*>\n", argv[0]);
printf("It is case sensitive!\n\n");
/* get the drives bit masks...1 is available, 0 is not available */
/* A = least significant bit... */
ULONG DriveMask = _getdrives();
/* if something wrong */
if(DriveMask == 0)
printf("_getdrives() failed with failure code: %d\n", GetLastError());
while(DriveMask)
{
printf("Found %s and start digging...\n", mydrives);
if(DriveMask & 1)
printf(mydrives);
/* for every found drive...do the file search */
FindThePath(mydrives, argv[1], buffer);
/* pointer increment */
++mydrives[0];
/* shift the bit masks binary to the right and repeat, means go to the next found drive */
DriveMask >>= 1;
}
return 0;
}
A sample output:
G:\vcnetprojek\win32prog\Debug>mysearch
Example usage: mysearch <test.txt> or <test.*> or <*.txt> or <*.*>
It is case sensitive!
G:\vcnetprojek\win32prog\Debug>mysearch *.txt
Example usage: mysearch <test.txt> or <test.*> or <*.txt> or <*.*>
It is case sensitive!
Found A: and start digging...
A:FindFile() error: 21
Found B: and start digging...
FindFile() error: 3
Found C: and start digging...
C:C:\backup.txt
C:\BDE32\DOC\bde32tok.txt
C:\BDE32\EXAMPLES\QUERY\query.txt
C:\BDE32\EXAMPLES\SNIPIT\snipit.txt
C:\CBuilderX\buildnum.txt
C:\CBuilderX\jdk1.4\bin\client\Xusage.txt
C:\CBuilderX\jdk1.4\lib\jvm.hprof.txt
C:\CBuilderX\jdk1.4\lib\jvm.jcov.txt
C:\CBuilderX\jdk1.4\README.txt
C:\CBuilderX\jdk1.4\THIRDPARTYLICENSEREADME.txt
C:\CBuilderX\thirdparty\InstallShield_MultiPlatform\help\estimatedTimetoInstall.txt
C:\CBuilderX\thirdparty\InstallShield_MultiPlatform\help\SampleRecordsFile.txt
C:\CBuilderX\thirdparty\InstallShield_MultiPlatform\help\SampleResponseFile.txt
C:\CBuilderX\thirdparty\InstallShield_MultiPlatform\idewizards\exclude.txt
C:\CBuilderX\thirdparty\InstallShield_MultiPlatform\launch.txt
C:\CBuilderX\thirdparty\InstallShield_MultiPlatform\platforms\as400ppk\ibmas400_topic_ProductBeanTemplate.txt
…
[Has been trimmed]
…
Further reading and digging:
Check the best selling C, C++ and Windows books at Amazon.com.
Microsoft Visual C++, online MSDN.
For Multibytes, Unicode characters and Localization please refer toLocale, Wide Character & Unicode (Story) and Windows Users & Groups tutorial (Implementation).
Notation used in MSDN isHungarian Notation instead of CamelCase and is discussed inC & C++ Notations.