/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbFileTools
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<05/02/2014: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbFileTools.h"
#include "xbVariant.h"
#include "xbSecurity.h"
#include "xbException.h"
#include "xbDnItf.h"
#include <Dsgetdc.h>
#include <Lm.h>
#include <shlobj.h>
#include <shellapi.h>

#pragma warning(push)
#pragma warning(disable: 4311) //'<function-style-cast>' : troncation de pointeur de 'const char *' à 'xbNptr'
#pragma warning(disable: 4312) //'cast de type' : la conversion de 'ulong' en 'HANDLE' d'une taille supérieure
xbString xbGetFilePath(HMODULE hMod,bool bLongName)
{
	char szFilename[512];
	*szFilename = 0;
	::GetModuleFileName(hMod,szFilename,sizeof(szFilename));

	if (bLongName)
		::GetLongPathName(szFilename,szFilename,sizeof(szFilename));

	return szFilename;
}

xbString xbGetFileName(HMODULE hMod,bool bLongName)
{
	char szFilename[512];
	char *pTitle;
	*szFilename = 0;
	::GetModuleFileName(hMod,szFilename,sizeof(szFilename));

	if (bLongName)
		::GetLongPathName(szFilename,szFilename,sizeof(szFilename));

	char *ptr = strrchr(szFilename,xbPath_SLASHch);
	if (ptr) pTitle = ptr+1;
	else pTitle = szFilename;
	if ((ptr = strrchr(pTitle,'.')) != NULL) *ptr = 0;

	return pTitle;
}

xbString xbGetFileName(LPCSTR pszFile)
{
	if (!pszFile) return xbGetFileName();

	char szFilename[512];
	char *pTitle;
	xbStrncpy(szFilename,pszFile,sizeof(szFilename)-1);
	szFilename[sizeof(szFilename)-1] = 0;
	char *ptr = strrchr(szFilename,xbPath_SLASHch);

	{
		char *ptr2 = strrchr(szFilename,xbPath_SLASH2ch);
		if (ptr2 > ptr) ptr = ptr2;
	}

	if (ptr) pTitle = ptr+1;
	else pTitle = szFilename;
	if ((ptr = strrchr(pTitle,'.')) != NULL) *ptr = 0;

	return pTitle;
}

xbString xbGetFileNameExt(HMODULE hMod,bool bLongName)
{
	char szFilename[512];
	*szFilename = 0;
	::GetModuleFileName(hMod,szFilename,sizeof(szFilename));
	if (bLongName)
		::GetLongPathName(szFilename,szFilename,sizeof(szFilename));

	char *ptr = strrchr(szFilename,xbPath_SLASHch);
	if (ptr) return ptr+1;
	return szFilename;
}

xbString xbGetFileNameExt(LPCSTR pszFile)
{
	if (!pszFile) return xbGetFileNameExt();

	const char *ptr = strrchr(pszFile,xbPath_SLASHch);

	{
		const char *ptr2 = strrchr(pszFile,xbPath_SLASH2ch);
		if (ptr2 > ptr) ptr = ptr2;
	}

	if (ptr) return ptr+1;
	return pszFile;
}

xbString xbGetFileExt(LPCSTR pszFile,bool bIncludePoint)
{
	xbAssert(pszFile != NULL);
	const char *pext = strrchr(pszFile,'.');
	if (!pext) return "";
	
	const char *ptr = strrchr(pszFile,xbPath_SLASHch);
	{
		const char *ptr2 = strrchr(pszFile,xbPath_SLASH2ch);
		if (ptr2 > ptr) ptr = ptr2;
	}
	if (pext <= ptr) return "";
	if (bIncludePoint) return pext;
	return pext+1;
}

xbString xbGetFileDir(HMODULE hMod,bool bEndSlash,bool bLongName)
{
	char szFilename[512];
	*szFilename = 0;
	::GetModuleFileName(hMod,szFilename,sizeof(szFilename));
	if (bLongName)
		::GetLongPathName(szFilename,szFilename,sizeof(szFilename));

	char *ptr = strrchr(szFilename,xbPath_SLASHch);
	if (!ptr) return "";
	if (bEndSlash) *(ptr+1) = 0;
	else *ptr = 0;
	return szFilename;
}

xbString xbGetFileDir(LPCSTR pszFile,bool bEndSlash)
{
	if (!pszFile) return xbGetFileDir();

	char szFilename[512];
	xbStrncpy(szFilename,pszFile,sizeof(szFilename)-1);
	szFilename[sizeof(szFilename)-1] = 0;
	char *ptr = strrchr(szFilename,xbPath_SLASHch);

	{
		char *ptr2 = strrchr(szFilename,xbPath_SLASH2ch);
		if (ptr2 > ptr) ptr = ptr2;
	}
	if (!ptr) return "";
	if (bEndSlash) *(ptr+1) = 0;
	else *ptr = 0;
	return szFilename;
}

bool xbFileExists(LPCSTR pszFile)
{
	ulong attr = ::GetFileAttributes(pszFile);
	if (attr == -1) return false;
	return (attr & FILE_ATTRIBUTE_DIRECTORY)?false:true;
}

bool xbDirExists(LPCSTR pszDir)
{
	ulong attr = ::GetFileAttributes(pszDir);
	if (attr == -1) return false;
	return (attr & FILE_ATTRIBUTE_DIRECTORY)?true:false;
}

bool xbPathExists(LPCSTR pszPath)
{
	ulong attr = ::GetFileAttributes(pszPath);
	if (attr == -1) return false;
	return true;
}

xbFileAttr::xbFileAttr(const xbString &file,xbOpMode opm)
{
	m_attr = ::GetFileAttributes(file);
	if (m_attr == -1 && opm == xbOp_Required)
		xbThrowEsys(xbHR_LAST,"xbFileAttr() : file '"+xbString(file)+"' doesn't exist");
}

bool xbFileAttr::Set(const xbString &file,xbOpMode opm)
{
	m_attr = ::GetFileAttributes(file);
	if (m_attr == -1 && opm == xbOp_Required)
		xbThrowEsys(xbHR_LAST,"xbFileAttr() : file '"+xbString(file)+"' doesn't exist");
	return m_attr != -1;
}

