2007年4月23日月曜日

VS2005 制作安装程序

文章

内容

备注

Customizing Setup Project

in Visual Studio.NET 2005

简单介绍了setup project中的Editor,详细的介绍了File System Editor。讲解了如何向setup中添加文件、文件夹,从某个project添加和从右键菜单添加文件的区别,以及如何创建快捷方式

建议新手在阅读其他文章之前,阅读这篇文章

VS.NET安装部署深入研究

详细介绍了如何让安装程序在部署asp.net时自动设置虚拟路径等

想在安装程序时自动注册com组件、设置环境变量等,建议看看这篇文章,有一定帮助

文章以asp.net为例讲解,但不限于asp.net

Use the Visual Studio 2005

Bootstrapper to Kick-Start

Your Installation

详细介绍了bootstrapper的功能;

个人感觉Bootstrapper主要用于VS2003制作安装程序时,将.net framework1.1MDAC2.7一起制作到安装程序中,其他作用不清楚

可以跳过前面得内容,直接从Using the Bootstrapper in Visual Studio开始看

文章以概念为主,操作讲的不多

Bootstrapper Manifest Generator

一款软件,可以帮助用户方便地生成BootstrapperManifest文件

firefox可能不能正常下载

http://www.cnblogs.com/

jinglecat/archive/2006/

08/21/482287.html

一些有用的链接

2007年4月13日金曜日

Show/Hide Static Panes

Show/Hide Static Panes
Rating: none


Oleg Galkin (view profile)
August 7, 1998

MFC static splitter windows have a significant limitation. Their panes can't be hidden or shown dynamically. To solve the problem I overrode the CSplitterWnd. The new class can hide a splitter window column. Sure, it's only one approach to the problem, but it's a good example anyway.

//////////////////////////////////////////////////////////////////
//
// splitex.h
// (c) 1997, Oleg G. Galkin

class CSplitterWndEx : public CSplitterWnd
{
protected:
int m_nHidedCol; // hide column number, -1 if all columns
// are shown

public:
CSplitterWndEx();

void ShowColumn();
void HideColumn(int colHide);

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSplitterWndEx)
//}}AFX_VIRTUAL

// Generated message map functions
protected:
//{{AFX_MSG(CSplitterWndEx)
// NOTE - the ClassWizard will add and remove member
// functions here.
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

//////////////////////////////////////////////////////////////////
//
// splitex.cpp
// (c) 1997, Oleg G. Galkin

#include "stdafx.h"
#include "splitex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//////////////////////////////////////////////////////////////////
/
// CSplitterWndEx

CSplitterWndEx::CSplitterWndEx() :
m_nHidedCol(-1)
{
}

void CSplitterWndEx::ShowColumn()
{
ASSERT_VALID(this);
ASSERT(m_nCols < m_nMaxCols);
ASSERT(m_nHidedCol != -1);

int colNew = m_nHidedCol;
m_nHidedCol = -1;
int cxNew = m_pColInfo[m_nCols].nCurSize;
m_nCols++; // add a column
ASSERT(m_nCols == m_nMaxCols);

// fill the hidden column
int col;
for (int row = 0; row < m_nRows; row++)
{
CWnd* pPaneShow = GetDlgItem(
AFX_IDW_PANE_FIRST + row * 16 + m_nCols);
ASSERT(pPaneShow != NULL);
pPaneShow->ShowWindow(SW_SHOWNA);

for (col = m_nCols - 2; col >= colNew; col--)
{
CWnd* pPane = GetPane(row, col);
ASSERT(pPane != NULL);
pPane->SetDlgCtrlID(IdFromRowCol(row, col + 1));
}

pPaneShow->SetDlgCtrlID(IdFromRowCol(row, colNew));
}

// new panes have been created -- recalculate layout
for (col = colNew + 1; col < m_nCols; col++)
m_pColInfo[col].nIdealSize = m_pColInfo[col - 1].nCurSize;
m_pColInfo[colNew].nIdealSize = cxNew;
RecalcLayout();
}

void CSplitterWndEx::HideColumn(int colHide)
{
ASSERT_VALID(this);
ASSERT(m_nCols > 1);
ASSERT(colHide < m_nCols);
ASSERT(m_nHidedCol == -1);
m_nHidedCol = colHide;

// if the column has an active window -- change it
int rowActive, colActive;
if (GetActivePane(&rowActive, &colActive) != NULL &&
colActive == colHide)
{
if (++colActive >= m_nCols)
colActive = 0;
SetActivePane(rowActive, colActive);
}

// hide all column panes
for (int row = 0; row < m_nRows; row++)
{
CWnd* pPaneHide = GetPane(row, colHide);
ASSERT(pPaneHide != NULL);
pPaneHide->ShowWindow(SW_HIDE);
pPaneHide->SetDlgCtrlID(
AFX_IDW_PANE_FIRST + row * 16 + m_nCols);

for (int col = colHide + 1; col < m_nCols; col++)
{
CWnd* pPane = GetPane(row, col);
ASSERT(pPane != NULL);
pPane->SetDlgCtrlID(IdFromRowCol(row, col - 1));
}
}
m_nCols--;
m_pColInfo[m_nCols].nCurSize = m_pColInfo[colHide].nCurSize;
RecalcLayout();
}

BEGIN_MESSAGE_MAP(CSplitterWndEx, CSplitterWnd)
//{{AFX_MSG_MAP(CSplitterWndEx)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

2007年4月12日木曜日

窗口切换分割详解

原帖及讨论:http://bbs.bc-cn.net/dispbbs.asp?boardid=55&id=85673

这里写一下窗口的切换于分割。一般这里说的是单文档界面或者多文档界面的各种分割与切换。多文档的作法和单文档没有什么区别,这里就以单文档为例。在本文最后我会列一个分割对话框的例子。这部份内容不是很少,在书上查得到的我就不详细说了。

