#INCLUDE WCONNECT.H

#Define MAX_INI_BUFFERSIZE  		512
#Define MAX_INI_ENUM_BUFFERSIZE 	8196

#DEFINE Testing .T.
 
 
#IF Testing
 
? "Timezone: " + TRANSFORM(GetTimeZone()) +  " minutes"
ltTime = DATETIME()
? "Current Time: ", ltTime
 
ltUtc = GetUtcTime(ltTime)
? "UTC Time: ", ltUtc
 
ltTime = FromUtcTime(ltUtc)
? "Back to local: ", ltTime
#ENDIF

*************************************************************
Define Class wwAPI As Custom
	*************************************************************
	***    Author: Rick Strahl
	***            (c) West Wind Technologies, 1997
	***   Contact: (541) 386-2087  / rstrahl@west-wind.com
	***  Function: Encapsulates several Windows API functions
	*************************************************************

	*** Custom Properties
	nLastError = 0

	Function Init
		************************************************************************
		* wwAPI :: Init
		*********************************
		***  Function: DECLARES commonly used DECLAREs so they're not redefined
		***            on each call to the methods.
		************************************************************************

		Declare Integer GetPrivateProfileString ;
			IN WIN32API ;
			STRING cSection, ;
			STRING cEntry, ;
			STRING cDefault, ;
			STRING @cRetVal, ;
			INTEGER nSize, ;
			STRING cFileName

		Declare Integer GetCurrentThread ;
			IN WIN32API

		Declare Integer GetThreadPriority ;
			IN WIN32API ;
			INTEGER tnThreadHandle

		Declare Integer SetThreadPriority ;
			IN WIN32API ;
			INTEGER tnThreadHandle, ;
			INTEGER tnPriority

		*** Open Registry Key
		Declare Integer RegOpenKey ;
			IN Win32API ;
			INTEGER nHKey, ;
			STRING cSubKey, ;
			INTEGER @nHandle

		*** Create a new Key
		Declare Integer RegCreateKey ;
			IN Win32API ;
			INTEGER nHKey, ;
			STRING cSubKey, ;
			INTEGER @nHandle

		*** Close an open Key
		Declare Integer RegCloseKey ;
			IN Win32API ;
			INTEGER nHKey

	Endfunc
	* Init

	Function MessageBeep
		************************************************************************
		* wwAPI :: MessageBeep
		**********************
		***  Function: MessageBeep API call runs system sounds
		***      Pass: lnSound   -   Uses FoxPro.h MB_ICONxxxxx values
		***    Return: nothing
		************************************************************************
		Lparameters lnSound
		Declare Integer MessageBeep ;
			IN WIN32API As MsgBeep ;
			INTEGER nSound
		= MsgBeep(lnSound)
	Endfunc
	* MessageBeep

	Function ReadRegistryString
		************************************************************************
		* wwAPI :: ReadRegistryString
		*********************************
		***  Function: Reads a string value from the registry.
		***      Pass: tnHKEY    -  HKEY value (in CGIServ.h)
		***            tcSubkey  -  The Registry subkey value
		***            tcEntry   -  The actual Key to retrieve
		***            tlInteger -  Optional - Return an DWORD value
		***    Return: Registry String or .NULL. on not found
		************************************************************************
		Lparameters tnHKey, tcSubkey, tcEntry, tlInteger
		Local lnRegHandle, lnResult, lnSize, lcDataBuffer, tnType

		tnHKey = Iif(Vartype(tnHKey) = "N", tnHKey, HKEY_LOCAL_MACHINE)

		lnRegHandle = 0

		*** Open the registry key
		lnResult = RegOpenKey(tnHKey, tcSubkey, @lnRegHandle)
		If lnResult # ERROR_SUCCESS
			*** Not Found
			Return .Null.
		Endif

		*** Return buffer to receive value
		If !tlInteger
			*** Need to define here specifically for Return Type
			*** for lpdData parameter or VFP will choke.
			*** Here it's STRING.
			Declare Integer RegQueryValueEx ;
				IN Win32API ;
				INTEGER nHKey, ;
				STRING lpszValueName, ;
				INTEGER dwReserved, ;
				INTEGER @lpdwType, ;
				STRING @lpbData, ;
				INTEGER @lpcbData

			lcDataBuffer = Space(MAX_INI_BUFFERSIZE)
			lnSize = Len(lcDataBuffer)
			lnType = REG_DWORD

			lnResult = RegQueryValueEx(lnRegHandle, tcEntry, 0, @lnType, ;
				  @lcDataBuffer, @lnSize)
		Else
			*** Need to define here specifically for Return Type
			*** for lpdData parameter or VFP will choke. 
			*** Here's it's an INTEGER
			Declare Integer RegQueryValueEx ;
				IN Win32API As RegQueryInt;
				INTEGER nHKey, ;
				STRING lpszValueName, ;
				INTEGER dwReserved, ;
				Integer @lpdwType, ;
				INTEGER @lpbData, ;
				INTEGER @lpcbData

			lcDataBuffer = 0
			lnSize = 4
			lnType = REG_DWORD
			lnResult = RegQueryInt(lnRegHandle, tcEntry, 0, @lnType, ;
				  @lcDataBuffer, @lnSize)
			If lnResult = ERROR_SUCCESS
				Return lcDataBuffer
			Else
				Return - 1
			Endif
		Endif
		= RegCloseKey(lnRegHandle)

		If lnResult # ERROR_SUCCESS
			*** Not Found
			Return .Null.
		Endif

		If lnSize < 2
			Return ""
		Endif

		*** Return string and strip out NULLs
		Return Substr(lcDataBuffer, 1, lnSize - 1)
	Endfunc
	* ReadRegistryString

	************************************************************************
	* Registry :: WriteRegistryString
	*********************************
	***  Function: Writes a string value to the registry.
	***            If the value doesn't exist it's created. If the key
	***            doesn't exist it is also created, but this will only
	***            succeed if it's the last key on the hive.
	***      Pass: tnHKEY    -  HKEY value (in WCONNECT.h)
	***            tcSubkey  -  The Registry subkey value
	***            tcEntry   -  The actual Key to write to
	***            tcValue   -  Value to write or .NULL. to delete key
	***            tlCreate  -  Create if it doesn't exist
	***    Assume: Use with extreme caution!!! Blowing your registry can
	***            hose your system!
	***    Return: .T. or .NULL. on error
	************************************************************************
	Function WriteRegistryString
		Lparameters tnHKey, tcSubkey, tcEntry, tcValue, tlCreate
		Local lnRegHandle, lnResult, lnSize, lcDataBuffer, tnType

		tnHKey = Iif(Type("tnHKey") = "N", tnHKey, HKEY_LOCAL_MACHINE)

		lnRegHandle = 0

		lnResult = RegOpenKey(tnHKey, tcSubkey, @lnRegHandle)
		If lnResult # ERROR_SUCCESS
			If !tlCreate
				Return .F.
			Else
				lnResult = RegCreateKey(tnHKey, tcSubkey, @lnRegHandle)
				If lnResult # ERROR_SUCCESS
					Return .F.
				Endif
			Endif
		Endif

		*** Need to define here specifically for Return Type!
		*** Here lpbData is STRING.
		Declare Integer RegSetValueEx ;
			IN Win32API ;
			INTEGER nHKey, ;
			STRING lpszEntry, ;
			INTEGER dwReserved, ;
			INTEGER fdwType, ;
			STRING lpbData, ;
			INTEGER cbData

		*** Check for .NULL. which means delete key
		If !Isnull(tcValue)
			*** Nope - write new value
			lnSize = Len(tcValue)
			If lnSize = 0
				tcValue = Chr(0)
			Endif

			lnResult = RegSetValueEx(lnRegHandle, tcEntry, 0, REG_SZ, ;
				  tcValue, lnSize)
		Else
			*** Delete a value from a key
			Declare Integer RegDeleteValue ;
				IN Win32API ;
				INTEGER nHKEY, ;
				STRING cEntry

			*** DELETE THE KEY
			lnResult = RegDeleteValue(lnRegHandle, tcEntry)
		Endif

		= RegCloseKey(lnRegHandle)

		If lnResult # ERROR_SUCCESS
			Return .F.
		Endif

		Return .T.
	Endproc
	* WriteRegistryString

	Function EnumKey
		************************************************************************
		* wwAPI :: EnumRegistryKey
		*********************************
		***  Function: Returns a registry key name based on an index
		***            Allows enumeration of keys in a FOR loop. If key
		***            is empty end of list is reached.
		***      Pass: tnHKey    -   HKEY_ root key
		***            tcSubkey  -   Subkey string
		***            tnIndex   -   Index of key name to get (0 based)
		***    Return: "" on error - Key name otherwise
		************************************************************************
		Lparameters tnHKey, tcSubkey, tnIndex
		Local lcSubKey, lcReturn, lnResult, lcDataBuffer

		lnRegHandle = 0

		*** Open the registry key
		lnResult = RegOpenKey(tnHKey, tcSubkey, @lnRegHandle)
		If lnResult # ERROR_SUCCESS
			*** Not Found
			Return .Null.
		Endif

		Declare Integer RegEnumKey ;
			IN WIN32API ;
			INTEGER nHKey, ;
			INTEGER nIndex, ;
			STRING @cSubkey, ;
			INTEGER nSize

		lcDataBuffer = Space(MAX_INI_BUFFERSIZE)
		lnSize = MAX_INI_BUFFERSIZE
		lnResult = RegEnumKey(lnRegHandle, tnIndex, @lcDataBuffer, lnSize)

		= RegCloseKey(lnRegHandle)

		If lnResult # ERROR_SUCCESS
			*** Not Found
			Return .Null.
		Endif

		Return Trim(Chrtran(lcDataBuffer, Chr(0), ""))
	Endfunc
	* EnumRegistryKey


	Function GetProfileString
		************************************************************************
		* wwAPI :: GetProfileString
		***************************
		***  Modified: 09/26/95
		***  Function: Read Profile String information from a given
		***            text file using Windows INI formatting conventions
		***      Pass: pcFileName   -    Name of INI file
		***            pcSection    -    [Section] in the INI file ("Drivers")
		***            pcEntry      -    Entry to retrieve ("Wave")
		***                              If this value is a null string
		***                              all values for the section are
		***                              retrieved seperated by CHR(13)s
		***    Return: Value(s) or .NULL. if not found
		************************************************************************
		Lparameters pcFileName, pcSection, pcEntry, pnBufferSize
		Local lcIniValue, lnResult

		*pcFileName=IIF(TYPE("pcFileName")="C",pcFileName,"")
		*pcSection=IIF(TYPE("pcSection")="C",pcSection,"")

		*** Default to 0, which means all entries!
		*pcEntry=IIF(TYPE("pcEntry")="C",pcEntry,0)

		*** Initialize buffer for result
		lcIniValue = Space(Iif( Type("pnBufferSize") = "N", pnBufferSize, MAX_INI_BUFFERSIZE) )

		lnResult = GetPrivateProfileString(pcSection, pcEntry, "*None*", ;
			  @lcIniValue, Len(lcIniValue), pcFileName)

		*** Strip out Nulls
		If Type("pcEntry") = "N" And pcEntry = 0
			*** 0 was passed to get all entry labels
			*** Seperate all of the values with a Carriage Return
			lcIniValue = Trim(Chrtran(lcIniValue, Chr(0), Chr(13)) )
		Else
			*** Individual Entry
			lcIniValue = Substr(lcIniValue, 1, lnResult)
		Endif

		*** On error the result contains "*None"
		If lcIniValue = "*None*"
			lcIniValue = .Null.
		Endif

		Return lcIniValue
	Endfunc
	* GetProfileString

	************************************************************************
	* wwAPI :: GetProfileSections
	*********************************
	***  Function: Retrieves all sections of an INI File
	***      Pass: @laSections   -   Empty array to receive sections
	***            lcIniFile     -   Name of the INI file
	***            lnBufSize     -   Size of result buffer (optional)
	***    Return: Count of Sections  
	************************************************************************
	Function aProfileSections
		Lparameters laSections, lcIniFile
		Local lnBufsize, lcBuffer, lnSize, lnResult, lnCount

		lnBufsize = Iif(Empty(lnBufsize), 16484, lnBufsize)

		Declare Integer GetPrivateProfileSectionNames ;
			IN WIN32API ;
			STRING @lpzReturnBuffer, ;
			INTEGER nSize, ;
			STRING lpFileName

		lcBuffer = Space(lnBufsize)
		lnSize = Len(lcBuffer)
		lnResult = GetPrivateProfileSectionNames(@lcBuffer, lnSize, lcIniFile)
		If lnResult < 3
			Return 0
		Endif

		lnCount = aParseString(@laSections, Trim(lcBuffer), Chr(0))
		lnCount = lnCount - 2
		If lnCount > 0
			Dimension laSections[lnCount]
		Endif

		Return lnCount
	Endfunc
	* wwAPI :: aProfileSections

	************************************************************************
	* wwAPI :: WriteProfileString
	*********************************
	***  Function: Writes a value back to an INI file
	***      Pass: pcFileName    -   Name of the file to write to
	***            pcSection     -   Profile Section
	***            pcKey         -   The key to write to
	***            pcValue       -   The value to write
	***    Return: .T. or .F.
	************************************************************************
	Function WriteProfileString
		Lparameters pcFileName, pcSection, pcEntry, pcValue

		Declare Integer WritePrivateProfileString ;
			IN WIN32API ;
			STRING cSection, String cEntry, String cEntry, ;
			STRING cFileName

		lnRetVal = WritePrivateProfileString(pcSection, pcEntry, pcValue, pcFileName)

		If lnRetVal = 1
			Return .T.
		Endif

		Return .F.
	Endfunc
	* WriteProfileString

	Function GetTempPath
		************************************************************************
		* wwAPI :: GetTempPath
		***********************
		***  Function: Returns the OS temporary files path
		***    Return: Temp file path with trailing "\"
		************************************************************************
		Local lcPath, lnResult

		*** API Definition:
		*** ---------------
		*** DWORD GetTempPath(cchBuffer, lpszTempPath)
		***
		*** DWORD cchBuffer;	/* size, in characters, of the buffer	*/
		*** LPTSTR lpszTempPath;	/* address of buffer for temp. path name	*/
		Declare Integer GetTempPath ;
			IN WIN32API As GetTPath ;
			INTEGER nBufSize, ;
			STRING @cPathName

		lcPath = Space(256)
		lnSize = Len(lcPath)

		lnResult = GetTPath(lnSize, @lcPath)

		If lnResult = 0
			lcPath = ""
		Else
			lcPath = Substr(lcPath, 1, lnResult)
		Endif

		Return lcPath
	Endfunc
	* eop GetTempPath

	Function GetEXEFile
		************************************************************************
		* wwAPI :: GetEXEFileName
		*********************************
		***  Function: Returns the Module name of the EXE file that started
		***            the current application. Unlike Application.Filename
		***            this function correctly returns the name of the EXE file
		***            for Automation servers too!
		***    Return: Filename or ""  (VFP.EXE is returned in Dev Version)
		************************************************************************
		Declare Integer GetModuleFileName ;
			IN WIN32API ;
			integer hinst, ;
			string @lpszFilename, ;
			integer @cbFileName

		lcFilename = Space(256)
		lnBytes = 255

		= GetModuleFileName(0, @lcFilename, @lnBytes)

		lnBytes = At(Chr(0), lcFilename)
		If lnBytes > 1
			lcFilename = Substr(lcFilename, 1, lnBytes - 1)
		Else
			lcFilename = ""
		Endif

		Return lcFilename
	Endfunc
	* GetEXEFileName


	************************************************************************
	* WinApi :: ShellExecute
	*********************************
	***    Author: Rick Strahl, West Wind Technologies
	***            http://www.west-wind.com/ 
	***  Function: Opens a file in the application that it's
	***            associated with.
	***      Pass: lcFileName  -  Name of the file to open
	***            lcWorkDir   -  Working directory
	***            lcOperation -  
	***    Return: 2  - Bad Association (invalid URL)
	***            31 - No application association
	***            29 - Failure to load application
	***            30 - Application is busy 
	***
	***            Values over 32 indicate success
	***            and return an instance handle for
	***            the application started (the browser) 
	************************************************************************
	***         FUNCTION ShellExecute
	***         LPARAMETERS lcFileName, lcWorkDir, lcOperation
	***         
	***         lcWorkDir=IIF(type("lcWorkDir")="C",lcWorkDir,"")
	***         lcOperation=IIF(type("lcOperation")="C",lcOperation,"Open")
	***         
	***         DECLARE INTEGER ShellExecute ;
	***             IN SHELL32.DLL ;
	***             INTEGER nWinHandle,;
	***             STRING cOperation,;   
	***             STRING cFileName,;
	***             STRING cParameters,;
	***             STRING cDirectory,;
	***             INTEGER nShowWindow
	***            
	***         RETURN ShellExecute(0,lcOperation,lcFilename,"",lcWorkDir,1)
	***         ENDFUNC
	***         * ShellExecute

	************************************************************************
	* wwAPI :: CopyFile
	*********************************
	***  Function: Copies File. Faster than Fox Copy and handles
	***            errors internally.
	***      Pass: tcSource -  Source File
	***            tcTarget -  Target File
	***            tnFlag   -  0* override, 1 don't
	***    Return: .T. or .F.
	************************************************************************
	Function CopyFile
		Lparameters lcSource, lcTarget, lnFlag
		Local lnRetVal

		*** Copy File and overwrite
		lnFlag = Iif(Type("nFlag") = "N", nFlag, 0)

		Declare Integer CopyFile ;
			IN WIN32API ;
			AS wCopyFile ;
			STRING @cSource, ;
			STRING @cTarget, ;
			INTEGER nFlag

		lnRetVal = wCopyFile(m.lcSource, m.lcTarget, m.lnFlag)

		Return Iif(m.lnRetVal = 0, .F., .T.)
	Endproc
	* CopyFile

	************************************************************************
	* wwAPI :: MoveFile
	*********************************
	***  Function: Rename/Moves a file
	***      Pass: tcSource -  Source File
	***            tcTarget -  Target File
	***    Return: .T. or .F.
	************************************************************************
	Function MoveFile
		Lparameters lcSource, lcTarget
		Local lnRetVal

		Declare Integer MoveFile In Win32API As wMoveFile String @cSource, String @cTarget
		lnRetVal = wMoveFile(m.lcSource, m.lcTarget)

		Return Iif(m.lnRetVal = 0, .F., .T.)
	Endfunc

	************************************************************************
	* wwAPI :: MoveFile
	*********************************
	***  Function: Deletes a file
	***      Pass: tcFile -  File path
	***    Return: .T. or .F.
	************************************************************************
	Function DeleteFile
		Lparameters tcFile
		Local lnRetVal

		Declare Integer DeleteFile In kernel32 as wDeleteFile String @pFileName
		lnRetVal = wDeleteFile(m.tcFile)

		Return Iif(m.lnRetVal = 0, .F., .T.)
	Endfunc

	Function GetUserName

	************************************************************************
	* wwAPI :: MoveFile
	*********************************
		Declare Integer GetUserName ;
			IN WIN32API ;
			STRING@ cComputerName, ;
			INTEGER@ nSize

		lcComputer = Space(80)
		lnSize = 80

		= GetUserName(@lcComputer, @lnSize)
		If lnSize < 2
			Return ""
		Endif

		Return Substr(lcComputer, 1, lnSize - 1)

	Function GetComputerName
		************************************************************************
		* wwAPI :: GetComputerName
		*********************************
		***  Function: Returns the name of the current machine
		***    Return: Name of the computer
		************************************************************************

		Declare Integer GetComputerName ;
			IN WIN32API ;
			STRING@ cComputerName, ;
			INTEGER@ nSize

		lcComputer = Space(80)
		lnSize = 80

		= GetComputerName(@lcComputer, @lnSize)
		If lnSize < 2
			Return ""
		Endif

		Return Substr(lcComputer, 1, lnSize)
	Endfunc
	* GetComputerName


	Function LogonUser
		************************************************************************
		* wwAPI :: LogonUser
		*********************************
		***  Function: Check whether a username and password is valid
		***    Assume: Account checking must have admin rights
		***      Pass: Username, Password and optionally a server
		***    Return: .T. or .F.
		************************************************************************
		Lparameters lcUsername, lcPassword, lcServer

		If Empty(lcUsername)
			Return .F.
		Endif
		If Empty(lcPassword)
			lcPassword = ""
		Endif
		If Empty(lcServer)
			lcServer = "."
		Endif

		#Define LOGON32_LOGON_INTERACTIVE   2
		#Define LOGON32_LOGON_NETWORK       3
		#Define LOGON32_LOGON_BATCH         4
		#Define LOGON32_LOGON_SERVICE       5

		#Define LOGON32_PROVIDER_DEFAULT    0

		Declare Integer LogonUser In WIN32API ;
			String lcUser, ;
			String lcServer, ;
			String lcPassword, ;
			INTEGER dwLogonType, ;
			Integer dwProvider, ;
			Integer @dwToken

		lnToken = 0
		lnResult = LogonUser(lcUsername, lcServer, lcPassword, ;
			  LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, @lnToken)

		Declare Integer CloseHandle In WIN32API Integer
		CloseHandle(lnToken)

		Return Iif(lnResult = 1, .T., .F.)
	Endfunc
	* wwAPI :: LogonUser

	Function GetSystemDir
		************************************************************************
		* wwAPI :: GetSystemDir
		*********************************
		***  Function: Returns the Windows System directory path
		***      Pass: llWindowsDir - Optional: Retrieve the Windows dir
		***    Return: Windows System directory or "" if failed
		************************************************************************
		Lparameter llWindowsDir
		Local lcPath, lnSize

		lcPath = Space(256)

		If !llWindowsDir
			Declare Integer GetSystemDirectory ;
				IN Win32API ;
				STRING  @pszSysPath, ;
				INTEGER cchSysPath
			lnSize = GetSystemDirectory(@lcPath, 256)
		Else
			Declare Integer GetWindowsDirectory ;
				IN Win32API ;
				STRING  @pszSysPath, ;
				INTEGER cchSysPath
			lnSize = GetWindowsDirectory(@lcPath, 256)
		Endif

		If lnSize > 0
			Return Substr(lcPath, 1, lnSize) + "\"
		Endif

		Return ""
	Endfunc
	* GetSystemDir


	Function GetCurrentThread
		************************************************************************
		* wwAPI :: GetCurrentThread
		*********************************
		***  Function: Returns handle to the current Process/Thread
		***    Return: Process Handle or 0
		************************************************************************
		Return GetCurrentThread()
	Endfunc
	* GetProcess

	************************************************************************
	* wwAPI :: GetThreadPriority
	*********************************
	***  Function: Gets the current Priority setting of the thread.
	***            Use to save and reset priority when bumping it up.
	***      Pass: tnThreadHandle
	************************************************************************
	Function GetThreadPriority
		Lparameter tnThreadHandle
		Return GetThreadPriority(tnThreadHandle)
	Endfunc
	* GetThreadPriority

	Function SetThreadPriority
		************************************************************************
		* wwAPI :: SetThreadPriority
		*********************************
		***  Function: Sets a thread process priority. Can dramatically
		***            increase performance of a task.
		***      Pass: tnThreadHandle
		***            tnPriority         0 - Normal
		***                               1 - Above Normal
		***                               2 - Highest Priority
		***                              15 - Time Critical
		***                              31 - Real Time (doesn't work w/ Win95)
		************************************************************************
		Lparameter tnThreadHandle, tnPriority
		Return SetThreadPriority(tnThreadHandle, tnPriority)
	Endfunc
	* GetThreadPriority


	Function PlayWave
		************************************************************************
		* wwapi :: PlayWave
		*******************
		***     Class: WinAPI
		***  Function: Plays the Wave File or WIN.INI
		***            [Sounds] Entry specified in the
		***            parameter. If the .WAV file or
		***            System Sound can't be found,
		***            SystemDefault beep is played
		***    Assume: Runs only under Windows
		***            uses MMSYSTEM.DLL  (Win 3.1)
		***                 WINMM.DLL  (32 bit Win)
		***      Pass: pcWaveFile - Full path of Wave file
		***                         or System Sound Entry
		***            pnPlayType - 1 - sound plays in background (default)
		***                         0 - sound plays - app waits
		***                         2 - No default sound if file doesn't exist
		***                         4 - Kill currently playing sound 
		***                         8 - Continous  
		***                         Values can be added together for combinations
		***  Examples:
		***    do PlayWav with "SystemQuestion"
		***    do PlayWav with "C:\Windows\System\Ding.wav"
		***    if PlayWav("SystemAsterisk")
		***
		***    Return: .t. if Wave was played .f. otherwise
		*************************************************************************
		Lparameter pcWaveFile, pnPlayType
		Local lhPlaySnd, llRetVal

		pnPlayType = Iif(Type("pnPlayType") = "N", pnPlayType, 1)

		llRetVal = .F.

		Declare Integer PlaySound ;
			IN WINMM.Dll  ;
			STRING cWave, Integer nModule, Integer nType

		If PlaySound(pcWaveFile, 0, pnPlayType) = 1
			llRetVal = .T.
		Endif

		Return llRetVal
	Endfunc
	*EOF PLAYWAV


	Function CreateGUID
		************************************************************************
		* wwapi::CreateGUID
		********************
		***    Author: Rick Strahl, West Wind Technologies
		***            http://www.west-wind.com/
		***  Modified: 01/26/98
		***  Function: Creates a globally unique identifier using Win32
		***            COM services. The vlaue is guaranteed to be unique
		***    Format: {9F47F480-9641-11D1-A3D0-00600889F23B}
		***    Return: GUID as a string or "" if the function failed 
		*************************************************************************
		Lparameters llRaw
		Local lcStruc_GUID, lcGUID, lnSize

		Declare Integer CoCreateGuid ;
			IN Ole32.Dll ;
			STRING @lcGUIDStruc

		Declare Integer StringFromGUID2 ;
			IN Ole32.Dll ;
			STRING cGUIDStruc, ;
			STRING @cGUID, ;
			LONG nSize

		*** Simulate GUID strcuture with a string
		lcStruc_GUID = Replicate(" ", 16)
		lcGUID = Replicate(" ", 80)
		lnSize = Len(lcGUID) / 2
		If CoCreateGuid(@lcStruc_GUID) # 0
			Return ""
		Endif

		If llRaw
			Return lcStruc_GUID
		Endif

		*** Now convert the structure to the GUID string
		If StringFromGUID2(lcStruc_GUID, @lcGUID, lnSize) = 0
			Return ""
		Endif

		*** String is UniCode so we must convert to ANSI
		Return  Strconv(Left(lcGUID, 76), 6)
		* Eof CreateGUID

	Function Sleep(lnMilliSecs)
		************************************************************************
		* wwAPI :: Sleep
		*********************************
		***  Function: Puts the computer into idle state. More efficient and
		***            no keyboard interface than Inkey()
		***      Pass: tnMilliseconds
		***    Return: nothing
		************************************************************************

		lnMilliSecs = Iif(Type("lnMillisecs") = "N", lnMilliSecs, 0)

		Declare Sleep ;
			IN WIN32API ;
			INTEGER nMillisecs

		= Sleep(lnMilliSecs)
	Endfunc
	* Sleep

	************************************************************************
	* wwAPI :: GetLastError
	*********************************
	***  Function:
	***    Assume:
	***      Pass:
	***    Return:
	************************************************************************
	Function GetLastError
		Declare Integer GetLastError In Win32API
		Return GetLastError()
	Endfunc
	* wwAPI :: GetLastError

	************************************************************************
	* wwAPI :: GetSystemErrorMsg
	*********************************
	***  Function: Returns the Message text for a Win32API error code.
	***      Pass: lnErrorNo  -  WIN32 Error Code
	***    Return: Error Message or "" if not found
	************************************************************************
	Function GetSystemErrorMsg
		Lparameters lnErrorNo, lcDLL
		Local szMsgBuffer, lnSize

		szMsgBuffer = Space(500)

		Declare Integer FormatMessage ;
			IN WIN32API ;
			INTEGER dwFlags, ;
			STRING lpvSource, ;
			INTEGER dwMsgId, ;
			INTEGER dwLangId, ;
			STRING @lpBuffer, ;
			INTEGER nSize, ;
			INTEGER  Arguments

		lnSize = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, lnErrorNo, ;
			  0, @szMsgBuffer, Len(szMsgBuffer), 0)

		If Len(szMsgBuffer) > 1
			szMsgBuffer = Substr(szMsgBuffer, 1, lnSize - 1 )
		Else
			szMsgBuffer = ""
		Endif

		Return szMsgBuffer


		#If .F.    &&!DEBUGMODE
			************************************************************************
			* wwAPI :: Error
			*********************************
			***  Function: Sets an Error number of the error occurred
			************************************************************************
	Function Error
		Lparameters nError, cMethod, nLine
		This.nLastError = nError
	Endfunc
	* Error
