The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
// PerfMon.xs
//       +==========================================================+
//       |                                                          |
//       |                        PerfMon.xs                        |
//       |                     ---------------                      |
//       |                                                          |
//       | Copyright (c) 2004 Glen Small. All rights reserved. 	    |
//       |   This program is free software; you can redistribute    |
//       | it and/or modify it under the same terms as Perl itself. |
//       |                                                          |
//       +==========================================================+
//
//
//	Use under GNU General Public License or Larry Wall's "Artistic License"
//
//Check the README.TXT file that comes with this package for details about
//	it's history.
//
// Changes made by Reinhard Pagitsch Copyright (c) August 2004:
// Function: add_counter() to can use process name.
// 
// Changes made by Reinhard Pagitsch Copyright (c) September 2004:
// Added: 
// Function CPU_Time()
// 


#define WIN32_LEAN_AND_MEAN

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "windows.h"
#include "PDH.h"
#include "PDHMSG.h"
#include "perf.h"
#include <stdio.h>
#include <tchar.h>
#include <Sddl.h>

#define INITIAL_SIZE 512


PDH_STATUS PPdhEnumObjects(LPCTSTR szMachineName, LPTSTR mszObjectList)
{
	DWORD pcchBufferLength=10000;
	PDH_STATUS stat = PdhEnumObjectsH(H_REALTIME_DATASOURCE,szMachineName,mszObjectList,&pcchBufferLength,PERF_DETAIL_WIZARD, TRUE);
	printf("The Len: %d %s %s\n",pcchBufferLength, szMachineName,mszObjectList);
	if(stat != ERROR_SUCCESS)
		return stat;
	return pcchBufferLength;

}

void CleanUp(HANDLE hprocess, HANDLE hdlToken)
{
	if(hprocess)
		CloseHandle(hprocess);
	if(hdlToken)
		CloseHandle(hdlToken);
}

BOOL ConvertSid(PSID pSid, LPTSTR szUser, LPTSTR szError)
   {

      DWORD cchUser = INITIAL_SIZE;
      TCHAR szDomain[INITIAL_SIZE];
      DWORD cchDomain = INITIAL_SIZE;
      SID_NAME_USE snu;
      ZeroMemory(szDomain, (sizeof(szDomain)/sizeof(TCHAR)));

      //
      // test if SID passed in is valid
      //
      if(!IsValidSid(pSid)) return FALSE;
      if(LookupAccountSid(NULL, pSid, szUser, &cchUser, szDomain, &cchDomain, &snu)==0)
      {
		FormatMessage( 
    		FORMAT_MESSAGE_ALLOCATE_BUFFER | 
    		FORMAT_MESSAGE_FROM_SYSTEM | 
    		FORMAT_MESSAGE_IGNORE_INSERTS,
    		NULL,
    		GetLastError(),
    		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    		szError,
    		0,
    		NULL );
      	return FALSE;
      }
      return TRUE;

   }

/* int GetLanguageInfo(LPSTR *lpLCData, int length, LCID Locale)
{
	LCTYPE LCType=LOCALE_NOUSEROVERRIDE |LOCALE_USE_CP_ACP;
	return GetLocaleInfo(
  Locale,      // locale identifier
  LCTYPE LCType,    // information type
  lpLCData,  // information buffer
  length       // size of buffer
);
} */

MODULE = Win32::Process::Perf		PACKAGE = Win32::Process::Perf



void
open_query()

	PREINIT:

		PDH_STATUS stat;
		HQUERY	hQwy;

	PPCODE:

		stat = PdhOpenQuery(NULL, 0, &hQwy);


		if(stat != ERROR_SUCCESS)
		{
			XPUSHs(sv_2mortal(newSViv(-1)));
		}
		else
		{
			XPUSHs(sv_2mortal(newSViv((long)hQwy)));
		}




void
CleanUp(objQuery)
	SV* objQuery

	PREINIT:
		PDH_STATUS stat;
		HQUERY	pObj;
	PPCODE:

		pObj = (HQUERY)SvIV(objQuery);
		stat = PdhCloseQuery(pObj);


