2007年12月20日星期四

获得Cxxxview指针

前提: 在要获得view指针的.cpp文档中加入view.h及其头文档:
//----include header for CDbinfoView *pView
#include "ColorEdit.h"
#include "ColorStatic.h"
#include "ListCtrlStyled.h"
#include "DBinfoReg.h"
#include "dbinfo.h"
#include "dbinfoSet.h"
#include "ADOpaelogbuch.h"
#include "ABBruchDialog.h"
#include "DBinfoSmall.h"
#include "DBinfoListCt.h"
#include "dbinfoDoc.h"
#include "dbinfoView.h"
//----include header for CDbinfoView *pView

获得view指针:
CMDIFrameWnd *pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;

// Get the active MDI child window.
CMDIChildWnd *pChild = (CMDIChildWnd *) pFrame->GetActiveFrame();

// or CMDIChildWnd *pChild = pFrame->MDIGetActive();

// Get the active view attached to the active MDI child window.
CDbinfoView *pView = (CDbinfoView *) pChild->GetActiveView();


参考:
在MFC类中各种类的指针的获取和应用

取得文件的 Version information

连接库函数:
//----add by zeng for GetFileVersionInfoSize() and GetFileVersionInfo()
#include
#include
#pragma comment( lib , "C:\\Programme\\Microsoft Visual Studio\\VC98\\Lib\\VERSION.LIB" )
//--

代码:
//----Get the file version:
// C:\Programme\Adobe\Reader 8.0\Reader\AcroRd32.exe
long len;
DWORD dwHandle;
len = GetFileVersionInfoSize((LPTSTR)(LPCSTR)sourceDB,&dwHandle);
PTSTR pBuffer = new TCHAR[len];
CString l;
l.Format("%d", len);
AfxMessageBox(l);
memset((void*)pBuffer,0,len);

//get .exe or .DLL resource info, put into pBuffer?
if(GetFileVersionInfo((LPTSTR)(LPCSTR)sourceDB,0,len,(PVOID)pBuffer))
{ //GetFileVersionInfo false.
l.Format("%d", (LPTSTR)pBuffer);
AfxMessageBox(l);
UINT puLen;
//PTSTR lplpBuffer = new TCHAR[len];
VS_FIXEDFILEINFO *lplpBuffer;
if ( VerQueryValue( pBuffer, "\\",(void**)&lplpBuffer, /*(LPVOID*) &lplpBuffer,*/ &puLen ) )
l.Format("%d", lplpBuffer->dwProductVersionLS);
AfxMessageBox(l);
//AfxMessageBox("break");
}

//--



VS_FIXEDFILEINFO 的结构:
typedef struct tagVS_FIXEDFILEINFO
{
DWORD dwSignature; /* e.g. 0xfeef04bd */
DWORD dwStrucVersion; /* e.g. 0x00000042 = "0.42" */
DWORD dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */
DWORD dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */
DWORD dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */
DWORD dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */
DWORD dwFileFlagsMask; /* = 0x3F for version "0.42" */
DWORD dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
DWORD dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
DWORD dwFileType; /* e.g. VFT_DRIVER */
DWORD dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
DWORD dwFileDateMS; /* e.g. 0 */
DWORD dwFileDateLS; /* e.g. 0 */
} VS_FIXEDFILEINFO;


参考:
http://www.cpp-tutor.de/mfc/mfc/kap7/lektion8.htm

VC++动态链接库

错误:Nichtaufgeloestes externes Symbol: funA
解决方案:
#pragma comment( lib , "C:\\Programme\\Microsoft Visual Studio\\VC98\\Lib\\VERSION.LIB" )

2007年12月19日星期三

Payback Coupons, gültig bis 31.03.2008

Payback Coupons

create()替代DoModal()打开Dialog

m_pListCt.m_small.Create(IDD_DIALOG_SMALL, this);
m_pListCt.m_small.ShowWindow(SW_SHOW);


//关闭Dialog
m_small.SendMessage(16,0,0);

关闭程序前询问,强行结束进程

void CMainFrame::OnSysCommand(UINT nID,LPARAM lParam)
{
//PostMessage( WM_MY_MESSAGE,0,0);
//SendMessageToDescendants(WM_MY_MESSAGE,0,0,true);
if((nID & 0xFFFFFFF0) ==SC_CLOSE)
{
if(AfxMessageBox("Warning: Wollen Sie die Pozess wicklich abbrechen?",MB_YESNO)!=IDYES)
{

return;
}
else
{
if( !TerminateProcess(GetCurrentProcess(),0) )
AfxMessageBox("Fehlermeldung: Programm kann nicht terminiert werden, bitte versuchen Sie Task-Manager!");

}

//Application->Terminate();
//DestroyWindow();
}
CWnd::OnSysCommand(nID,lParam);
}

获取命令行参数 -s -x...

CWinApp* pApp = AfxGetApp(); //取得CMyWinApp对象指针
pApp-> m_lpCmdLine; //命令行参数,如"-s -q"

2007年12月13日星期四

lstrcat,连结两个LPTSTR 字符串

LPCSTR :指向一个字符串常量的指针(每个字符是占一个字节)
LPCTSTR :指向一个字符串常量的指针(每个字符是占两个字节)
LPTSTR :指向一个字符串的指针(每个字符是占两个字节)

LPTSTR lstrcat(
LPTSTR lpString1,
LPTSTR lpString2
);
Parameters:
lpString1
[in, out] Pointer to a null-terminated string. The buffer must be large enough to contain both strings.
lpString2
[in] Pointer to the null-terminated string to be appended to the string specified in the lpString1 parameter.

Return Value:
If the function succeeds, the return value is a pointer to the buffer.
If the function fails, the return value is NULL and lpString1 may not be null-terminated.

2007年12月12日星期三

高速上险些追尾,魂都吓掉了。。。。只差几厘米

2007年12月11号,阴雨绵绵,一连好几天了。六点钟下了班,一脚油门,从路德维希堡上了高速门,直奔女友学校,打算接上她一起去健身。正值下班高峰,车灯连成一条条长龙,如流光飞逝,随着高低起伏的公路远远望去,疑是银河落九天。。。走神了。大概是上个星期高速上超速三十多被闪的缘故,一路上我开得还算比较慢,小心翼翼,全神贯注。。。保守,再保守。二十多分钟后,高速分道,上了一条三条Fahrbahn的Autobahn,我留在了外道,车流变得拥挤起来,时速还是稳定的保持在90-100左右,不认得路,我一边开一边不时地瞟一下小Navi,也就电光火石的一瞬间,等我的视线重新回到前方路面的时候,前面那辆红车的轮廓在我眼中骤然放大,越来越近,我终于反应过来了,前面在Notbremsen!! 猛踩刹车,身体随着惯性往前猛地一倾,轮胎磨擦路面发出尖啸, 车子还是往前冲,无可阻挡,前面车继续减速中,用力踩,眼睁睁的看着车头往前车屁股上面凑上去。。。凑上去。。。近得都比得上街边的停车车距了,眼看着要顶上车屁股了,我的车好像缓了一缓----其实是前面那辆车松了Bremsen,不再减速了,距离终于拉开,在这一刻,跳到嗓子口的心咚咚的沉了回去,脚松开,这时候时速大概二十多,车挂着五挡的Kupplung一抖一抖的往前爬,回过神来,换二档,还是有点快了,又挂一档,七魂六魄还在游荡,都是下意识的动作。终于停下来了,踩稳刹车,看一下四周,才发现右边道上的车也在慢慢爬,车流又动了,神情恍惚的慢慢往前开,一切都像是虚幻,仿佛不在真实世界里。前面几辆车在打闪要求并道,我也机械跟着打闪,等到前面的红车往右一拐,出现在我眼前的是一个事故现场:两辆车打着双闪,头尾相连。这个时候大家都变得很nett,我一直打着右闪灯,盯着前面两辆破车发愣,右边的车也耐心的停下等我并道,过了好一会我才回过神来,手忙脚乱的并到右边,继续往前开,这时候才发现,自己的手在抖,不知何时,已是一身大汗。