bool xbMakeDir(const xbString &_dir,bool bRecursive,const xbSecurityAttr *lpSecurityAttributes,xbOpMode opm,bool *pbCreated)
{
	if (!lpSecurityAttributes)
	{
		xbSecurityAttr secAttr;
		return xbMakeDir(_dir,bRecursive,&secAttr,opm,pbCreated);
	}
	if (_dir.IsEmpty()) return true;
	xbString dir(_dir);
	while(!dir.IsEmpty() && (dir.Last() == xbPath_SLASHch
		|| dir.Last() == xbPath_SLASH2ch)) 
		dir.Length = dir.Length - 1;
	if (dir.IsEmpty()) 
		return true;
	ulong attr = ::GetFileAttributes(dir);
fileExists:
	if (attr != -1)
	{
		if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
		{
			if (opm == xbOp_Required)
				xbThrow xbEapp("Can't create directory : file '"+dir+"' exists and isn't a directory");
			return false;
		}
		if (pbCreated)
			*pbCreated = false;
		return true;
	}

	if (bRecursive) 
	{
		if (!xbMakeDir(xbGetFileDir(dir,false),true,lpSecurityAttributes,opm))
			return false;
	}

	if (!::CreateDirectory(dir,lpSecurityAttributes?lpSecurityAttributes->Ptr():NULL))
	{
		xbHR err = xbHR_LAST;
		if ((attr = ::GetFileAttributes(dir)) != -1)
			goto fileExists; //multithread case
		
		if (opm == xbOp_Required)
			xbThrow xbEapp(err,"Can't create directory '"+dir+"'");

		return false;
	}
	if (pbCreated)
		*pbCreated = true;
	return true;
}

xbString xbGetFullPath(LPCSTR pszFile)
{
	char *pFileName;
	char buf[512];
	int ret = ::GetFullPathName(pszFile,sizeof(buf)-1,buf,&pFileName);
	buf[sizeof(buf)-1] = 0;
	if (ret <= 0) 
		xbThrowEsys(xbHR_LAST,"xbGetFullPath() : can't retrieve full path for file '"+xbString(pszFile)+"'");
	if (ret >= sizeof(buf)) 
		xbThrowEsys("xbGetFullPath() : full path is too long for file '"+xbString(pszFile)+"'");
	return buf;
}

xbString xbGetLongPath(LPCSTR pszFile)
{
 //GetLongPathName n'existe pas sous 95
	char buf[512];
	int ret = ::GetLongPathName(pszFile,buf,sizeof(buf)-1);
	if (ret <= 0)
		return pszFile;
//		xbThrowEsys(xbHR_LAST,"xbGetLongPath() : can't retrieve long path for file '"+xbString(pszFile)+"'");
	if (ret >= sizeof(buf)) 
		xbThrowEsys("xbGetLongPath() : long path is too long for file '"+xbString(pszFile)+"'");
	return buf;
}

xbString xbGetShortPath(LPCSTR pszFile)
{
	char buf[512];
	int ret = ::GetShortPathName(pszFile,buf,sizeof(buf)-1);
	buf[sizeof(buf)-1] = 0;
	if (ret <= 0) 
		return pszFile;
//		xbThrowEsys(xbHR_LAST,"xbGetShortPath() : can't retrieve short path for file '"+xbString(pszFile)+"'");
	if (ret >= sizeof(buf)) 
		xbThrowEsys("xbGetShortPath() : short path is too long for file '"+xbString(pszFile)+"'");
	return buf;
}

bool xbCopyFile(LPCSTR pszSrce,LPCSTR pszDest,bool bFailIfExist,xbOpMode opm)
{
	if (!::CopyFile(pszSrce,pszDest,bFailIfExist?TRUE:FALSE))
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp(xbHR_LAST,"Can't copy file '"+xbString(pszSrce)+"' to new file '"+xbString(pszDest)+"' !\n\nSource full path: "+::xbGetFullPath(pszSrce)+"\nDestination: "+::xbGetFullPath(pszDest));
		return false;
	}
	return true;
}

bool xbMoveFile(LPCSTR pszSrce,LPCSTR pszDest,xbOpMode opm)
{
	if (!::MoveFile(pszSrce,pszDest))
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp(xbHR_LAST,"Can't move file '"+xbString(pszSrce)+"' to new location '"+xbString(pszDest)+"'");
		return false;
	}
	return true;
}

bool xbDeleteFile(LPCSTR pszSrce,xbOpMode opm)
{
	if (strncmp(pszSrce,"\\\\?\\",4) != 0 && strlen(pszSrce) < MAX_PATH - 10)
	{
		if (!::DeleteFile(pszSrce))
		{
			if (opm == xbOp_Required)
				xbThrow xbEapp(xbHR_LAST,"Can't delete file '"+xbString(pszSrce)+"'");
			return false;
		}
		return true;
	}

	xbStringW bstr;
	if (xbStrncmp(pszSrce,"\\\\?\\",4) == 0)
		bstr = xbStringW(pszSrce);
	else
	{
		bstr = L"\\\\?\\";
		bstr.Add(xbStringW(pszSrce));
	}

	if (!::DeleteFileW(bstr))
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp(xbHR_LAST,"Can't delete file '"+xbString(pszSrce)+"'","Wide version");
		return false;
	}
	return true;
}
bool xbDeleteDir(LPCSTR pszSrce,xbOpMode opm)
{
	if (!::RemoveDirectory(pszSrce))
	{
		if (opm == xbOp_Required)
			xbThrow xbEapp(xbHR_LAST,"Can't delete directory '"+xbString(pszSrce)+"'");
	}
	return true;
}

bool xbDirEnum::Open(const xbDirEnum *pParent,const xbString &path, xbOpMode opm)
{
	Close();
	m_pParent = pParent;
	if (pParent) m_path = pParent->m_dir + path.RetTrim();
	else m_path = path.RetTrim();
	if (m_path.IsEmpty())
	{
		if (pParent)
			xbThrowEsys("xbDirEnum::Open() : path is empty");
		m_dir = ".\\";
		m_path = ".\\*.*";
	}
	else if (::xbDirExists(m_path))
	{
		if (!m_path.IsEmpty() && m_path.Last() != '\\'
			&& m_path.Last() != '/') m_path += '\\';
		m_dir = m_path;
		m_path += "*.*";
	}
	else
	{
		m_dir = ::xbGetFileDir(m_path);
		if (m_dir.IsEmpty())
		{
			m_dir = ".\\";
			m_path = ".\\"+::xbGetFileNameExt(m_path);
		}
		else
		{
			if (!::xbDirExists(m_dir))
			{
				if (opm == xbOp_Optional) return false;
				xbThrow xbEapp("Directory '"+m_dir+"' doesn't exist");
			}
		}
	}
	m_shortDir.Empty();
	return true;
}