一 般常用的MFC视窗结构是文档/视窗结构(document/view architecture)。有很多人说这个结构浪费不少资源,不够节约。但我觉得作到界面这一级浪费点资源没什么太大问题。只要不漏内存,不影响效率就 已经足够好了。何况这是微软最推崇的标准界面。
文档/视窗(document/view architecture)结构主要由四个class组成。document类,view类,framework类和app类。app类是程序的引擎,在 MFC中是最不不要关心的一个类。framwork是窗口的框架,在程序运行开始的时候先生成框架,然后是document class,这里是用来存储数据的。然后是view类,用来显示数据同时作数据交换的。单文档界面只有一个document class,但可以有多了view class。至少有一个view class是active的。可以用GetActiveView()得到它的指针。 没个和document class 关联的view class都有一个control ID,这个ID是一个整数。如果总共只显示一个view class,这个class的control ID是AFX_IDW_PANE_FIRST,如果同时显示好几个view class就需要用分割器(splitter)割开。class 名字叫CSplitterWnd。CSplitterWnd有两种不同的切割framework的方式。一种叫动态的,用Create()来实现,切的很 不理想。没见过多少class用这种切法。真正应用广泛的是静态切割,用CReateStatic实现。当然从名字上就可以看出静态切割的缺点,就是不能 动态重新切分。在本文中我会介绍一个可以实现静态切割的程序。被分割器隔开的窗口的Control ID可以通过IdFromRowCol(row, col)函数得到,row和col是窗口的行数和列数。其数值也是在AFX_IDW_PANE_FIRST。也是一个比较大的数字。所以隐藏当前不想显示的view时把他的control ID改成一个1,2,3之类的很小的数就可以了。

基本知识就说这些,肯定不够详细,大家可以参照Visual C++的各种教程找到详细资料。下面开始说一些具体问题了。从单窗口开始。
1。在Framework中显示一个View。通过菜单或按钮切换成不同的view。假设有三种view: CViewA, CViewB,CViewC。用三个常数表示他们不显示时的control ID.

enum eView {ViewA, ViewB, ViewC};
在CMainFrame加上下面一个函数就可以实现不同窗口的切换了。很易懂,唯一没有说的就是CCreateContext context,这是每次Create一个view时必须设定的。其实也就是m_pCurrentDoc这个指向当前document class的指针需要设定,其它的取默认值就可以了。

void CMainFrame::SwitchToView(eView nView)
{
CView* pOldActiveView = GetActiveView();
CView* pNewActiveView = (CView*) GetDlgItem(nView);
if (pNewActiveView == NULL)
{
switch (nView)
{
case ViewA:
pNewActiveView = (CView*) new CViewA;
break;
case ViewB:
pNewActiveView = (CView*) new CViewB;
break;
case ViewC:
pNewActiveView = (CView*) new CViewC;
break;
}
CCreateContext context;
context.m_pCurrentDoc = pOldActiveView->GetDocument();
pNewActiveView->Create(NULL, NULL, WS_BORDER|WS_CHILD,
CFrameWnd::rectDefault, this, nView, &context);
pNewActiveView->OnInitialUpdate();
}

SetActiveView(pNewActiveView);
pNewActiveView->ShowWindow(SW_SHOW);
pOldActiveView->ShowWindow(SW_HIDE);
pOldActiveView->SetDlgCtrlID(m_nCurrentView);
pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
m_nCurrentView = nView;
RecalcLayout();
}

下面是这个例子的全程序:
点击下载改文件


2。显示1个,2个或4个窗口。
需要用splitter class,这里就不详细说了,任何Visual C++书上都有。无论是Dynamic的还是Static的。

3。显示1个,2个或4个窗口。同时窗口可以切换。
这里只讲静态窗口的切换,动态的效果不是很好,用户不想切的时候也会自动切。
静 态窗口的切换的效果就是Window Explorer那样,左边的目录栏一点右面就跟着变了。这里需要在已有的CSplitterWnd的基础上写一点小小的增强。需要一个切换功能。从 CSplitterWnd继承出一个class,例如叫CDynViewSplitter。

BOOL CDynViewSplitter::ReplaceView(int row, int col,CRuntimeClass * pViewClass,SIZE size)
{
CCreateContext context;
BOOL bSetActive;


// Get pointer to CDocument object so that it can be used in the creation
// process of the new view
CDocument * pDoc= ((CView *)GetPane(row,col))->GetDocument();
CView * pActiveView=GetParentFrame()->GetActiveView();
if (pActiveView==NULL || pActiveView==GetPane(row,col))
bSetActive=TRUE;
else
bSetActive=FALSE;

// set flag so that document will not be deleted when view is destroyed
pDoc->m_bAutoDelete=FALSE;
// Delete existing view
((CView *) GetPane(row,col))->DestroyWindow();
// set flag back to default
pDoc->m_bAutoDelete=TRUE;

// Create new view

context.m_pNewViewClass=pViewClass;
context.m_pCurrentDoc=pDoc;
context.m_pNewDocTemplate=NULL;
context.m_pLastView=NULL;
context.m_pCurrentFrame=NULL;

CreateView(row,col,pViewClass,size, &context);

CView * pNewView= (CView *)GetPane(row,col);

if (bSetActive==TRUE)
GetParentFrame()->SetActiveView(pNewView);

RecalcLayout();
GetPane(row,col)->SendMessage(WM_PAINT);

return TRUE;
}
这里对用完了的view是destroy掉了,处理和第一种不大一样。其它的没什么值得说的。下面是这个程序的源码:
点击下载改文件


4。 这是个以前没有想过的问题,静态窗口的重新切分,时分时合。由于有了上面两个例子结合一下就可以了。需要知道的是CSplitterWnd在最开始切分窗 口CreateStatic的时候不可以切成一行一列,也就是不切。CreateStatic一定要作真正的切割。这给整个问题带来了不少麻烦。好在 CSplitterWnd的员程序全都可以读到,只有两千多行。看一看construct之后作的事情的确很多,但desctructor很简单,所以合 并之前把自己的CSplitterWnd删掉就可以了。下面是这个例子可以在当窗口CViewA,单窗口CViewB,双窗口 CViewMenu/CViewA之间互相切换,在窗窗口的时候还可以实现右边窗口CViewA到CViewB的切换。

点击下载改文件