MFC中自由使用自定义消息

消息映射、循环机制是Windows程序运行的基本方式。VC++ MFC 中有许多现成的消息句柄,可当我们需要完成其它的任务,需要自定义消息,就遇到了一些困难。在MFC ClassWizard中不允许添加用户自定义消息,所以我们必须在程序中添加相应代码,以便可以象处理其它消息一样处理自定义消息。通常的做法是采取以下步骤:

  第一步:定义消息。

  推荐用户自定义消息至少是WM_USER+100,因为很多新控件也要使用WM_USER消息。

#define WM_MY_MESSAGE (WM_USER+100)

  第二步:实现消息处理函数。该函数使用WPRAM和LPARAM参数并返回LPESULT。

LPESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
// TODO: 处理用户自定义消息
...
return 0;
}

  第三步:在类头文件的AFX_MSG块中说明消息处理函数:

class CMainFrame:public CMDIFrameWnd
{
...
// 一般消息映射函数
protected:
// {{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnTimer(UINT nIDEvent);
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
}

  第四步:在用户类的消息块中,使用ON_MESSAGE宏指令将消息映射到消息处理函数中。

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_TIMER()
ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

第五步:发送消息。
//---- use the Frame pointobject
CMDIFrameWnd *pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
pFrame->PostMessage( WM_MY_MESSAGE,0,0);
//--

MFC中消息拦截

Method 1:
BOOL CDbinfoView::PreTranslateMessage(MSG* pMsg)
{
// If edit control is visible in tree view control, when you send a
// WM_KEYDOWN message to the edit control it will dismiss the edit
// control. When the ENTER key was sent to the edit control, the
// parent window of the tree view control is responsible for updating
// the item's label in TVN_ENDLABELEDIT notification code.
if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_LBUTTONDOWN)//pMsg->message == WM_CLOSE)
{
int iID (-1);
switch(pMsg->wParam)
{
case VK_RETURN:

//HWND hWnd=::GetFocus();
//int iID=::GetDlgCtrlID(hWnd);
iID=::GetDlgCtrlID(::GetFocus());

//confirm the message from which Control
if(iID == IDC_EDIT_PATH )
{
//data is being retrieved from DialogForm into FileURL
UpdateData(true);
//Todo:............
}
break;
case VK_TAB:

break;
case VK_LBUTTON:

break;

}

//edit->SendMessage(WM_KEYDOWN, pMsg->wParam, pMsg->lParam);
//return TRUE;

}

// CXxxx can be a CFormView, Cdialog, or CPropertyPage class.
return CWnd::PreTranslateMessage(pMsg);
}


Method 2:

PeekMessage 消息机制
PeekMessage

GetMessage不将控制传回给程序,直到从程序的消息队列中取得消息,但是PeekMessage总是立刻传回,而不论一个消息是否出现。当消息队列中有一个消息时,PeekMessage的传回值为TRUE(非0),并且将按通常方式处理消息。当队列中没有消息时,PeekMessage传回 FALSE(0)。
这使得我们可以改写普通的消息循环。我们可以将如下所示的循环:
while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

return msg.wParam ;


替换为下面的循环:
while (TRUE)

{

if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))

{

if (msg.message == WM_QUIT)

break ;

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

else

{

// 完成某些工作的其它行程序

}

}

return msg.wParam ;


注意,WM_QUIT消息被另外挑出来检查。在普通的消息循环中您不必这么作,因为如果GetMessage接收到一个WM_QUIT消息,它将传回0,但是PeekMessage用它的传回值来指示是否得到一个消息,所以需要对WM_QUIT进行检查。
如果PeekMessage的传回值为TRUE,则消息按通常方式进行处理。如果传回值为FALSE,则在将控制传回给Windows之前,还可以作一点工作(如显示另一个随机矩形)。
(尽管Windows文件上说,您不能用PeekMessage从消息队列中删除WM_PAINT消息,但是这并不是什么大不了的问题。毕竟, GetMessage并不从消息队列中删除WM_PAINT消息。从队列中删除WM_PAINT消息的唯一方法是令窗口显示区域的失效区域变得有效,这可以用ValidateRect和ValidateRgn或者BeginPaint和EndPaint对来完成。如果您在使用PeekMessage从队列中取出WM_PAINT消息后,同平常一样处理它,那么就不会有问题了。所不能作的是使用如下所示的程序代码来清除消息队列中的所有消息:
while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) ;


这行叙述从消息队列中删除WM_PAINT之外的所有消息。如果队列中有一个WM_PAINT消息,程序就会永远地陷在while循环中。)

通过OnSysCommand关闭程序控制

在MainFrame利用WINDOWS的消息循环机制,拦截关闭窗口消息:
1:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
//手动添加:
ON_MESSAGE(WM_SYSCOMMAND,OnSysCommand)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

2:
void CMainFrame::OnSysCommand(UINT nID,LPARAM lParam)
{
if((nID & 0xFFFFFFF0) ==SC_CLOSE)
{
if(AfxMessageBox("Warning: Wollen Sie die Pozess wicklich abbrechen?",MB_OKCANCEL)!=IDOK)
return;
//如果关闭程序 do something:..........
}
CWnd::OnSysCommand(nID,lParam);
}


3.在CDialog中:

void CDBinfoSmall::OnSysCommand(UINT nID, LPARAM lParam)
{
//AfxMessageBox("mouse call");
if((nID & 0xFFFFFFF0) == SC_CLOSE)
{

if(AfxMessageBox("Warning: Wollen Sie die Pozess wicklich abbrechen?",MB_YESNO) == IDNO)
{

//AfxMessageBox( GetCommandLine() );
return;

}
else
{
//---- use the View pointobject, close the m_DaoDatabase;
CMDIFrameWnd *pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;

// Get the active MDI child window.
CMDIChildWnd *pChild = (CMDIChildWnd *) pFrame->GetActiveFrame();

// or CMDIChildWnd *pChild = pFrame->MDIGetActive();

// Get the active view attached to the active MDI child window.
CDbinfoView *pView = (CDbinfoView *) pChild->GetActiveView();

//close the source db m_DaoDatabase by using pView
if( pView->m_DaoDatabase.IsOpen())
{
pView->m_DaoDatabase.Close();
//
//AfxMessageBox("m_DaoDatabase.Close();");

}
if (plc.existFile(plc.InstallDir + "\\Database\\apollo.ldb"))
DeleteFile(plc.InstallDir + "\\Database\\apollo.ldb");
//close the target db in cdbinfolistct,
//pView->m_pListCt.DB_Close(); //runtime error!!
//----
if( !TerminateProcess(GetCurrentProcess(),0) )
AfxMessageBox("Fehlermeldung: Programm kann nicht terminiert werden, bitte versuchen Sie Task-Manager!");

}

}

/////////////////////////////////////////////////
//
// * Making other messages be answered, e.SC_MOVE
//
/////////////////////////////////////////////////
CDialog::OnSysCommand(nID,lParam);

}