void xbDirEnum::Reset()
{
	m_bDataValid = false;
	if (m_dh == INVALID_HANDLE_VALUE) return;
	::FindClose(m_dh);
	m_dh = INVALID_HANDLE_VALUE;
}

void xbDirEnum::Close()
{
	Reset();
	m_path.Empty();
	m_dir.Empty();
	m_pParent = NULL;
	m_bDataValid = false;
	m_hr = E_FAIL;
	m_shortDir.Empty();
}

void xbDirEnum::ResetAttr()
{
	m_attrExcl = 0;
	m_attrIncl = -1;
}

void xbDirEnum::ResetExt()
{
	m_extExcl.Clear();
	m_extIncl.Clear();
	m_fileExcl.Clear();
	m_fileIncl.Clear();
}

void xbDirEnum::ExcludeAttr(ulong attr)
{
	if (m_pParent) xbThrowEsys("xbDirEnum::ExcludeAttr() : this xbDirEnum is in slave mode (it has a parent), you can't change its attributes");
	m_attrExcl = attr;
}

void xbDirEnum::IncludeAttr(ulong attr)
{
	if (m_pParent) xbThrowEsys("xbDirEnum::IncludeAttr() : this xbDirEnum is in slave mode (it has a parent), you can't change its attributes");
	m_attrIncl = attr;
}

void xbDirEnum::ExcludeExt(const xbString &ext)
{
	if (m_pParent) xbThrowEsys("xbDirEnum::ExcludeExt() : this xbDirEnum is in slave mode (it has a parent), you can't change its attributes");
	m_extExcl.Clear();
	m_fileExcl.Clear();
	ext.Split(';',&m_extExcl);
	CheckExt(m_extExcl,m_fileExcl,m_attrExcl);
}

void xbDirEnum::IncludeExt(const xbString &ext)
{
	if (m_pParent) xbThrowEsys("xbDirEnum::IncludeExt() : this xbDirEnum is in slave mode (it has a parent), you can't change its attributes");
	m_extIncl.Clear();
	m_fileIncl.Clear();
	ext.Split(';',&m_extIncl);
	CheckExt(m_extIncl,m_fileIncl,m_attrIncl);
}

void xbDirEnum::ExcludeExt(const xbArray<xbString> &ext)
{
	if (m_pParent) xbThrowEsys("xbDirEnum::ExcludeExt() : this xbDirEnum is in slave mode (it has a parent), you can't change its attributes");
	m_extExcl = ext;
	m_fileExcl.Clear();
	CheckExt(m_extExcl,m_fileExcl,m_attrExcl);
}

void xbDirEnum::IncludeExt(const xbArray<xbString> &ext)
{
	if (m_pParent) xbThrowEsys("xbDirEnum::IncludeExt() : this xbDirEnum is in slave mode (it has a parent), you can't change its attributes");
	m_extIncl = ext;
	m_fileIncl.Clear();
	CheckExt(m_extIncl,m_fileIncl,m_attrIncl);
}

void xbDirEnum::CheckExt(xbArray<xbString> &ext,xbArray<xbString> &files,ulong &attr)
{
	for(int no = 0; no < ext.Length; no++)
	{
		xbString &ptr = ext.GetAt(no);
		ptr.Trim();
		if (ptr.IsEmpty())
		{
			ext.Delete(no);
			no--;
			continue;
		}
		if (ptr.First() == '.') 
			continue;
		for(;;)
		{
			if (ptr.First() == '=')
			{
				files.Add(ptr.Mid(1));
				break;
			}
			if (ptr.CompareNoCase("dir") == 0)
			{
				attr |= FILE_ATTRIBUTE_DIRECTORY;
				break;
			}
			if (ptr.CompareNoCase("archive") == 0)
			{
				attr |= FILE_ATTRIBUTE_ARCHIVE;
				break;
			}
			if (ptr.CompareNoCase("compressed") == 0)
			{
				attr |= FILE_ATTRIBUTE_COMPRESSED;
				break;
			}
			if (ptr.CompareNoCase("encrypted") == 0)
			{
				attr |= FILE_ATTRIBUTE_ENCRYPTED;
				break;
			}
			if (ptr.CompareNoCase("hidden") == 0)
			{
				attr |= FILE_ATTRIBUTE_HIDDEN;
				break;
			}
			if (ptr.CompareNoCase("normal") == 0)
			{
				attr |= FILE_ATTRIBUTE_NORMAL;
				break;
			}
			if (ptr.CompareNoCase("offline") == 0)
			{
				attr |= FILE_ATTRIBUTE_OFFLINE;
				break;
			}
			if (ptr.CompareNoCase("readonly") == 0)
			{
				attr |= FILE_ATTRIBUTE_READONLY;
				break;
			}
			if (ptr.CompareNoCase("temporary") == 0)
			{
				attr |= FILE_ATTRIBUTE_TEMPORARY;
				break;
			}

			xbThrowEsys("xbDirEnum::CheckExt() : '"+ptr+"' isn't a valid extension or attribute");
			break;
		}
		ext.Delete(no);
		no--;
	}

	ext.Sort(xbSortStringNC);
}

bool xbDirEnum::IsAttrOk(ulong attr) const
{
	if (m_pParent) return m_pParent->IsAttrOk(attr);

	if ((attr & m_attrExcl) != 0) return false;
	if ((attr & m_attrIncl) == 0) return false;
	return true;
}

bool xbDirEnum::IsExtOk(const xbString &_ext) const
{
	if (m_pParent) return m_pParent->IsExtOk(_ext);

	if (m_extIncl.IsEmpty() && m_extExcl.IsEmpty()) return true;
	xbString ext(_ext);
	if (ext.IsEmpty()) ext = ".";

	if (!m_extExcl.IsEmpty())
	{
		if (m_extExcl.SortedFind(&ext,xbFindStringNC)) return false;
	}
	if (!m_extIncl.IsEmpty())
	{
		if (m_extIncl.SortedFind(&ext,xbFindStringNC)) return true;
		return false;
	}
	return true;
}