#Endif

Enddefine
*EOC wwAPI


************************************************************************
FUNCTION GetTimeZone
*********************************
***  Function: Returns the TimeZone offset from GMT including
***            daylight savings. Result is returned in minutes.
************************************************************************
PUBLIC __TimeZone
 
	*** Cache the timezone so this is fast
	IF VARTYPE(__TimeZone) = "N"
	   RETURN __TimeZone
	ENDIF
	 
	DECLARE integer GetTimeZoneInformation IN Win32API ;
	   STRING @ TimeZoneStruct
	   
	lcTZ = SPACE(256)
	 
	lnDayLightSavings = GetTimeZoneInformation(@lcTZ)
	lnOffset = CharToBin(SUBSTR(lcTZ,1,4),.T.)
	 
	*** Subtract an hour if daylight savings is active
	IF lnDaylightSavings = 2
	   lnOffset = lnOffset - 60
	ENDIF
	 
	__TimeZone = lnOffset
    
RETURN lnOffSet


************************************************************************
FUNCTION CharToBin(lcBinString,llSigned)
****************************************
***  Function: Binary Numeric conversion routine. 
***            Converts DWORD or Unsigned Integer string
***            to Fox numeric integer value.
***      Pass: lcBinString -  String that contains the binary data 
***            llSigned    -  if .T. uses signed conversion
***                           otherwise value is unsigned (DWORD)
***    Return: Fox number
************************************************************************
	LOCAL m.i, lnWord
	 
	lnWord = 0
	FOR m.i = 1 TO LEN(lcBinString)
	 lnWord = lnWord + (ASC(SUBSTR(lcBinString, m.i, 1)) * (2 ^ (8 * (m.i - 1))))
	ENDFOR
	 
	IF TYPE('llSigned') = 'L' AND llSigned AND lnWord > 0x80000000
	  lnWord = lnWord - 1 - 0xFFFFFFFF
	ENDIF
	 
	RETURN lnWord

************************************************************************
*  GetUtcTime
****************************************
***  Function: Returns UTC time from local time
***    Assume:
***      Pass:
***    Return:
************************************************************************
FUNCTION GetUtcTime(ltTime)
 
	IF EMPTY(ltTime)
	    ltTime = DATETIME()
	ENDIF
	 
	*** Adjust the timezone offset
	RETURN ltTime + (GetTimeZone() * 60)    
ENDFUNC
*   GetUtcTime
 
************************************************************************
*  FromUtcTime
****************************************
***  Function: Returns local time from UTC Time
***    Assume:
***      Pass:
***    Return:
************************************************************************
FUNCTION FromUtcTime(ltTime)
	RETURN ltTime - (GetTimeZone() * 60)
ENDFUNC
*   FromUtcTime


* Convert Unix time to datetime
FUNCTION UnixTime2Datetime(tnUnixTime)
	RETURN {^1970/01/01 00:00:00} + tnUnixTime

* Convert datetime to Unix time
FUNCTION Datetime2UnixTime(ttDT)
	IF EMPTY(m.ttDT)
		ttDT = DATETIME()
	ENDIF 	
	RETURN ttDT - {^1970/01/01 00:00:00}