5。多个窗口的分割,不只1X1,1X2,2X1,2X2。可以分得十分复杂,比VC IDE上的窗口还多都可以。这时需要用多个Splitter。
6。对话框的切分,没有标准的MFC class,需要自己写一个。
5和6的例子我回头加上。

使用VC6.0实现窗口的任意分割

一、关于CSplitterWnd类
我们在使用CuteFtp或者NetAnt等工具的时候,一般都会被其复杂的界面所吸引,在这些界面中窗口被分割为若干的区域,真正做到了窗口的任意分 割。 那么我们自己如何创建类似的界面,也实现窗口的任意的分割呢 ?在VC6.0中这就需要使用到CSplitterWnd类。CSplitterWnd看上去像是一种特殊的框架窗口,每个窗口都被相同的或者不同的视图 所填充。当窗口被切分后用户可以使用鼠标移动切分条来调整窗口的相对尺寸。虽然VC6.0支持从AppWizard中创建分割窗口,但是自动加入的分割条 总是不能让我们满意,因此我们还是通过手工增加代码来熟悉这个类。
CSplitterWnd的构造函数主要包括下面三个。

BOOL Create(CWnd* pParentWnd,int nMaxRows,int nMaxCols,SIZE sizeMin,CCreateContext* pContext,DWORD dwStyle,UINT nID);
功能描述:该函数用来创建动态切分窗口。 参数含义:pParentWnd 切分窗口的父框架窗口。 nMaxRows,nMaxCols是创建的最大的列数和行数。 sizeMin是窗格的现实大小。 pContext 大多数情况下传给父窗口。 nID是字窗口的ID号.
BOOL CreateStatic(CWnd* pParentWnd,int nRows,int nCols,DWORD dwStyle,UINT nID) 
功能描述:用来创建切分窗口。 参数含义同上。
BOOL CreateView (int row,int col,CruntimeClass* pViewClass,SIZE sizeinit,CcreateContext* pContext);
功能描述:为静态切分的窗口的网格填充视图。在将视图于切分窗口联系在一起的时候必 须先将切分窗口创建好。
参数含义:同上。
从CSplitterWnd源程序可以看出不管是使用动态创建Create还是使用静态创建CreateStatic,在函数中都调用了一个保护函数 CreateCommon,从下面的CreateCommon函数中的关键代码可以看出创建CSplitterWnd的实质是创建了一系列的MDI子窗 口。
DWORD dwCreateStyle = dwStyle & ~(WS_HSCROLL|WS_VSCROLL);
if (afxData.bWin4)
dwCreateStyle &= ~WS_BORDER; //create with the same wnd-class as MDI-Frame (no erase bkgnd)
if (!CreateEx(0, _afxWndMDIFrame, NULL, dwCreateStyle,
0, 0, 0, 0,pParentWnd->m_hWnd, (HMENU)nID, NULL))
return FALSE; // create invisible

二、创建嵌套分割窗口
2.1创建动态分割窗口
动态分割窗口使用Create方法。下面的代码将创建2x2的窗格。
m_wndSplitter.Create(this,2,2,CSize(100,100),pContext);

但是动态创建的分割窗口的窗格数目不能超过2x2,而且对于所有的窗格,都必须共享同一个视图,所受的限制也比较多,因此我们不将动态创建作为重点。我们的主要精力放在静态分割窗口的创建上。
2.2创建静态分割窗口
与动态创建相比,静态创建的代码要简单许多,而且可以最多创建16x16的窗格。不同的窗格我们可以使用CreateView填充不同的视图。
在这里我们将创建CuteFtp的窗口分割。CuteFtp的分割情况如下:
CCuteFTPView
CView2
CView3
CView4

创建步骤:
▲ 在创建之前我们必须先用AppWizard生成单文档CuteFTP,生成的视类为 CCuteFTPView.同时在增加三个视类或者从视类继承而来的派生类CView2,CView3 CView4.
增加成员:
在Cmainfrm.h中我们将增加下面的代码:

CSplitterWnd wndSplitter1;
CSplitterWnd wndSplitter2;
重载CMainFrame::OnCreateClient()函数:
BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT  /*lpcs*/, CCreateContext* pContext)
{ //创建一个静态分栏窗口,分为三行一列
if(m_wndSplitter1.CreateStatic(this,3,1)==NULL)
return FALSE;
//将CCuteFTPView连接到0行0列窗格上
m_wndSplitter1.CreateView(0,0,RUNTIME_CLASS(CCuteFTPView),CSize(100,100), pContext);
m_wndSplitter1.CreateView(2,0,RUNTIME_CLASS(CView4),CSize(100,100),pContext);
//将CView4连接到0行2列
if(m_wndSplitter2.CreateStatic(&m_wndSplitter,1,2,WS_CHILD|WS_VISIBLE,
m_wndSplitter.IdFromRowCol(1, 0))==NULL)
return FALSE; //将第1行0列再分开1行2列
//将CView2类连接到第二个分栏对象的0行0列
m_wndSplitter2.CreateView(0,0,RUNTIME_CLASS(CView2),CSize(400,300),pContext);
//将CView3类连接到第二个分栏对象的0行1列
m_wndSplitter2.CreateView(0,1,RUNTIME_CLASS(CView3),CSize(400,300),pContext);
return TRUE;
}
2.3实现各个分割区域的通信
有文档相连的视图之间的通信
由AppWizard生成的CCuteFTPView是与文档相连的,同时我们也让CView2与文档相连,因此我们需要修改CCuteFTPApp的InitInstance()函数,我们将增加下面的部分。
AddDocTemplate (new CMultiDocTemplate(IDR_VIEW2TYPE,

RUNTIME_CLASS(CMainDoc),
RUNTIME_CLASS(CMDIChildWnd),
RUNTIME_CLASS(CView2)));
我们现在来实现CCuteFTPView与CView2之间的通信。由于跟文档类相连的视图类 是不能安全的与除文档类之外的其余的视图类通信的。因此我们只能让他们都与文档 类通信。在文档中我们设置相应的指针以用来获的各个视图。我们重载 CCuteFTPView::OnOpenDocument()函数;
CCuteFTPView* pCuteFTPView;
CView2* pView2;
POSITION pos;
CView* pView;
while(pos!=NULL)
{
pView=GetNextView(pos);
if(pView->IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL)
pCuteFTPView=(CCuteFTPView*)pView;
else(pView->IsKindOf(RUNTIME_CLASS(CCuteFTPView))==NULL)
pView2=(CView2*)pView;
}
这样我们在文档类中就获的了跟它相连的所有的视图的指针。
如果需要在 CCuteFTPView中调用CView2中的一个方法DoIt()则代码如下:
CCuteFTPDoc* pDoc=GetDocument();
CView2* pView2=pDoc->pView3;
pView3.DoIt();