bool xbDirEnum::IsFileOk(ulong attr,const xbString &fname) const
{
	if (m_pParent) return m_pParent->IsFileOk(attr,fname);

	if (!IsAttrOk(attr))
		return false;

	if (!m_fileExcl.IsEmpty())
	{
		const xbString *pStr = m_fileExcl.Begin();
		const xbString *pEnd = m_fileExcl.End();
		while(pStr < pEnd)
		{
			if (xbMatchPath(*pStr,fname) == 0)
				return false;
			pStr++;
		}
	}
	if (!m_fileIncl.IsEmpty())
	{
		const xbString *pStr = m_fileIncl.Begin();
		const xbString *pEnd = m_fileIncl.End();
		while(pStr < pEnd)
		{
			if (xbMatchPath(*pStr,fname) == 0)
				return true;
			pStr++;
		}
		if (m_extIncl.IsEmpty() && m_extExcl.IsEmpty())
			return false;
	}
	
	return (attr & FILE_ATTRIBUTE_DIRECTORY) 
		|| IsExtOk(::xbGetFileExt(fname));
}

bool xbDirEnum::Next(xbString *pFileName,bool bReqIfFirst)
{
	if (!IsOpened()) xbThrowEsys("xbDirEnum::Next() : xbDirEnum not opened");
	bool bIsFirst = (m_dh == INVALID_HANDLE_VALUE);
	for(;;)
	{
		if (m_dh == INVALID_HANDLE_VALUE)
		{
			m_dh = ::FindFirstFile(m_path,&m_data);
			if (m_dh == INVALID_HANDLE_VALUE) 
			{
				m_bDataValid = false;
				m_hr = xbHR_LAST;
				if (bReqIfFirst) xbThrowEsys(m_hr,"xbDirEnum::Next() on path '"+m_path+"' failed");
				return false;
			}
		}
		else if (!::FindNextFile(m_dh,&m_data))
		{
			m_bDataValid = false;
			m_hr = xbHR_LAST;
			if (bReqIfFirst && bIsFirst) xbThrowEsys(m_hr,"xbDirEnum::Next() on path '"+m_path+"' failed");
			return false;
		}

		xbString filename(m_data.cFileName);
		if (filename == "." || filename == "..") continue;
		if (!IsFileOk(m_data.dwFileAttributes,filename))
			continue;

		if (pFileName) *pFileName = filename;

		break;
	}
	
	m_bDataValid = true;
	m_hr = S_OK;
	
	return true;
}

xbString xbDirEnum::GetDir() const
{
	if (!IsOpened()) xbThrowEsys("xbDirEnum::GetDir() : xbDirEnum not opened");
	return m_dir;
}

xbString xbDirEnum::GetShortDir() const
{
	if (!IsOpened()) xbThrowEsys("xbDirEnum::GetShortDir() : xbDirEnum not opened");
	if (m_shortDir.IsEmpty()) 
	{
		m_shortDir = ::xbGetShortPath(m_dir);
		if (!m_shortDir.IsEmpty() && m_shortDir.Last() != '\\'
			&& m_shortDir.Last() != '/') 
			m_shortDir += '\\';
		if (m_shortDir.IsEmpty()) m_shortDir = ".\\";
	}
	return m_shortDir;
}

xbString xbDirEnum::GetName() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetName() on path '"+m_path+"'failed : no current file");
	return m_data.cFileName;
}

xbString xbDirEnum::GetShortName() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetShortName() on path '"+m_path+"'failed : no current file");
	#ifdef _PDA
	return m_data.cFileName;
	#else
	return m_data.cAlternateFileName;
	#endif
}

xbString xbDirEnum::GetPath() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetName() on path '"+m_path+"'failed : no current file");
	return m_dir + m_data.cFileName;
}

xbString xbDirEnum::GetShortPath() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetShortPath() on path '"+m_path+"'failed : no current file");
	#ifdef _PDA
	return GetShortDir() + m_data.cFileName;
	#else
	return GetShortDir() + m_data.cAlternateFileName;
	#endif
}

xbString xbDirEnum::GetFullPath() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetFullPath() on path '"+m_path+"'failed : no current file");
	return ::xbGetFullPath(m_dir + m_data.cFileName);
}

xbString xbDirEnum::GetFullShortPath() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetFullShortPath() on path '"+m_path+"'failed : no current file");
	xbString path(::xbGetFullPath(m_dir + m_data.cFileName));
	return ::xbGetShortPath(path);
}

xbFileAttr xbDirEnum::GetAttr() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetAttr() on path '"+m_path+"'failed : no current file");
	return m_data.dwFileAttributes;
}

xbDateTime	xbDirEnum::GetCreationDate() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetCreationDate() on path '"+m_path+"'failed : no current file");
	return m_data.ftCreationTime;
}

xbDateTime	xbDirEnum::GetAccessDate() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetAccessDate() on path '"+m_path+"'failed : no current file");
	return m_data.ftLastAccessTime;
}

xbDateTime	xbDirEnum::GetWriteDate() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetWriteDate() on path '"+m_path+"'failed : no current file");
	return m_data.ftLastWriteTime;
}

FILETIME	xbDirEnum::GetCreationFT() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetCreationFT() on path '"+m_path+"'failed : no current file");
	return m_data.ftCreationTime;
}

FILETIME	xbDirEnum::GetAccessFT() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetAccessFT() on path '"+m_path+"'failed : no current file");
	return m_data.ftLastAccessTime;
}

FILETIME	xbDirEnum::GetWriteFT() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetWriteFT() on path '"+m_path+"'failed : no current file");
	return m_data.ftLastWriteTime;
}

uint64	xbDirEnum::GetFileSize() const
{
	if (!m_bDataValid) xbThrowEsys(m_hr,"xbDirEnum::GetFileSize() on path '"+m_path+"'failed : no current file");

	return (uint64(m_data.nFileSizeHigh) << 32) + uint64(m_data.nFileSizeLow);
}

xbString	xbGetFilePath(const xbString &_file,const xbString &dfltpath,const xbString &dfltext)
{
	xbString file(_file.RetTrim());
	{
		const char *ptr = xbStrrchr(file,'.');
		if (!ptr) 
			file += '.' + dfltext;
		else
		{
			const char *ptr2 = xbStrrchr(file,xbPath_SLASHch);
			{
				const char *ptr3 = xbStrrchr(file,xbPath_SLASH2ch);
				if (ptr3 > ptr2) 
					ptr2 = ptr3;
			}
			if (ptr2 > ptr) 
				file += '.' + dfltext;
		}
	}
	if (file.StartWithNC("http:")) 
		return file;
//	if (::GetFileAttributes(file) != 0xFFFFFFFF) return file;

	if (file.IndexOf(':') >= 0 || file.StartWith("\\\\")) 
		return file;

	return dfltpath + file;
}