4.OnSysCommand() 与 OnLButtonDown()在CDialog中响应区别:

OnSysCommand 响应了对对话框标题栏的鼠标点击
OnLButtonDown 响应了对对话框框体的鼠标点击(不包括标题栏)

RGB color codes

http://www.pitt.edu/~nisg/cis/web/cgi/rgb.html

2007年12月11日星期二

发送button Onclick 消息

GetDlgItem(IDC_BUTTON_ROLLBACK)->PostMessage( WM_LBUTTONDOWN,BM_CLICK,0);
GetDlgItem(IDC_BUTTON_ROLLBACK)->PostMessage( WM_LBUTTONUP, BM_CLICK,0);

改变窗口标题

调用CWnd : : SetWindowText可以改变任何窗口(包括控件)的标题。

//Set title for application's main frame window .
AfxGetMainWnd ( ) —> SetWindowText (_T("Application title") );

//Set title for View's MDI child frame window .
GetParentFrame ( ) —> SetWindowText ("_T ("MDI Child Frame new title") );

//Set title for dialog's push button control.
GetDigitem (IDC_BUTTON) —> SetWindowText (_T ("Button new title ") );

如果需要经常修改窗口的标题(注:控件也是窗口),应该考虑使用半文档化的函数AfxSetWindowText。该函数在AFXPRIV.H中说明,在WINUTIL.CPP中实现,在联机帮助中找不到它,它在AFXPRIV.H中半文档化, 在以后发行的MFC中将文档化。

AfxSetWindowText的实现如下:

voik AFXAPI AfxSetWindowText (HWND hWndCtrl , LPCTSTR IpszNew )
{
itn nNewLen= Istrlen (Ipaznew);
TCHAR szOld [256];
//fast check to see if text really changes (reduces flash in the controls )
if (nNewLen >_contof (szOld) ||::GetWindowText (hWndCrtl , szOld , _countof (szOld) !=nNewLen ||Istrcmp (szOld , IpszNew )! = 0
{
//change it
::SetWindowText (hWndCtrl , IpszNew );
}
}

2007年12月10日星期一

调出主窗口

AfxGetMainWnd();//调出主窗口

2007年12月7日星期五

CreateThread

例1:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
char a[255];
strcpy(a,"qinwei");
DWORD dwThreadId;
CreateThread(NULL,0,ClientThread,(LPVOID)a,0,&dwThreadId);
}
DWORD WINAPI ClientThread(LPVOID lpParam)
{
char* a = (char* )lpParam;
ShowMessage(a);// <------------通过
return 0;
}

例2:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
POINT a;
DWORD dwThreadId;
a.x=7;
CreateThread(NULL,0,ClientThread,(LPVOID)&a,0,&dwThreadId);
}
DWORD WINAPI ClientThread(LPVOID lpParam)
{
POINT* a = (POINT* )lpParam;
ShowMessage(a->x);// <------------出错
return 0;
}

2007年12月6日星期四

几种控件的绑定

m_pListCt->pList = (CListCtrl*) GetDlgItem(IDC_LIST_DB);

if(!m_pListCt->pList->m_hWnd) //check if the ColorListstyle was already assigned
m_pListCt->pList->SubclassDlgItem(IDC_LIST_DB,this);

//hide the button:
GetDlgItem(IDC_BUTTON_ROLLBACK)->EnableWindow(false);

2007年12月5日星期三

CreateProcess 能够返回错误码

STARTUPINFO si = { sizeof(STARTUPINFO) };

si.dwFlags = STARTF_USESHOWWINDOW;

si.wShowWindow = SW_HIDE;

PROCESS_INFORMATION pi;

CreateProcess(NULL, _T("app.exe"), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);

if (showwait)
{
while( WaitForSingleObject(pi.hProcess, 5000)!=WAIT_OBJECT_0 )
{
if ( str.Find(_T("tool")) == -1 ) //str is not the execution of tool -wait
{
WinExec(CPublic::tool + "-wait4",SW_SHOWNORMAL);
}
}
}
else
{
WaitForSingleObject(pi.hProcess, INFINITE);
}

DWORD exit_code;

GetExitCodeProcess(pi.hProcess, &exit_code);

// TODO: check for errors

// TODO: free handles

我的代码:

BOOL CPublic::ProcessFiles(CString App, CString Command, BOOL showwait)
{
BOOL bProcess = false;
try
{
LPCTSTR lpApplicationName = NULL;
LPTSTR lpCommandLine;

Command = (LPCTSTR)(App + CString(" ") + Command);
//lpApplicationName = "\"" +App +"\"";
lpCommandLine = (char*)(LPCSTR) Command;

//TCHAR * lpCommandLine = new TCHAR();
//lpCommandLine = _tcsdup(TEXT("\"" +App + "\" " + Command));

STARTUPINFO si = { sizeof(STARTUPINFO) };

si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;

PROCESS_INFORMATION pi;

CreateProcess(lpApplicationName, lpCommandLine, NULL, NULL, FALSE, 0, NULL, (LPCTSTR)InstallDir, &si, &pi);
//CString str = GetCommandLine();
//AfxMessageBox(str);
if (showwait)
{
while( WaitForSingleObject(pi.hProcess, 5000)!=WAIT_OBJECT_0 )
{
if ( App.Find(_T("tool")) == -1 ) //str is not the execution of tool -wait
{
CPublic::ProcessMessages();
WinExec((LPCTSTR)(tool + CString("-wait4")),SW_SHOWNORMAL);
CPublic::ProcessMessages();

}

}

}
else
{
//WaitForSingleObject(pi.hProcess, INFINITE);
while( WaitForSingleObject(pi.hProcess, 500)!=WAIT_OBJECT_0 )
{
CPublic::ProcessMessages();


}

}
CPublic::ProcessMessages();
DWORD exit_code;

if ( GetExitCodeProcess(pi.hProcess, &exit_code) )
bProcess = true;


////////////////////////////////////////////////////////////////
//
// * edit by zengzn@gamil.com on 11.04.2008
//
// * "exit_code" is now known:
// 0: application success ran in win.
// 10: application terminates itself.
// 1: user terminate the process.
// ....
// * so we can judge that if a process was crashed or killed
// by user or self terminated
//
////////////////////////////////////////////////////////////////
//CString s;
//s.Format("%d",exit_code);
//AfxMessageBox(s);

if (exit_code != 10 && exit_code != 0)
bProcess = false;


// TODO: free handles

}catch(CException &e){
CString sMeldungsText;
char sErrorText[256];
e.GetErrorMessage(sErrorText,255);
sMeldungsText.Format("%s: %s%s","ProcessFiles()","ATAGAB/AKASSE -MakeTagab: Es kam zu einem allgemeinen Fehler at: ",sErrorText);
AfxMessageBox((LPCTSTR)sMeldungsText);

}
return bProcess;

}

MFC中窗体的控制

1、在CMainFrame的PreCreateWindow(CREATESTRUCT& cs)函数中,加入:
cs.cx=300;
cs.cy=400;
即可控制窗体初始化时的大小。

2、加入这段代码可以设置一个没有“最小化”按钮和“最大化”按钮,也没有大小可调边框的主框架窗口。此窗口最初在屏幕上居中。
cs.style = WS_OVERLAPPED | WS_SYSMENU | WS_BORDER;
// Size the window to 1/3 screen size and center it
cs.cy = ::GetSystemMetrics(SM_CYSCREEN) / 3;
cs.cx = ::GetSystemMetrics(SM_CXSCREEN) / 3;
cs.y = cs.cy;// ((cs.cy * 3) - cs.cy) / 2;
cs.x = cs.cx;//((cs.cx * 3) - cs.cx) / 2;

3、如果要在创建窗体前设置标题,需加入一下两行:
cs.style &= ~FWS_ADDTOTITLE; //如果style中已经有WS_OVERLAPPED,如上例,就已经包含了~FWS_ADDTOTITLE项;
cs.lpszName = "http://www.icafe8.net";

4、如果在窗体创建时改变窗体的一些属性,可以用SetWindowLong()函数,如:
SetWindowLong(m_hWnd,GWL_STYLE, GetWindowLong(m_hWnd,GWL_STYLE) & ~WS_MAXIMIZEBOX); //先获取窗体的类型,然后禁用最大化按钮。

5、创建一个窗体:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
WNDCLASS wndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
wndcls.hCursor=LoadCursor(NULL,IDC_HELP);
wndcls.hIcon=LoadIcon(NULL,IDI_ERROR);
wndcls.hInstance=AfxGetInstanceHandle();
wndcls.lpfnWndProc=::DefWindowProc;
wndcls.lpszClassName="limeng's window";
wndcls.lpszMenuName=NULL;
wndcls.style=CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndcls); //将窗体注册到类
cs.lpszClass="limeng's window"; //这里的名字必须和上面创建的wndcls类的名字相同
return TRUE;
}
注意:上面的代码只写在MainFrame中,必须在View窗体中增加最后一行,否则将无法看到效果,因为View窗体始终覆盖在Main窗体之上。