无文档视图与文档关联视图之间的通信
CView3和CView4都是不与文档相关联的。我们现在实现CView3与CView2的通信.正如前面所说,CView2只能安全的与 CCuteFTPDoc通信,因此,CView3如果需要跟CView2通信,也必须借助于文档类。因此程序的关键是如何在CView3中获得文档的指 针。视图类中没有这样的类成员可以用来直接访问文档类。但是我们知道在主窗口类MainFrame中我们可以获得程序的任意窗口类的指针。因此我们只要获 得程序主窗口了的指针,就可以解决问题了。代码实现在CView3中访问CView2中的DoIt()方法。

CView3中的代码如下:
CMainFrame* MainFrame=(CMainFrame*)this->GetParent()->GetParent();

CCuteFTPDoc* Doc=(CCuteFTPDoc*)MainFrame->GetActiveDocument();
if(Doc!=NULL) Doc->DoIt();

CCuteFTPDoc中的相应的处理函数DoIt()代码如下:

CView2* pView2;
POSITION pos;
CView* pView;
while(pos!=NULL)
{
pView=GetNextView(pos);
if(pView->IsKindOf(RUNTIME_CLASS(CView2))==NULL)
pView2=(CView2*)pView;
}
pView2->DoIt();
无文档关联视图之间的通信
CView3和CView4都是不跟文档相连的,如何实现他们之间的通信呢。 正如我们在上面所说的那样,由于在主框架中我们可以访问任意的视图,因此我们的主要任 务还是在程序中获得主框架的指针。在CView3中访问CView4中的方法DoIt()。
CMainFrame* MainFrame=(CMainFrame*)this->GetParent()->GetParent();

CView4* View4=(CView4*)MainFrame->m_wndSplitter1.GetPane(2,0);
View4->DoIt();

到现在我们已经实现了CuteFTP的主窗口的框架并且能够实现他们之间相互通信的框架。 同样的我们可以实现其他的一些流行界面例如NetAnts,Foxmail的分割。

三、关于对话框的分割
到目前为止,只有基于文档/视图的程序才能使用CSplitterWnd,而基于对话框的应用程序却不支持CSplitterWnd,但是如果我们在继承 类中重载一些虚拟方法,也能使CSplitterWnd 在对话框程序中使用。从MFC的源程序WinSplit.cpp中可以看出,为了获得父窗口的地方程序都调用了虚拟方法GetParentFrame (),因此如果在对话框中使用,我们必须将它改为GetParent();因此我们将CSplitterWnd的下面几个方法重载。
virtual void StartTracking(int ht);
virtual CWnd* GetActivePane(int* pRow = NULL, int* pCol = NULL);
virtual void SetActivePane( int row, int col, CWnd* pWnd = NULL );
virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
virtual BOOL OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult );
virtual BOOL OnWndMsg( UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult );
具体实现如下,实现中我将给出原有代码的主要部分以及修改后的代码以作对比。
在cpp文件中加入下面的枚举类型。
enum HitTestValue
{
noHit = 0,//表示没有选中任何对象
vSplitterBox = 1,
hSplitterBox = 2,
bothSplitterBox = 3,
vSplitterBar1 = 101,//代表各个方向的水平分割条
vSplitterBar15 = 115,
hSplitterBar1 = 201,//代表垂直方向的各个分割条
hSplitterBar15 = 215,
splitterIntersection1 = 301,//代表各个交叉点
splitterIntersection225 = 525
};


CWnd* CxSplitterWnd::GetActivePane(int* pRow, int* pCol)
{
ASSERT_VALID(this);
//获得当前的获得焦点的窗口
//下面注释粗体的是原有的代码的主要部分。
// CWnd* pView = NULL;
//CFrameWnd* pFrameWnd = GetParentFrame();
//ASSERT_VALID(pFrameWnd);
//pView = pFrameWnd->GetActiveView();
//if (pView == NULL)
// pView = GetFocus();
CWnd* pView = GetFocus();
if (pView != NULL && !IsChildPane(pView, pRow, pCol))
pView = NULL;
return pView;
}

void CxSplitterWnd::SetActivePane( int row, int col, CWnd* pWnd)
{
CWnd* pPane = pWnd == NULL ? GetPane(row, col) : pWnd;
//下面加注释粗体的是原有代码的主要部分。
//FrameWnd* pFrameWnd = GetParentFrame();
//ASSERT_VALID(pFrameWnd);
//pFrameWnd->SetActiveView((CView*)pPane);
pPane->SetFocus();//修改后的语句
}

void CxSplitterWnd::StartTracking(int ht)
{
ASSERT_VALID(this);
if (ht == noHit)
return;
// GetHitRect will restrict ''''m_rectLimit'''' as appropriate

GetInsideRect(m_rectLimit);
if (ht >= splitterIntersection1 && ht <= splitterIntersection225)

{
// split two directions (two tracking rectangles)

int row = (ht - splitterIntersection1) / 15;

int col = (ht - splitterIntersection1) % 15;

GetHitRect(row + vSplitterBar1, m_rectTracker);

int yTrackOffset = m_ptTrackOffset.y;
m_bTracking2 = TRUE;
GetHitRect(col + hSplitterBar1, m_rectTracker2);

m_ptTrackOffset.y = yTrackOffset;
}
else if (ht == bothSplitterBox)
{
// hit on splitter boxes (for keyboard)
GetHitRect(vSplitterBox, m_rectTracker);
int yTrackOffset = m_ptTrackOffset.y;
m_bTracking2 = TRUE;
GetHitRect(hSplitterBox, m_rectTracker2);
m_ptTrackOffset.y = yTrackOffset; // center it
m_rectTracker.OffsetRect(0, m_rectLimit.Height()/2); m_rectTracker2.OffsetRect(m_rectLimit.Width()/2,
0);
}
else
{
// only hit one bar
GetHitRect(ht, m_rectTracker);
}

//下面加注释的将从程序中删去。
//CView* pView = (CView*)GetActivePane();
//if (pView != NULL && pView->IsKindOf(RUNTIME_CLASS(CView)))
//{
// ASSERT_VALID(pView);
// CFrameWnd* pFrameWnd = GetParentFrame();
//ASSERT_VALID(pFrameWnd);
//pView->OnActivateFrame(WA_INACTIVE, pFrameWnd);
// }
// steal focus and capture
SetCapture();
SetFocus();
// make sure no updates are pending
RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_UPDATENOW);

