www.pudn.com > OSDemo.rar > Tab4.cpp
// Tab4.cpp : implementation file
//
#include "stdafx.h"
#include "OSDemo.h"
#include "Tab4.h"
#include "PieChartCtrl.h"
#include "CheckerCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
FAT FileFAT;
CFile FATIO;
CString CurrentUser;
int CurrentID;
bool bLogin;
bool bFormated;
bool WriteFAT()
{
FATIO.SeekToBegin();
FATIO.Write(&FileFAT,sizeof(FileFAT));
return true;
}
bool CheckLogin()
{
if (!bLogin)
{
AfxMessageBox("请先登录文件系统!");
return false;
}
else
return true;
}
bool ReadFAT()
{
ZeroMemory(&FileFAT,sizeof(FileFAT));
FATIO.SeekToBegin();
if (!FATIO.Read(&FileFAT,sizeof(FileFAT))) return false;
return true;
}
bool FileExist(CString FilePath)
{
HANDLE Handle;
WIN32_FIND_DATA W32;
Handle=FindFirstFile(FilePath,&W32);
if (Handle!=INVALID_HANDLE_VALUE)
return true;
else
return false;
}
bool Format()
{
CString FATFile;
int i;
char fname[128];
::GetCurrentDirectory(128,fname); //FAT表信息保存在当前目录的VDISK.DAT中
FATFile.Format("%s",fname);
if (FATFile.Right(1)!="\\")
FATFile+="\\VDISK.DAT";
else
FATFile+="VDISK.DAT";
CFile fout;
if( !fout.Open(FATFile,CFile::modeCreate|CFile::modeWrite,NULL))
{
return false;
}
ZeroMemory(&FileFAT,sizeof(FileFAT));
FileFAT.SuperBlock.HeadInfo.pStack=0;
for(i=0;i<10;i++)
{
FileFAT.SuperBlock.HeadInfo.pBlock[i]=i+1;
}
fout.WriteHuge(&FileFAT,sizeof(FileFAT));
BYTE *Buffer;
Buffer=(BYTE*)malloc(DISKSIZE);
ZeroMemory(Buffer,DISKSIZE);
BLOCK newBlock;
for (i=10;i<=990;i+=10)
{
ZeroMemory(&newBlock,sizeof(newBlock));
newBlock.HeadInfo.pStack=0;
for (int k=0;k<10;k++)
{
newBlock.HeadInfo.pBlock[k]=i+1+k;
}
memcpy(Buffer+32*(i-1),&newBlock,32);
}
fout.WriteHuge(Buffer,DISKSIZE);
free(Buffer);
fout.Close();
AfxMessageBox("创建虚拟磁盘文件成功!请选择用户管理添加新用户。");
return true;
}
void StartIO() //打开VDISK.DAT文件,把该文件句柄保存在全局变量中,如果该文件不存在,创建并初始化该文件。
{
CString FATFile;
char fname[128];
::GetCurrentDirectory(128,fname); //FAT表信息保存在当前目录的VDISK.DAT中
FATFile.Format("%s",fname);
if (FATFile.Right(1)!="\\")
FATFile+="\\VDISK.DAT";
else
FATFile+="VDISK.DAT";
bFormated=true;
if (!FileExist(FATFile))
{
AfxMessageBox("当前目录下找不到VDISK.DAT,该文件是用来模拟磁盘及文件管理表。\n单击确定创建新的虚拟磁盘文件!");
bFormated=false;
if (!Format())
{
AfxMessageBox("创建文件系统出错!");
return;
}
}
if(!FATIO.Open(FATFile,CFile::modeReadWrite|CFile::shareDenyWrite,NULL))
{
AfxMessageBox("调入FAT表失败,文件系统可能出错!");
bFormated=false;
return;
}
}
bool ReadBlock(int id, BLOCK* buffer) //读指定块号内容(32字节)到buffer所指向的单元
{
if(!FATIO) return false;
if(!FATIO.Seek(sizeof(FAT)+(id-1)*BLOCKSIZE,CFile::begin)) return false;
if(FATIO.Read(buffer,BLOCKSIZE)!=BLOCKSIZE) return false;
return true;
}
bool WriteBlock(int id, BLOCK* buffer) //把buffer所指向的单元的内容(32字节)写到指定的物理块中
{
if(!FATIO) return false;
if(!FATIO.Seek(sizeof(FAT)+(id-1)*BLOCKSIZE,CFile::begin)) return false;
FATIO.Write(buffer,BLOCKSIZE);
return true;
}
USHORT AllocBlock() //分配一物理块,成功返回逻辑块号,失败返回0;
{
if(!FileFAT.SuperBlock.HeadInfo.pBlock[FileFAT.SuperBlock.HeadInfo.pStack]) return 0; //没有空间可分配,失败返回
if(FileFAT.SuperBlock.HeadInfo.pStack<9)
{
WriteFAT();
return FileFAT.SuperBlock.HeadInfo.pBlock[FileFAT.SuperBlock.HeadInfo.pStack++];
}
else
{
WriteFAT();
USHORT returnBlock=FileFAT.SuperBlock.HeadInfo.pBlock[9];
if(!ReadBlock(returnBlock,&FileFAT.SuperBlock)) return 0;
return returnBlock;
}
WriteFAT();
return 0;
}
bool FreeBlock(USHORT BlockID) //回收一物理块,成功返回TRUE,失败返回FALSE
{
if(FileFAT.SuperBlock.HeadInfo.pStack>0)
{
FileFAT.SuperBlock.HeadInfo.pBlock[--FileFAT.SuperBlock.HeadInfo.pStack]=BlockID;
BLOCK newBlock;
if (!WriteBlock(BlockID,&newBlock)) return false;
}
else
{
if (!WriteBlock(BlockID,&FileFAT.SuperBlock)) return false;
FileFAT.SuperBlock.HeadInfo.pStack=9;
FileFAT.SuperBlock.HeadInfo.pBlock[9]=BlockID;
}
WriteFAT();
return true;
}
class CDlgUser : public CDialog
{
// Construction
public:
void ShowUser();
void AddListUser(CString Name,CString FileNum);
void InitUserList();
CDlgUser(CWnd* pParent = NULL); // standard constructor
CImageList* ImageList1;
CImageList* ImageList2;
// Dialog Data
//{{AFX_DATA(CDlgUser)
enum { IDD = IDD_DLG_USER };
CListCtrl m_UserListView;
CString m_UserName;
//}}AFX_DATA
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDlgUser)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
// Generated message map functions
//{{AFX_MSG(CDlgUser)
virtual BOOL OnInitDialog();
afx_msg void OnDelUser();
afx_msg void OnAddUser();
afx_msg void OnClickUserList(NMHDR* pNMHDR, LRESULT* pResult);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
int ItemUser;
};
/////////////////////////////////////////////////////////////////////////////
// CDlgUser dialog
CDlgUser::CDlgUser(CWnd* pParent /*=NULL*/)
: CDialog(CDlgUser::IDD, pParent)
{
//{{AFX_DATA_INIT(CDlgUser)
m_UserName = _T("");
//}}AFX_DATA_INIT
}
void CDlgUser::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDlgUser)
DDX_Control(pDX, IDC_USER_LIST, m_UserListView);
DDX_Text(pDX, IDC_USER_NAME, m_UserName);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CDlgUser, CDialog)
//{{AFX_MSG_MAP(CDlgUser)
ON_BN_CLICKED(IDC_DEL_USER, OnDelUser)
ON_BN_CLICKED(ID_ADD_USER, OnAddUser)
ON_NOTIFY(NM_CLICK, IDC_USER_LIST, OnClickUserList)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CDlgUser message handlers
BOOL CDlgUser::OnInitDialog()
{
CDialog::OnInitDialog();
InitUserList();
ShowUser();
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CDlgUser::InitUserList()
{
DWORD dwStyle = m_UserListView.GetExtendedStyle();
ListView_SetExtendedListViewStyle(m_UserListView.m_hWnd, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);
m_UserListView.InsertColumn(0,"用户名",LVCFMT_LEFT,100);
m_UserListView.InsertColumn(1,"文件数",LVCFMT_RIGHT,50);
ImageList1 = new CImageList();
ImageList1->Create(16, 16, ILC_COLOR8 | ILC_MASK, 9, 9);
ImageList1->Add(AfxGetApp()->LoadIcon(IDI_USER));
m_UserListView.SetImageList(ImageList1, LVSIL_SMALL);
//m_UserListView.SetImageList(ImageList2, LVSIL_NORMAL);
}
void CDlgUser::OnDelUser()
{
UpdateData(true);
int i,j,k,pos;
for(i=0;iLoadIcon(IDI_USER));
m_UserList.SetImageList(&m_ImageList, LVSIL_NORMAL);
}
/////////////////////////////////////////////////////////////////////////////
// CTab4 dialog
CTab4::CTab4(CWnd* pParent /*=NULL*/)
: CDialog(CTab4::IDD, pParent)
{
//{{AFX_DATA_INIT(CTab4)
m_FileContent = _T("");
//}}AFX_DATA_INIT
}
void CTab4::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CTab4)
DDX_Control(pDX, IDC_BTN_LOGIN, m_LoginBtn);
DDX_Control(pDX, IDC_FILETREE, m_FileTree);
DDX_Control(pDX, IDC_FILELIST, m_FileList);
DDX_Text(pDX, IDC_FILE_CONTENT, m_FileContent);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CTab4, CDialog)
//{{AFX_MSG_MAP(CTab4)
ON_BN_CLICKED(IDC_BTN_LOGIN, OnBtnLogin)
ON_BN_CLICKED(IDC_BTN_ADD, OnBtnAdd)
ON_BN_CLICKED(IDC_BTN_MODIFY, OnBtnModify)
ON_BN_CLICKED(IDC_BTN_USER, OnBtnUser)
ON_BN_CLICKED(IDC_BTN_DELETE, OnBtnDelete)
ON_BN_CLICKED(IDC_BTN_DISK, OnBtnDisk)
ON_BN_CLICKED(IDC_BTN_CLOSE, OnBtnClose)
ON_BN_CLICKED(IDC_BTN_OPEN, OnBtnOpen)
ON_NOTIFY(NM_CLICK, IDC_FILELIST, OnClickFilelist)
ON_WM_CTLCOLOR()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CTab4 message handlers
BOOL CTab4::OnInitDialog()
{
CDialog::OnInitDialog();
bLogin=false;
ItemCount=0;
m_bluecolor=RGB(239,239,239); // blue
m_textcolor=RGB(0,0,255); // white text
m_bluebrush.CreateSolidBrush(m_bluecolor); // blue background
InitFileList();
InitFileTree();
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CTab4::InitFileList()
{
DWORD dwStyle = m_FileList.GetExtendedStyle();
ListView_SetExtendedListViewStyle(m_FileList.m_hWnd, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);
m_FileList.InsertColumn(0,"文件名",LVCFMT_LEFT,100);
m_FileList.InsertColumn(1,"文件主",LVCFMT_LEFT,60);
m_FileList.InsertColumn(2,"文件大小",LVCFMT_RIGHT,80);
m_FileList.InsertColumn(3,"修改时间",LVCFMT_LEFT,120);
m_FileList.InsertColumn(4,"状态",LVCFMT_LEFT,40);
m_FileList.InsertColumn(5,"属性",LVCFMT_LEFT,40);
m_pImageList = new CImageList();
m_pImageListL = new CImageList();
SetupImages(m_pImageList, 16);
SetupImages(m_pImageListL, 32);
m_FileList.SetImageList(m_pImageList, LVSIL_SMALL);
m_FileList.SetImageList(m_pImageListL, LVSIL_NORMAL);
}
void CTab4::InitFileTree()
{
TreeImageList = new CImageList();
TreeImageList->Create(20, 20, ILC_COLOR8 | ILC_MASK, 9, 9);
TreeImageList->Add(AfxGetApp()->LoadIcon(IDI_COMPUTER));
TreeImageList->Add(AfxGetApp()->LoadIcon(IDI_USER));
TreeImageList->Add(AfxGetApp()->LoadIcon(IDI_FILE));
TreeImageList->Add(AfxGetApp()->LoadIcon(IDI_ICON2));
m_FileTree.SetImageList(TreeImageList,TVSIL_NORMAL);
HTREEITEM hRoot = m_FileTree.InsertItem("请先登录文件系统",0,0);
}
bool CTab4::AddFile(CString Path, CString FName, int FSize,CString Attr)
{
return true;
}
void CTab4::AddListFile(CString FName,CString FileOwn,CString FSize,CString Date,CString State,CString Attr)
{
LVITEM lvitem;
int iActualItem;
for(int iSubItem=0;iSubItem<6;iSubItem++)
{
lvitem.mask = LVIF_TEXT|(iSubItem == 0? LVIF_IMAGE : 0);
lvitem.iItem = (iSubItem == 0)? ++ItemCount : iActualItem;
lvitem.iSubItem = iSubItem;
switch(iSubItem)
{
case 0:
lvitem.pszText =(LPTSTR)(LPCTSTR)FName;
Attr.MakeUpper();
if (Attr.Find("E")>0)
lvitem.iImage=1;
else
lvitem.iImage=0;
break;
case 1:
lvitem.pszText = (LPTSTR)(LPCTSTR)FileOwn;
break;
case 2:
FSize+=" 字节";
lvitem.pszText = (LPTSTR)(LPCTSTR)FSize;
break;
case 3:
lvitem.pszText = (LPTSTR)(LPCTSTR)Date;
break;
case 4:
lvitem.pszText = (LPTSTR)(LPCTSTR)State;
break;
case 5:
lvitem.pszText = (LPTSTR)(LPCTSTR)Attr;
break;
}
if (iSubItem == 0)
iActualItem = m_FileList.InsertItem(&lvitem);
else
m_FileList.SetItem(&lvitem);
}
}
void CTab4::SetupImages(CImageList *mImageList, int iSize)
{
mImageList->Create(iSize, iSize, ILC_COLOR8 | ILC_MASK, 9, 9);
mImageList->Add(AfxGetApp()->LoadIcon(IDI_FILE));
mImageList->Add(AfxGetApp()->LoadIcon(IDI_ICON2));
}
void CTab4::OnBtnAdd()
{
if(!CheckLogin()) return;
CDlgNewFile DlgNewFile;
DlgNewFile.flag=true;
if(DlgNewFile.DoModal()==IDOK)
{
if(CreateNewFile(DlgNewFile.m_FileName,DlgNewFile.m_Content,true,(!DlgNewFile.m_Read),DlgNewFile.m_Execute,DlgNewFile.m_Share))
{
MessageBox("添加文件成功!","提示:",MB_ICONINFORMATION);
}
else
{
MessageBox("添加文件失败!可能磁盘空间不足或已超过用户允许保存的文件数!","提示:",MB_ICONEXCLAMATION);
}
}
ShowUserFiles();
}
void CTab4::OnBtnModify()
{
if(!CheckLogin()) return;
CDlgNewFile DlgNewFile;
DlgNewFile.flag=false;
DlgNewFile.m_FileName=SelectedFileName;
DlgNewFile.m_Content=ReadFile(SelectedFileName);
CString Attribute=m_FileList.GetItemText(SelectPos,5);
DlgNewFile.m_Execute=Attribute.Find("E")>0?true:false;
DlgNewFile.m_Read=Attribute.Find("W")>0?false:true;
DlgNewFile.m_Share=Attribute.Find("S")>0?true:false;
//DlgNewFile.UpdateData(false);
if(DlgNewFile.DoModal()==IDOK)
{
if(ModifyFile(DlgNewFile.m_FileName,DlgNewFile.m_Content,true,(!DlgNewFile.m_Read),DlgNewFile.m_Execute,DlgNewFile.m_Share))
{
MessageBox("修改文件成功!","提示:",MB_ICONINFORMATION);
}
else
{
MessageBox("修改文件失败!","提示:",MB_ICONEXCLAMATION);
}
}
ShowUserFiles();
}
void CTab4::OnBtnLogin()
{
FATIO.Close();
StartIO();
if(!bFormated)
{
CDlgUser DlgUser;
DlgUser.DoModal();
return;
}
CDlgLogin dlgLogin;
ZeroMemory(&OpenList,sizeof(OpenList));
if(dlgLogin.DoModal()==IDOK)
{
CurrentUser=dlgLogin.UserName;
CurrentID=dlgLogin.UserID;
ShowUserFiles();
bLogin=true;
CRect rect;
m_FileList.GetWindowRect(&rect);
CPoint pt = rect.CenterPoint();
ShowBallTip(pt,"单击列表的文件项,可以选定文件查看其内容或是其它操作!");
}
}
/////////////////////////////////////////////////////////////////////////////
// CDlgUser dialog
void CTab4::OnBtnUser()
{
CDlgUser DlgUser;
DlgUser.DoModal();
}
void CDlgUser::OnAddUser()
{
UpdateData(true);
if (FileFAT.UserNum>=10)
{
AfxMessageBox("用户数量已达到最大10个,你可以删除一些无用的帐号!");
return;
}
for(int i=0;i<10;i++)
{
if(FileFAT.Mfd[i].IsExist==false)
{
wsprintf(FileFAT.Mfd[i].UserName,m_UserName);
FileFAT.Mfd[i].IsExist=true;
FileFAT.Mfd[i].nItem=0;
break;
}
}
FileFAT.UserNum++;
WriteFAT();
ReadFAT();
ShowUser();
m_UserName.Empty();
UpdateData(false);
}
void CDlgUser::ShowUser()
{
m_UserListView.DeleteAllItems();
ReadFAT();
int i;
//CString str;
//str.Format("%d",FileFAT.UserNum);
//MessageBox(str);
CString strItem;
for (i=0;iiItem,pNMListView->iSubItem);
CString ComboText;
for (int i=0;i32)
wsprintf(Content,FileContent.Mid(i*32,32));
else
wsprintf(Content,FileContent.Mid(i*32,count));
if(!WriteBlock(BlockID,(BLOCK*)Content)) return false;
}
else
{
return false;
}
}
wsprintf(FileFAT.Mfd[CurrentID].ufd[FilePos].FileName,FileName.Left(15));
FileFAT.Mfd[CurrentID].ufd[FilePos].IsExist=true;
FileFAT.Mfd[CurrentID].ufd[FilePos].BlockNum=BlockNum;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrExecute=AttrExecute;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrRead=AttrRead;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrWrite=AttrWrite;
FileFAT.Mfd[CurrentID].ufd[FilePos].IsShared=AttrShare;
FileFAT.Mfd[CurrentID].ufd[FilePos].FileLen=FileContent.GetLength();
//str.Format("FileName:%s",FileFAT.Mfd[CurrentID].ufd[FilePos].FileName);
//MessageBox(str);
wsprintf(FileFAT.Mfd[CurrentID].ufd[FilePos].Time,GetCurrentTime());
FileFAT.Mfd[CurrentID].nItem++;
WriteFAT();
ShowUserFiles();
return true;
}
CString CTab4::ReadFile(CString FileName)
{
int i,FilePos;
bool flag=false;
char Buffer[32];
CString strReturn;
for(i=0;iiItem,0);
SelectPos=pNMListView->iItem;
SelectedFileName=m_FileList.GetItemText(pNMListView->iItem,0);
m_FileContent=ReadFile(strFName);
UpdateData(false);
*pResult = 0;
}
bool CTab4::ModifyFile(CString FileName, CString FileContent, bool AttrRead, bool AttrWrite, bool AttrExecute, bool AttrShare)
{
int i,FilePos,BlockNum,BlockID;
bool flag=false;
FileContent.TrimLeft();
FileContent.TrimRight();
char Content[32];
for(i=0;i32)
wsprintf(Content,FileContent.Mid(i*32,32));
else
wsprintf(Content,FileContent.Mid(i*32,count));
if(!WriteBlock(BlockID,(BLOCK*)Content)) return false;
}
else
{
return false;
}
}
wsprintf(FileFAT.Mfd[CurrentID].ufd[FilePos].FileName,FileName.Left(15));
FileFAT.Mfd[CurrentID].ufd[FilePos].IsExist=true;
FileFAT.Mfd[CurrentID].ufd[FilePos].BlockNum=BlockNum;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrExecute=AttrExecute;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrRead=AttrRead;
FileFAT.Mfd[CurrentID].ufd[FilePos].AttrWrite=AttrWrite;
FileFAT.Mfd[CurrentID].ufd[FilePos].IsShared=AttrShare;
FileFAT.Mfd[CurrentID].ufd[FilePos].FileLen=FileContent.GetLength();
wsprintf(FileFAT.Mfd[CurrentID].ufd[FilePos].Time,GetCurrentTime());
WriteFAT();
ShowUserFiles();
return true;
}
void CTab4::ShowBlock(int fileid)
{
CString str,temp;
for (int i=0;iGetWindowRect(rect);
ScreenToClient(rect);
m_ctrlChecker.Create(WS_VISIBLE | WS_CHILD | WS_VSCROLL | WS_TABSTOP, rect, this, IDC_STATIC_CHECKER);
m_ctrlChecker.SetTotalBlocks(1000);
/*for(register i = 10; i < 35; i++)
m_ctrlChecker.SetBlock(i, RGB(0, 155, 0));
for(i = 50; i < 120; i++)
m_ctrlChecker.SetBlock(i, RGB(255, 0, 0));
for(i = 150; i < 255; i++)
m_ctrlChecker.SetBlock(i, RGB(0, 0, i));
for(i = 256; i < 300; i++)
m_ctrlChecker.SetBlock(i, RGB(0, 0, 0));
m_ctrlChecker.Refresh();*/
}
HBRUSH CTab4::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr;
switch (nCtlColor)
{
case CTLCOLOR_EDIT:
case CTLCOLOR_MSGBOX:
switch (pWnd->GetDlgCtrlID())
{
case IDC_FILE_CONTENT: // first CEdit control ID
// put your own CONTROL ID here
pDC->SetBkColor(m_bluecolor); // change the background color
pDC->SetTextColor(m_textcolor); // change the text color
hbr = (HBRUSH) m_bluebrush; // apply the brush
break;
// otherwise do default handling of OnCtlColor
default:
hbr=CDialog::OnCtlColor(pDC,pWnd,nCtlColor);
}
break;
// otherwise do default handling of OnCtlColor
default:
hbr=CDialog::OnCtlColor(pDC,pWnd,nCtlColor);
}
return hbr; // return brush
}
void CDlgDisk::CalcBlock()
{
int i,j,k,sum;
for (i=0;iiItem,0);
UpdateData(false);
*pResult = 0;
}
void CDlgLogin::OnDblclkUserlist(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = 0;
OnOK();
}
void CTab4::ShowBallTip(CPoint pt, CString msg)
{
LOGFONT lf;
::ZeroMemory (&lf, sizeof (lf));
lf.lfHeight = 15;
lf.lfWeight = FW_BOLD;
lf.lfUnderline = FALSE;
::strcpy (lf.lfFaceName, _T("楷体_GB2312"));
m_pBalloonTip = CBalloonTip::Show(pt, CSize(250, 100), msg, lf, 5, TRUE);
}