6、如果觉得上面的方法太烦,我们可以通过AfxRegisterWndClass函数简单的改变一个窗体的样式:
可以在MainFrame的PreCreateWindow中加入:
cs.lpszClass=AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,0,0,LoadIcon(NULL,IDI_WARNING)); //因为在框架中设置鼠标和底色是无法体现的,所以设置为0。
在View的PreCreateWindow中加入:
cs.lpszClass=AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, LoadCursor(NULL,IDC_HELP),(HBRUSH)GetStockObject(DKGRAY_BRUSH), 0);//而在View窗体中,图表设置又是无效的,所以为0

7、有意思的的是,如果在Main和View的PreCreateWindow中用默认的参数注册窗口:
cs.lpszClass=AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);
结果,鼠标是默认的箭头鼠标,图标为window默认的波浪图标,但是窗口因为是空的画刷,所以你可以看到一个透明的窗口,但是你拖动窗体时,窗体不会重绘,所以能保持刚才的背景图画,除非窗体被重画的动作发生,如最小化,或别的窗体在此窗体的顶部停留。

8、这里我们可以通过SetClassLong对窗体的单个属性进行设置。
在main框架函数的OnCreate函数中写入:
SetClassLong(m_hWnd,GCL_HICON,(LONG)LoadIcon(NULL,IDI_WARNING));//设置窗体的图标为IDI_ERROR
右键点View窗体类,增加一个WM_CREATE消息相应函数,在View窗体的OnCreate函数中写入:
SetClassLong(m_hWnd,GCL_HBRBACKGROUND,(LONG)GetStockObject(DKGRAY_BRUSH));//设置背景为灰色
SetClassLong(m_hWnd,GCL_HCURSOR,(LONG)LoadCursor(NULL,IDC_HELP));//设置鼠标为help类型
便可以得到如6中相同的效果。

9、接下来我们试着设计一个程序,让他在3个图标中每秒切换一个。
首先我们在插入资源中,导入3张.ico的图片,资源ID号分别为IDI_ICON1,IDI_ICON2,IDI_ICON3。然后在MainFrame中添加一个数组成员变量:
private:
HICON m_hIcons[3];
在MainFrame的OnCreate函数中,将icon加载进来:
m_hIcons[0]=LoadIcon(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ICON1)); /*方法一,通过全局函数直接获得当前实例的句柄,然后直接将图标的ID号转换为资源名称。*/
m_hIcons [1]=LoadIcon(theApp.m_hInstance,MAKEINTRESOURCE(IDI_ICON2)); /*方法二,利用 CWinApp派生的App类中。注意要在函数前面申明引用外部变量extern CStyleApp theApp。*/
m_hIcons[2]=LoadIcon(AfxGetApp()->m_hInstance,MAKEINTRESOURCE(IDI_ICON3)); /*方法三,通过获得应用程序的成员变量。*/
SetTimer(1,1000,NULL); //设置一个每1秒钟触发一次的OnTimer事件
在MainFrame中增加一个OnTimer的Windows消息相应函数:
static int index=0; //注意用静态变量,否则会被每次调用OnTimer事件时初始化为0。
SetClassLong(m_hWnd,GCL_HICON,(LONG)m_hIcons[index]);
index=++index%3; //取模,让index限定在0-2之间。

2007年11月27日星期二

CString常用方法简介

CString异常:
http://www.codeguru.com/forum/archive/index.php/t-212765.html

CString->char[]:
Char c[] = (char*)(LPCSTR)str

Int 转CString :
int n=1;
CString str;
str.Format("%d",n);

CString 转 Int
CString str="1";
int n=atoi(str.GetBuffer(0));

char * 转 CString:
char sz[128];
CString str;
str.Format("%s",sz);

CString 转 char *:
CString str;
int nLength=str.GetLength();
char * sz=new char[nLength];
sz=str.GetBuffer(0);

CString::Compare

int Compare( LPCTSTR lpsz ) const;

返回值 字符串一样 返回0

小于lpsz 返回-1

大于lpsz 返回1

区分大小字符

CString s1( "abc" );

CString s2( "abd" );

ASSERT( s1.Compare( s2 ) == -1 );

ASSERT( s1.Compare( "abe" ) == -1 );



CString::CompareNoCase

int CompareNoCase( LPCTSTR lpsz ) const;

返回值 字符串一样 返回0

小于lpsz 返回-1

大于lpsz 返回1

不区分大小字符



CString::Collate

int Collate( LPCTSTR lpsz ) const;

同CString::Compare



CString::CollateNoCase

int CollateNocase( LPCTSTR lpsz ) const;

同CString::CompareNoCase



CString::CString

CString( );

CString( const CString& stringSrc );

CString( TCHAR ch, int nRepeat = 1 );

CString( LPCTSTR lpch, int nLength );

CString( const unsigned char* psz );

CString( LPCWSTR lpsz );

CString( LPCSTR lpsz );

例子最容易说明问题

CString s1;

CString s2( "cat" );

CString s3 = s2;

CString s4( s2 + " " + s3 );

CString s5( 'x' ); // s5 = "x"

CString s6( 'x', 6 ); // s6 = "xxxxxx"