void
add_counter(PName, ObjectName, CounterName, machine, pQwy, pError)
	SV* PName
	SV* ObjectName
	SV* CounterName
	SV* machine
	SV* pQwy
	SV* pError

	PREINIT:

		DWORD dwSize;
		DWORD dwGlen;
		HCOUNTER cnt;
		HQUERY hQwy;
		char str[512];
		PDH_STATUS	stat;
		STRLEN len1;
		STRLEN len2;
		STRLEN len3;
		STRLEN len4;

	PPCODE:

		hQwy = (HQUERY)SvIV(pQwy);

		dwGlen = 0;
		dwSize = 256;

		len1 = sv_len(ObjectName);
		len2 = sv_len(CounterName);
		len3 = sv_len(PName);
		len4 = sv_len(machine);
		if(!SvPOK(ObjectName))
		{
			croak("No process given");
		}
		if(!SvPOK(CounterName))
		{
			croak("No counter given");
		}
		
		sprintf(str,"\\\\%s\\%s(%s)\\%s", SvPV(machine,len4),SvPV(PName,len3), SvPV(ObjectName,len1),SvPV(CounterName,len2));
		//printf("%s\n", str);
		stat = PdhAddCounter(hQwy, (LPTSTR)str, dwGlen, &cnt);
			switch(stat)
			{
				case ERROR_SUCCESS:

					XPUSHs(sv_2mortal(newSViv((long)cnt)));
					break;

				case PDH_CSTATUS_BAD_COUNTERNAME:

					sv_setpv(pError, "The counter name path string could not be parsed or interpreted.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_COUNTER:

					sv_setpv(pError, "The specified counter was not found.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_COUNTERNAME:

					sv_setpv(pError, "An empty counter name path string was passed in.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_MACHINE:

					sv_setpv(pError, "A computer entry could not be created.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_OBJECT:

					sv_setpv(pError, "The specified object could not be found.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_FUNCTION_NOT_FOUND:

					sv_setpv(pError, "The calculation function for this counter could not be determined.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_INVALID_ARGUMENT:

					sv_setpv(pError, "One or more arguments are invalid.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_INVALID_HANDLE:

					sv_setpv(pError, "The query handle is not valid.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_MEMORY_ALLOCATION_FAILURE:

					sv_setpv(pError, "A memory buffer could not be allocated.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				default:

					sv_setpv(pError, "Failed to add the counter - don't know why");
					XPUSHs(sv_2mortal(newSViv(-1)));
		}
		





void
collect_data(pQwy, pError)
	SV* pQwy
	SV* pError

	PREINIT:
		HQUERY hQwy;
		PDH_STATUS stat;
	PPCODE:
		hQwy = (HQUERY)SvIV(pQwy);
		stat = PdhCollectQueryData(hQwy);

		switch(stat)
		{
			case ERROR_SUCCESS:

				XPUSHs(sv_2mortal(newSViv(0)));
				break;

			case PDH_INVALID_HANDLE:

				sv_setpv(pError, "The query handle is not valid.");
				XPUSHs(sv_2mortal(newSViv(-1)));
				break;

			case PDH_NO_DATA:

				sv_setpv(pError, "The query does not currently have any counters.");
				XPUSHs(sv_2mortal(newSViv(-1)));
				break;

			default:

				sv_setpv(pError, "Collect Data Failed - I don't know why");
				XPUSHs(sv_2mortal(newSViv(-1)));
				break;

		}

void
collect_counter_value(pQwy, pCounter, pError)
	SV* pQwy
	SV* pCounter
	SV* pError

	PREINIT:

		HQUERY hQwy;
		HCOUNTER hCnt;
		PDH_STATUS stat;
		PDH_FMT_COUNTERVALUE val;
		DWORD dwType;

	PPCODE:

		hQwy = (HQUERY)SvIV(pQwy);
		hCnt = (HCOUNTER)SvIV(pCounter);

		stat = PdhGetFormattedCounterValue(hCnt, PDH_FMT_LONG | PDH_FMT_NOSCALE , &dwType, &val);
		

		switch(stat)
		{
			case ERROR_SUCCESS:
				if(val.CStatus == PDH_CSTATUS_VALID_DATA)
					XPUSHs(sv_2mortal(newSViv(val.longValue)));
				else {
					sv_setpv(pError, "PDH_Cstatus is not PDH_CSTATUS_VALID_DATA.");
					XPUSHs(sv_2mortal(newSViv(-1)));
				}

				break;

			case PDH_INVALID_ARGUMENT:

				sv_setpv(pError, "An argument is not correct or is incorrectly formatted.");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_INVALID_DATA:

				sv_setpv(pError, "The specified counter does not contain valid data or a successful status code.");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_INVALID_HANDLE:

				sv_setpv(pError, "The counter handle is not valid.");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			default:

				sv_setpv(pError, "Failed to get the counter value - I don't know why.");
				XPUSHs(sv_2mortal(newSViv(-1)));

		}


void
list_objects(pBox, pError)
	SV*	pBox
	SV* pError

	PREINIT:

		DWORD dwSize;
		PDH_STATUS stat;
		char* szBuffer;
		char* szBox;
		STRLEN len;

	PPCODE:

		len = sv_len(pBox);
		szBox = SvPV(pBox, len);

		stat = PdhEnumObjects(NULL, szBox, NULL, &dwSize, PERF_DETAIL_EXPERT, 0);

		Newz(0, szBuffer, (int)dwSize, char);

		stat = PdhEnumObjects(NULL, szBox, szBuffer, &dwSize, PERF_DETAIL_EXPERT, 0);

		switch(stat)
		{
			case ERROR_SUCCESS:

				XPUSHs(sv_2mortal(newSVpv(szBuffer, 0)));

				break;

			case PDH_MORE_DATA:

				printf("There are more entries available to return than there is room in the buffer\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_INSUFFICIENT_BUFFER:

				sv_setpv(pError, "The buffer provided is not large enough to contain any data.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_INVALID_ARGUMENT:

				sv_setpv(pError, "A required argument is invalid or a reserved argument is not NULL.\n");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			default:

				sv_setpv(pError, "I have no idea what went wrong\n");
				XPUSHs(sv_2mortal(newSViv(-1)));
		}

		Safefree(szBuffer);

void
connect_to_box(pBox, pError)
	SV* pBox
	SV* pError

	PREINIT:

		PDH_STATUS stat;
		char* szBox;
		STRLEN len;

	PPCODE:

		len = sv_len(pBox);
		szBox = SvPV(pBox, len);

		stat = PdhConnectMachine(szBox);

		switch(stat)
		{
			case ERROR_SUCCESS:

				XPUSHs(sv_2mortal(newSViv(0)));

				break;

			case PDH_CSTATUS_NO_MACHINE:

				sv_setpv(pError, "Unable to connect to the specified machine");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			case PDH_MEMORY_ALLOCATION_FAILURE:

				sv_setpv(pError, "Unable to allocate a dynamic memory block due to too many applications running on the system or an insufficient memory paging file.");
				XPUSHs(sv_2mortal(newSViv(-1)));

				break;

			default:

				sv_setpv(pError, "ERROR: Don't really know what happened though !");
				XPUSHs(sv_2mortal(newSViv(-1)));
		}

void 
explain_counter(pObject, pCounter, pInstance, pQwy, pError)
	SV* pObject
	SV* pCounter
	SV* pInstance
	SV* pQwy
	SV* pError

	PREINIT:

		PDH_COUNTER_PATH_ELEMENTS	GStruct;
		PDH_COUNTER_INFO* cntInfo;
		DWORD dwSize;
		DWORD dwSize1;
		DWORD dwGlen;
		HCOUNTER cnt;
		HQUERY hQwy;
		char str[256];
		PDH_STATUS	stat;
		STRLEN len1;
		STRLEN len2;
		STRLEN len3;

	PPCODE:

		hQwy = (HQUERY)SvIV(pQwy);

		dwGlen = 1;
		dwSize = 256;
		cntInfo = NULL;

		len1 = sv_len(pObject);
		len2 = sv_len(pCounter);

		if(SvNIOK(pInstance))
		{
			GStruct.szInstanceName = NULL;
		}
		else
		{
			len3 = sv_len(pInstance);
			GStruct.szInstanceName = SvPV(pInstance, len3);
		}

		GStruct.szObjectName = SvPV(pObject, len1);
		GStruct.szCounterName = SvPV(pCounter, len2);
		GStruct.szMachineName = NULL;
		GStruct.szParentInstance = NULL;
		GStruct.dwInstanceIndex = 0;

		stat = PdhMakeCounterPath(&GStruct, (char*)str, &dwSize, NULL);

		if(stat != ERROR_SUCCESS)
		{
			sv_setpv(pError, "Path to that counter isn't valid");
			XPUSHs(sv_2mortal(newSViv(-1)));
		}
		else
		{
			switch(stat)
			{
				case ERROR_SUCCESS:

					stat = PdhGetCounterInfo(&cnt, 1, &dwSize1, cntInfo);

					New(0, cntInfo, (int)dwSize1, PDH_COUNTER_INFO);

					stat = PdhGetCounterInfo(&cnt, 1, &dwSize1, cntInfo);

					if(stat ==  ERROR_SUCCESS)
					{
						XPUSHs(sv_2mortal(newSVpv(cntInfo->szExplainText, 0)));

						Safefree(cntInfo);
					}
					else
					{
						sv_setpv(pError, "Failed to get the explain text for this counter");
						XPUSHs(sv_2mortal(newSViv(-1)));
					}

					break;

				case PDH_CSTATUS_BAD_COUNTERNAME:

					sv_setpv(pError, "The counter name path string could not be parsed or interpreted.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_COUNTER:

					sv_setpv(pError, "The specified counter was not found.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_COUNTERNAME:

					sv_setpv(pError, "An empty counter name path string was passed in.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_MACHINE:

					sv_setpv(pError, "A computer entry could not be created.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_CSTATUS_NO_OBJECT:

					sv_setpv(pError, "The specified object could not be found.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_FUNCTION_NOT_FOUND:

					sv_setpv(pError, "The calculation function for this counter could not be determined.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_INVALID_ARGUMENT:

					sv_setpv(pError, "One or more arguments are invalid.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_INVALID_HANDLE:

					sv_setpv(pError, "The query handle is not valid.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				case PDH_MEMORY_ALLOCATION_FAILURE:

					sv_setpv(pError, "A memory buffer could not be allocated.");
					XPUSHs(sv_2mortal(newSViv(-1)));

					break;

				default:

					sv_setpv(pError, "Failed to add the counter - don't know why");
					XPUSHs(sv_2mortal(newSViv(-1)));
			}
		}


void
GetProcessUser(sv_PID,pError)
	SV* sv_PID
	SV* pError

	PREINIT:
		HANDLE hprocess = 0;
		HANDLE hdlToken = 0;
		DWORD PPID;
		DWORD cbBuffer;
		DWORD cbRequired;
		TOKEN_USER* pUserInfo = NULL;
		TCHAR       szUser[INITIAL_SIZE];
		TCHAR	    szError[INITIAL_SIZE];
		TCHAR 	    temp[INITIAL_SIZE];
		LPVOID lpMsgBuf;
	PPCODE:
	{
		ZeroMemory(szUser, (sizeof(szUser)/sizeof(TCHAR)));
		ZeroMemory(szError, (sizeof(szError)/sizeof(TCHAR)));
		PPID = (DWORD)SvIV(sv_PID);
		hprocess = OpenProcess(PROCESS_QUERY_INFORMATION, 1, PPID);
		if(hprocess != NULL)
		{
			if(OpenProcessToken(hprocess,TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hdlToken)!=0)
			{
				cbBuffer = 0;
				if(!GetTokenInformation(hdlToken,TokenUser,NULL,cbBuffer,&cbRequired)) {
					if (GetLastError() == ERROR_INSUFFICIENT_BUFFER){
            					pUserInfo = (TOKEN_USER *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbRequired);
            					if (pUserInfo == NULL)
            					{
            						wsprintf(temp, "Can not allocate memory to pUserInfo");
            						sv_setpv(pError, temp);
               						XSRETURN(-1);
               					}
						cbBuffer = cbRequired;
						cbRequired = 0;
						if(!GetTokenInformation(hdlToken,TokenUser,(LPVOID)pUserInfo,cbBuffer,&cbRequired))
						{
							FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0,NULL );
							wsprintf(temp, "GetTokenInformation() failed: %s", (LPTSTR)lpMsgBuf);
							sv_setpv(pError, temp);
							XSRETURN(-1);
						}
						// Get the username for the Process
						if(!ConvertSid((pUserInfo->User).Sid, szUser, szError))
						{
							CleanUp(hprocess,hdlToken);
							sv_setpv(pError, szError);
							XSRETURN(-1);
						}
					} else
					{
						
						FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0,NULL );
						CleanUp(hprocess,hdlToken);
						wsprintf(temp, "GetLastError returns: %s", (LPTSTR)lpMsgBuf);
						sv_setpv(pError, temp);
						XSRETURN(-1);
					}
				} else {
					CleanUp(hprocess,hdlToken);
					sv_setpv(pError, "GetTokenInformation(): unknown error.");
					XSRETURN(-1);
				}
				
			} else {
				FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf,0,NULL );
				CleanUp(hprocess,hdlToken);
				//printf("GetLastError returns: %s\n", (LPCTSTR)lpMsgBuf);
				wsprintf(temp, "ProcessToken could not be opened: %s", (LPTSTR)lpMsgBuf);
				sv_setpv(pError, temp);
				XPUSHs(sv_2mortal(newSViv(-1)));
			}
		} else {
			FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0,NULL );
			CleanUp(hprocess,hdlToken);
			//printf("GetLastError returns: %s\n", (LPCTSTR)lpMsgBuf);
			wsprintf(temp, "Process could not be opened: %s", (LPTSTR)lpMsgBuf);
			sv_setpv(pError, temp);
			XPUSHs(sv_2mortal(newSViv(-1)));
		}
		if (!HeapFree(GetProcessHeap(), 0, (LPVOID)pUserInfo))
		{
         		sv_setpv(pError, "HeapFree() failed.");
			XSRETURN(-1);
         	}
		CleanUp(hprocess,hdlToken);
		XPUSHs(sv_2mortal(newSVpv((char *)szUser, strlen(szUser))));
	}	// End PPCODE


void
OSIsSupported(pError)
		SV* pError
	PREINIT:
		OSVERSIONINFOEX osvi;
		BOOL bOsVersionInfoEx;
		int supported;
	PPCODE:
	{
		supported=-1;
		ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
   		osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
		if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
   		{
      			osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
			if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) 
			{
				sv_setpv(pError, "Can not get OS Version!");
				XSRETURN(-1);
			}
   		}
   		switch (osvi.dwPlatformId)
		{
			case VER_PLATFORM_WIN32_NT:
				if((osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) ||  (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 ) || ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) || (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0))
					supported = 1;
				break;
			
			default:
				supported = -1;
				sv_setpv(pError, "OS not supported.");
				break;
		}
		XPUSHs(sv_2mortal(newSViv(supported)));
   	}

void
CPU_Time(sv_PID,pError)
		SV* sv_PID
		SV* pError
	PREINIT:
		HANDLE hprocess;
		FILETIME CreationTime;
		FILETIME ExitTime;
		FILETIME KernelTime;
		FILETIME UserTime;
		SYSTEMTIME SystemTime;
		int retcode;
		//long min;
		//long hour;
		//long sec;
		long seconds = 0;
		int err=0;
		//char   wszMsgBuff[512];
		DWORD PID; 
	PPCODE:
			PID = (DWORD)SvIV(sv_PID);
			SetLastError(0);
			if((hprocess = OpenProcess(PROCESS_QUERY_INFORMATION, 0, PID)) != NULL)
			{
				retcode = GetProcessTimes(hprocess,&CreationTime,&ExitTime,&KernelTime,&UserTime);
				if(retcode) {
					retcode = FileTimeToSystemTime(&KernelTime, &SystemTime);
					seconds = (long)SystemTime.wSecond + ((long)SystemTime.wMinute*60) + ((long)SystemTime.wHour*3600);	//Cpu time?
					retcode = FileTimeToSystemTime(&UserTime, &SystemTime);
					seconds = seconds + ((long)SystemTime.wSecond + ((long)SystemTime.wMinute*60) + ((long)SystemTime.wHour*3600));
					CloseHandle(hprocess);
					XPUSHs(sv_2mortal(newSViv(seconds)));
				} else {
					sv_setpv(pError, "Process coud not be opened.");
					XPUSHs(sv_2mortal(newSViv(-1)));
				}
			} else {
				err = GetLastError();
				sv_setpv(pError, "Process coud not be opened.");
				XPUSHs(sv_2mortal(newSViv(-1)));
			}
			

void
_GetLanguage(Locale)
		int Locale
	PREINIT:
	//TCHAR data[60];
	LANGID SID;
	LANGID UID;
	LANGID LID;
	LCID ID;
	PPCODE:
	{
		SID = GetSystemDefaultLangID();
		UID = GetUserDefaultLangID();
		ID=GetUserDefaultLCID();
		//printf("GetUserDefaultLCID: %X GetSystemDefaultLCID: %X\n", ID, GetSystemDefaultLCID());
		
		if(SID != UID)
			LID = SID;
		else
			LID = UID;
		//printf("The LangID: %X SID: %X UID %X\n", LID, SID, UID);
		//XPUSHs(sv_2mortal(newSVpv((char *)data, strlen(data))));
		XPUSHs(sv_2mortal(newSViv(LID)));
	}


void
_PdhEnumObjects(szMachineName, mszObjectList)
		SV* szMachineName
		SV* mszObjectList
	PREINIT:
		char *machine;
		char objectList[10000];
		STRLEN len1;
		//STRLEN len2;
		PDH_STATUS ret=0;
	PPCODE:
	{
		len1 = sv_len(szMachineName);
		machine=SvPV(szMachineName,len1);
		printf("The machine: %s\n", machine);
		ret=PPdhEnumObjects(machine,(LPTSTR)objectList);
		printf("Return code: %d\n", ret);
	}