// set tracking state and appropriate cursor
m_bTracking = TRUE;
OnInvertTracker(m_rectTracker);
if (m_bTracking2)
OnInvertTracker(m_rectTracker2);
m_htTrack = ht;
SetSplitCursor(ht);
}

BOOL CxSplitterWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
if (CWnd::OnCommand(wParam, lParam))
return TRUE;
//下面粗体的是原程序的语句
//return GetParentFrame()->SendMessage(WM_COMMAND, wParam, lParam);

return GetParent()->SendMessage(WM_COMMAND, wParam, lParam);

}
BOOL CxSplitterWnd::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult )
{
if (CWnd::OnNotify(wParam, lParam, pResult))
return TRUE;
//下面粗体的是源程序的语句
//*pResult = GetParentFrame()->SendMessage(WM_NOTIFY,
wParam, lParam);

*pResult = GetParent()->SendMessage(WM_NOTIFY, wParam, lParam);
return TRUE;
}

BOOL CxSplitterWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
// The code line below is necessary if using CxSplitterWnd
in a regular dll
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
return CWnd::OnWndMsg(message, wParam, lParam, pResult);

}
这样我们就可以在对话框中使用CxSplitterWnd类了。

四、CSplitterWnd的扩展
CSplitterWnd扩展话题是很多的,我们可以通过对原有方法的覆盖或者增加新的方法来扩展CSplitterWnd。我们在此仅举两个方面的例子。
4.1锁定切分条
当用户创建好分割窗口后,有时并不希望通过拖动切分条来调节窗口的大小。这时就必须锁定切分条。锁定切分条的最简单的方法莫过于不让 CSplitterWnd来处理WM_LBUTTONDOWN,WM_MOUSEMOVE,WM_SETCURSOR消息,而是将这些消息交给CWnd窗 口进行处理,从而屏蔽掉这些消息。拿WM_LBUTTONDOWN处理过程来说。修改为如下:
void CXXSplitterWnd::OnLButtonDown(UINT nFlags,CPoint point)
{
CWnd::OnLButtonDown(nFlags,point);
}
其余的处理方法类似。
4.2切分条的定制
由Window自己生成的切分条总是固定的,没有任何的变化,我们在使用一些软件比如ACDSee的时候却能发现它们的切分条却是和自动生成的切分条不一 样的。那么如何定制自己的切分条呢?通过重载CSplitterWnd的虚方法OnDrawSplitter和OnInvertTracker可以达到这 样的目的。下面的代码生成的效果是分割窗口的边界颜色为红色,分割条的颜色为绿色.代码如下:
void CSplitterWndEx::OnDrawSplitter(CDC *pDC, ESplitType nType, const CRect &rectArg)
{
if(pDC==NULL)
{
RedrawWindow(rectArg,NULL,RDW_INVALIDATE|RDW_NOCHILDREN);
return;
}
ASSERT_VALID(pDC);
CRect rc=rectArg;
switch(nType)
{
case splitBorder:
//重画分割窗口边界,使之为红色
pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));
rc.InflateRect(-CX_BORDER,-CY_BORDER);
pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));

return;
case splitBox:
pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));
rc.InflateRect(-CX_BORDER,-CY_BORDER);
pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));
rc.InflateRect(-CX_BORDER,-CY_BORDER);
pDC->FillSolidRect(rc,RGB(0,0,0));
pDC->Draw3dRect(rc,RGB(0,0,0),RGB(0,0,0));
return;
case splitBar:
//重画分割条,使之为绿色
pDC->FillSolidRect(rc,RGB(255,255,255));
rc.InflateRect(-5,-5);
pDC->Draw3dRect(rc,RGB(255,0,0),RGB(255,0,0));

return;
default:
ASSERT(FALSE);
}
pDC->FillSolidRect(rc,RGB(0,0,255));
}
void CSplitterWndEx::OnInvertTracker(CRect &rect)
{
ASSERT_VALID(this);
ASSERT(!rect.IsRectEmpty());
ASSERT((GetStyle()&WS_CLIPCHILDREN)==0);
CRect rc=rect;
rc.InflateRect(2,2);
CDC* pDC=GetDC();
CBrush* pBrush=CDC::GetHalftoneBrush();
HBRUSH hOldBrush=NULL;
if(pBrush!=NULL) hOldBrush=(HBRUSH)SelectObject(pDC->m_hDC,pBrush->m_hObject);
pDC->PatBlt(rc.left,rc.top,rc.Width(),rc.Height(),BLACKNESS);

if(hOldBrush!=NULL)
SelectObject(pDC->m_hDC,hOldBrush);
ReleaseDC(pDC);
}
同样我们只要继承CSplitterWnd中的其余的一些虚拟方法就可以生成具有自己个性的分割窗口了。

2007年4月10日火曜日

IDLの言語構造一覧

下の表はIDLにおけるデータの型と扱うことのできるデータの範囲を表したもである。

C言語との大きな違いはIDLのBYTE型がC言語のunsigned charに対応しているぐらい。

なので言語構造はC言語と基本的には変らない。