CString s7((LPCSTR)ID_FILE_NEW); // s7 = "Create a new document"

CString city = "Philadelphia";



CString::Delete

int Delete( int nIndex, int nCount = 1);

返回值是被删除前的字符串的长度

nIndex是第一个被删除的字符,nCount是一次删除几个字符。根据我实验得出的结果:当nCount>要删除字符串的最大长度(GetCount() - nIndex)时会出错,当nCount过大,没有足够的字符删除时,此函数不执行。

例子

CString str1,str2,str3;

char a;

str1 = "nihao";

str2 = "nIhao";

int x;

// int i=(str1 == str2);

str1.Delete(2,3);

如果nCount(3) > GetCount() – nIndex (5-2)就会执行错误



CString::Empty

Void Empty( );

没有返回值 清空操作;

例子

CString s( "abc" );

s.Empty();

ASSERT( s.GetLength( ) == 0 );



CString::Find

int Find( TCHAR ch ) const;

int Find( LPCTSTR lpszSub ) const;

int Find( TCHAR ch, int nStart ) const;

int Find( LPCTSTR lpszSub, int nStart ) const;

返回值 不匹配的话返回 -1; 索引以0 开始

nStar 代表以索引值nStart 的字符开始搜索 ,

即为包含以索引nStart字符后的字符串

例子

CString s( "abcdef" );

ASSERT( s.Find( 'c' ) == 2 );

ASSERT( s.Find( "de" ) == 3 );

Cstring str(“The stars are aligned”);

Ing n = str.Find('e',5);

ASSERT(n == 12)



CString::FindOneOf

int FindOneOf( LPCTSTR lpszCharSet ) const;

返回值 不匹配的话返回 -1; 索引以0 开始

注意::返回此字符串中第一个在lpszCharSet中 也包括字符并且从零开始的索引值

例子

CString s( "abcdef" );

ASSERT( s.FindOneOf( "xd" ) == 3 ); // 'd' is first match.



CString::Format

void Format( LPCTSTR lpszFormat, ... );

void Format( UINT nFormatID, ... );

lpszFormat 一个格式控制字符串

nFormatID 字符串标识符

例子

CString str;

Str.Format(“%d”,13);

此时Str为13



CString::GetAt

TCHAR GetAt( int nIndex ) const;

返回标号为nIndex的字符,你可以把字符串理解为一个数组,GetAt类似于[].注意nIndex的范围,如果不合适会有调试错误。



CString::GetBuffer

LPTSTR GetBuffer( int nMinBufLength );

返回值

一个指向对象的(以空字符结尾的)字符缓冲区的LPTSTR 指针。

参数

nMinBufLength

字符缓冲区的以字符数表示的最小容量。这个值不包括一个结尾的空字符的空间。

说明

此成员函数返回一个指向CString 对象的内部字符缓冲区的指针。返回的LPTSTR 不是const,因此可以允许直接修改CString 的内容。如果你使用由GetBuffer 返回的指针来改变字符串的内容,你必须在使用其它的CString 成员函数之前调用ReleaseBuffer 函数。

在调用ReleaseBuffer 之后,由GetBuffer 返回的地址也许就无效了,因为其它的CString 操作可能会导致CString 缓冲区被重新分配。如果你没有改变此CString 的长度,则缓冲区不会被重新分配。当此CString 对象被销毁时,其缓冲区内存将被自动释放。

注意,如果你自己知道字符串的长度,则你不应该添加结尾的空字符。但是,当你用ReleaseBuffer 来释放该缓冲区时,你必须指定最后的字符串长度。如果你添加了结尾的空字符, 你应该给ReleaseBuffer 的长度参数传递-1 ,ReleaseBuffer 将对该缓冲区执行strlen 来确定它的长度。

下面的例子说明了如何用CString::GetBuffer。

// CString::GetBuffer 例子

CString s( "abcd" );

#ifdef _DEBUG

afxDump << "CString s " << s << "\n";

#endif

LPTSTR p = s.GetBuffer( 10 );

strcpy( p, "Hello" ); // 直接访问CString 对象。

s.ReleaseBuffer( );

#ifdef _DEBUG

afxDump << "CString s " << s << "\n";

#endif



CString::GetLength

int GetLength( ) const;

返回值

返回字符串中的字节计数。

说明

此成员函数用来获取这个CString 对象中的字节计数。这个计数不包括结尾的空字符。

对于多字节字符集(MBCS),GetLength 按每一个8 位字符计数;即,在一个多字节字符中的开始和结尾字节被算作两个字节。

示例

下面的例子说明了如何使用CString::GetLength。

// CString::GetLength 示例

CString s( "abcdef" );

ASSERT( s.GetLength() == 6 );



CString::Insert

int Insert( int nIndex, TCHAR ch );

int Insert( int nIndex, LPCTSTR pstr );

返回修改后的长度,nIndex是字符(或字符串)插入后的索引号例子

CString str( “HockeyBest”);

int n = str.Insert( 6, “is” );

ASSERT( n == str.GetLength( ) );

printf( “1: %s\n”, ( LPCTSTR ) str );

n = str.Insert( 6, ' ' );

ASSERT( n == str.GetLength( ) );

printf ( “2: %s\n”, (LPCTSTR) STR );

n = str.Insert(555, ‘1’);

ASSERT( n == str.GetLength ( ) );

printf ( “3: %s\n”, ( LPCTSTR ) str );

输出

1. Hockeyis Best

2. Hockey is Best

3. Hockey is Best!



CString::IsEmpty

BOOL IsEmpty( ) const;

返回值

如果CString 对象的长度为0,则返回非零值;否则返回0。

说明

此成员函数用来测试一个CString 对象是否是空的。

示例

下面的例子说明了如何使用CString::IsEmpty。

// CString::IsEmpty 示例

CString s;

ASSERT( s.IsEmpty() );

请参阅 CString::GetLength



CString::Left

CString Left( int nCount ) const;

throw( CMemoryException );

返回的字符串是前nCount个字符。

例子

CString s( _T("abcdef") );

ASSERT( s.Left(2) == _T("ab") );



CString::LoadString

BOOL LoadString( UINT nID );

throw( CMemoryException );

返回值

如果加载资源成功则返回非零值;否则返回0。

nID 一个Windows 字符串资源ID。

说明 此成员函数用来读取一个由nID 标识的Windows 字符串资源,并放入一个已有CString 对象中。

示例

下面的例子说明了如何使用CString::LoadString。

// CString::LoadString 示例

#define IDS_FILENOTFOUND 1

CString s;

if (! s.LoadString( IDS_FILENOTFOUND ))



CString::MakeLower

void MakeLower( );

改变字符的小写



CString::MakeReverse

void MakeReverse( );

字符倒置



CString::MakeUpper

void MakeUpper( );

改变字符的大写

CString::Mid
CString Mid( int nFirst ) const;
CString Mid( int nFirst, int nCount ) const;
nCount代表要提取的字符数, nFirst代表要提取的开始索引位置

例子
CString s( _T("abcdef") );
ASSERT( s.Mid( 2, 3 ) == _T("cde") );

CString::ReleaseBuffer

void ReleaseBuffer( int nNewLength = -1 );

参数

nNewLength

此字符串的以字符数表示的新长度,不计算结尾的空字符。如果这个字