bool xbSetCurrentDir(const xbString &dir,xbOpMode opm)
{
	if (!::SetCurrentDirectory(dir))
	{
		if (opm == xbOp_Optional) return false;
		xbThrowEsys(xbHR_LAST,"Can't set current directory to '"+dir+"'");
	}
	return true;
}

xbString xbGetCurrentDir(bool bEndSlash)
{
	char buf[512];
	DWORD ret = ::GetCurrentDirectory(sizeof(buf)-1,buf);
	buf[sizeof(buf)-1] = 0;
	if (ret <= 0) 
		xbThrowEsys(xbHR_LAST,"xbGetCurrentDir() : can't retrieve current directory path");
	if (ret >= sizeof(buf)) 
		xbThrowEsys("xbGetCurrentDir() : current directory path is too long");

	if (bEndSlash) return xbEndSlash(buf);
	return xbNoEndSlash(buf);
}

xbString xbGetSystemDir(bool bEndSlash)
{
/*	//32 bits versions on 64 bits os:
	// retourner le repertoire SysWow64 pour les appli 32 sur OS64
	// retourner le repertoire System32 pour les appli 32 sur OS32
	// retourner le repertoire System32 pour les appli 64 sur OS64
#ifndef XDCBASE_X64
	eProtectBegin();
	xbString str(::xbGetShellFolder(CSIDL_SYSTEMX86));
	if (bEndSlash) return xbEndSlash(str);
	return xbNoEndSlash(str);
	eProtectEndTrace();
#endif
*/

	char buf[512];
	DWORD ret = ::GetSystemDirectory(buf,sizeof(buf)-1);
	buf[sizeof(buf)-1] = 0;
	if (ret <= 0) 
		xbThrowEsys(xbHR_LAST,"xbGetSystemDir() : can't retrieve system directory path");
	if (ret >= sizeof(buf)) 
		xbThrowEsys("xbGetSystemDir() : system directory path is too long");

	if (bEndSlash) return xbEndSlash(buf);
	return xbNoEndSlash(buf);
}

xbString xbGetWindowsDir(bool bEndSlash)
{
	char buf[512];
	DWORD ret = ::GetWindowsDirectory(buf,sizeof(buf)-1);
	buf[sizeof(buf)-1] = 0;
	if (ret <= 0) 
		xbThrowEsys(xbHR_LAST,"xbGetWindowsDir() : can't retrieve windows directory path");
	if (ret >= sizeof(buf)) 
		xbThrowEsys("xbGetWindowsDir() : windows directory path is too long");

	if (bEndSlash) return xbEndSlash(buf);
	return xbNoEndSlash(buf);
}

xbString xbGetXdcPrgDir(bool bEndSlash, LPCSTR pAppDir, bool bCreate)
{
	xbString str(::xbGetShellFolder(CSIDL_PROGRAM_FILES)+(xbPath_SLASH "XDCUBE"));
	if (pAppDir)
	{
		str += xbPath_SLASH;
		str += pAppDir;
	}

	if (bCreate)
	{
		::xbMakeDir(str,true);
	}

	if (bEndSlash) return xbEndSlash(str);
	return xbNoEndSlash(str);
}

xbString xbGetXdcPrgDataDir(bool bEndSlash, LPCSTR pAppDir, bool bCreate)
{
	xbString str(::xbGetShellFolder(CSIDL_APPDATA)+(xbPath_SLASH "XDCUBE"));
	if (pAppDir)
	{
		str += xbPath_SLASH;
		str += pAppDir;
	}

	if (bCreate)
	{
		::xbMakeDir(str,true);
	}

	if (bEndSlash) return xbEndSlash(str);
	return xbNoEndSlash(str);
}

xbString xbGetXdcMenuDir(bool bEndSlash, LPCSTR pAppDir, bool bCreate)
{
	xbString str(::xbGetShellFolder(CSIDL_COMMON_PROGRAMS)+(xbPath_SLASH "XDCUBE"));
	if (pAppDir)
	{
		str += xbPath_SLASH;
		str += pAppDir;
	}

	if (bCreate)
	{
		::xbMakeDir(str,true);
	}

	if (bEndSlash) return xbEndSlash(str);
	return xbNoEndSlash(str);
}

xbString xbGetUserName()
{
	char buf[512];
	DWORD szBuf = sizeof(buf)-1;
	if (!::GetUserName(buf,&szBuf))
		xbThrowEsys(xbHR_LAST,"xbGetUserName() : can't retrieve user name");
	buf[sizeof(buf)-1] = 0;
	return buf;
}

xbString xbGetComputerName()
{
	char buf[512];
	DWORD szBuf = sizeof(buf)-1;
	if (!::GetComputerName(buf,&szBuf))
		xbThrowEsys(xbHR_LAST,"xbGetComputerName() : can't retrieve computer name");
	buf[sizeof(buf)-1] = 0;
	return buf;
}

xbString xbGetTempDir(bool bEndSlash)
{
	char buf[512];
	DWORD ret = ::GetTempPath(sizeof(buf)-1,buf);
	buf[sizeof(buf)-1] = 0;
	if (ret <= 0) 
		xbThrowEsys(xbHR_LAST,"xbGetTempDir() : can't retrieve temporary files directory path");
	if (ret >= sizeof(buf)) 
		xbThrowEsys("xbGetTempDir() : temporary files directory path is too long");

	if (bEndSlash) return xbEndSlash(buf);
	return xbNoEndSlash(buf);
}

xbString xbGetTempFile(const char *pszPrefix,uint uUnique,const char *pszDir)
{
	if (!pszPrefix) pszPrefix = "MMM";

	xbString path;
	if (pszDir) path = pszDir;
	else path = ::xbGetTempDir();
	
	char buf[MAX_PATH*2];
	if (!::GetTempFileName(path,pszPrefix,uUnique,buf)) xbThrowEsys(xbHR_LAST,"xbGetNewTempFilePath() : can't create temp file");
	
	return buf;
}

xbString xbEndSlash(LPCSTR pszSrce,bool bAslash)
{
	if (!pszSrce) return xbPath_SLASH;
	const char *ptr = pszSrce + strlen(pszSrce) - 1;
	while (ptr >= pszSrce && (*ptr == '\\' || *ptr == '/')) ptr--;
	return xbString(pszSrce,int(1 + xbNptr(ptr) - xbNptr(pszSrce))) + (bAslash?'\\':'/');
}