配列名
範囲(10進数)
備考
BYTE byte 0~255 1byte 符号なし
INT Integer -32,768~32,767 2byte 整数
UINT Unsigned Integer 0~65533 2byte 整数
LONG 32-bit signed integer -2147483648~2147483647 4byte 整数
ULONG 32-bit unsigned integer 0~4294967295 4byte 整数
64bit LONG 64-bit signed integer -922,37203685477,808~922337203685477807
8byte 整数
64bit ULONG 64-bit unsigned integer 0~1844674,07370955,615 8byte 整数
FLOAT 32-bit, single-precision, -10^38~10^38 4byte 実数
DOUBLE 64-bit, double-precision -10^308~10^308 8byte 実数
COMPLEX A real-imaginary pair of single-precision ** 複素数
DCOMPLEX real-imaginary pair of double-precision ** 倍精度複素数

 

配列宣言関数

下表はデータを格納する配列をつくる時に宣言する関数。

例のようにA=INTARR(300,300,3)と宣言すると、XYZ平面において300*300*3の要素を持った整数型の配列Aがでる。

配列は最大8次元まで宣言することができる。

配列の限界はマシンのスペックに依存する。

関数
説明
STRARR 文字型配列の宣言 A=STARR(10,10)
BYTARR バイト型配列の宣言 **
INTARR 整数型配列の宣言 A=INTARR(300,300,3)
UUINTARR 符号なし整数型配列の宣言 **
LONARR ロングワード型配列の宣言 **
LON64ARR 64ビットロングワード型配列の宣言 **
ULONARR 符号なしロングワード型配列の宣言 **
ULON64ARR 64ビット符号なしロングワード型配列の宣言 **
FLTARR 浮動小数点型配列の宣言 A=FLTARR(10,20,30,5)
DBLARR 倍精度浮動小数点型配列の宣言 **
COMPLEXARR 複素数配列の宣言 **
DCOMPLEXARR 倍精度複素数配列の宣言 **

 

型変換関数

存在する配列の型を別の型に変換する関数。

例のように、浮動小数点型から整数型に変換すると、小数点以下は切り捨てられる。

四捨五入したいときはFIX(値+0.5)になる。

関数
説明
STRING 文字列型に変換 **
BYTE バイト型に変換 **
FIX 整数型に変換 A=1.7 → B=FIX(A) → print, B → 1
UINT 符号なし整数型に変換 **
LONG ロングワード型に変換 **
LONG64 64ビットロングワード型に変換 **
ULONG 符号なしロングワード型に変換 **
ULONG64 64ビット符号なしロングワード型に変換 **
FLOAT 浮動小数点型に変換 A=1 → B=FLOAT(A) → print, B → 1.00000
DOUBLE 倍精度浮動小数点型に変換 **
COMPLEX 複素数に変換 **
DCOMPLEX 倍精度複素数に変換 **

 

以上を必要に応じて覚えるべし。

SQLite B+树实现代码

作者:tamsyn 来源:http://supercyber.139.com/article/253784.html

这个结构一般用于数据库的索引,综合效率非常高,像 Berkerly DB , sqlite , mysql 数据库都使用了这个算法处理索引。
如果想自己做个小型数据库,可能参考一下这个算法的实现,可能会对你有所帮助。

其中的注册很详细,不用再多说了。

/* btrees.h */
/*
* 平衡多路树的一种重要方案。
* 在 1970 年由 R. Bayer 和 E. McCreight 发明。
*/
#define M 1
/* B 树的阶,即非根节点中键的最小数目。
* 有些人把阶定义为非根节点中子树的最大数目。
*/
typedef
int typekey;
typedef
struct btnode { /* B-Tree 节点 */
int d; /* 节点中键的数目 */
typekey k[
2*M]; /**/
char *v[2*M]; /**/
struct btnode *p[2*M+1]; /* 指向子树的指针 */
} node,
*btree;
/*
* 每个键的左子树中的所有的键都小于这个键,
* 每个键的右子树中的所有的键都大于等于这个键。
* 叶子节点中的每个键都没有子树。
*/

/* 当 M 等于 1 时也称为 2-3 树
* +----+----+
* | k0 | k1 |
* +-+----+----+---
* | p0 | p1 | p2 |
* +----+----+----+
*/
extern int btree_disp; /* 查找时找到的键在节点中的位置 */
extern char * InsValue; /* 与要插的键相对应的值 */

extern btree search(typekey, btree);
extern btree insert(typekey,btree);
extern btree delete(typekey,btree);
extern int height(btree);
extern int count(btree);
extern double payload(btree);
extern btree deltree(btree);
/* end of btrees.h */

/*******************************************************/

/*******************************************************/

/* btrees.c */
#include
#include
#include
"btrees.h"

btree search(typekey, btree);
btree insert(typekey,btree);
btree delete(typekey,btree);
int height(btree);
int count(btree);
double payload(btree);
btree deltree(btree);

static void InternalInsert(typekey, btree);
static void InsInNode(btree, int);
static void SplitNode(btree, int);
static btree NewRoot(btree);

static void InternalDelete(typekey, btree);
static void JoinNode(btree, int);
static void MoveLeftNode(btree t, int);
static void MoveRightNode(btree t, int);
static void DelFromNode(btree t, int);
static btree FreeRoot(btree);

static btree delall(btree);
static void Error(int,typekey);

int btree_disp; /* 查找时找到的键在节点中的位置 */
char * InsValue = NULL; /* 与要插的键相对应的值 */
static int flag; /* 节点增减标志 */
static int btree_level = 0; /* 多路树的高度 */
static int btree_count = 0; /* 多路树的键总数 */
static int node_sum = 0; /* 多路树的节点总数 */
static int level; /* 当前访问的节点所处的高度 */
static btree NewTree; /* 在节点分割的时候指向新建的节点 */
static typekey InsKey; /* 要插入的键 */

btree search(typekey key, btree t)
{
int i,j,m;
level
=btree_level-1;
while (level >= 0){
for(i=0, j=t->d-1; i t->k[m])?(i=m+1):(j=m));
if (key == t->k){
btree_disp
= i;
return t;
}
if (key > t->k) /* i == t->d-1 时有可能出现 */
i
++;
t
= t->p;
level
--;
}
return NULL;
}