符串是以空字符结尾的,则参数的缺省值-1 将把CString 的大小设置为

字符串的当前长度。

说明

使用ReleaseBuffer 来结束对由GetBuffer 分配的缓冲区的使用。如果你知道缓

冲区中的字符串是以空字符结尾的,则可以省略nNewLength 参数。如果字符

串不是以空字符结尾的,则可以使用nNewLength 指定字符串的长度。在调用

ReleaseBuffer 或其它CString 操作之后,由GetBuffer 返回的地址是无效的。

示例

下面的例子说明了如何使用CString::ReleaseBuffer。

// CString::ReleaseBuffer 示例

CString s;

s = "abc";

LPTSTR p = s.GetBuffer( 1024 );

strcpy(p, "abc"); // 直接使用该缓冲区

ASSERT( s.GetLength() == 3 ); // 字符串长度 = 3

s.ReleaseBuffer(); // 释放多余的内存,现在p 无效。

ASSERT( s.GetLength() == 3 ); // 长度仍然是3



CString::Remove

int CString::Remove ( TCHAR ch );

返回值

返回从字符串中移走的字符数。如果字符串没有改变则返回零。

参数

ch

要从一个字符串中移走的字符。

说明

此成员函数用来将ch 实例从字符串中移走。与这个字符的比较是区分大小写

的。

示例

// 从一个句子中移走小写字母'c':

CString str (“This is a test.”);

int n = str.Remove( 't' );

ASSERT( n == 2 );

ASSERT( str ==“This is a es. ” );



CString::Replace

int Replace( TCHAR chOld, TCHAR chNew );

int Replace( LPCTSTR lpszOld, LPCTSTR lpszNew );

返回值

返回被替换的字符数。如果这个字符串没有改变则返回零。

参数

chOld

要被chNew 替换的字符。

chNew

要用来替换chOld 的字符。

lpszOld

一个指向字符串的指针,该字符串包含了要被lpszNew 替换的字符。

lpszNew

一个指向字符串的指针,该字符串包含了要用来替换lpszOld 的字符。

说明

此成员函数用一个字符替换另一个字符。函数的第一个原形在字符串中用chNew

现场替换chOld。函数的第二个原形用lpszNew 指定的字符串替换lpszOld 指定

的子串。

在替换之后,该字符串有可能增长或缩短;那是因为lpszNew 和lpszOld 的长度

不需要是相等的。两种版本形式都进行区分大小写的匹配。

示例

// 第一个例子,old 和new 具有相同的长度。

CString strZap( “C - -” );

int n = strZap.Replace('-', '+' );

ASSERT( n == 2 );

ASSERT(strZap == “C++” );

// 第二个例子,old 和new 具有不同的长度。

CString strBang( “Everybody likes ice hockey” );

n = strBang.Replace( “hockey”, “golf” );

ASSERT( n ==1 );

n = strBang.Replace ( “likes” , “plays” );

ASSERT( n == 1 );

n = strBang.Replace( “ice”, NULL );

ASSERT( n == 1 );

ASSERT( strBang == “Everybody plays golg” );

// 注意,现在在你的句子中有了一个额外的空格。

// 要移走这个额外的空格,可以将它包括在要被替换的字符串中,例如,“ice ”。



CString::ReverseFind

int ReverseFind( TCHAR ch ) const;

返回值

返回此CString 对象中与要求的字符匹配的最后一个字符的索引;如果没有找

到需要的字符则返回-1。

参数

ch

要搜索的字符。

说明

此成员函数在此CString 对象中搜索与一个子串匹配的最后一个字符。此函数

类似于运行时函数strrchr。

示例

// CString::ReverseFind 示例

CString s( "abcabc" );

ASSERT( s.ReverseFind( 'b' ) == 4 );



CString::Right

CString Right( int nCount ) const;

throw( CMemoryException );

返回的字符串是最后nCount个字符。

CString s( _T("abcdef") );

ASSERT( s.Right(2) == _T("ef") );



CString:: SetAt

void SetAt( int nIndex, TCHAR ch );



你可以把字符串理解为一个数组,SetAt类似于[].注意nIndex的范围,如果不合适会有调试错误。 Ch 更替字符, 把nIndex位置上的字符 变成ch



CString s( "abc" );

s.MakeReverse();

ASSERT( s == "cba" );





CString::TrimLeft

void TrimLeft( );

void CString::TrimLeft( TCHAR chTarget );

如果没有参数,从左删除字符(\n\t空格等),至到遇到一个非此类字符. 当然你也可以指定删除那些字符. 如果指定的参数是字符串,那么遇上其中的一个字符就删除.
\n 换行符
\t TAB字符
CString str = "\n\t a";

str.TrimLeft();

str为“a”;



CString str = "abbcadbabcadb ";

str.TrimLeft("ab");

结果"cadbabcadb "

str.TrimLeft("ac");

结果"bcadbabcadb "



CString::TrimRight

void TrimRight( );

void CString::TrimRight( TCHAR chTarget );

void CString::TrimRight( LPCTSTR lpszTargets );

同上

2007年11月15日星期四

ADO(ActiveX Data Object)技术特征

3.1 部件对象模型(COM)是Windows平台上的一个通用的接口标准。ADO正是建立在部件对象模型基础上的一个数据库接口部件,是当前最流行的数据库访问技术。
它有以下的特点:
l 它是基于OLE DB访问接口的,它对OLE DB的接口进行了卦装,定义了ADO对象,屏蔽了底层数据库接口之间的差异。属于数据库访问当中的高层接口。
l ADO有选择的使用不同的底层数据库接口,以提供最高的性能。当数据库供应商提供了符合OLE DB的数据库驱动程序时,ADO直接使用该驱动程序,这是时效率较高;当不存在这类驱动时,访问ODBC接口,这时效率较低,绝大多数数据库的供应商都提供ODBC的标准数据库驱动。
l ADO是一个二进制的标准,独立于任何编程语言,只要应用程序使用的语言支持访问COM对象,就可以使用ADO部件操作数据库。