xbString xbNoEndSlash(LPCSTR pszSrce)
{
	if (!pszSrce) return "";
	const char *ptr = pszSrce + strlen(pszSrce) - 1;
	while (ptr >= pszSrce && (*ptr == '\\' || *ptr == '/')) ptr--;
	return xbString(pszSrce,int(1 + xbNptr(ptr) - xbNptr(pszSrce)));
}


#ifdef _PDA

#include <WINIOCTL.H>
extern "C" __declspec(dllimport) 
BOOL KernelIoControl(
  DWORD dwIoControlCode, LPVOID lpInBuf, DWORD nInBufSize, 
  LPVOID lpOutBuf, DWORD nOutBufSize, LPDWORD lpBytesReturned
);
#define IOCTL_HAL_GET_DEVICEID CTL_CODE(FILE_DEVICE_HAL, 21, METHOD_BUFFERED, FILE_ANY_ACCESS)
bool xbGetComputerCode( DWORD* id )
{
	if ( !id )
		return false;
	DWORD dwOutBytes;
	const int nBuffSize = 4096;
	byte arrOutBuff[nBuffSize];

	BOOL bRes = ::KernelIoControl(IOCTL_HAL_GET_DEVICEID, 
                                     0, 0, arrOutBuff, nBuffSize, &dwOutBytes);

	if (bRes) 
	{
		DWORD cid=0;
		for ( DWORD i=0;i<dwOutBytes;i++ )
		{
			cid++;
			DWORD	partone = cid&0x000007FF;
			DWORD	parttwo = cid&0xFFFFF800;
			cid = (partone<<21)|(parttwo>>11);
			cid ^= arrOutBuff[i];
		}
		*id = cid;
		return true;
	}
	return false;
}

#else
bool xbGetComputerCode( DWORD* id )
{
	if ( !id )
		return false;

	TCHAR	windir[MAX_PATH];
	GetWindowsDirectory( windir, MAX_PATH );
	windir[3]='\0';
	if ( !::GetVolumeInformation(windir, NULL, 0, id, NULL, NULL, NULL, 0 ) )
		return false;

	return true;
}
#endif

#ifndef _PDA
xbString xbGetShellFolder(int nFolder,bool bNoEndSlash,bool bCreate) //for nFolder see : SHGetSpecialFolderLocation
{
//	HRtoE(::SHGetSpecialFolderPath(NULL,buf,nFolder,bCreate?TRUE:FALSE)); absent sur certaines versions de NT4 (Shell32.dll)
	xbRefPtr<IMalloc>	prMalloc;
	HRtoE(::SHGetMalloc(prMalloc.Addr()));
	LPITEMIDLIST	pidl = NULL;
	HRtoE(::SHGetSpecialFolderLocation(NULL,nFolder,&pidl));
	char buf[MAX_PATH*2];
	try
	{
		HRtoE(::SHGetPathFromIDList(pidl,buf));
	}
	catch(...)
	{
		prMalloc->Free(pidl);
		throw;
	}
	prMalloc->Free(pidl);

	if (bNoEndSlash)
		return xbNoEndSlash(buf);
	return xbEndSlash(buf);
}
#endif

#ifdef _PDA
xbString xbGetShellFolder(int ,bool ,bool ) //for nFolder see : SHGetSpecialFolderLocation
{
	xbThrowEsys(E_NOTIMPL,"xbGetShellFolder useless");
}
#endif

#ifdef _PDA
bool xbCreateShortCut(const xbString &,const xbString &,const xbString &,const xbString &, DWORD )
{
	xbThrowEsys(E_NOTIMPL,"xbCreateShortCut useless");
}
#else
bool xbCreateShortCut(const xbString &path,const xbString &desc,const xbString &target,const xbString &arg,xbRefPtr<IShellLink> *pprShLink, DWORD dwClsContext)
{
	xbRefPtr<IShellLink>	prLink;
	prLink.CreateInstance(CLSID_ShellLink,NULL,dwClsContext);

	xbRefQIPtr<IPersistFile>	prLinkFile(prLink);

	bool bRet = true;
	xbBSTR bstr(path);
	if (::xbFileExists(path))
	{
		HRtoE(prLinkFile->Load(bstr,STGM_READ));
		bRet = false;
	}
	if (desc.IsValid()) HRtoE(prLink->SetDescription(desc));
	
	if (target.IsValid()) HRtoE(prLink->SetPath(target));

	if (arg.IsValid()) HRtoE(prLink->SetArguments(arg));

	if (pprShLink) *pprShLink = prLink;

	HRtoE(prLinkFile->Save(bstr,TRUE));

	return bRet;
}
#endif

int	xbMatchPath(const xbString &fnameWildCard,const xbString &fname)
{
	xbString wc(fnameWildCard);
	wc.Lower();
	xbString fn(fname);
	fn.Lower();
	return xbMatchPathCase(wc,fn);
}

int	xbMatchPathCase(const xbString &fnameWildCard,const xbString &fname)
{
	const uchar *pWC = (const uchar*)((const char*)fnameWildCard),*pLastWC = NULL;
	const uchar *pF = (const uchar*)((const char*)fname),*pLastF = NULL;
	uchar wc,fc;

	for(;;)
	{
		wc = *pWC;
		fc = *(pF++);
		if (wc == '\\') wc = '/';
		if (fc == '\\') fc = '/';
		if (wc == '*')
		{
			if (*(pWC+1) == fc)
			{
				if (!fc)
				{
					wc = 0;
					break;
				}
				pLastWC = pWC;
				pLastF = pF;
				pWC+=2;
				continue;
			}
			if (!fc)
				break;

			continue;
		}
		if (!wc || !fc) break;

		if (wc == '?')
		{
			pWC++;
			continue;
		}
		if (wc == fc)
		{
			pWC++;
			continue;
		}
		if (!pLastWC)
			break;
		pWC = pLastWC;
		pLastWC = NULL;
	}
	return int(wc) - int(fc);
}

static DWORD	CRC32_table[256];
static bool		CRC32_table_created = false;