btree insert(typekey key, btree t)
{
level
=btree_level;
InternalInsert(key, t);
if (flag == 1) /* 根节点满之后,它被分割成两个半满节点 */
t
=NewRoot(t); /* 树的高度增加 */
return t;
}

void InternalInsert(typekey key, btree t)
{
int i,j,m;

level
--;
if (level < 0){ /* 到达了树的底部: 指出要做的插入 */
NewTree
= NULL; /* 这个键没有对应的子树 */
InsKey
= key; /* 导致底层的叶子节点增加键值+空子树对 */
btree_count
++;
flag
= 1; /* 指示上层节点把返回的键插入其中 */
return;
}
for(i=0, j=t->d-1; i t->k[m])?(i=m+1):(j=m));
if (key == t->k) {
Error(
1,key); /* 键已经在树中 */
flag
= 0;
return;
}
if (key > t->k) /* i == t->d-1 时有可能出现 */
i
++;
InternalInsert(key, t
->p);

if (flag == 0)
return;
/* 有新键要插入到当前节点中 */
if (t->d < 2*M) {/* 当前节点未满 */
InsInNode(t, i);
/* 把键值+子树对插入当前节点中 */
flag
= 0; /* 指示上层节点没有需要插入的键值+子树,插入过程结束 */
}
else /* 当前节点已满,则分割这个页面并把键值+子树对插入当前节点中 */
SplitNode(t, i);
/* 继续指示上层节点把返回的键值+子树插入其中 */
}

/*
* 把一个键和对应的右子树插入一个节点中
*/
void InsInNode(btree t, int d)
{
int i;
/* 把所有大于要插入的键值的键和对应的右子树右移 */
for(i = t->d; i > d; i--){
t
->k = t->k[i-1];
t
->v = t->v[i-1];
t
->p[i+1] = t->p;
}
/* 插入键和右子树 */
t
->k = InsKey;
t
->p[i+1] = NewTree;
t
->v = InsValue;
t
->d++;
}
/*
* 前件是要插入一个键和对应的右子树,并且本节点已经满
* 导致分割这个节点,插入键和对应的右子树,
* 并向上层返回一个要插入键和对应的右子树
*/
void SplitNode(btree t, int d)
{
int i,j;
btree temp;
typekey temp_k;
char *temp_v;
/* 建立新节点 */
temp
= (btree)malloc(sizeof(node));
/*
* +---+--------+-----+-----+--------+-----+
* | 0 | ...... | M | M+1 | ...... |2*M-1|
* +---+--------+-----+-----+--------+-----+
* |<- M+1 ->|<- M-1 ->|
*/
if (d > M) { /* 要插入当前节点的右半部分 */
/* 把从 2*M-1 到 M+1 的 M-1 个键值+子树对转移到新节点中,
* 并且为要插入的键值+子树空出位置
*/
for(i=2*M-1,j=M-1; i>=d; i--,j--) {
temp
->k[j] = t->k;
temp
->v[j] = t->v;
temp
->p[j+1] = t->p[i+1];
}
for(i=d-1,j=d-M-2; j>=0; i--,j--) {
temp
->k[j] = t->k;
temp
->v[j] = t->v;
temp
->p[j+1] = t->p[i+1];
}
/* 把节点的最右子树转移成新节点的最左子树 */
temp
->p[0] = t->p[M+1];
/* 在新节点中插入键和右子树 */
temp
->k[d-M-1] = InsKey;
temp
->p[d-M] = NewTree;
temp
->v[d-M-1] = InsValue;
/* 设置要插入上层节点的键和值 */
InsKey
= t->k[M];
InsValue
= t->v[M];

}
else { /* d <= M */
/* 把从 2*M-1 到 M 的 M 个键值+子树对转移到新节点中 */
for(i=2*M-1,j=M-1; j>=0; i--,j--) {
temp
->k[j] = t->k;
temp
->v[j] = t->v;
temp
->p[j+1] = t->p[i+1];
}
if (d == M) /* 要插入当前节点的正中间 */
/* 把要插入的子树作为新节点的最左子树 */
temp
->p[0] = NewTree;
/* 直接把要插入的键和值返回给上层节点 */
else { /* (d /* 把节点当前的最右子树转移成新节点的最左子树 */
temp
->p[0] = t->p[M];
/* 保存要插入上层节点的键和值 */
temp_k
= t->k[M-1];
temp_v
= t->v[M-1];
/* 把所有大于要插入的键值的键和对应的右子树右移 */
for(i=M-1; i>d; i--) {
t
->k = t->k[i-1];
t
->v = t->v[i-1];
t
->p[i+1] = t->p;
}
/* 在节点中插入键和右子树 */
t
->k[d] = InsKey;
t
->p[d+1] = NewTree;
t
->v[d] = InsValue;
/* 设置要插入上层节点的键和值 */
InsKey
= temp_k;
InsValue
= temp_v;
}
}
t
->d =M;
temp
->d = M;
NewTree
= temp;
node_sum
++;
}

btree delete(typekey key, btree t)
{
level
=btree_level;
InternalDelete(key, t);
if (t->d == 0)
/* 根节点的子节点合并导致根节点键的数目随之减少,
* 当根节点中没有键的时候,只有它的最左子树可能非空
*/
t
=FreeRoot(t);
return t;
}

void InternalDelete(typekey key, btree t)
{
int i,j,m;
btree l,r;
int lvl;

level
--;
if (level < 0) {
Error(
0,key); /* 在整个树中未找到要删除的键 */
flag
= 0;
return;
}
for(i=0, j=t->d-1; i t->k[m])?(i=m+1):(j=m));
if (key == t->k) { /* 找到要删除的键 */
if (t->v != NULL)
free(t
->v); /* 释放这个节点包含的值 */
if (level == 0) { /* 有子树为空则这个键位于叶子节点 */
DelFromNode(t,i);
btree_count
--;
flag
= 1;
/* 指示上层节点本子树的键数量减少 */
return;
}
else { /* 这个键位于非叶节点 */
lvl
= level-1;
/* 找到前驱节点 */
r
= t->p;
while (lvl > 0) {
r
= r->p[r->d];
lvl
--;
}
t
->k=r->k[r->d-1];
t
->v=r->v[r->d-1];
r
->v[r->d-1]=NULL;
key
= r->k[r->d-1];
}
}
else if (key > t->k) /* i == t->d-1 时有可能出现 */
i
++;
InternalDelete(key,t
->p);
/* 调整平衡 */
if (flag == 0)
return;
if (t->p->d < M) {
if (i == t->d) /* 在最右子树中发生了删除 */
i
--; /* 调整最右键的左右子树平衡 */
l
= t->p;
r
= t->p[i+1];
if (r->d > M)
MoveLeftNode(t,i);
else if(l->d > M)
MoveRightNode(t,i);
else {
JoinNode(t,i);
/* 继续指示上层节点本子树的键数量减少 */
return;
}
flag
= 0;
/* 指示上层节点本子树的键数量没有减少,删除过程结束 */
}
}