3.2 ADO接口简介
本系统要用ADO当中的三个基本接口:_ConnectionPtr接口、_CommandPtr接口和、_RecordsetPtr接口。 _ConnectionPtr接口返回一个记录集或一个空指针。通常使用它来创建一个数据连接或执行一条不返回任何结果的SQL语句,如一个存储过程。用 _ConnectionPtr接口返回一个记录集不是一个好的使用方法。通常同CDatabase一样,使用它创建一个数据连接,然后使用其它对象执行数据输入输出操作。
_CommandPtr接口返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用_CommandPtr接口时,可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。如果只执行一次或几次数据访问操作,后者是比较好的选择。但如果要频繁访问数据库,并要返回很多记录集,那么,应该使用全局_ConnectionPtr接口创建一个数据连接,然后使用 _CommandPtr接口执行存储过程和SQL语句。
_RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定,游标控制等。同_CommandPtr 接口一样,它不一定要使用一个已经创建的数据连接,可以用一个连接串代替连接指针赋给_RecordsetPtr的connection成员变量,让它自己创建数据连接。如果要使用多个记录集,最好的方法是同Command对象一样使用已经创建了数据连接的全局—ConnectionPtr接口,然后使用 _RecordsetPtr执行存储过程和SQL语句。
3.3 ADO当中的COM数据类型
在ADO当中使用的输入输出数据类型大部分是标准数据类型。但是一些参数(如:数据库字段值,出错处理信息,SQL命令等)必须使用专用的COM数据类型:VARIANT,BSTR, HRESULT,SAFEARRAY及相关的COM封装类:_variant_t, _bstr_t 和_com_error。
l VARIANT
VARIANT是一个结构,该结构的成员包含数据的类型名和数据的取值。VARIANT结构中可以存放各种各样的数据,包括编程语言的各种数据类型。如:货币,日期,所有的COM接口类型。_variant_t类封装了VARIANT数据类型,并提供了管理VARIANT的方法。在ADO接口操作当中数据库的操作都是经过_variant_t类封装的VARIANT结构来传递的。
l BSTR
BSTR是一个包含了字符串长度值及其缓冲区的结构,_bstr_t类封装了BSTR的数据结构和管理该数据的方法。我在ADO接口中字符串的传递就是通过它来传递的!
l HRESULT
在COM当中,大多数操作要返回一个HRESULT的返回值,用来测定方法调用是否成功。它是一个32位的无符号数。例如:我在进行更新多表的操作时,如果只对的某一个表进行了更新,而另外的表没有成功执行,类型库的封装程序就会检查到这个值,调用_com_issue_error()方法,抛出相应的 _com_error异常,我们就要对这个异常捕获,并进行相应的处理,否则,就会产生脏数据。

在Visual C++中用ADO进行数据库编程

1. 生成应用程序框架并初始化OLE/COM库环境

  创建一个标准的MFC AppWizard(exe)应用程序,然后在使用ADO数据库的InitInstance函数中初始化OLE/COM库(因为ADO库是一个COM DLL库)。
本例为:

 BOOL CAdotestDlg::OnInitDialog()
 {
::CoInitialize(NULL); //初始化OLE/COM库环境
}

程序最后要调用 ::CoUninitialize();//释放程序占用的COM 资源。
另外:

m_pRecordset->Close(); 注意!!!不要多次关闭!!!!!!!!!!!!
m_pConnection->Close();
m_pRecordset = NULL;
m_pConnection = NULL;

 2. 引入ADO库文件

  使用ADO前必须在工程的stdafx.h文件最后用直接引入符号#import引入ADO库文件,以使编译器能正确编译。代码如下:
#import "C:\Program Files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
  ADO类的定义是作为一种资源存储在ADO DLL(msado15.dll)中,在其内部称为类型库。类型库描述了自治接口,以及C++使用的COM vtable接口。当使用#import指令时,在运行时Visual C++需要从ADO DLL中读取这个类型库,并以此创建一组C++头文件。这些头文件具有.tli 和.tlh扩展名,读者可以在项目的目录下找到这两个文件。在C++程序代码中调用的ADO类要在这些文件中定义。
 程序的第三行指示ADO对象不使用名称空间。在有些应用程序中,由于应用程序中的对象与ADO中的对象之间可能会出现命名冲突,所以有必要使用名称空间。如果要使用名称空间,则可把第三行程序修改为: rename_namespace("AdoNS")。第四行代码将ADO中的EOF(文件结束)更名为adoEOF,以避免与定义了自己的EOF的其他库冲突。

 3.利用智能指针进行数据库操作

  在CaboutDlg头文件中定义两个ADO智能指针类实例,并在对话框中加入一个ListCtrl。
 

class CAdotestDlg : public CDialog
{
_ConnectionPtr m_pConnection;
_RecordsetPtr m_pRecordset;
   ClistCtrl m_List;
......
}

ADO库包含三个智能指针:_ConnectionPtr、_CommandPtr和_RecordsetPtr。

_ConnectionPtr通常被用来创建一个数据连接或执行一条不返回任何结果的SQL语句,如一个存储过程。
_CommandPtr返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用_CommandPtr接口时,可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。_RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定、游标控制等。

  在使用ADO程序的事件响应中OnButton1加入以下代码:
 

void CAdotestDlg::OnButton1()
{
m_List.ResetContent();
m_pConnection.CreateInstance(_uuidof(Connection)); //初始化Connection指针
m_pRecordset.CreateInstance(_uuidof(Recordset));//初始化Recordset指针

try
{
m_pConnection->Open("DSN=ADOTest","","",0); //连接叫作ADOTest的ODBC数据源
//注意:这是连接不需要用户ID或密码的open 函数
// 否则形式为 ->Open("DSN=test;uid=sa;pwd=123;","","",0);


// 执行SQL语句得到一个记录集把其指针赋值给m_pRecordset
CString strSql="select * from middle";
BSTR bstrSQL = strSql.AllocSysString();
m_pRecordset->Open(bstrSQL,(IDispatch*)m_pConnection,adOpenDynamic,adLockOptimistic,adCmdText);
//adOpenDynamic:动态 adLockOptimistic乐观封锁法 adCmdText:文本查询语句

while(!m_pRecordset->adoEOF)//遍历所有记录
{
//取纪录字段值方式之一
_variant_t TheValue; //VARIANT数据类型
TheValue = m_pRecordset->GetCollect("BIG_NAME");//得到字段BIG_NAME的值
if(TheValue.vt!=VT_NULL)
m_List.AddString((char*)_bstr_t(TheValue));
//将该值加入到列表控件中

//取纪录字段值方式之二
// _bstr_t TheValue1=m_pRecordset->Fields->GetItem("BIG_NAME")->Value;
// CString temp=TheValue1.copy();
// m_List.AddString(temp);

//数据类型转换
_variant_t vUsername,vBirthday,vID,vOld;
TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",
vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday);


m_pRecordset->MoveNext();//转到下一条纪录
}
m_pRecordset->Close();
m_pConnection->Close();
}
catch (_com_error e)//异常处理
{
AfxMessageBox(e.ErrorMessage());
}
m_pRecordset->Close(); //注意!!!不要多次关闭!!!!否则会出错
m_pConnection->Close();
m_pRecordset = NULL;
m_pConnection = NULL;
}

  程序中通过_variant_t和_bstr_t转换COM对象和C++类型的数据, _variant_t类封装了OLE自治VARIANT数据类型。在C++中使用_variant_t类要比直接使用VARIANT数据类型容易得多。

  好,编译后该程序就能运行了,但记住运行前要创建一个叫ADOTest的ODBC数据源。该程序将把表middle中的BIG_NAME字段值显示在列表控件中。



 4.执行SQL命令并取得结果记录集

为了取得结果记录集,我们定义一个指向Recordset对象的指针:_RecordsetPtr m_pRecordset;
并为其创建Recordset对象的实例: m_pRecordset.CreateInstance("ADODB.Recordset");
SQL命令的执行可以采用多种形式,下面我们一进行阐述。

(1)利用Connection对象的Execute方法执行SQL命令
Execute方法的原型如下所示:

_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options )
其中CommandText是命令字串,通常是SQL命令。

参数RecordsAffected是操作完成后所影响的行数,

参数Options表示CommandText中内容的类型,Options可以取如下值之一:
adCmdText:表明CommandText是文本命令
adCmdTable:表明CommandText是一个表名
adCmdProc:表明CommandText是一个存储过程
adCmdUnknown:未知

