Thread: Compiling Program as .exe with no dependancy on other files

  1. #1
    Registered User
    Join Date
    Nov 2009
    Posts
    1

    Compiling Program as .exe with no dependancy on other files

    Hello,

    Sorry, I'm not a programmer at all however I'm having problems recompiling this program within Visual Studio 2005. I tried Visual Studio Express 2008 - but that required a VC90.dll file (.Net Runtime loading on the server)

    The requirement is that the .exe works without any other files or DLLs etc, when compiled with Min-GW it compiles fine.

    I tried with Visual Studio 2005 but it requires VC80.dll!?

    This is the code I have which has been modified for Visual Studio to compile it without errors.

    See here, I posted it on Experts-Exchange but I'm getting stuck!

    Compiling C++ Code - Works in Min-GW - Want it compiled using Visual Studio : C++ compile

    (Scroll right to bottom)

    Code:
    // +------------------------------------------------------------------+
    // |          _           _           _       _   __   ____ ___       |
    // |       __| |_  ___ __| |__  _ __ | |__   / | /  \ |__ /( _ )      |
    // |      / _| ' \/ -_) _| / / | '  \| / /   | || () | |_ \/ _ \      |
    // |      \__|_||_\___\__|_\_\_|_|_|_|_\_\   |_(_)__(_)___/\___/      |
    // |                                            check_mk 1.0.38       |
    // |                                                                  |
    // | Copyright Mathias Kettner 2009                mk@mathias-kettner |
    // +------------------------------------------------------------------+
    // 
    // This file is part of check_mk 1.0.38.
    // The official homepage is at http://mathias-kettner.de/check_mk.
    // 
    // check_mk is free software;  you can redistribute it and/or modify it
    // under the  terms of the  GNU General Public License  as published by
    // the Free Software Foundation in version 2.  check_mk is  distributed
    // in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
    // out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
    // PARTICULAR PURPOSE. See the  GNU General Public License for more de-
    // ails.  You should have  received  a copy of the  GNU  General Public
    // License along with GNU Make; see the file  COPYING.  If  not,  write
    // to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
    // Boston, MA 02110-1301 USA.
     
     
    // Looking for documentation on Win32-API? Here are some of the
    // documents that I used:
     
    // Registry:
    // http://msdn.microsoft.com/en-us/library/ms724897.aspx
     
    // Performance-Counters:
    // http://msdn.microsoft.com/en-us/library/aa373178(VS.85).aspx
     
    // Eventlogs:
    // http://msdn.microsoft.com/en-us/library/aa363672(VS.85).aspx
    // http://msdn.microsoft.com/en-us/library/bb427356(VS.85).aspx
     
    // System Error Codes:
    // http://msdn.microsoft.com/en-us/library/ms681381(VS.85).aspx
     
    // This program needs at least windows version 0x0500
    // (Window 2000 / Windows XP)
    #define WINVER 0x0500
     
    #include <winsock2.h>
    #include <windows.h>
    #include <stdio.h>
    #include <winbase.h>
    #include <winreg.h>    // performance counters from registry
    #include <tlhelp32.h>  // list of processes
    #include <stdarg.h>
    #include <time.h>
     
    //#pragma comment(lib, "WS2_32.lib")
    #pragma comment(lib, "WSock32.lib")
     
     
    #define CHECK_MK_VERSION "1.0.38 [Visual Studio Build]"
    #define CHECK_MK_AGENT_PORT 6556
    #define SERVICE_NAME "Check_MK_Agent"
    #define KiloByte 1024
     
    bool do_tcp = false;
    bool should_terminate = false;
    bool logwatch_send_initial_entries = false;
    bool logwatch_suppress_info = true;
     
    // dynamic buffer for event log entries. Grows with the
    // time as needed. Never shrinked.
    char *eventlog_buffer = 0;
    int eventlog_buffer_size = 0;
     
    // Our memory of what event logs we know and up to
    // which record entry we have seen its messages so
    // far. We do not want to make use of C++ features
    // here so sorry for the mess...
    unsigned num_eventlogs = 0;
    #define  MAX_EVENTLOGS 128
    DWORD    known_record_numbers[MAX_EVENTLOGS];
    char    *eventlog_names[MAX_EVENTLOGS];
    bool     newly_found[MAX_EVENTLOGS];
     
     
    #ifdef DEBUG
    void debug(char *text)
    {
    	FILE *debugout = fopen("C:\\check_mk_agent.log", "a");
    	if (debugout) {
    		fprintf(debugout, "%s\n", text);
    		fflush(debugout);
    		fclose(debugout);
    	}
    }
    #else
    #define debug(C) 
    #endif
     
    bool verbose_mode = false;
    void verbose(const char *format, ...)
    {
    	if (!verbose_mode)
    		return;
     
    	va_list ap;
    	va_start(ap, format);
    	printf("DEBUG: ");
    	vprintf(format, ap);
    	printf("\n");
    	fflush(stdout);
    }
     
     
     
    void outputCounter(SOCKET &out, BYTE *datablock, int counter, 
    				   PERF_OBJECT_TYPE *objectPtr, PERF_COUNTER_DEFINITION *counterPtr);
    void outputCounterValue(SOCKET &out, PERF_COUNTER_BLOCK *counterBlockPtr, int size, unsigned offset);
    double current_time();
     
     
    // determine system root by reading the environment variable
    // %SystemRoot%. This variable is used in the registry entries
    // that describe eventlog messages.
    const char *system_root()
    {
    	static char root[128];
    	if (0 < GetWindowsDirectory(root, sizeof(root)))
    		return root;
    	else
    		return "C:\\WINDOWS";
    }
     
     
    char *llu_to_string(unsigned long long value)
    {
    	static char buffer[64];
     
    	if (value == 0) {
    		strcpy(buffer, "0");
    		return buffer;
    	}
     
    	buffer[63] = 0;
     
    	char *write = buffer + 63;
    	while (value > 0) {
    		if (write <= buffer) {
    			strcpy(buffer, "(invalid)");
    			return buffer;
    		}
    		char digit = (value % 10) + '0';
    		*--write = digit;
    		value = value / 10;
    	}
    	return write;
    }
     
     
     
    void output(SOCKET &out, const char *format, ...)
    {
    	static char outbuffer[4096];
     
    	va_list ap;
    	va_start(ap, format);
    	int len = vsnprintf(outbuffer, sizeof(outbuffer), format, ap);
    	if (do_tcp) {
    		while (!should_terminate) {
    			int result = send(out, outbuffer, len, 0);
    			if (result == SOCKET_ERROR) {
    				debug("send() failed");
    				int error = WSAGetLastError();
    				if (error == WSAEINTR) {
    					debug("INTR. Nochmal...");
    					continue;
    				}
    				else if (error == WSAEINPROGRESS) {
    					debug("INPROGRESS. Nochmal...");
    					continue;
    				}
    				else if (error == WSAEWOULDBLOCK) {
    					debug("WOULDBLOCK. Komisch. Breche ab...");
    					break;
    				}
    				else {
    					debug("Anderer Fehler. Gebe auf\n");
    					break;
    				}
    			}
    			else if (result == 0)
    				debug("send() returned 0");
    			else if (result != len)
    				debug("send() sent too few bytes");
    			break;
    		}
    	}    
    	else
    		fwrite(outbuffer, len, 1, stdout);
    }
     
     
    void section_df(SOCKET &out)
    {
    	output(out, "<<<df>>>\n");
    	TCHAR buffer[4096];
    	DWORD len = GetLogicalDriveStrings(sizeof(buffer), buffer);
     
    	TCHAR *end = buffer + len;
    	TCHAR *drive = buffer;
    	while (drive < end) {
    		UINT drvType = GetDriveType(drive);
    		if (drvType == DRIVE_FIXED)  // only process local harddisks
    		{
    			ULARGE_INTEGER free_avail, total, free;
    			free_avail.QuadPart = 0;
    			total.QuadPart = 0;
    			free.QuadPart = 0;
    			int returnvalue = GetDiskFreeSpaceEx(drive, &free_avail, &total, &free);
    			if (returnvalue > 0) {
    				double perc_used = 0;
    				if (total.QuadPart > 0)
    					perc_used = 100 - (100 * free_avail.QuadPart / total.QuadPart);
     
    				TCHAR fsname[128];
    				if (!GetVolumeInformation(drive, 0, 0, 0, 0, 0, fsname, 128))
    					fsname[0] = 0;
     
    				output(out, "%-10s %-8s ", drive, fsname);
    				output(out, "%s ", llu_to_string(total.QuadPart / KiloByte));
    				output(out, "%s ", llu_to_string((total.QuadPart - free_avail.QuadPart) / KiloByte));
    				output(out, "%s ", llu_to_string(free_avail.QuadPart / KiloByte));
    				output(out, "%3.0f%% ", perc_used);
    				output(out, "%s\n", drive);
    			}
    		}
    		drive += strlen(drive) + 1;
    	}
    }
     
     
    void section_ps(SOCKET &out)
    {
    	output(out, "<<<ps>>>\n");
    	HANDLE hProcessSnap;
    	PROCESSENTRY32 pe32;
     
    	hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    	if (hProcessSnap != INVALID_HANDLE_VALUE) 
    	{
    		pe32.dwSize = sizeof(PROCESSENTRY32);
    		if (Process32First(hProcessSnap, &pe32)) {
    			do {
    				output(out, "%s\n", pe32.szExeFile);
    			} while (Process32Next(hProcessSnap, &pe32));
    		}
    		CloseHandle(hProcessSnap);
    	}
    }
     
     
    void section_services(SOCKET &out)
    {
    	output(out, "<<<services>>>\n");
    	SC_HANDLE scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE);
    	if (scm != INVALID_HANDLE_VALUE) {
    		DWORD bytes_needed = 0;
    		DWORD num_services = 0;
    		// first determine number of bytes needed
    		EnumServicesStatusEx(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, 
    			NULL, 0, &bytes_needed, &num_services, 0, 0);
    		if (GetLastError() == ERROR_MORE_DATA && bytes_needed > 0) {
    			BYTE *buffer = (BYTE *)malloc(bytes_needed);
    			if (buffer) {
    				if (EnumServicesStatusEx(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, 
    					buffer, bytes_needed, 
    					&bytes_needed, &num_services, 0, 0))
    				{
    					ENUM_SERVICE_STATUS_PROCESS *service = (ENUM_SERVICE_STATUS_PROCESS *)buffer;
    					for (unsigned i=0; i<num_services; i++) {
    						DWORD state = service->ServiceStatusProcess.dwCurrentState;
    						const char *state_name = "unknown";
    						switch (state) {
    				  case SERVICE_CONTINUE_PENDING: state_name = "continuing"; break;
    				  case SERVICE_PAUSE_PENDING:    state_name = "pausing"; break;
    				  case SERVICE_PAUSED:           state_name = "paused"; break;
    				  case SERVICE_RUNNING:          state_name = "running"; break;
    				  case SERVICE_START_PENDING:    state_name = "starting"; break;
    				  case SERVICE_STOP_PENDING:     state_name = "stopping"; break;
    				  case SERVICE_STOPPED:          state_name = "stopped"; break;
    						}
     
    						// The service name usually does not contain spaces. But
    						// in some cases it does. We replace them with _ in order
    						// the keep it in one space-separated column. Since we own
    						// the buffer, we can simply change the name inplace.
    						for (char *w=(char *)(service->lpServiceName); *w; w++) {
    							if (*w == ' ')
    								*w = '_';
    						}                 
     
    						output(out, "%-18s %-8s %s\n", 
    							service->lpServiceName, state_name,
    							service->lpDisplayName);
    						service ++;
    					}
    				}
    				free(buffer);
    			}
    		}
    		CloseServiceHandle(scm);
    	}
    }
     
    #define DEFAULT_BUFFER_SIZE 40960L
     
    // Hilfsfunktionen zum Navigieren in den Performance-Counter Binaerdaten
    PERF_OBJECT_TYPE *FirstObject(PERF_DATA_BLOCK *dataBlock) { 
    	return (PERF_OBJECT_TYPE *) ((BYTE *)dataBlock + dataBlock->HeaderLength); 
    }
    PERF_OBJECT_TYPE *NextObject(PERF_OBJECT_TYPE *act) {
    	return (PERF_OBJECT_TYPE *) ((BYTE *)act + act->TotalByteLength); 
    }
    PERF_COUNTER_DEFINITION *FirstCounter(PERF_OBJECT_TYPE *perfObject) {
    	return (PERF_COUNTER_DEFINITION *) ((BYTE *) perfObject + perfObject->HeaderLength); 
    }
    PERF_COUNTER_DEFINITION *NextCounter(PERF_COUNTER_DEFINITION *perfCounter) {
    	return (PERF_COUNTER_DEFINITION *) ((BYTE *) perfCounter + perfCounter->ByteLength); 
    }
    PERF_COUNTER_BLOCK *GetCounterBlock(PERF_INSTANCE_DEFINITION *pInstance) {
    	return (PERF_COUNTER_BLOCK *) ((BYTE *)pInstance + pInstance->ByteLength); 
    }
    PERF_INSTANCE_DEFINITION *FirstInstance (PERF_OBJECT_TYPE *pObject) {
    	return (PERF_INSTANCE_DEFINITION *)  ((BYTE *) pObject + pObject->DefinitionLength); 
    }
    PERF_INSTANCE_DEFINITION *NextInstance (PERF_INSTANCE_DEFINITION *pInstance) {
    	return (PERF_INSTANCE_DEFINITION *) ((BYTE *)pInstance + pInstance->ByteLength + GetCounterBlock(pInstance)->ByteLength); 
    }
     
     
    void dump_performance_counters(SOCKET &out, unsigned counter)
    {
    	// registry entry is ascii representation of counter index
    	char counter_index_name[8];
    	_snprintf(counter_index_name, sizeof(counter_index_name), "%u", counter);
     
    	// allocate block to store counter data block
    	DWORD size = DEFAULT_BUFFER_SIZE;
    	BYTE *data = new BYTE[DEFAULT_BUFFER_SIZE];
    	DWORD type;
    	DWORD ret;
     
    	// Holt zu einem bestimmten Counter den kompletten Binärblock aus der
    	// Registry. Da man vorher nicht weiß, wie groß der Puffer sein muss,
    	// kann man nur mit irgendeiner Größe anfangen und dann diesen immer
    	// wieder größer machen, wenn er noch zu klein ist. >:-P
    	while ((ret = RegQueryValueEx(HKEY_PERFORMANCE_DATA, counter_index_name, 
    		0, &type, data, &size)) != ERROR_SUCCESS) 
    	{
    		if (ret == ERROR_MORE_DATA) // WIN32 API sucks...
    		{
    			// Der Puffer war zu klein. Toll. Also den Puffer größer machen
    			// und das ganze nochmal probieren.
    			size += DEFAULT_BUFFER_SIZE;
    			debug("Buffer for RegQueryValueEx too small. Resizing...");
    			delete [] data;
    			data = new BYTE [size];
    		} else {
    			// Es ist ein anderer Fehler aufgetreten. Abbrechen.
    			delete [] data;
    			return;
    		}
    	}
     
    	PERF_DATA_BLOCK *dataBlockPtr = (PERF_DATA_BLOCK *)data;
     
    	// Determine first object in list of objects
    	PERF_OBJECT_TYPE *objectPtr = FirstObject(dataBlockPtr);
     
    	// Now walk through the list of objects. The bad news is:
    	// even if we expect only one object, windows might send
    	// us more that one object. We need to scan a list of objects
    	// in order to find the one we have asked for. >:-P
    	for (unsigned int a=0 ; a < dataBlockPtr->NumObjectTypes ; a++) 
    	{
    		// Have we found the object we seek? 
    		if (objectPtr->ObjectNameTitleIndex == counter)
    		{
    			// Yes. Great. Now: each object consist of a lot of counters.
    			// We walk through the list of counters in this object:
     
    			// get pointer to first counter
    			PERF_COUNTER_DEFINITION *counterPtr = FirstCounter(objectPtr);
     
    			// Now we make a first quick walk through all counters, only in order
    			// to find the beginning of the data block (which comes after the
    			// counter definitions)
    			PERF_COUNTER_DEFINITION *last_counter = FirstCounter(objectPtr);
    			for(unsigned int b=0 ; b < objectPtr->NumCounters ; b++) 
    				last_counter = NextCounter(last_counter);
    			BYTE *datablock = (BYTE *)last_counter;
     
    			// Now walk through the counter list a second time and output
    			// all non-zero counters
    			for(unsigned int b=0 ; b < objectPtr->NumCounters ; b++) 
    			{
    				outputCounter(out, datablock, counter, objectPtr, counterPtr);
    				counterPtr = NextCounter(counterPtr);
    			}
    		}
    		// naechstes Objekt in der Liste
    		objectPtr = NextObject(objectPtr);
    	}
    	delete [] data;
    }
     
     
    void outputCounter(SOCKET &out, BYTE *datablock, int counter, 
    				   PERF_OBJECT_TYPE *objectPtr, PERF_COUNTER_DEFINITION *counterPtr)
    {
    	int num_instances = objectPtr->NumInstances;
    	unsigned offset = counterPtr->CounterOffset;
    	int size = counterPtr->CounterSize;
     
    	// determine the type of the counter (for verbose output)
    	const char *countertypename = "(unknown)";
    	switch (counterPtr->CounterType) {
    	case PERF_COUNTER_COUNTER:                countertypename = "counter"; break;
    	case PERF_COUNTER_QUEUELEN_TYPE:          countertypename = "queuelen_type" ; break;
    	case PERF_SAMPLE_COUNTER:                 countertypename = "sample_counter"; break;
    		//  case PERF_OBJ_TIME_TIMER:                 countertypename = "obj_time_timer"; break;
    		//  case PERF_COUNTER_100NS_QUEUELEN_TYPE:    countertypename = "100ns_queuelen_type"; break;
    		//  case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE: countertypename = "obj_time_queuelen_type"; break;
    	case PERF_COUNTER_TIMER:                  countertypename = "timer"; break;
    	case PERF_COUNTER_TIMER_INV:              countertypename = "timer_inv"; break;
    	case PERF_COUNTER_BULK_COUNT:             countertypename = "bulk_count"; break;
    		//  case PERF_COUNTER_LARGE_QUEUELEN_TYPE:    countertypename = "large_queuelen_type"; break;
    	case PERF_COUNTER_MULTI_TIMER:            countertypename = "multi_timer"; break;
    	case PERF_COUNTER_MULTI_TIMER_INV:        countertypename = "multi_timer_inv"; break;
    	case PERF_COUNTER_RAWCOUNT:               countertypename = "rawcount"; break;
    	case PERF_COUNTER_RAWCOUNT_HEX:           countertypename = "rawcount_hex"; break;
    		//  case PERF_COUNTER_DELTA:                  countertypename = "delta"; break;
    	case PERF_COUNTER_LARGE_RAWCOUNT:         countertypename = "large_rawcount"; break;
    	case PERF_COUNTER_LARGE_RAWCOUNT_HEX:     countertypename = "large_rawcount_hex"; break;
    		//  case PERF_COUNTER_LARGE_DELTA:            countertypename = "large_delta"; break;
    	case PERF_100NSEC_TIMER:                  countertypename = "100nsec_timer"; break;
    	case PERF_100NSEC_TIMER_INV:              countertypename = "100nsec_timer_inv"; break;
    	case PERF_100NSEC_MULTI_TIMER:            countertypename = "100nsec_multi_timer"; break;
    	case PERF_100NSEC_MULTI_TIMER_INV:        countertypename = "100nsec_multi_timer_inv"; break;
    	case PERF_SAMPLE_FRACTION:                countertypename = "sample_fraction"; break;
    	case PERF_RAW_FRACTION:                   countertypename = "raw_fraction"; break;
    		//  case PERF_LARGE_RAW_FRACTION:             countertypename = "large_raw_fraction"; break;
    		//  case PERF_PRECISION_SYSTEM_TIMER:         countertypename = "precision_system_timer"; break;
    		//  case PERF_PRECISION_100NS_TIMER:          countertypename = "precision_100ns_timer"; break;
    		//  case PERF_PRECISION_OBJECT_TIMER:         countertypename = "precision_object_timer"; break;
    	case PERF_AVERAGE_TIMER:                  countertypename = "average_timer"; break;
    	case PERF_AVERAGE_BULK:                   countertypename = "average_bulk"; break;
    	case PERF_SAMPLE_BASE:                countertypename = "sample_base"; break;
    	case PERF_AVERAGE_BASE:               countertypename = "average_base"; break;
    	case PERF_COUNTER_MULTI_BASE:         countertypename = "multi_base"; break;
    	case PERF_RAW_BASE:                   countertypename = "raw_base"; break;
    		//  case PERF_LARGE_RAW_BASE:             countertypename = "large_raw_base"; break;
    	case PERF_ELAPSED_TIME:               countertypename = "elapsed_time"; break;
    	case PERF_COUNTER_TEXT:               countertypename = "text"; break;
    	case PERF_COUNTER_NODATA:             countertypename = "nodata"; break;
    	case PERF_COUNTER_HISTOGRAM_TYPE:         countertypename = "histogram_type"; break;
    	}
     
    	// Output index of counter object and counter, and timestamp
    	output(out, "%d:%d %.2f", counter, counterPtr->CounterNameTitleIndex, current_time());
     
    	// If this is a multi-instance-counter, loop over the instances
    	if (num_instances >= 0) 
    	{
    		// get pointer to first instance
    		PERF_INSTANCE_DEFINITION *instancePtr = FirstInstance(objectPtr);
     
    		for(int b=0 ; b<objectPtr->NumInstances ; b++) 
    		{
    			// PERF_COUNTER_BLOCK dieser Instanz ermitteln.
    			PERF_COUNTER_BLOCK *counterBlockPtr = GetCounterBlock(instancePtr);
    			outputCounterValue(out, counterBlockPtr, size, offset);
    			instancePtr = NextInstance(instancePtr);
    		}
     
    	}
    	else { // instanceless counter
    		PERF_COUNTER_BLOCK *counterBlockPtr = (PERF_COUNTER_BLOCK *) datablock;
    		outputCounterValue(out, counterBlockPtr, size, offset);
    	}
    	output(out, " %s\n", countertypename);
    }
     
     
    void outputCounterValue(SOCKET &out, PERF_COUNTER_BLOCK *counterBlockPtr, int size, unsigned offset)
    {
    	if (size == 4) {
    		DWORD value = *((DWORD *)((BYTE *)counterBlockPtr) + offset);
    		output(out, " %lu", value);
    	}
    	else if (size == 8) {
    		DWORD *data_at = (DWORD *)(((BYTE *)counterBlockPtr) + offset);
    		DWORDLONG value = (DWORDLONG)*data_at + ((DWORDLONG)*(data_at + 1) << 32);
    		output(out, " %s", llu_to_string(value));
    	}
    	else
    		output(out, " unknown");
    }
     
     
    double current_time()
    {
    	SYSTEMTIME systime;
    	FILETIME filetime;
    	GetSystemTime(&systime);
    	SystemTimeToFileTime(&systime, &filetime);
    	unsigned long long ft = (unsigned long long)(filetime.dwLowDateTime)
    		+ (((unsigned long long)filetime.dwHighDateTime) << 32);
    	return ft / 10000000.0;
    }
     
     
    void grow_eventlog_buffer(int newsize)
    {
    	delete [] eventlog_buffer;
    	eventlog_buffer = new char[newsize];
    	eventlog_buffer_size = newsize;
    }
     
     
    bool output_eventlog_entry(SOCKET &out, char *dllpath, EVENTLOGRECORD *event, char type_char, 
    						   const char *logname, const char *source_name, char **strings)
    {
    	char msgbuffer[512];
    	char dll_realpath[128];
    	HINSTANCE dll;
     
    	// if no dllpath is NULL, we output the message without text conversion and
    	// always succeed. If a dll pathpath is given, we only succeed if the conversion
    	// is successfull.
     
    	if (dllpath) {
    		// to make it even more difficult, dllpath may containt %SystemRoot%, which
    		// must be replaced with the environment variable %SystemRoot% (most probably - 
    		// but not entirely for sure - C:\WINDOWS
    		if (strncmp(dllpath, "%SystemRoot%", 12) == 0)
    			_snprintf(dll_realpath, sizeof(dll_realpath), "%s%s", system_root(), dllpath + 12);
    		else
    			_snprintf(dll_realpath, sizeof(dll_realpath), "%s", dllpath);
     
    		// I found this path in the official API documentation. I hope
    		// it's correct for all windows versions
    		dll =  LoadLibrary(dll_realpath);
    		if (!dll) {
    			return false;
    		}
    	}
    	else
    		dll = NULL;
     
    	DWORD len = FormatMessage(
    		FORMAT_MESSAGE_ARGUMENT_ARRAY | 
    		FORMAT_MESSAGE_FROM_HMODULE | 
    		FORMAT_MESSAGE_FROM_SYSTEM,
    		dll,                        
    		event->EventID,                 
    		0, // accept any language
    		(LPTSTR)msgbuffer,         
    		sizeof(msgbuffer),         
    		strings);
    	if (dll)
    		FreeLibrary(dll);
     
    	if (len == 0) {
    		if (dllpath)
    			return false;
    		// if message cannot be converted than at least output the text strings
    		msgbuffer[0] = 0;
    		char *w = msgbuffer;
    		int sizeleft = sizeof(msgbuffer) - 1;
    		int n=0;
    		while (strings[n]) {
    			char *s = strings[n];
    			int l = strlen(s);
    			if (l < sizeleft) {
    				strcpy(w, s);
    				w += l;
    				*w++ = ' ';
    			}
    			n++;
    		}
    		*w = 0;
    	}
     
    	// I'm not sure if Windows zero-terminates this if msg is too long...
    	msgbuffer[sizeof(msgbuffer)-1] = 0; 
     
    	// replace newlines with spaces. check_mk expects one message each line.
    	char *w = msgbuffer;
    	while (*w) {
    		if (*w == '\n' || *w == '\r') *w = ' ';
    		w++;
    	}
     
    	// convert UNIX timestamp to local time
    	struct tm *t = localtime((time_t *)&event->TimeGenerated);
    	char timestamp[64];
    	strftime(timestamp, sizeof(timestamp), "%b %d %H:%M:%S", t);
     
    	output(out, "%c %s %lu %s %s\n", type_char, timestamp,
    		event->EventID, source_name, msgbuffer);
    	return true;
    }
     
     
    void process_eventlog_entries(SOCKET &out, const char *logname, char *buffer, 
    							  DWORD bytesread, DWORD *record_number, bool just_find_end,
    							  int *worst_state)
    {
    	char *strings[64];
    	char regpath[128];
    	BYTE dllpath[128];
    	char source_name[128];
     
    	EVENTLOGRECORD *event = (EVENTLOGRECORD *)buffer;
    	while (bytesread > 0) 
    	{
    		*record_number = event->RecordNumber;
     
    		char type_char;
    		int this_state;
    		switch (event->EventType) {
    	  case EVENTLOG_ERROR_TYPE:
    		  type_char = 'C'; this_state = 2; break;
    	  case EVENTLOG_WARNING_TYPE:
    		  type_char = 'W'; this_state = 1; break;
    	  case EVENTLOG_INFORMATION_TYPE:
    		  type_char = '.'; this_state = 0; break;
    	  case EVENTLOG_AUDIT_SUCCESS:
    		  type_char = '.'; this_state = 0; break;
    	  case EVENTLOG_AUDIT_FAILURE:
    		  type_char = 'C'; this_state = 2; break;
    	  default:
    		  type_char = 'u'; this_state = 1; break;
    		}
    		if (*worst_state < this_state)
    			*worst_state = this_state;
     
    		if (!just_find_end) 
    		{
    			LPCTSTR lpSourceName = (LPCTSTR) ((LPBYTE) event + sizeof(EVENTLOGRECORD));
     
    			// prepare source name without spaces (for check_mk output)
    			strncpy(source_name, lpSourceName, sizeof(source_name));
    			char *w = source_name;
    			while (*w) {
    				if (*w == ' ') *w = '_';
    				*w++;
    			}
     
    			// prepare array of zero terminated strings to be inserted
    			// into message template.
    			DWORD num_strings = event->NumStrings;
    			char *s = ((char *)event) + event->StringOffset;
    			unsigned ns;
    			for (ns = 0; ns < num_strings; ns++) {
    				if (ns >= 63) break;
    				strings[ns] = s;
    				s += strlen(s) + 1;
    			}
    			strings[ns] = 0;
     
     
    			// Windows eventlog entries refer to texts stored in a DLL >:-P
    			// We need to load this DLL. First we need to look up which
    			// DLL to load in the registry. Hard to image how one could
    			// have contrieved this more complicated...
    			_snprintf(regpath, sizeof(regpath), 
    				"SYSTEM\\CurrentControlSet\\Services\\Eventlog\\%s\\%s",
    				logname, lpSourceName);
    			DWORD size = sizeof(dllpath);
    			HKEY key;
    			DWORD ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0,
    				KEY_READ, &key);
     
    			bool success = false;
    			if (ret == ERROR_SUCCESS) 
    			{
    				if (ERROR_SUCCESS == RegQueryValueEx(key, "EventMessageFile", NULL, NULL, dllpath, &size))
    				{
    					// Answer may contain more than one DLL. They are separated
    					// by semicola. Not knowing which one is the correct one, I try
    					// all
    					char *token = strtok((char *)dllpath, ";");
    					while (token) {
    						if (output_eventlog_entry(out, token, event, type_char, logname, source_name, strings)) {
    							success = true;
    							break;
    						}
    						token = strtok(NULL, ";");
    					}
    				}
    				RegCloseKey(key);
    			}
    			// No text conversion succeeded. Output without text anyway
    			if (!success) {
    				output_eventlog_entry(out, NULL, event, type_char, logname, source_name, strings);
    			}
     
    		} // type_char != '.'
     
    		bytesread -= event->Length;
    		event = (EVENTLOGRECORD *) ((LPBYTE) event + event->Length);
    	}
    }
     
     
    void output_eventlog(SOCKET &out, const char *logname, 
    					 DWORD *record_number, bool just_find_end)
    {
    	if (eventlog_buffer_size == 0) {
    		const int initial_size = 65536;
    		eventlog_buffer = new char[initial_size];
    		eventlog_buffer_size = initial_size;
    	}
     
    	HANDLE hEventlog = OpenEventLog(NULL, logname);
    	DWORD bytesread = 0;
    	DWORD bytesneeded = 0;
    	if (hEventlog) {
    		output(out, "[[[%s]]]\n", logname);
    		int worst_state = 0;
    		DWORD old_record_number = *record_number;
     
    		// we scan all new entries twice. At the first run we check if
    		// at least one warning/error message is present. Only if this
    		// is the case we make a second run where we output *all* messages,
    		// even the informational once.
    		for (int t=0; t<2; t++)
    		{
    			*record_number = old_record_number;
    			verbose("Starting from entry number %u", old_record_number);
    			while (true) {
    				DWORD flags;
    				if (*record_number == 0) {
    					if (t == 1) {
    						verbose("Need to reopen Logfile in order to find start again.");
    						CloseEventLog(hEventlog);
    						hEventlog = OpenEventLog(NULL, logname);
    						if (!hEventlog) {
    							verbose("Failed to reopen event log. Bailing out.");
    							return;
    						}
    					}
    					flags = EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ;
    				}
    				else {
    					verbose("Previous record number was %d. Doing seek read.", *record_number);
    					flags = EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ;
    				}
     
    				if (ReadEventLog(hEventlog, 
    					flags,
    					*record_number + 1,
    					eventlog_buffer,
    					eventlog_buffer_size,
    					&bytesread,
    					&bytesneeded))
    				{
    					process_eventlog_entries(out, logname, eventlog_buffer, 
    						bytesread, record_number, just_find_end || t==0, &worst_state);
    				}
    				else {
    					DWORD error = GetLastError();
    					if (error == ERROR_INSUFFICIENT_BUFFER) {
    						grow_eventlog_buffer(bytesneeded);
    					}
    					// found current end of log
    					else if (error == ERROR_HANDLE_EOF) {
    						verbose("End of logfile reached at entry %u. Worst state is %d", 
    							*record_number, worst_state);
    						break;
    					}
    					// invalid parameter can also mean end of log
    					else if (error == ERROR_INVALID_PARAMETER) {
    						verbose("Invalid parameter at entry %u (could mean end of logfile). Worst state is %d",
    							*record_number, worst_state);
    						break;
    					}
    					else {
    						output(out, "ERROR: Cannot read eventlog '%s': error %u\n", logname, error);
    						break;
    					}
    				}
    			}
    			if (worst_state == 0 && logwatch_suppress_info) {
    				break; // nothing important found. Skip second run
    			}
    		}
    		CloseEventLog(hEventlog);
    	}
    	else {
    		output(out, "[[[%s:missing]]]\n", logname);
    	}
    }
     
     
    // The output imitates that of the Linux agent. That makes
    // a special check for check_mk unneccessary:
    // <<<mem>>>.
    // MemTotal:       514104 kB
    // MemFree:         19068 kB
    // SwapTotal:     1048568 kB
    // SwapFree:      1043732 kB
     
    void section_mem(SOCKET &out)
    {
    	output(out, "<<<mem>>>\n");
     
    	MEMORYSTATUSEX statex;
    	statex.dwLength = sizeof (statex);
    	GlobalMemoryStatusEx (&statex);
     
    	output(out, "MemTotal:  %11d kB\n", statex.ullTotalPhys / 1024);
    	output(out, "MemFree:   %11d kB\n", statex.ullAvailPhys / 1024);
    	output(out, "SwapTotal: %11d kB\n", (statex.ullTotalPageFile - statex.ullTotalPhys) / 1024);
    	output(out, "SwapFree:  %11d kB\n", (statex.ullAvailPageFile - statex.ullAvailPhys) / 1024);
    }
     
     
    void section_winperf(SOCKET &out)
    {
     
    	output(out, "<<<winperf>>>\n");
     
    	for (int i=0; i<700; i+=2) {
    		if (i != 230 && i != 232 && i != 786 && i != 740)
    			dump_performance_counters(out, i);
    	}
    }
     
     
    // Keeps memory of an event log we have found. It
    // might already be known and will not be stored twice.
    void register_eventlog(char *logname)
    {
    	if (num_eventlogs >= MAX_EVENTLOGS)
    		return; // veeery unlikely
     
    	// check if we already know this one...
    	for (unsigned i=0; i < num_eventlogs; i++) {
    		if (!strcmp(logname, eventlog_names[i])) {
    			newly_found[i] = true; // remember its still here
    			return;
    		}
    	}
     
    	// yet unknown. register it.
    	known_record_numbers[num_eventlogs] = 0;
    	eventlog_names[num_eventlogs] = strdup(logname); 
    	newly_found[num_eventlogs] = true;
    	num_eventlogs ++;
    }
     
    void unregister_all_eventlogs()
    {
    	for (unsigned i=0; i < num_eventlogs; i++)
    		free(eventlog_names[i]);
    	num_eventlogs = 0;
    }
     
    /* Look into the registry in order to find out, which
    event logs are available. */
    bool find_eventlogs(SOCKET &out)
    {
    	for (unsigned i=0; i<num_eventlogs; i++)
    		newly_found[i] = 0;
     
    	char regpath[128];
    	_snprintf(regpath, sizeof(regpath), 
    		"SYSTEM\\CurrentControlSet\\Services\\Eventlog");
    	HKEY key;
    	DWORD ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_ENUMERATE_SUB_KEYS, &key);
     
    	bool success = true;
    	if (ret == ERROR_SUCCESS) 
    	{
    		DWORD i = 0;
    		char buffer[128];
    		DWORD len;
    		while (true) 
    		{
    			len = sizeof(buffer);
    			DWORD r = RegEnumKeyEx(key, i, buffer, &len, NULL, NULL, NULL, NULL);
    			if (r == ERROR_SUCCESS)
    				register_eventlog(buffer);
    			else if (r != ERROR_MORE_DATA)
    			{
    				if (r != ERROR_NO_MORE_ITEMS) {
    					output(out, "ERROR: Cannot enumerate over event logs: error code %d\n", r);
    					success = false;
    				}
    				break;
    			}
    			i ++;
    		}
    		RegCloseKey(key);
    	}
    	else {
    		success = false;
    		output(out, "ERROR: Cannot open registry key %s for enumeration: error code %d\n",
    			regpath, GetLastError());
    	}
    	return success;
    }
     
     
    // The output of this section is compatible with
    // the logwatch agent for Linux and UNIX
    void section_eventlog(SOCKET &out)
    {
    	// This agent remembers the record numbers
    	// of the event logs up to which messages have
    	// been processed. When started, the eventlog
    	// is skipped to the end. Historic messages are
    	// not been processed. 
    	static bool first_run = true;
     
    	output(out, "<<<logwatch>>>\n");
    	if (find_eventlogs(out))
    	{
    		for (unsigned i=0; i < num_eventlogs; i++) {
    			if (!newly_found[i]) // not here any more!
    				output(out, "[[[%s:missing]]]\n", eventlog_names[i]);
    			else
    				output_eventlog(out, eventlog_names[i], &known_record_numbers[i], 
    				first_run && !logwatch_send_initial_entries);
    		}
    	}
    	first_run = false;
    }
     
     
    void output_data(SOCKET &out)
    {
    	output(out, "<<<check_mk>>>\n");
    	output(out, "Version: %s\n", CHECK_MK_VERSION);
    	section_df(out);
    	section_ps(out);
    	section_mem(out);
    	section_services(out);
    	section_winperf(out);
    	section_eventlog(out);
    }
     
     
    void listen_tcp_loop()
    {
    	WSADATA wsa;
    	if (0 != WSAStartup(MAKEWORD(2, 0), &wsa)) {
    		fprintf(stderr, "Cannot initialize winsock.\n");
    		exit(1);
    	}
     
    	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
    	if (s == INVALID_SOCKET) {
    		fprintf(stderr, "Cannot create socket.\n");
    		exit(1);
    	}
     
    	SOCKADDR_IN addr;
    	memset(&addr, 0, sizeof(SOCKADDR_IN));
    	addr.sin_family = AF_INET;
    	addr.sin_port = htons(CHECK_MK_AGENT_PORT);
    	addr.sin_addr.s_addr = ADDR_ANY;
     
    	if (SOCKET_ERROR == bind(s, (SOCKADDR *)&addr, sizeof(SOCKADDR_IN))) {
    		fprintf(stderr, "Cannot bind socket to port %d\n", CHECK_MK_AGENT_PORT);
    		exit(1);
    	}
     
    	if (SOCKET_ERROR == listen(s, 5)) {
    		fprintf(stderr, "Cannot listen to socket\n");
    		exit(1);
    	}
     
    	SOCKET connection;
    	// Loop for ever.
    	debug("Die Schleife gestartet.");
    	while (!should_terminate) 
    	{
    		// Das Dreckswindows kann nicht vernuenftig gleichzeitig auf
    		// ein Socket und auf ein Event warten. Weil ich nicht extra
    		// deswegen mit Threads arbeiten will, verwende ich einfach 
    		// select() mit einem Timeout und polle should_terminate.
     
    		fd_set fds;
    		FD_ZERO(&fds);
    		FD_SET(s, &fds);
    		struct timeval timeout;
    		timeout.tv_sec = 0;
    		timeout.tv_usec = 500000;
    		if (1 == select(1, &fds, NULL, NULL, &timeout)) 
    		{
    			connection = accept(s, NULL, NULL);
    			debug("Habe accepted.");
    			if (connection != INVALID_SOCKET) {
     
    				debug("socket ist auch da.");
    				output_data(connection);
     
    				closesocket(connection);
    			}
    		}
    		else if (!should_terminate) {
    			Sleep(1); // should never happen
    		}
    	}
    	debug("Schleife beendet.");
    	closesocket(s);
    	WSACleanup();
    }
     
    void usage()
    {
    	fprintf(stderr, "Usage: \n"
    		"check_mk_agent install -- install as Windows NT service Check_Mk_Agent\n"
    		"check_mk_agent remove  -- remove Windows NT service\n"
    		"check_mk_agent adhoc   -- open TCP port %d and answer request until killed\n"
    		"check_mk_agent test    -- test output of plugin, do not open TCP port\n"
    		"check_mk_agent debug   -- similar to test, but with lots of debug output\n", CHECK_MK_AGENT_PORT);
    	exit(1);
    }
     
     
     
    // Zeugs fuer Windows Service
    TCHAR*                gszServiceName = (TCHAR *)TEXT(SERVICE_NAME);
    SERVICE_STATUS        serviceStatus;
    SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
     
     
    void WINAPI ServiceControlHandler( DWORD controlCode )
    {
    	switch ( controlCode )
    	{
    	case SERVICE_CONTROL_INTERROGATE:
    		break;
     
    	case SERVICE_CONTROL_SHUTDOWN:
    	case SERVICE_CONTROL_STOP:
    		should_terminate = true;
    		serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
    		SetServiceStatus( serviceStatusHandle, &serviceStatus );
    		return;
     
    	case SERVICE_CONTROL_PAUSE:
    		break;
     
    	case SERVICE_CONTROL_CONTINUE:
    		break;
     
    	default:
    		if ( controlCode >= 128 && controlCode <= 255 )
    			// user defined control code
    			break;
    		else
    			// unrecognised control code
    			break;
    	}
     
    	SetServiceStatus( serviceStatusHandle, &serviceStatus );
    }
     
    void WINAPI ServiceMain(DWORD, TCHAR* [] )
    {
    	// initialise service status
    	serviceStatus.dwServiceType               = SERVICE_WIN32_OWN_PROCESS;
    	serviceStatus.dwCurrentState              = SERVICE_STOPPED;
    	serviceStatus.dwControlsAccepted          = 0;
    	serviceStatus.dwWin32ExitCode             = NO_ERROR;
    	serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
    	serviceStatus.dwCheckPoint              = 0;
    	serviceStatus.dwWaitHint                = 0;
     
    	serviceStatusHandle = RegisterServiceCtrlHandler( gszServiceName,
    		ServiceControlHandler );
     
    	if ( serviceStatusHandle )
    	{
    		// service is starting
    		serviceStatus.dwCurrentState = SERVICE_START_PENDING;
    		SetServiceStatus( serviceStatusHandle, &serviceStatus );
     
    		// Service running
    		serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP |
    			SERVICE_ACCEPT_SHUTDOWN);
    		serviceStatus.dwCurrentState = SERVICE_RUNNING;
    		SetServiceStatus( serviceStatusHandle, &serviceStatus );
     
    		do_tcp = true;
    		listen_tcp_loop();
     
    		// service is now stopped
    		serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP |
    			SERVICE_ACCEPT_SHUTDOWN);
    		serviceStatus.dwCurrentState = SERVICE_STOPPED;
    		SetServiceStatus( serviceStatusHandle, &serviceStatus );
    	}
    }
     
    void RunService()
    {
    	debug("Dienst aufgerufen!");
    	SERVICE_TABLE_ENTRY serviceTable[] =
    	{
    		{ gszServiceName, ServiceMain },
    		{ 0, 0 }
    	};
     
    	StartServiceCtrlDispatcher( serviceTable );
    }
     
    void InstallService()
    {
    	SC_HANDLE serviceControlManager = OpenSCManager( 0, 0,
    		SC_MANAGER_CREATE_SERVICE );
     
    	if ( serviceControlManager )
    	{
    		char path[ _MAX_PATH + 1 ];
    		if ( GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0 )
    		{
    			SC_HANDLE service = CreateService( serviceControlManager,
    				gszServiceName, gszServiceName,
    				SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
    				SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path,
    				0, 0, 0, 0, 0 );
    			if ( service )
    			{
    				CloseServiceHandle( service );
    				printf(SERVICE_NAME " Installed Successfully\n");
    			}
    			else
    			{
    				if(GetLastError() == ERROR_SERVICE_EXISTS)
    					printf(SERVICE_NAME " Already Exists.\n");
    				else
    					printf(SERVICE_NAME " Was not Installed Successfully. Error Code %d\n", (int)GetLastError());
    			}
    		}
     
    		CloseServiceHandle( serviceControlManager );
    	}
    }
     
    void UninstallService()
    {
    	SC_HANDLE serviceControlManager = OpenSCManager( 0, 0,
    		SC_MANAGER_CONNECT );
     
    	if ( serviceControlManager )
    	{
    		SC_HANDLE service = OpenService( serviceControlManager,
    			gszServiceName, SERVICE_QUERY_STATUS | DELETE );
    		if ( service )
    		{
    			SERVICE_STATUS serviceStatus;
    			if ( QueryServiceStatus( service, &serviceStatus ) )
    			{
    				if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
    				{
    					if(DeleteService( service ))
    						printf(SERVICE_NAME " Removed Successfully\n");
    					else
    					{
    						DWORD dwError;
    						dwError = GetLastError();
    						if(dwError == ERROR_ACCESS_DENIED)
    							printf("Access Denied While trying to Remove " SERVICE_NAME " \n");
    						else if(dwError == ERROR_INVALID_HANDLE)
    							printf("Handle invalid while trying to Remove " SERVICE_NAME " \n");
    						else if(dwError == ERROR_SERVICE_MARKED_FOR_DELETE)
    							printf(SERVICE_NAME " already marked for deletion\n");
    					}
    				}
    				else
    				{
    					printf(SERVICE_NAME " is still Running.\n");
    				}
    			}
    			CloseServiceHandle( service );
    		}
    		CloseServiceHandle( serviceControlManager );
    	}
    }
     
     
    void do_test()
    {
    	do_tcp = false;
    	SOCKET dummy;
    	output_data(dummy);
    }
     
     
    void do_debug()
    {
    	verbose_mode = true;
    	do_tcp = false;
    	logwatch_send_initial_entries = true;
    	logwatch_suppress_info = false;
    	SOCKET dummy;
    	find_eventlogs(dummy);
    	//section_eventlog(dummy);
    }
     
    void do_adhoc()
    {
    	do_tcp = true;
    	printf("Listening for TCP connections on port %d\n", CHECK_MK_AGENT_PORT);
    	printf("Close window or press Ctrl-C to exit\n");
    	fflush(stdout);
     
    	should_terminate = false;
    	listen_tcp_loop(); // runs for ever or until Ctrl-C
    }
     
    void do_install()
    {
    	InstallService();
    }
     
    void do_remove()
    {
    	UninstallService();
    }
     
    void cleanup()
    {
    	if (eventlog_buffer_size > 0)
    		delete [] eventlog_buffer;
    	unregister_all_eventlogs(); // frees a few bytes
    }
     
     
    int main(int argc, char **argv)
    {
    	if (argc > 2)
    		usage();
    	else if (argc <= 1)
    		RunService();
    	else if (!strcmp(argv[1], "test"))
    		do_test();
    	else if (!strcmp(argv[1], "adhoc"))
    		do_adhoc();
    	else if (!strcmp(argv[1], "install"))
    		do_install();
    	else if (!strcmp(argv[1], "remove"))
    		do_remove();
    	else if (!strcmp(argv[1], "debug"))
    		do_debug();
    	else
    		usage();
     
    	cleanup();
    }

  2. #2
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    change the runtime library to multithreaded, not multithreaded debug. The debug runtime uses external DLL's, the static runtime does not.

  3. #3
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    /MT - no external references.
    /MD - external references.
    There are also debug specific versions of the switches.

    I must also note that static option is NOT necessarily better than dynamic. Static will swell the size of your executable since all the code must be put inside it. Dynamic will only require the runtime to be installed once.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Program Plan
    By Programmer_P in forum C++ Programming
    Replies: 0
    Last Post: 05-11-2009, 01:42 AM
  2. Client-server system with input from separate program
    By robot-ic in forum Networking/Device Communication
    Replies: 3
    Last Post: 01-16-2009, 03:30 PM
  3. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 02:39 PM
  4. Linking header files, Source files and main program(Accel. C++)
    By Daniel Primed in forum C++ Programming
    Replies: 3
    Last Post: 01-17-2006, 11:46 AM
  5. How To Make The Program Installed In Program Files Folder?
    By javacvb in forum Windows Programming
    Replies: 4
    Last Post: 11-05-2003, 05:33 PM