/*
* 合并一个节点的某个键对应的两个子树
*/
void JoinNode(btree t, int d)
{
btree l,r;
int i,j;
l
= t->p[d];
r
= t->p[d+1];

/* 把这个键下移到它的左子树 */
l
->k[l->d] = t->k[d];
l
->v[l->d] = t->v[d];
/* 把右子树中的所有键值和子树转移到左子树 */
for (j=r->d-1,i=l->d+r->d; j >= 0 ; j--,i--) {
l
->k = r->k[j];
l
->v = r->v[j];
l
->p = r->p[j];
}
l
->p[l->d+r->d+1] = r->p[r->d];
l
->d += r->d+1;
/* 释放右子树的节点 */
free(r);
/* 把这个键右边的键和对应的右子树左移 */
for (i=d; i < t->d-1; i++) {
t
->k = t->k[i+1];
t
->v = t->v[i+1];
t
->p[i+1] = t->p[i+2];
}
t
->d--;
node_sum
--;
}
/*
* 从一个键的右子树向左子树转移一些键,使两个子树平衡
*/
void MoveLeftNode(btree t, int d)
{
btree l,r;
int m; /* 应转移的键的数目 */
int i,j;
l
= t->p[d];
r
= t->p[d+1];
m
= (r->d - l->d)/2;

/* 把这个键下移到它的左子树 */
l
->k[l->d] = t->k[d];
l
->v[l->d] = t->v[d];
/* 把右子树的最左子树转移成左子树的最右子树
* 从右子树向左子树移动 m-1 个键+子树对
*/
for (j=m-2,i=l->d+m-1; j >= 0; j--,i--) {
l
->k = r->k[j];
l
->v = r->v[j];
l
->p = r->p[j];
}
l
->p[l->d+m] = r->p[m-1];
/* 把右子树的最左键提升到这个键的位置上 */
t
->k[d] = r->k[m-1];
t
->v[d] = r->v[m-1];
/* 把右子树中的所有键值和子树左移 m 个位置 */
r
->p[0] = r->p[m];
for (i=0; id-m; i++) {
r
->k = r->k[i+m];
r
->v = r->v[i+m];
r
->p = r->p[i+m];
}
r
->p[r->d-m] = r->p[r->d];
l
->d+=m;
r
->d-=m;
}
/*
* 从一个键的左子树向右子树转移一些键,使两个子树平衡
*/
void MoveRightNode(btree t, int d)
{
btree l,r;
int m; /* 应转移的键的数目 */
int i,j;
l
= t->p[d];
r
= t->p[d+1];

m
= (l->d - r->d)/2;
/* 把右子树中的所有键值和子树右移 m 个位置 */
r
->p[r->d+m]=r->p[r->d];
for (i=r->d-1; i>=0; i--) {
r
->k[i+m] = r->k;
r
->v[i+m] = r->v;
r
->p[i+m] = r->p;
}
/* 把这个键下移到它的右子树 */
r
->k[m-1] = t->k[d];
r
->v[m-1] = t->v[d];
/* 把左子树的最右子树转移成右子树的最左子树 */
r
->p[m-1] = l->p[l->d];
/* 从左子树向右子树移动 m-1 个键+子树对 */
for (i=l->d-1,j=m-2; j>=0; j--,i--) {
r
->k[j] = l->k;
r
->v[j] = l->v;
r
->p[j] = l->p;
}
/* 把左子树的最右键提升到这个键的位置上 */
t
->k[d] = l->k;
t
->v[d] = l->v;
l
->d-=m;
r
->d+=m;
}
/*
* 把一个键和对应的右子树从一个节点中删除
*/
void DelFromNode(btree t, int d)
{
int i;
/* 把所有大于要删除的键值的键左移 */
for(i=d; i < t->d-1; i++) {
t
->k = t->k[i+1];
t
->v = t->v[i+1];
}
t
->d--;
}
/*
* 建立有两个子树和一个键的根节点
*/
btree NewRoot(btree t)
{
btree temp;
temp
= (btree)malloc(sizeof(node));
temp
->d = 1;
temp
->p[0] = t;
temp
->p[1] = NewTree;
temp
->k[0] = InsKey;
temp
->v[0] = InsValue;
btree_level
++;
node_sum
++;
return(temp);
}
/*
* 释放根节点,并返回它的最左子树
*/
btree FreeRoot(btree t)
{
btree temp;
temp
= t->p[0];
free(t);
btree_level
--;
node_sum
--;
return temp;
}

void Error(int f,typekey key)
{
if (f)
printf(
"Btrees error: Insert %d! ",key);
else
printf(
"Btrees error: delete %d! ",key);
}

int height(btree t)
{
return btree_level;
}

int count(btree t)
{
return btree_count;
}
double payload(btree t)
{
if (node_sum==0)
return 1;
return (double)btree_count/(node_sum*(2*M));
}
btree deltree (btree t)
{
level
=btree_level;
btree_level
= 0;
return delall(t);

}
btree delall(btree t)
{
int i;
level
--;
if (level >= 0) {
for (i=0; i < t->d; i++)
if (t->v != NULL)
free(t
->v);
if (level > 0)
for (i=0; i<= t->d ; i++)
t
->p=delall(t->p);
free(t);
}
return NULL;
}

/* end of btrees.c */