Execute执行完后返回一个指向记录集的指针,下面我们给出具体代码并作说明。
_variant_t RecordsAffected;
///执行SQL命令:CREATE TABLE创建表格users,users包含四个字段:整形ID,字符串username,整形old,日期型birthday
m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday DATETIME)",
&RecordsAffected,
adCmdText);
///往表格里面添加记录
m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) VALUES (1, 'Washington',25,'1970/1/1')",&RecordsAffected,adCmdText);
///将所有记录old字段的值加一
m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText);
///执行SQL统计命令得到包含记录条数的记录集
m_pRecordset = m_pConnection->Execute("SELECT COUNT(*) FROM users",&RecordsAffected,adCmdText);
_variant_t vIndex = (long)0;
_variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一个字段的值放入vCount变量
上两句可以写成— _variant_t vCount = m_pRecordset->GetCollect((_variant_t)((long)0));
m_pRecordset->Close();///关闭记录集
CString message;
message.Format("共有%d条记录",vCount.lVal);
AfxMessageBox(message);///显示当前记录条数

(2)利用Command对象来执行SQL命令

_CommandPtr m_pCommand;
m_pCommand.CreateInstance("ADODB.Command");
_variant_t vNULL;
vNULL.vt = VT_ERROR;
vNULL.scode = DISP_E_PARAMNOTFOUND;///定义为无参数
m_pCommand->ActiveConnection = m_pConnection;///非常关键的一句,将建立的连接赋值给它
m_pCommand->CommandText = "SELECT * FROM users";///命令字串
m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///执行命令,取得记录集

在这段代码中我们只是用Command对象来执行了SELECT查询语句,Command对象在进行存储过程的调用中能真正体现它的作用。下次我们将详细介绍。

(3)直接用Recordset对象进行查询取得记录集
实例——

void CGmsaDlg::OnDBSelect()
{
// TODO: Add your control notification handler code here
_RecordsetPtr Rs1; //定义Recordset对象
_bstr_t Connect("DSN=GMS;UID=sa;PWD=;");//定义连接字符串
_bstr_t Source ("SELECT count(*) FROM buaa.mdb010"); //要执行的SQL语句
::CoInitialize(NULL); //初始化Rs1对象
HRESUL hr = Rs1.CreateInstance( __uuidof( Recordset ) );
//省略对返回值hr的判断
Rs1->Open( Source,
Connect,
adOpenForwardOnly,
adLockReadOnly,
-1 );
_variant_t temp=Rs1->GetCollect(_variant_t((long)0));
CString strTemp=(char* )(_bstr_t)temp;
MessageBox("OK!"+strTemp);
}

例如

m_pRecordset->Open("SELECT * FROM users",
_variant_t((IDispatch *)m_pConnection,true),
adOpenStatic,
adLockOptimistic,
adCmdText);

Open方法的原型是这样的:
HRESULT Recordset15::Open ( const _variant_t & Source,
const _variant_t & ActiveConnection,
enum CursorTypeEnum CursorType,
enum LockTypeEnum LockType,
long Options )
其中:
①Source是数据查询字符串
②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象)
③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:
enum CursorTypeEnum
{
adOpenUnspecified = -1,///不作特别指定
adOpenForwardOnly = 0,///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用
adOpenKeyset = 1,///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。
adOpenDynamic = 2,///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。
adOpenStatic = 3///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你的记录集来说是不可见的。
};
④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:
enum LockTypeEnum
{
adLockUnspecified = -1,///未指定
adLockReadOnly = 1,///只读记录集
adLockPessimistic = 2,悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制
adLockOptimistic = 3,乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做数据的更新、插入、删除等动作
adLockBatchOptimistic = 4,乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。
};
⑤Options可以取如下值之一:
adCmdText:表明CommandText是文本命令
adCmdTable:表明CommandText是一个表名
adCmdProc:表明CommandText是一个存储过程
adCmdUnknown:未知

5. 记录集的遍历、更新
根据我们刚才通过执行SQL命令建立好的users表,它包含四个字段:ID,username,old,birthday
以下的代码实现:打开记录集,遍历所有记录,删除第一条记录,添加三条记录,移动光标到第二条记录,
更改其年龄,保存到数据库。

_variant_t vUsername,vBirthday,vID,vOld;
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance("ADODB.Recordset");
m_pRecordset->Open("SELECT * FROM users",
_variant_t((IDispatch*)m_pConnection,true),
adOpenStatic,
adLockOptimistic,
adCmdText);
while(!m_pRecordset->adoEOF)
{
vID = m_pRecordset->GetCollect(_variant_t((long)0));///取得第1列的值,从0开始计数,
///你也可以直接给出列的名称,如下一行
vUsername = m_pRecordset->GetCollect("username");///取得username字段的值
vOld = m_pRecordset->GetCollect("old");
vBirthday = m_pRecordset->GetCollect("birthday");
///在DEBUG方式下的OUTPUT窗口输出记录集中的记录
if(vID.vt != VT_NULL && vUsername.vt != VT_NULL && vOld.vt != VT_NULL && vBirthday.vt != VT_NULL)
TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n",
vID.lVal,
(LPCTSTR)(_bstr_t)vUsername,
vOld.lVal,
(LPCTSTR)(_bstr_t)vBirthday);
m_pRecordset->MoveNext();///移到下一条记录
}
m_pRecordset->MoveFirst();///移到首条记录
m_pRecordset->Delete(adAffectCurrent);///删除当前记录
///添加三条新记录并赋值
for(int i=0;i<3;i++)
{
m_pRecordset->AddNew();///添加新记录
m_pRecordset->PutCollect("ID",_variant_t((long)(i+10)));
m_pRecordset->PutCollect("username",_variant_t("叶利钦"));
m_pRecordset->PutCollect("old",_variant_t((long)71));
m_pRecordset->PutCollect("birthday",_variant_t("1930-3-15"));
}
m_pRecordset->Move(1,_variant_t((long)adBookmarkFirst));///从第一条记录往下移动一条记录,即移动到第二条记录处
m_pRecordset->PutCollect(_variant_t("old"),_variant_t((long)45));///修改其年龄
m_pRecordset->Update();///保存到库中

备注:多次查询可把查询过程做成一个函数ExecuteSQL让m_pRecordset获得连接指针m_pConnection查询结果

void ExecuteSQL(_ConnectionPtr m_pConnection, _RecordsetPtr m_pRecordset,CString strSql)
{
//执行Select 语句
BSTR bstrSQL = strSql.AllocSysString();
try
{
m_pRecordset->Open(bstrSQL,(IDispatch*)m_pConnection,adOpenDynamic,adLockOptimistic,adCmdText);
//adOpenDynamic:动态 adLockOptimistic乐观封锁法 adCmdText:文本查询语句

}

catch(_com_error error)
{
CString errorMessage;
errorMessage.Format("%s",(LPTSTR)error.Description());
AfxMessageBox(errorMessage);
}
}

//出错处理:
3127——没有找到目标表
3092——目标表已经存在
例如:
catch(const _com_error e)
{
AfxMessageBox(e.Description());
long errorCode=e.WCode();
if(3127==errorCode) AfxMessageBox("表不存在");
if(3092==errorCode) AfxMessageBox("表已经存在");
return FALSE;
}

2007年8月2日星期四

pattern match

消除Text中"<>< /a >"的等标签:
$artikel = ereg_replace("<[^<]*>", "", $artikel);