xbCRC32::xbCRC32( )
{
	// build the crc table if necessary
	if ( !CRC32_table_created )
	{
		CRC32_table_created = true;
		DWORD	poly = 0xEDB88320L;
		for (DWORD i = 0; i < 256; i++)
        {
			DWORD crc = i;
			for (DWORD j = 8; j > 0; j--)
			{
				if (crc & 1)
					crc = (crc >> 1) ^ poly;
				else
					crc >>= 1;
            }
			CRC32_table[i] = crc;
        }
	}
	m_crc = 0xFFFFFFFF;
}

void xbCRC32::Reset( )
{
	m_crc = 0xFFFFFFFF;
}

void xbCRC32::Push( const byte *src, uint len )
{
	while ( len-- )
	{
		m_crc = (m_crc>>8)^CRC32_table[(m_crc^(*src)) & 0xFF ];
		src++;
	}
}

/*
void xbCRC32::FromStream( stream& st )
{
	Reset( );
	int	oldpos	= st.GetPos( );
	int	pos		= 0;
	BYTE	buffer[64];
	int		bfz = sizeof(buffer);
	while ( bfz==sizeof(buffer) )
	{
		bfz = st.Read( pos, buffer, bfz );
		pos+=bfz;
		Push( buffer, bfz );
	}
}
*/

xbString xbGetUncPath(LPCSTR pszFile,xbOpMode opm)
{
	xbString path;
	if (!xbGetFileUrl(pszFile,&path))
		return pszFile;
//	if (xbGetUncPathPart(ret))
//		return ret;
	char buf[512];
	DWORD infoSize = sizeof(buf);
	UNIVERSAL_NAME_INFO *pInfo = (UNIVERSAL_NAME_INFO*)&buf;
	pInfo->lpUniversalName = NULL;
	DWORD ret = ::WNetGetUniversalName(path,UNIVERSAL_NAME_INFO_LEVEL,pInfo,&infoSize);
	if (ret == NO_ERROR)
		return pInfo->lpUniversalName;

	if (ret == ERROR_NOT_CONNECTED || ret == ERROR_BAD_DEVICE)
		return path;

	if (ret == ERROR_CONNECTION_UNAVAIL && pInfo->lpUniversalName)
		return pInfo->lpUniversalName;

	if (opm == xbOp_Optional)
		return xbString();

	if (ret == ERROR_MORE_DATA)
		xbThrowEsys("xbGetUncPath() : unc path is too long for file '"+xbString(pszFile)+"'");

	if (ret == ERROR_CONNECTION_UNAVAIL)
		xbThrowEsys("xbGetUncPath() : can't retrieve unc path for file '"+xbString(pszFile)+"', connection not available");
//	if (ret == ERROR_EXTENDED_ERROR)
	xbThrowEsys((HRESULT)ret,"xbGetUncPath() : can't retrieve unc path for file '"+xbString(pszFile)+"'");
	return xbString();
}

xbString xbGetLocalPath(LPCSTR pszFile,xbOpMode opm)
{
	xbBSTR sMachine(xbAllocLib);
	xbString sPath;
	{
		xbString _sMachine;
		if (!xbGetUncPathPart(pszFile,&_sMachine,&sPath))
			return pszFile;
		sMachine.Set("\\\\"+_sMachine);
	}
	xbBSTR sShare(xbAllocLib,::xbSubPath(sPath,&sPath));

	SHARE_INFO_2 *pInfo;
	NET_API_STATUS res;
	if((res = ::NetShareGetInfo(sMachine.GetWritePtr(),sShare.GetWritePtr(),2,(LPBYTE *) &pInfo)) != NERR_Success)
	{
		if (opm == xbOp_Required)
			xbThrowEsys((HRESULT)res,"xbGetLocalPath() : can't retrieve local path for file '"+xbString(pszFile)+"'");
		return xbString();
	}
	xbString localPath(pInfo->shi2_path);
	::NetApiBufferFree(pInfo);
	if (sPath.IsValid())
		localPath += xbPath_SLASH + sPath;
	return localPath;
}

/*
//#define SECURITY_KERNEL
//#include <security.h>
//#include <secext.h>
xbString xbGetUserNameFull()
{
	char buf[512];
	DWORD szBuf = sizeof(buf)-1;
	if (!::GetUserNameEx(NameCanonical,buf,&szBuf))
		xbThrowEsys(xbHR_LAST,"xbGetUserName() : can't retrieve user name");
	buf[sizeof(buf)-1] = 0;
	return buf;
}
*/

xbString xbGetUserDomain()
{
	LPSTR pSiteName = NULL;
	DWORD ret = ::DsGetSiteName(NULL,&pSiteName);
	if (ret != NO_ERROR)
	{
		if (ret == ERROR_NO_SITENAME)
			return xbString();

		xbThrow xbEapp(ret,"Can't get user domain name");
	}
	xbString sret(pSiteName);
	::NetApiBufferFree(pSiteName);
	return sret;
}
xbString xbGetUserDomainController()
{
	LPSTR pCtrlName = NULL;
	DWORD ret = ::NetGetDCName(NULL,NULL,(LPBYTE*)&pCtrlName);
	if (ret != NERR_Success)
	{
		if (ret == NERR_DCNotFound || ret == ERROR_INVALID_NAME)
			return xbString();

		xbThrow xbEapp(ret,"Can't get user domain name");
	}
	xbString sret(pCtrlName);
	::NetApiBufferFree(pCtrlName);
	return sret;
}

xbString xbGetUserInfo(xbGetUserInfoType infoType)
{
	LPWKSTA_USER_INFO_1 pBuf = NULL;
	NET_API_STATUS nStatus = NetWkstaUserGetInfo(NULL,1,(LPBYTE *)&pBuf);
	//
	// If the call succeeds, print the information
	//  about the logged-on user.
	//
	xbStringW sret;
	if (nStatus == NERR_Success && pBuf)
	{
		switch(infoType)
		{
		case xbGetUserInfoType_Name:
			sret = pBuf->wkui1_username;
			break;
		case xbGetUserInfoType_Domain:
			sret = pBuf->wkui1_logon_domain;
			break;
		case xbGetUserInfoType_OtherDomain:
			sret = pBuf->wkui1_oth_domains;
			break;
		case xbGetUserInfoType_LogonServer:
			sret = pBuf->wkui1_logon_server;
			break;
		default:
			sret = xbStringW(pBuf->wkui1_username)
				+L";"+xbStringW(pBuf->wkui1_logon_domain)
				+L";"+xbStringW(pBuf->wkui1_oth_domains)
				+L";"+xbStringW(pBuf->wkui1_logon_server);
			break;
		}
   }

   if (pBuf != NULL)
      NetApiBufferFree(pBuf);

   return xbString(sret);
}

