php-src/sapi/isapi/stresstest/stresstest.cpp
Zeev Suraski 9877acdb32 - Implement CompareStringWithFile()
- Remove a stupid NULL assignment that slipped in the last commit
2001-01-15 15:13:50 +00:00

854 lines
20 KiB
C++

/*
* ======================================================================= *
* File: stress .c *
* stress tester for isapi dll's *
* based on cgiwrap *
* ======================================================================= *
*
*/
#define WIN32_LEAN_AND_MEAN
#include <afx.h>
#include <afxtempl.h>
#include <winbase.h>
#include <winerror.h>
#include <httpext.h>
#include <stdio.h>
#include <stdlib.h>
// These are things that go out in the Response Header
//
#define HTTP_VER "HTTP/1.0"
#define SERVER_VERSION "Http-Srv-Beta2/1.0"
//
// Simple wrappers for the heap APIS
//
#define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))
#define xfree(s) HeapFree(GetProcessHeap(),0,(s))
//
// The mandatory exports from the ISAPI DLL
//
#define NUM_THREADS 10
#define ITERATIONS 1
HANDLE StartNow;
// quick and dirty environment
typedef CMapStringToString TEnvironment;
TEnvironment IsapiEnvironment;
typedef struct _TResults {
LONG ok;
LONG bad;
} TResults;
CStringArray IsapiFileList; // list of filenames
CStringArray TestNames; // --TEST--
CStringArray IsapiGetData; // --GET--
CStringArray IsapiPostData; // --POST--
CStringArray IsapiMatchData; // --EXPECT--
CArray<TResults, TResults> Results;
typedef struct _TIsapiContext {
HANDLE in;
HANDLE out;
DWORD tid;
TEnvironment env;
} TIsapiContext;
//
// Prototypes of the functions this sample implements
//
extern "C" {
HINSTANCE hDll;
typedef BOOL (WINAPI *VersionProc)(HSE_VERSION_INFO *) ;
typedef DWORD (WINAPI *HttpExtProc)(EXTENSION_CONTROL_BLOCK *);
BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *, TIsapiContext *) ;
BOOL WINAPI GetServerVariable(HCONN, LPSTR, LPVOID, LPDWORD );
BOOL WINAPI ReadClient(HCONN,LPVOID,LPDWORD);
BOOL WINAPI WriteClient(HCONN,LPVOID,LPDWORD,DWORD);
BOOL WINAPI ServerSupportFunction(HCONN,DWORD,LPVOID,LPDWORD,LPDWORD);
VersionProc IsapiGetExtensionVersion;
HttpExtProc IsapiHttpExtensionProc;
HSE_VERSION_INFO version_info;
}
char * MakeDateStr(VOID);
char * GetEnv(char *);
DWORD CALLBACK IsapiThread(void *);
int stress_main(const char *filename,
const char *arg,
const char *postfile,
const char *matchdata);
BOOL bUseTestFiles = FALSE;
char temppath[MAX_PATH];
void stripcrlf(char *line)
{
DWORD l = strlen(line)-1;
if (line[l]==10 || line[l]==13) line[l]=0;
l = strlen(line)-1;
if (line[l]==10 || line[l]==13) line[l]=0;
}
#define COMPARE_BUF_SIZE 1024
BOOL CompareFiles(const char*f1, const char*f2)
{
FILE *fp1, *fp2;
bool retval;
char buf1[COMPARE_BUF_SIZE], buf2[COMPARE_BUF_SIZE];
int length1, length2;
if ((fp1=fopen(f1, "r"))==NULL) {
return FALSE;
}
if ((fp2=fopen(f2, "r"))==NULL) {
fclose(fp1);
return FALSE;
}
retval = TRUE; // success oriented
while (true) {
length1 = fread(buf1, 1, sizeof(buf1), fp1);
length2 = fread(buf2, 1, sizeof(buf2), fp2);
// check for end of file
if (feof(fp1)) {
if (!feof(fp2)) {
retval = FALSE;
}
break;
} else if (feof(fp2)) {
if (!feof(fp1)) {
retval = FALSE;
}
break;
}
// compare data
if (length1!=length2
|| memcmp(buf1, buf2, length1)!=0) {
retval = FALSE;
break;
}
}
fclose(fp1);
fclose(fp2);
return retval;
}
BOOL CompareStringWithFile(const char *filename, const char *str, unsigned int str_length)
{
FILE *fp;
bool retval;
char buf[COMPARE_BUF_SIZE];
unsigned int offset=0, readbytes;
if ((fp=fopen(filename, "r"))==NULL) {
return FALSE;
}
retval = TRUE; // success oriented
while (true) {
readbytes = fread(buf, 1, sizeof(buf), fp);
// check for end of file
if (feof(fp)) {
break;
}
if (offset+readbytes > str_length
|| memcmp(buf, str+offset, readbytes)!=NULL) {
retval = FALSE;
break;
}
}
fclose(fp);
return retval;
}
BOOL ReadGlobalEnvironment(const char *environment)
{
if (environment) {
FILE *fp = fopen(environment, "r");
DWORD i=0;
if (fp) {
char line[2048];
while (fgets(line,sizeof(line)-1,fp)) {
// file.php arg1 arg2 etc.
char *p = strchr(line, '=');
if (p) {
*p=0;
IsapiEnvironment[line]=p+1;
}
}
fclose(fp);
return IsapiEnvironment.GetCount() > 0;
}
}
return FALSE;
}
BOOL ReadFileList(const char *filelist)
{
FILE *fp = fopen(filelist, "r");
if (!fp) {
printf("Unable to open %s\r\n", filelist);
}
char line[2048];
int i=0;
while (fgets(line,sizeof(line)-1,fp)) {
// file.php arg1 arg2 etc.
stripcrlf(line);
if (strlen(line)>3) {
char *p = strchr(line, ' ');
if (p) {
*p = 0;
// get file
IsapiFileList.Add(line);
IsapiGetData.Add(p+1);
} else {
// just a filename is all
IsapiFileList.Add(line);
IsapiGetData.Add("");
}
}
// future use
IsapiPostData.Add("");
IsapiMatchData.Add("");
TestNames.Add("");
i++;
}
Results.SetSize(TestNames.GetSize());
fclose(fp);
return IsapiFileList.GetSize() > 0;
}
void DoThreads() {
if (IsapiFileList.GetSize() == 0) {
printf("No Files to test\n");
return;
}
printf("Starting Threads...\n");
// loop creating threads
DWORD tid;
HANDLE threads[NUM_THREADS];
for (DWORD i=0; i< NUM_THREADS; i++) {
threads[i]=CreateThread(NULL, 0, IsapiThread, NULL, CREATE_SUSPENDED, &tid);
}
for (i=0; i< NUM_THREADS; i++) {
if (threads[i]) ResumeThread(threads[i]);
}
// wait for threads to finish
WaitForMultipleObjects(NUM_THREADS, threads, TRUE, INFINITE);
}
void DoFileList(const char *filelist, const char *environment)
{
// read config files
if (!ReadFileList(filelist)) {
printf("No Files to test!\r\n");
return;
}
ReadGlobalEnvironment(environment);
DoThreads();
}
/**
* ParseTestFile
* parse a single phpt file and add it to the arrays
*/
BOOL ParseTestFile(const char *path, const char *fn)
{
// parse the test file
char filename[MAX_PATH];
_snprintf(filename, sizeof(filename)-1, "%s\\%s", path, fn);
char line[1024];
memset(line, 0, sizeof(line));
CString cTest, cSkipIf, cPost, cGet, cFile, cExpect;
printf("Reading %s\r\n", filename);
enum state {none, test, skipif, post, get, file, expect} parsestate = none;
FILE *fp = fopen(filename, "r");
char *tn = _tempnam(temppath,"pht.");
char *en = _tempnam(temppath,"exp.");
FILE *ft = fopen(tn, "w+");
FILE *fe = fopen(en, "w+");
if (fp && ft && fe) {
while (fgets(line,sizeof(line)-1,fp)) {
if (line[0]=='-') {
if (_strnicmp(line, "--TEST--", 8)==0) {
parsestate = test;
continue;
} else if (_strnicmp(line, "--SKIPIF--", 10)==0) {
parsestate = skipif;
continue;
} else if (_strnicmp(line, "--POST--", 8)==0) {
parsestate = post;
continue;
} else if (_strnicmp(line, "--GET--", 7)==0) {
parsestate = get;
continue;
} else if (_strnicmp(line, "--FILE--", 8)==0) {
parsestate = file;
continue;
} else if (_strnicmp(line, "--EXPECT--", 10)==0) {
parsestate = expect;
continue;
}
}
switch (parsestate) {
case test:
stripcrlf(line);
cTest = line;
break;
case skipif:
cSkipIf += line;
break;
case post:
cPost += line;
break;
case get:
cGet += line;
break;
case file:
fputs(line, ft);
break;
case expect:
fputs(line, fe);
break;
}
}
fclose(fp);
fclose(ft);
fclose(fe);
if (!cTest.IsEmpty()) {
IsapiFileList.Add(tn);
TestNames.Add(cTest);
IsapiGetData.Add(cGet);
IsapiPostData.Add(cPost);
IsapiMatchData.Add(en);
return TRUE;
}
}
return FALSE;
}
/**
* GetTestFiles
* Recurse through the path and subdirectories, parse each phpt file
*/
BOOL GetTestFiles(const char *path)
{
// find all files .phpt under testpath\tests
char FindPath[MAX_PATH];
WIN32_FIND_DATA fd;
memset(&fd, 0, sizeof(WIN32_FIND_DATA));
_snprintf(FindPath, sizeof(FindPath)-1, "%s\\*.*",path);
HANDLE fh = FindFirstFile(FindPath, &fd);
if (fh != INVALID_HANDLE_VALUE) {
do {
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
!strchr(fd.cFileName, '.')) {
// subdirectory, recurse into it
char NewFindPath[MAX_PATH];
_snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s",path, fd.cFileName);
GetTestFiles(NewFindPath);
} else if (strstr(fd.cFileName, ".phpt")) {
// got test file, parse it now
if (ParseTestFile(path, fd.cFileName)) {
printf("Test File Added: %s\\%s\r\n", path, fd.cFileName);
}
}
memset(&fd, 0, sizeof(WIN32_FIND_DATA));
} while (FindNextFile(fh, &fd) != 0);
FindClose(fh);
}
return IsapiFileList.GetSize() > 0;
}
void DeleteTempFiles(const char *mask)
{
char FindPath[MAX_PATH];
WIN32_FIND_DATA fd;
memset(&fd, 0, sizeof(WIN32_FIND_DATA));
_snprintf(FindPath, sizeof(FindPath)-1, "%s\\%s",temppath, mask);
HANDLE fh = FindFirstFile(FindPath, &fd);
if (fh != INVALID_HANDLE_VALUE) {
do {
char NewFindPath[MAX_PATH];
_snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s",temppath, fd.cFileName);
DeleteFile(NewFindPath);
memset(&fd, 0, sizeof(WIN32_FIND_DATA));
} while (FindNextFile(fh, &fd) != 0);
FindClose(fh);
}
}
void DoTestFiles(const char *filelist, const char *environment)
{
if (!GetTestFiles(filelist)) {
printf("No Files to test!\r\n");
return;
}
Results.SetSize(IsapiFileList.GetSize());
ReadGlobalEnvironment(environment);
DoThreads();
printf("\r\nRESULTS:\r\n");
// show results:
DWORD r = Results.GetSize();
for (DWORD i=0; i< r; i++) {
TResults result = Results.GetAt(i);
printf("%s\r\nOK: %d FAILED: %d\r\n", TestNames.GetAt(i), result.ok, result.bad);
}
// delete temp files
printf("Deleting Temp Files\r\n");
DeleteTempFiles("exp.*");
DeleteTempFiles("pht.*");
printf("Done\r\n");
}
int main(int argc, char* argv[])
{
LPVOID lpMsgBuf;
char *filelist=NULL, *environment=NULL;
if (argc < 3) {
// look for phpt files in tests
printf("USAGE: stresstest [L|T] filelist [environment]\r\n");
return 0;
} else {
if (argv[1][0]=='T') bUseTestFiles = TRUE;
if (argc > 1) filelist = argv[2];
if (argc > 2) environment = argv[3];
}
GetTempPath(sizeof(temppath), temppath);
hDll = LoadLibrary("php4isapi.dll"); // Load our DLL
if (!hDll) {
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
fprintf(stderr,"Error: Dll 'php4isapi.dll' not found -%d\n%s\n", GetLastError(),lpMsgBuf);
LocalFree( lpMsgBuf );
return -1;
}
//
// Find the exported functions
IsapiGetExtensionVersion = (VersionProc)GetProcAddress(hDll,"GetExtensionVersion");
if (!IsapiGetExtensionVersion) {
fprintf(stderr,"Can't Get Extension Version %d\n",GetLastError());
return -1;
}
IsapiHttpExtensionProc = (HttpExtProc)GetProcAddress(hDll,"HttpExtensionProc");
if (!IsapiHttpExtensionProc) {
fprintf(stderr,"Can't Get Extension proc %d\n",GetLastError());
return -1;
}
// This should really check if the version information matches what we
// expect.
//
if (!IsapiGetExtensionVersion(&version_info) ) {
fprintf(stderr,"Fatal: GetExtensionVersion failed\n");
return -1;
}
if (bUseTestFiles) {
char TestPath[MAX_PATH];
if (filelist != NULL)
_snprintf(TestPath, sizeof(TestPath)-1, "%s\\tests", filelist);
else strcpy(TestPath, "tests");
DoTestFiles(TestPath, environment);
} else {
DoFileList(filelist, environment);
}
// cleanup
// We should really free memory (e.g., from GetEnv), but we'll be dead
// soon enough
FreeLibrary(hDll);
return 0;
}
DWORD CALLBACK IsapiThread(void *p)
{
DWORD filecount = IsapiFileList.GetSize();
for (DWORD j=0; j<ITERATIONS; j++) {
for (DWORD i=0; i<filecount; i++) {
// execute each file
CString testname = TestNames.GetAt(i);
BOOL ok = FALSE;
if (stress_main(IsapiFileList.GetAt(i),
IsapiGetData.GetAt(i),
IsapiPostData.GetAt(i),
IsapiMatchData.GetAt(i))) {
InterlockedIncrement(&Results[i].ok);
ok = TRUE;
} else {
InterlockedIncrement(&Results[i].bad);
ok = FALSE;
}
if (testname.IsEmpty()) {
printf("Thread %d File %s\n", GetCurrentThreadId(), IsapiFileList.GetAt(i));
} else {
printf("tid %d: %s %s\n", GetCurrentThreadId(), testname, ok?"OK":"FAIL");
}
Sleep(10);
}
}
printf("Thread ending...\n");
return 0;
}
/*
* ======================================================================= *
* In the startup of this program, we look at our executable name and *
* replace the ".EXE" with ".DLL" to find the ISAPI DLL we need to load. *
* This means that the executable need only be given the same "name" as *
* the DLL to load. There is no recompilation required. *
* ======================================================================= *
*/
BOOL stress_main(const char *filename,
const char *arg,
const char *postdata,
const char *matchdata)
{
EXTENSION_CONTROL_BLOCK ECB;
DWORD rc;
TIsapiContext context;
// open output and input files
context.tid = GetCurrentThreadId();
CString fname;
fname.Format("%08X.out", context.tid);
context.out = CreateFile(fname, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
if (context.out==INVALID_HANDLE_VALUE) {
printf("failed to open output file %s\n", fname);
return 0;
}
// not using post files
context.in = INVALID_HANDLE_VALUE;
//
// Fill the ECB with the necessary information
//
if (!FillExtensionControlBlock(&ECB, &context) ) {
fprintf(stderr,"Fill Ext Block Failed\n");
return -1;
}
// check for command line argument,
// first arg = filename
// this is added for testing php from command line
context.env.RemoveAll();
context.env["PATH_TRANSLATED"]= filename;
context.env["SCRIPT_MAP"]= filename;
context.env["CONTENT_TYPE"]= "";
context.env["CONTENT_LENGTH"]= "";
context.env["QUERY_STRING"]= arg;
context.env["METHOD"]="GET";
char buf[MAX_PATH];
if (postdata && *postdata !=0) {
ECB.cbAvailable = strlen(postdata);
ECB.cbTotalBytes = ECB.cbAvailable;
ECB.lpbData = (unsigned char *)postdata;
context.env["METHOD"]="POST";
_snprintf(buf, sizeof(buf)-1, "%d", ECB.cbTotalBytes);
context.env["CONTENT_LENGTH"]=buf;
context.env["CONTENT_TYPE"]="application/x-www-form-urlencoded";
}
ECB.lpszPathTranslated = strdup(filename);
ECB.lpszQueryString = strdup(arg);
// Call the DLL
//
rc = IsapiHttpExtensionProc(&ECB);
free(ECB.lpszPathTranslated);
free(ECB.lpszQueryString);
BOOL ok = TRUE;
if (context.out != INVALID_HANDLE_VALUE) CloseHandle(context.out);
// compare the output with the EXPECT section
if (matchdata && *matchdata != 0) {
ok = CompareStringWithFile(fname, matchdata, strlen(matchdata));
}
DeleteFile(fname);
//if (rc == HSE_STATUS_PENDING) // We will exit in ServerSupportFunction
// Sleep(INFINITE);
return ok;
}
//
// GetServerVariable() is how the DLL calls the main program to figure out
// the environment variables it needs. This is a required function.
//
BOOL WINAPI GetServerVariable(HCONN hConn, LPSTR lpszVariableName,
LPVOID lpBuffer, LPDWORD lpdwSize){
DWORD rc;
CString value;
TIsapiContext *c = (TIsapiContext *)hConn;
if (!c) return FALSE;
if (IsapiEnvironment.Lookup(lpszVariableName, value)) {
rc = value.GetLength();
strncpy((char *)lpBuffer, value, *lpdwSize-1);
} else if (c->env.Lookup(lpszVariableName, value)) {
rc = value.GetLength();
strncpy((char *)lpBuffer, value, *lpdwSize-1);
} else
rc = GetEnvironmentVariable(lpszVariableName,(char *)lpBuffer,*lpdwSize) ;
if (!rc) { // return of 0 indicates the variable was not found
SetLastError(ERROR_NO_DATA);
return FALSE;
}
if (rc > *lpdwSize) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
*lpdwSize =rc + 1 ; // GetEnvironmentVariable does not count the NULL
return TRUE;
}
//
// Again, we don't have an HCONN, so we simply wrap ReadClient() to
// ReadFile on stdin. The semantics of the two functions are the same
//
BOOL WINAPI ReadClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize) {
TIsapiContext *c = (TIsapiContext *)hConn;
if (!c) return FALSE;
if (c->in != INVALID_HANDLE_VALUE)
return ReadFile(c->in,lpBuffer,(*lpdwSize), lpdwSize,NULL);
return FALSE;
}
//
// ditto for WriteClient()
//
BOOL WINAPI WriteClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize,
DWORD dwReserved) {
TIsapiContext *c = (TIsapiContext *)hConn;
if (!c) return FALSE;
if (c->out != INVALID_HANDLE_VALUE)
return WriteFile(c->out,lpBuffer,*lpdwSize, lpdwSize,NULL);
return FALSE;
}
//
// This is a special callback function used by the DLL for certain extra
// functionality. Look at the API help for details.
//
BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType){
char *lpszRespBuf;
char * temp = NULL;
DWORD dwBytes;
BOOL bRet;
switch(dwHSERequest) {
case (HSE_REQ_SEND_RESPONSE_HEADER) :
lpszRespBuf = (char *)xmalloc(*lpdwSize);//+ 80);//accomodate our header
if (!lpszRespBuf)
return FALSE;
wsprintf(lpszRespBuf,"%s",
//HTTP_VER,
/* Default response is 200 Ok */
//lpvBuffer?lpvBuffer:"200 Ok",
/* Create a string for the time. */
//temp=MakeDateStr(),
//SERVER_VERSION,
/* If this exists, it is a pointer to a data buffer to
be sent. */
lpdwDataType?(char *)lpdwDataType:NULL);
if (temp) xfree(temp);
dwBytes = strlen(lpszRespBuf);
bRet = WriteClient(0,lpszRespBuf,&dwBytes,0);
xfree(lpszRespBuf);
break;
//
// A real server would do cleanup here
case (HSE_REQ_DONE_WITH_SESSION):
//ExitThread(0);
break;
//
// This sends a redirect (temporary) to the client.
// The header construction is similar to RESPONSE_HEADER above.
//
case (HSE_REQ_SEND_URL_REDIRECT_RESP):
lpszRespBuf = (char *)xmalloc(*lpdwSize +80) ;
if (!lpszRespBuf)
return FALSE;
wsprintf(lpszRespBuf,"%s %s %s\r\n",
HTTP_VER,
"302 Moved Temporarily",
(lpdwSize > 0)?lpvBuffer:0);
xfree(temp);
dwBytes = strlen(lpszRespBuf);
bRet = WriteClient(0,lpszRespBuf,&dwBytes,0);
xfree(lpszRespBuf);
break;
default:
return FALSE;
break;
}
return bRet;
}
//
// Makes a string of the date and time from GetSystemTime().
// This is in UTC, as required by the HTTP spec.`
//
char * MakeDateStr(void){
SYSTEMTIME systime;
char *szDate= (char *)xmalloc(64);
char * DaysofWeek[] = {"Sun","Mon","Tue","Wed","Thurs","Fri","Sat"};
char * Months[] = {"NULL","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug",
"Sep","Oct","Nov","Dec"};
GetSystemTime(&systime);
wsprintf(szDate,"%s, %d %s %d %d:%d.%d",DaysofWeek[systime.wDayOfWeek],
systime.wDay,
Months[systime.wMonth],
systime.wYear,
systime.wHour,systime.wMinute,
systime.wSecond );
return szDate;
}
//
// Fill the ECB up
//
BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *ECB, TIsapiContext *context) {
char * temp;
ECB->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
ECB->dwVersion = MAKELONG(HSE_VERSION_MINOR,HSE_VERSION_MAJOR);
ECB->ConnID = (void *)context;
//
// Pointers to the functions the DLL will call.
//
ECB->GetServerVariable = GetServerVariable;
ECB->ReadClient = ReadClient;
ECB->WriteClient = WriteClient;
ECB->ServerSupportFunction = ServerSupportFunction;
//
// Fill in the standard CGI environment variables
//
ECB->lpszMethod = GetEnv("REQUEST_METHOD");
if (!ECB->lpszMethod) ECB->lpszMethod = "GET";
ECB->lpszQueryString = GetEnv("QUERY_STRING");
ECB->lpszPathInfo = GetEnv("PATH_INFO");
ECB->lpszPathTranslated = GetEnv("PATH_TRANSLATED");
ECB->cbTotalBytes=( (temp=GetEnv("CONTENT_LENGTH")) ? (atoi(temp)): 0);
ECB->cbAvailable = 0;
ECB->lpbData = (unsigned char *)"";
ECB->lpszContentType = GetEnv("CONTENT_TYPE");
return TRUE;
}
//
// Works like _getenv(), but uses win32 functions instead.
//
char * GetEnv(LPSTR lpszEnvVar) {
char *var,dummy;
DWORD dwLen;
if (!lpszEnvVar)
return "";
dwLen =GetEnvironmentVariable(lpszEnvVar,&dummy,1);
if (dwLen == 0)
return "";
var = (char *)xmalloc(dwLen);
if (!var)
return "";
(void)GetEnvironmentVariable(lpszEnvVar,var,dwLen);
return var;
}