xbString xbGetUserGroups()
{
	xbString sret;
	DWORD cEntries,cTotalEntries;
	LPGROUP_USERS_INFO_0 pBuf = NULL;
//	LPWSTR pSrvName = NULL;

	xbBSTR userName(xbAllocLib,::xbGetUserInfo(xbGetUserInfoType_Name));
	xbBSTR srvName(xbAllocLib,::xbGetUserInfo(xbGetUserInfoType_LogonServer));
	NET_API_STATUS nStatus = ::NetUserGetGroups(srvName,userName,0,(LPBYTE*)&pBuf,MAX_PREFERRED_LENGTH,&cEntries,&cTotalEntries);

	if (nStatus == NERR_Success)
	{
		LPGROUP_USERS_INFO_0 pTmpBuf = pBuf;
		for (DWORD i = 0; i < cEntries; i++,pTmpBuf++)
		{
			if (pTmpBuf->grui0_name)
			{
				xbString sname(pTmpBuf->grui0_name);
				if (sret.IsValid())
					sret += ";";

				sret += sname;
			}
		}
		if (cEntries < cTotalEntries)
			sret += ";...";
	}

	if (pBuf != NULL)
	  NetApiBufferFree(pBuf);

   return sret;
}

xbString xbGetUserGroupsLocal()
{
	xbString sret;
	DWORD cEntries,cTotalEntries;
	LPLOCALGROUP_USERS_INFO_0 pBuf = NULL;
//	LPWSTR pSrvName = NULL;

	xbBSTR userName(xbAllocLib,::xbGetUserInfo(xbGetUserInfoType_Name));
	xbBSTR srvName(xbAllocLib,::xbGetUserInfo(xbGetUserInfoType_LogonServer));
	NET_API_STATUS nStatus = ::NetUserGetLocalGroups(srvName,userName,0,0,(LPBYTE*)&pBuf,MAX_PREFERRED_LENGTH,&cEntries,&cTotalEntries);

	if (nStatus == NERR_Success)
	{
		LPLOCALGROUP_USERS_INFO_0 pTmpBuf = pBuf;
		for (DWORD i = 0; i < cEntries; i++,pTmpBuf++)
		{
			if (pTmpBuf->lgrui0_name)
			{
				xbString sname(pTmpBuf->lgrui0_name);
				if (sret.IsValid())
					sret += ";";

				sret += sname;
			}
		}
		if (cEntries < cTotalEntries)
			sret += ";...";
	}

	if (pBuf != NULL)
	  NetApiBufferFree(pBuf);

   return sret;
}

HANDLE xbLaunchFile(ulong mode, xbHWND hWinParent, const xbString &sExe, const xbString &sParam, const xbString &sDir)
{
	int	shmode;
	switch((mode & xbLaunch_OpenModeMask))
	{
	case xbLaunch_Hide:
		shmode = SW_HIDE;
		break;
	case xbLaunch_Minimize:
		shmode = SW_SHOWMINIMIZED;
		break;
	case xbLaunch_Maximize:
		shmode = SW_SHOWMAXIMIZED;
		break;
	case xbLaunch_ShowNA:
		shmode = SW_SHOWNA;
		break;
	case xbLaunch_ShowMinNA:
		shmode = SW_SHOWMINNOACTIVE;
		break;
	case xbLaunch_Show :
		shmode = SW_SHOWNORMAL;
		break;
	default:
		xbAssert(!"xbLaunchFile() : bad launch mode");
		shmode = SW_SHOWNORMAL;
		break;
	}
	if (mode & xbLaunch_ShellExecute)
	{
		xbAssert((mode & xbLaunch_CpModeMask) == 0);
		SHELLEXECUTEINFO	shinfo;
		memset(&shinfo,0,sizeof(shinfo));
		shinfo.cbSize = sizeof(shinfo);
		shinfo.fMask = SEE_MASK_NOCLOSEPROCESS|SEE_MASK_FLAG_NO_UI;
		if (mode & xbLaunch_WaitDDEend)
			shinfo.fMask |= SEE_MASK_FLAG_DDEWAIT;

		shinfo.hwnd = hWinParent;
		shinfo.lpDirectory = sDir;
		shinfo.lpParameters = (sParam.IsEmpty()?NULL:(LPCSTR)sParam);
		shinfo.lpFile = sExe;
		shinfo.nShow = shmode;
		if (!ShellExecuteEx(&shinfo))
			xbThrowEsys(xbHR_LAST,"Can't open '"+sExe+"' !");
		if (mode & xbLaunch_WaitEnd)
			return (HANDLE)xbWaitProcessEnd(shinfo.hProcess);
		return shinfo.hProcess;
	}
	xbAssert((mode & xbLaunch_ShModeMask) == 0);

	STARTUPINFO stinfo;
	memset(&stinfo,0,sizeof(stinfo));
	stinfo.cb = sizeof(stinfo);
	stinfo.dwFlags = STARTF_USESHOWWINDOW;
	stinfo.wShowWindow = shmode;

	PROCESS_INFORMATION	prinfo;
	memset(&prinfo,0,sizeof(prinfo));

	xbString cmdline('\"'+sExe+'\"');
	if (!sParam.IsEmpty()) cmdline += " "+sParam;
	char *pCmdLine = cmdline.GetWritePtr(cmdline.GetLength() + 100);
	ulong creatmode = NORMAL_PRIORITY_CLASS;
	if (!(mode & xbLaunch_NoDetach)) 
	{
		if (mode & xbLaunch_NewConsole)
			creatmode |= CREATE_NEW_CONSOLE;
		else
			creatmode |= DETACHED_PROCESS;
	}
	if (mode & xbLaunch_Hide)
		creatmode |= CREATE_NO_WINDOW;
	if (!::CreateProcess( NULL ,pCmdLine,NULL,NULL,FALSE,
		creatmode,NULL,sDir.IsEmpty()?(LPCSTR)NULL:(LPCSTR)sDir,&stinfo,&prinfo))
		xbThrowEsys(xbHR_LAST,"Can't execute '"+sExe+"' !");

	if (mode & xbLaunch_WaitEnd)
	{
		return (HANDLE)xbWaitProcessEnd(prinfo.hProcess);
	}
	return prinfo.hProcess;
}

#pragma warning(pop)
