2009年3月27日金曜日

sap FI/CO 学习指引

sap FI/CO 学习指引
1为什么要学FI/CO
SAP系统的核心是什么?也许没有唯一准确的答案。但是,无论从SAP的发展史还是从企业管理的业务实践看,可以肯定的说,FI/CO在SAP中居于核心地位。要学好SAP Functional Modules就必须先学FI/CO,它体现了典型的SAP Concept原设计者的模式。
对于现在的一些没有学过fico的,或者不懂fico的functional modules consultant,其中有很多人业绩很不错,他们的成长过程自然也有一定的社会原因,所谓存在就是合理的。从北美看,由于这里是完全自由的 Market Economy,人人都有同等的机会从事任何工作,而对所做工作的质量则有所保留,这就出现了没有学习FI/CO 就开始学习MM,SD, PP……等模块的现象。这样直接导致了这些人只懂一段Application,根本不了解为什麽是这样,原设计的背景是什麽。这样,无疑会影响他们的工作,影响对客户的价值创造。但是老板们为 了降低成本,对team里二级层次的Functional Modules的人员不做过多的要求,依然雇佣。因为他们相信,在他们自己或核心经理这些懂得FI/CO的人员指导下,团队也可以完成FI/CO, MM, SD, PP……等模块的实施项目。

然而,如果我们要对sap顾问学习成长最佳理念和路径展开分析和研究,还是要遵循理性和科学的精神,探讨应该是什么和如何是。

FI/CO在SAP中居于核心地位,要达到对SAP系统融会贯通的理解必须理解FI/CO。而且,从雇佣方面看,是否具有FICO知识直接影响你在面试时的表现。在面试中没有一定的财务分析技能是很难通过的。而这些技能的基础 是掌握SAP系统的核心领域FI/CO。在公司中真正懂SAP Functionalities的人是绝对有FI/CO知识的,只是多少而已。特别是做为Supervisor/Manager更清楚这一点。

2.学好FI是作为学习CO的基础
为什么学好CO就必须先学好FI?因为企业的数据首先从外部进来,然后由FI“织出”一件华丽的外衣给外界看。之后最原始的FI数据则进入了CO,由CO按不同的Management Concept合成为不同的企业解决方案。
FI 的课程或者说业务可以说涵盖了一般企业的功能区,从General ledger到Account Payable, Account Receivable,从Treasury到Profit and Loss……等等,要真正将FI弄透弄明白非要下一番功夫不可,而无论是FI的Configuring Job或 End User Job, 除了要有关于Table 的知识之外还要有很强的Business 知识。 要想学好FI/CO, 最合适的是具有那种复合型的知识背景者,既懂财会又懂IT知识,那是做Configuring工作和End User工作的基础。如果学习者不学习这两种知识不具备这两种知识或不能有效的将两者结合在一起,则可能最后只懂得一些表面上的东西,更难考取证书,如果考取也只是侥幸,不能在实践中最大化的发挥它的价值。

3.CO是精华之精华

据资深顾问讲,CO是核心的核心。
何以这样呢?
综览SAP系统,其它众多的模块,无论功能多么强大,业务如复杂,从最终的价值体现看其实都是为CO而产生的。没有管理,企业则大乱;没有 CO,企业则无管理可言。SAP就是为了搞好企业管理而设计的,你说是不是其它所有的模块都围着它转?例如,MM, SD, PP……等的信息进入FI, 再由FI进入CO, 最后由CO深入加工汇总企业整体各个流程的财务管理信息, 并在此基础上进行分析决策。可以想象,如果CO学不好,则不能掌握企业的Management Concept,可以说根本就做不了任何FI/CO的Configuration。CO是将FI和企业的Management,以及你的 Consultant职业连接起来的工具。
这一点,在SAP的培训体系中也有所体现。
在CO的课程结构里,德国的原设计者们,是从Cost Element Accounting 入手,然后到Cost Center Accounting,直到Internal Order Accounting和Profit Centre Accounting。内容之庞大,关系之复杂,非一般的人所能想象。往深里讲,这种内容体系和设计思路,它集中体现了德意志的Management文化理念。特别 是Internal Order Accounting,是CO精华之一。


要想将CO弄透,无论是针对Configuring Job或End User Job,需要极强的Controlling 知识。因为Configuring 是和Business紧密相连的,他是客户的Business要求在SAP系统中的实施和再现。所以说Table Configuring只是其中一个小小环节而已。如果不将Business和IT知识有效的结合在一起,还是上面提到的那句话,学了以后,可能连基本的知识都不能掌握,更不用 说考取证书了。


4.如何才能学好FI/CO,并考取证书


其一,学习者要结合自身的知识背景、特点和需求来学。如果你是 Engineer出身,那么本身的综合素质比较不错,所以能学得很不错,但FI知识相对就弱些,在学FI时要尽可能的将将有关会计的部分吃透,甚至要掌握内容远比SAP FI所要求的要多,这样才能打下好的基础。
其二,这里先说说FI/CO考证的 结构。CO占了考题的一半以上,FI则是一半不到。由于FI和会计法规联系较紧密,可能比较规范,需要Engineer们投入一定的时间和精力去了解、理解和掌握。但是,由于CO是真 正的打造企业的Management,相对来说可以有些“随心所欲”,所以CO反而给予了较大的发展的空间,这个应该是有利于engineer们的学习的。

如果是Engineer出身,在学习FI时可以从G/L (General Ledge) 入手,然后走向A/P,A/R。可以这么说,几乎所有的FI End User 和FI Project都以他们做为基础的,这三样都不会做,就无从做FI的Configuring了;对学习CO来说,可以从CEA (Cost Element Accounting)入手,然后到CCA (Cost Center Accounting),致于Internal Order Accounting则可以放在后面,因为它是CO精华中的精华。由于CO与会计法规联系较少,它可以作为Engineer们的突破口,并 且在考试中它是重点的重点。

在学习中,还要多向高手学习,如果学习中能得到有实际工作经验及学术理论水平的高手或老师的指导,将大大加快学习进度提高学习效果。
另外,真实的环境能使你的学习 快捷有效。

SAP模块顺口溜及各模块介绍

SAP模块顺口溜及各模块介绍

sap是庞大的,模块是多多的,功能是强大的,搞懂是没门的。
sd是灵巧的,五脏是俱全的,满足是不能的,报表是经常的。
pp是复杂的,相同是很少的,mrp是要的,精确是不能的。
mm是重要的,数据是多多的,做好是稀有的,目前是紧缺的。
fi是核心的,记账是主要的,工作是轻松的,地位是高高的。
co是控制的,与fi是配合的,凭证是很多的,成本是不准的。
abap是必须的,开发是经常的,地位是没有的,作用是点缀的。
basis是装机的,debug是常有的,精通是困难的,abap是兼职的。
HR是搞人的,会作是很少的,研究是需要的,潜力是无穷的。
workflow是神奇的,功能是炫目的,做通是很少的,因而是不做的。
qm是质量的,上的是不多的,思路是奇特的,冲突是必然的。
pm是见过的,功能是明显的,做做是蛮好的,培训是需要的。
apo是传说的,上的是没有的,目标是理想的,成功是偶然的。
crm是起步的,客户是听说的,用好是没有的,完善是需要的。
bw是早有的,产品是多样的,需求是渐多的,招人是必要的。
市场是巨大的,erp是需要的,签单是可能的,打折是一定的。
kick off是要有的,首期是会付的,蓝图是要做的,确认是艰苦的。
实施是痛苦的,修改是经常的,说服是需要的,项目是继续的。
数据是庞大的,整理是艰苦的,手输是不能的,batch是要编的。
客户是刁蛮的,要求是无理的,说话是牛的,干活是不行的。
key user是难做的,加班是经常的,工资是不多的,衰老是优先的。
上线是被动的,不上是不行的,时间是紧张的,恐惧是不必的。


SAP系统包含大量模块,这些模块共同发挥作用来执行公司的业务管理任务。简单的说是将企业的三大流:物流,资金流,信息流进行全面一体化管理的管理信息系统。在企业中,这三大系统本身就是集成体,它们互相之间有相应的接口,能够很好的整合在一起来对企业进行管理。在管理功能上,它共有12个系统模块:

(1)财务会计模块(FI),它可提供应收、应付、总账、合并、投资、基金、现金管理等功能,这些功能可以根据各分支机构的需要来进行调整,并且往往是多语种的。同时,科目的设置会遵循任何一个特定国家中的有关规定。

(2)管理会计模块(CO),它包括利润及成本中心、产品成本、项目会计、获利分析等功能,它不仅可以控制成本,还可以控制公司的目标,另外还提供信息以帮助高级管理人员作出决策或制定规划。

(3)资产管理模块(AM),具有固定资产、技术资产、投资控制等管理功能。

(4)销售与分销模块(SD),其中包括销售计划、询价报价、订单管理、运输发货、发票等的管理,同时可对分销网络进行有效的管理。



(5)物料管理模块(MM),主要有采购、库房与库存管理、MRP、供应商评价等管理功能。

(6)生产计划模块(PP),可实现对工厂数据、生产计划、MRP、能力计划、成本核算等的管理,使得企业能够有效的降低库存,提高效率。同时各个原本分散的生产流程的自动连接,也使得生产流程能够前后连贯的进行,而不会出现生产脱节,耽误生产交货时间。

(7)质量管理模块(QM),可提供质量计划、质量检测、质量控制、质量文档等功能。

(8)工厂维修模块,可提供维护及检测计划、交易所处理、历史数据、报告分析。

(9)人力资源模块(HR),其中包括:薪资、差旅、工时、招聘、发展计划、人事成本等功能。

(10) 项目管理模块(PS),具有项目计划、项目预算、能力计划、资源管理、结果分析等功能。

(11) 工作流管理(WF),可提供工作定义、流程管理、电子邮件、信息传送自动化等功能。

(12) 行业解决方案(IS),可针对不同的行业提供特殊的应用和方案。这些功能覆盖了企业供应链上的所有环节,能帮助企业实现整体业务经营运作的管理和控制。

Visual C++(包含Windows API和MFC)开发中常见问题的答案

Visual C++(包含Windows API和MFC)开发中常见问题的答案

1,简述VC6下如何进行程序的调试。

在主菜单"Build"中,有一个Start Build的子菜单,它下面包含了Go菜单(快捷键为F5),选择后,程序将从当前语句进入调试运行,直到遇到断点或程序结束。

将鼠标移动到要调试的代码行,单击鼠标右键选择“Insert/Remove Breakpoint”,或者按下F9,可以在该行上添加断点,此时断点代码行前面出现一个棕色的圈,再次选择将清除断点。进入调试状态后,Debug菜单将取代Build菜单出现在菜单栏中,它下面包含常用的调试操作,如Step Over,单步运行并不跟踪到调用的函数内部;其他还包括Step Into,Step Out, Stop Debugging等调试方法。



2, 简述在VC6建立的工程中后缀为.cpp,.h,.rc,.dsp,.dsw的文件的作用是什么?

.cpp是源程序代码C++文件

.h是包含函数声明和变量定义的头文件

.rc是定义资源的资源脚本文件

.dsp是工程文件,记录当前工程的有关信息

.dsw是工作区文件,一个工作区可能包含一个或多个工程



3, 已知一个对话框上有一个编辑框控件,ID为IDC_EDIT1,为其关联了CEdit类型的变量m_edit1,使用两种方法,说明如何改变编辑框内部的文本为"Hello",写出程序代码的片断。

第一种方法:m_edit1.SetSel(0,-1);

m_edit1.ReplaceSel("Hello");

第二种方法:SetWindowText("Hello");



4, 简述使用Windows API编写的一个基本的Windows应用程序框架的结构。

Windows API编写的基本应用程序框架至少应该包含程序入口函数WinMain和窗口函数WndProc。在主函数WinMain里面包含窗口类的定义和注册,窗口的创建和显示以及消息循环。



5, 消息在Windows中的数据类型是什么,它有哪些成员变量,各有什么含义

消息的数据类型是MSG,它是一个结构体,其成员变量主要包括hwnd,表示消息的窗口句柄;message代表消息的类型;wParam和lParam包含消息的附加信息,随不同的消息有所不同。



6, Windows的鼠标消息的长参数lParam与字参数wParam的含义是什么

鼠标消息的长参数lParam的低字节包含了鼠标光标位置的x坐标值,lParam的高字节包含了鼠标光标位置的y坐标值;字参数wParam内包含了指示当前按下的各种虚键状态的值。



7, 说明使用一个非模态对话框的注意问题和用到的Windows API函数

使用一个非模态对话框应该注意一定要在样式中包含WS_VISIBLE才能正常显示;创建对话框使用CreateDialog函数;消息循环部分应该使用IsDialogMessage过滤消息;关闭对话框使用函数DestroyWindow。



8, 简述在MFC应用程序中UpdateData函数的作用及其参数含义与使用场合。

UpdateData只有一个BOOL类型的参数,UpdateData(FALSE)一般用于对话框控件连接的变量值刷新屏幕显示;UpdateData(TRUE)用于获取屏幕数据到对话框控件连接的变量中。



9, 列举列表框控件能够接受的三个消息类型,并说明其作用

LB_ADDSTRING用于在列表框中加入一项字符串;LB_DIR用于在列表框中列出指定文件;LB_GETTEXT用于获取指定项的文本。



10, 在一个对话框上添加了三个单选按钮,要使它们之间自动实现互斥,应该注意什么问题,在VC环境下如何操作?

要实现一组单选按钮的自动互斥,应该让它们的控件ID值连续,并设置第一个单选按钮的Group属性,其他的不设。



11, 简述由一个文档类派生自己的文档类,并实现文档的存取需要哪些步骤。

首先为每一个文档类型从CDocument派生一个相应的文档类;然后为该文档类添加成员变量以保存数据;最后重载Serialize成员函数以实现文档数据的串行化。



12, 列举视图类(CView)的三个子类,并简要说明其作用。

CScrollView类提供视图的滚动显示;CEditView类支持在视图中的文本编辑操作;CHtmlView类支持在视图中显示和操作html文件。



13, Visual C++ 6.0如何进入调试状态,在调试状态下能够显示哪些调试窗口,列举三个,其作用分别是什么?

启动调试后,在View菜单的Debug Window子菜单下可以打开一些辅助调试的窗口

Watch:显示察看当前语句和前面语句中变量值的窗口

Call Stack:显示察看调用堆栈的窗口

Memory:显示察看内存中内容的窗口



14, 说明位图资源的创建及显示过程的步骤,并给出相应的Windows API函数名。

首先定义位图句柄HBITMAP hBitmap;第二步使用LoadBitMap加载位图;第三步,调用CreateCompatibleDC向系统申请内存设备环境句柄,并调用函数SelectObject把位图选入内存设备环境;第四步,调用BitBlt函数将位图从内存设备环境输出到指定的窗口设备环境中,从而实现显示位图。



15, 如何获取字体句柄从而实现字体的输出,并给出相应的Windows API函数名。

首先定义字体句柄变量HFONT hF;然后调用函数GetStockObject获取系统的字体句柄,或者调用CreateFont得到自定义的字体句柄;最后调用SelectObject把字体句柄选入设备环境。



16, 列举三种按钮的类型,并说明其作用和创建方法之间的不同之处。

常用的按钮有普通按钮、单选按钮、复选框,和组框。普通按钮作用是帮助用户触发指定动作;单选按钮一般各选项之间存在互斥性;复选框用来显示一组选项供用户选择,各选项之间不存在互斥;组框主要用于把控件分成不同的组并加以说明.



17, 要使一个静态控件显示一个位图并能接受用户输入,应该注意什么问题。

要使静态控件显示位图,必须设定其风格包含SS_BITMAP,并在创建静态控件窗口,即调用CreateWindow时指定并加载位图;要使静态控件能够接收用户输入,必须设定其风格包含SS_NOTIFY。



18, 列举滚动条控件的四种类型的动作标识,并说明其发生的场合。

常用的滚动条控件的动作标识包括(对于垂直滚动条):SB_LINEUP表示向上滚动一行;SB_LINEDOWN表示向下滚动一行;SB_PAGEUP表示向上滚动一页;SB_PAGEDOWN表示向下滚动一页。



19,说明使系统定时器消息(WM_TIMER)的使用方法及其用到的Windows API函数

使用定时器消息的方法是:首先调用SetTimer函数定义定时器消息,包括消息产生的时间间隔等;然后在相应的WM_TIMER消息处理里添加定时器消息响应代码;最后调用KillTimer释放该定时器。



20,MFC应用程序向导能够创建那几种类型的应用程序框架,哪些采用了文档/视图结构。

MFC应用程序向导能建立基于单文档(SDI),基于多文档(MDI)和基于对话框(Dialog Based)三种应用程序的框架。其中前两种采用了文档/视图结构。



21,列举五种控件,说明其作用和MFC对应的类名

CStatic是静态文本控件窗口,用于标注、分隔对话框或窗口中的其他控件;CButton是按钮控件窗口,为对话框或窗口中的按钮、单选按钮和多选按钮等提供一个总的类;CScrollBar是滚动条控件窗口,提供滚动条的功能,用于在对话框或窗口中的一个控件,通过它在某一范围内定位;CListBox是列表框控件窗口,列表框用于显示一组列表项,用户可以进行观察和选择;CProgressCtrl是进度条控件窗口,用于指示一个操作的进度。



22,MFC的文档/视图结构中说明视图类如何访问文档类,文档类如何通知视图类进行更新,给出成员方法名?

MFC的文档/视图结构中,视图类通过其成员方法GetDocument获得对应文档类的指针,从而访问文档类的数据;文档类通过其成员方法UpdateAllViews通知所有视图,文档已经被修改,视图应该被重画。



23,简述在一个基于对话框的MFC应用程序框架中添加一个编辑框(编辑框的控件ID是IDC_EDIT1,已经为其连接了变量m_edit1),要求在其中动态显示示当前时间,时间格式为“HH:mm:ss",如"15:20:16",每一秒钟刷新一次,如何实现,给出代码片断。(提示:使用定时器SetTimer)

实现方法:实现定时器,每隔一秒钟发出WM_TIMER消息,并在该消息相应函数中添加代码更新编辑框内容。

第一步:为对话框的WM_INITDIALOG的消息响应函数OnInitDialog中添加代码: SetTimer(1,100,NULL);

第二步: 为对话框添加WM_TIMER的消息相应函数OnTimer,并在其中添加代码:

CTime tNow;

tNow=CTime::GetCurrentTime();

CString sNow=tNow.Format("%I:%M:%S");

m_edit1.SetSel(0,-1);

m_edit1.ReplaceSel(sNow);

第二步: 为对话框的WM_DESTRYOY的消息响应函数OnDestroy中添加代码 KillTimer(1);


标签集:TAGS:

const用法小结

const用法小结
前两天看代码的时候,发现很奇怪的const用法,于是在网上搜了一下,原来有这么多用法,不敢独享,拿上来和与我一样菜的小生们学习~

1. const常量,如const int max = 100;
优点:const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误(边际效应)

2. const 修饰类的数据成员。如:
class A

{

const int size;



}

const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类声明中初始化const数据成员,因为类的对象未被创建时,编译器不知道const 数据成员的值是什么。如

class A

{

const int size = 100; //错误

int array[size]; //错误,未知的size

}

const数据成员的初始化只能在类的构造函数的初始化表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现。如

class A

{…

enum {size1=100, size2 = 200 };

int array1[size1];

int array2[size2];

}

枚举常量不会占用对象的存储空间,他们在编译时被全部求值。但是枚举常量的隐含数据类型是整数,其最大值有限,且不能表示浮点数。

3. const修饰指针的情况,见下式:

int b = 500;
const int* a = & [1]
int const *a = & [2]
int* const a = & [3]
const int* const a = & [4]

如果你能区分出上述四种情况,那么,恭喜你,你已经迈出了可喜的一步。不知道,也没关系,我们可以参考《Effective c++》Item21上的做法,如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。因此,[1]和[2]的情况相同,都是指针所指向的内容为常量(const放在变量声明符的位置无关),这种情况下不允许对内容进行更改操作,如不能*a = 3 ;[3]为指针本身是常量,而指针所指向的内容不是常量,这种情况下不能对指针本身进行更改操作,如a++是错误的;[4]为指针本身和指向的内容均为常量。

4. const的初始化

先看一下const变量初始化的情况
1) 非指针const常量初始化的情况:A b;
const A a = b;

2) 指针const常量初始化的情况:

A* d = new A();
const A* c = d;
或者:const A* c = new A();
3)引用const常量初始化的情况:
A f;
const A& e = f; // 这样作e只能访问声明为const的函数,而不能访问一

般的成员函数;

[思考1]: 以下的这种赋值方法正确吗?
const A* c=new A();
A* e = c;
[思考2]: 以下的这种赋值方法正确吗?
A* const c = new A();
A* b = c;

5. 另外const 的一些强大的功能在于它在函数声明中的应用。在一个函数声明中,const 可以修饰函数的返回值,或某个参数;对于成员函数,还可以修饰是整个函数。有如下几种情况,以下会逐渐的说明用法:A& operator=(const A& a);
void fun0(const A* a );
void fun1( ) const; // fun1( ) 为类成员函数
const A fun2( );

1) 修饰参数的const,如 void fun0(const A* a ); void fun1(const A& a);
调用函数的时候,用相应的变量初始化const常量,则在函数体中,按照const所修饰的部分进行常量化,如形参为const A* a,则不能对传递进来的指针的内容进行改变,保护了原指针所指向的内容;如形参为const A& a,则不能对传递进来的引用对象进行改变,保护了原对象的属性。
[注意]:参数const通常用于参数为指针或引用的情况,且只能修饰输入参数;若输入参数采用“值传递”方式,由于函数将自动产生临时变量用于复制该参数,该参数本就不需要保护,所以不用const修饰。

[总结]对于非内部数据类型的输入参数,因该将“值传递”的方式改为“const引用传递”,目的是为了提高效率。例如,将void Func(A a)改为void Func(const A &a)

对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x)不应该改为void Func(const int &x)

2) 修饰返回值的const,如const A fun2( ); const A* fun3( );
这样声明了返回值后,const按照"修饰原则"进行修饰,起到相应的保护作用。const Rational operator*(const Rational& lhs, const Rational& rhs)
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
}

返回值用const修饰可以防止允许这样的操作发生:Rational a,b;
Radional c;
(a*b) = c;

一般用const修饰返回值为对象本身(非引用和指针)的情况多用于二目操作符重载函数并产生新对象的时候。
[总结]

1. 一般情况下,函数的返回值为某个对象时,如果将其声明为const时,多用于操作符的重载。通常,不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。原因如下:如果返回值为某个对象为const(const A test = A 实例)或某个对象的引用为const(const A& test = A实例) ,则返回值具有const属性,则返回实例只能访问类A中的公有(保护)数据成员和const成员函数,并且不允许对其进行赋值操作,这在一般情况下很少用到。

2. 如果给采用“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。如:

const char * GetString(void);

如下语句将出现编译错误:

char *str=GetString();

正确的用法是:

const char *str=GetString();

3. 函数返回值采用“引用传递”的场合不多,这种方式一般只出现在类的赙值函数中,目的是为了实现链式表达。如:

class A

{…

A &operate = (const A &other); //负值函数

}
A a,b,c; //a,b,c为A的对象



a=b=c; //正常

(a=b)=c; //不正常,但是合法

若负值函数的返回值加const修饰,那么该返回值的内容不允许修改,上例中a=b=c依然正确。(a=b)=c就不正确了。
[思考3]: 这样定义赋值操作符重载函数可以吗?
const A& operator=(const A& a);

6. 类成员函数中const的使用
一般放在函数体后,形如:void fun() const;
任何不会修改数据成员的函数都因该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其他非const成员函数,编译器将报错,这大大提高了程序的健壮性。如:

class Stack

{

public:

void Push(int elem);

int Pop(void);

int GetCount(void) const; //const 成员函数

private:

int m_num;

int m_data[100];

};

int Stack::GetCount(void) const

{

++m_num; //编译错误,企图修改数据成员m_num

Pop(); //编译错误,企图调用非const函数

Return m_num;

}

7. 使用const的一些建议

1 要大胆的使用const,这将给你带来无尽的益处,但前提是你必须搞清楚原委;
2 要避免最一般的赋值操作错误,如将const变量赋值,具体可见思考题;
3 在参数中使用const应该使用引用或指针,而不是一般的对象实例,原因同上;
4 const在成员函数中的三种用法(参数、返回值、函数)要很好的使用;
5 不要轻易的将函数的返回值类型定为const;
6除了重载操作符外一般不要将返回值类型定为对某个对象的const引用;

[思考题答案]
1 这种方法不正确,因为声明指针的目的是为了对其指向的内容进行改变,而声明的指针e指向的是一个常量,所以不正确;
2 这种方法正确,因为声明指针所指向的内容可变;
3 这种做法不正确;
在const A::operator=(const A& a)中,参数列表中的const的用法正确,而当这样连续赋值的时侯,问题就出现了:
A a,b,c:
(a=b)=c;
因为a.operator=(b)的返回值是对a的const引用,不能再将c赋值给const常量。
给C++初学者的50个忠告
1.把C++当成一门新的语言学习(和C没啥关系!真的。);
  2.看《Thinking In C++》,不要看《C++变成死相》;
  3.看《The C++ Programming Language》和《Inside The C++ Object Model》,不要因为他们很难而我们自己是初学者所以就不看;
  4.不要被VC、BCB、BC、MC、TC等词汇所迷惑——他们都是集成开发环境,而我们要学的是一门语言;
  5.不要放过任何一个看上去很简单的小编程问题——他们往往并不那么简单,或者可以引伸出很多知识点;
  6.会用Visual C++,并不说明你会C++;
  7.学class并不难,template、STL、generic programming也不过如此——难的是长期坚持实践和不遗余力的博览群书;
  8.如果不是天才的话,想学编程就不要想玩游戏——你以为你做到了,其实你的C++水平并没有和你通关的能力一起变高——其实可以时刻记住:学C++是为了编游戏的;
  9.看Visual C++的书,是学不了C++语言的;
  10.浮躁的人容易说:XX语言不行了,应该学YY;——是你自己不行了吧!?
  11.浮躁的人容易问:我到底该学什么;——别问,学就对了;
  12.浮躁的人容易问:XX有钱途吗;——建议你去抢银行;
  13.浮躁的人容易说:我要中文版!我英文不行!——不行?学呀!
  14.浮躁的人容易问:XX和YY哪个好;——告诉你吧,都好——只要你学就行;
  15.浮躁的人分两种:a)只观望而不学的人;b)只学而不坚持的人;
  16.把时髦的技术挂在嘴边,还不如把过时的技术记在心里;
  17.C++不仅仅是支持面向对象的程序设计语言;
  18.学习编程最好的方法之一就是阅读源代码;
  19.在任何时刻都不要认为自己手中的书已经足够了;
  20.请阅读《The Standard C++ Bible》(中文版:标准C++宝典),掌握C++标准;
  21.看得懂的书,请仔细看;看不懂的书,请硬着头皮看;
  22.别指望看第一遍书就能记住和掌握什么——请看第二遍、第三遍;
  23.请看《Effective C++》和《More Effective C++》以及《Exceptional C++》;
  24.不要停留在集成开发环境的摇篮上,要学会控制集成开发环境,还要学会用命令行方式处理程序;
  25.和别人一起讨论有意义的C++知识点,而不是争吵XX行不行或者YY与ZZ哪个好;
  26.请看《程序设计实践》,并严格的按照其要求去做;
  27.不要因为C和C++中有一些语法和关键字看上去相同,就认为它们的意义和作用完全一样;
  28.C++绝不是所谓的C的“扩充”——如果C++一开始就起名叫Z语言,你一定不会把C和Z语言联系得那么紧密;
  29.请不要认为学过XX语言再改学C++会有什么问题——你只不过又在学一门全新的语言而已;
  30.读完了《Inside The C++ Object Model》以后再来认定自己是不是已经学会了C++;
  31.学习编程的秘诀是:编程,编程,再编程;
  32.请留意下列书籍:《C++面向对象高效编程(C++ Effective Object-Oriented Software Construction)》《面向对象软件构造(Object-Oriented Software Construction)》《设计模式(Design Patterns)》《The Art of Computer Programming》;
  33.记住:面向对象技术不只是C++专有的;
  34.请把书上的程序例子亲手输入到电脑上实践,即使配套光盘中有源代码;
  35.把在书中看到的有意义的例子扩充;
  36.请重视C++中的异常处理技术,并将其切实的运用到自己的程序中;
  37.经常回顾自己以前写过的程序,并尝试重写,把自己学到的新知识运用进去;
  38.不要漏掉书中任何一个练习题——请全部做完并记录下解题思路;
  39.C++语言和C++的集成开发环境要同时学习和掌握;
  40.既然决定了学C++,就请坚持学下去,因为学习程序设计语言的目的是掌握程序设计技术,而程序设计技术是跨语言的;
  41.就让C++语言的各种平台和开发环境去激烈的竞争吧,我们要以学习C++语言本身为主;
  42.当你写C++程序写到一半却发现自己用的方法很拙劣时,请不要马上停手;请尽快将余下的部分粗略的完成以保证这个设计的完整性,然后分析自己的错误并重新设计和编写(参见43);
  43.别心急,设计C++的class确实不容易;自己程序中的class和自己的class设计水平是在不断的编程实践中完善和发展的;
  44.决不要因为程序“很小”就不遵循某些你不熟练的规则——好习惯是培养出来的,而不是一次记住的;
  45.每学到一个C++难点的时候,尝试着对别人讲解这个知识点并让他理解——你能讲清楚才说明你真的理解了;
  46.记录下在和别人交流时发现的自己忽视或不理解的知识点;
  47.请不断的对自己写的程序提出更高的要求,哪怕你的程序版本号会变成Version 100.XX;
  48.保存好你写过的所有的程序——那是你最好的积累之一;
  49.请不要做浮躁的人;
  50.请热爱C++!

typedef用法小结

typedef用法小结

这两天在看程序的时候,发现很多地方都用到typedef,在结构体定义,还有一些数组等地方都大量的用到.但是有些地方还不是很清楚,今天下午,就想好好研究一下.上网搜了一下,有不少资料.归纳一下:
来源一:Using typedef to Curb Miscreant Code
Typedef 声明有助于创建平台无关类型,甚至能隐藏复杂和难以理解的语法。不管怎样,使用 typedef 能为代码带来意想不到的好处,通过本文你可以学习用 typedef 避免缺欠,从而使代码更健壮。
typedef 声明,简称 typedef,为现有类型创建一个新的名字。比如人们常常使用 typedef 来编写更美观和可读的代码。所谓美观,意指 typedef 能隐藏笨拙的语法构造以及平台相关的数据类型,从而增强可移植性和以及未来的可维护性。本文下面将竭尽全力来揭示 typedef 强大功能以及如何避免一些常见的陷阱。
如何创建平台无关的数据类型,隐藏笨拙且难以理解的语法?

使用 typedefs 为现有类型创建同义字。

定义易于记忆的类型名
  typedef 使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。类型出现在所声明的变量名字中,位于 ''typedef'' 关键字右边。例如:
typedef int size;
  此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。它仅仅为现有类型添加一个同义字。你可以在任何需要 int 的上下文中使用 size:
void measure(size * psz);
size array[4];
size len = file.getlength();
std::vector vs;
  typedef 还可以掩饰符合类型,如指针和数组。例如,你不用象下面这样重复定义有 81 个字符元素的数组:
char line[81];
char text[81];
定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:
typedef char Line[81];
Line text, secondline;
getline(text);
同样,可以象下面这样隐藏指针语法:
typedef char * pstr;
int mystrcmp(pstr, pstr);
  这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个‘const char *'类型的参数。因此,它可能会误导人们象下面这样声明 mystrcmp():
int mystrcmp(const pstr, const pstr);
  这是错误的,按照顺序,‘const pstr'被解释为‘char * const'(一个指向 char 的常量指针),而不是‘const char *'(指向常量 char 的指针)。这个问题很容易解决:
typedef const char * cpstr;
int mystrcmp(cpstr, cpstr); // 现在是正确的
记住:不管什么时候,只要为指针声明 typedef,那么都要在最终的 typedef 名称中加一个 const,以使得该指针本身是常量,而不是对象。

代码简化
  上面讨论的 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是 typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。例如:
typedef int (*PF) (const char *, const char *);
  这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:
PF Register(PF pf);
  Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的:
int (*Register (int (*pf)(const char *, const char *)))
(const char *, const char *);
  很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:"OK,有人还会写这样的代码吗?",快速浏览一下揭示 signal()函数的头文件 ,一个有同样接口的函数。

typedef 和存储类关键字(storage class specifier)
  这种说法是不是有点令人惊讶,typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。这并是说 typedef 会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的变量声明。下面将带到第二个陷阱:
typedef register int FAST_COUNTER; // 错误
  编译通不过。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。

促进跨平台开发
  typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:
typedef long double REAL;
在不支持 long double 的机器上,该 typedef 看起来会是下面这样:
typedef double REAL;
并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样: 、
typedef float REAL;
   你不用对源代码做任何修改,便可以在每一种平台上编译这个使用 REAL 类型的应用程序。唯一要改的是 typedef 本身。在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。不是吗? 标准库广泛地使用 typedef 来创建这样的平台无关类型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 这样的 typedef 还隐藏了长长的,难以理解的模板特化语法,例如:basic_string,allocator> 和 basic_ofstream>。

作者简介
  Danny Kalev 是一名通过认证的系统分析师,专攻 C++ 和形式语言理论的软件工程师。1997 年到 2000 年期间,他是 C++ 标准委员会成员。最近他以优异成绩完成了他在普通语言学研究方面的硕士论文。 业余时间他喜欢听古典音乐,阅读维多利亚时期的文学作品,研究 Hittite、Basque 和 Irish Gaelic 这样的自然语言。其它兴趣包括考古和地理。Danny 时常到一些 C++ 论坛并定期为不同的 C++ 网站和杂志撰写文章。他还在教育机构讲授程序设计语言和应用语言课程。
来源二:(http://www.ccfans.net/bbs/dispbbs.asp?boardid=30&id=4455)
C语言中typedef用法
1. 基本解释

  typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。

  在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。

  至于typedef有什么微妙之处,请你接着看下面对几个问题的具体阐述。
 2. typedef & 结构的问题

  当用下面的代码定义一个结构时,编译器报了一个错误,为什么呢?莫非C语言不允许在结构中包含指向它自己的指针吗?请你先猜想一下,然后看下文说明:
typedef struct tagNode
{
 char *pItem;
 pNode pNext;
} *pNode;
  答案与分析:

  1、typedef的最简单使用
typedef long byte_4;
  给已知数据类型long起个新名字,叫byte_4。

  2、 typedef与结构结合使用
typedef struct tagMyStruct
{
 int iNum;
 long lLength;
} MyStruct;
  这语句实际上完成两个操作:

  1) 定义一个新的结构类型
struct tagMyStruct
{
 int iNum;
 long lLength;
};
  分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct 关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。
  我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
  2) typedef为这个新的结构起了一个名字,叫MyStruct。
typedef struct tagMyStruct MyStruct;
  因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
  答案与分析
  C语言当然允许在结构中包含指向它自己的指针,我们可以在建立链表等数据结构的实现上看到无数这样的例子,上述代码的根本问题在于typedef的应用。
  根据我们上面的阐述可以知道:新结构建立的过程中遇到了pNext域的声明,类型是pNode,要知道pNode表示的是类型的新名字,那么在类型本身还没有建立完成的时候,这个类型的新名字也还不存在,也就是说这个时候编译器根本不认识pNode。

  解决这个问题的方法有多种:

  1)、
typedef struct tagNode
{
 char *pItem;
 struct tagNode *pNext;
} *pNode;

  2)、
typedef struct tagNode *pNode;
struct tagNode
{
 char *pItem;
 pNode pNext;
};
  注意:在这个例子中,你用typedef给一个还未完全声明的类型起新名字。C语言编译器支持这种做法。

  3)、规范做法:
struct tagNode
{
 char *pItem;
 struct tagNode *pNext;
};
typedef struct tagNode *pNode;
 3. typedef & #define的问题

  有下面两种定义pStr数据类型的方法,两者有什么不同?哪一种更好一点?
typedef char *pStr;
#define pStr char *;
  答案与分析:
  通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:
typedef char *pStr1;
#define pStr2 char *;
pStr1 s1, s2;
pStr2 s3, s4;
  在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。

  #define用法例子:
#define f(x) x*x
main( )
{
 int a=6,b=2,c;
 c=f(a) / f(b);
 printf("%d \\n",c);
}
  以下程序的输出结果是: 36。
  因为如此原因,在许多C语言编程规范中提到使用#define定义时,如果定义中包含表达式,必须使用括号,则上述定义应该如下定义才对:
#define f(x) (x*x)

  当然,如果你使用typedef就没有这样的问题。
  4. typedef & #define的另一例

  下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?
typedef char * pStr;
char string[4] = "abc";
const char *p1 = string;
const pStr p2 = string;
p1++;
p2++;
  答案与分析:
   是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。

  #define与typedef引申谈
  1) #define宏定义有一个特别的长处:可以使用 #ifdef ,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。
  2) typedef也有一个特别的长处:它符合范围规则,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种特性。
  5. typedef & 复杂的变量声明
  在编程实践中,尤其是看别人代码的时候,常常会遇到比较复杂的变量声明,使用typedef作简化自有其价值,比如:
  下面是三个变量的声明,我想使用typdef分别给它们定义一个别名,请问该如何做?
>1:int *(*a[5])(int, char*);
>2:void (*b[10]) (void (*)());
>3. doube(*)() (*pa)[9];
  答案与分析:

  对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。
>1:int *(*a[5])(int, char*);
//pFun是我们建的一个类型别名
typedef int *(*pFun)(int, char*);
//使用定义的新类型来声明对象,等价于int* (*a[5])(int, char*);
pFun a[5];

>2:void (*b[10]) (void (*)());
//首先为上面表达式蓝色部分声明一个新类型
typedef void (*pFunParam)();
//整体声明一个新类型
typedef void (*pFun)(pFunParam);
//使用定义的新类型来声明对象,等价于void (*b[10]) (void (*)());
pFun b[10];

>3. doube(*)() (*pa)[9];
//首先为上面表达式蓝色部分声明一个新类型
typedef double(*pFun)();
//整体声明一个新类型
typedef pFun (*pFunParam)[9];
//使用定义的新类型来声明对象,等价于doube(*)() (*pa)[9];
pFunParam pa

2009年3月23日月曜日

上海市人事局、上海市公安局关于进一步解决专业技术人员和管理人员夫妻两地分居问题的通知

上海市人事局、上海市公安局关于进一步解决专业技术人员和管理人员夫妻两地分居问题的通知



沪人[1999]122号

各区、县人事局、公安局(分局),各部、委、办、局(集团公司),大专院校、科研院所人事(干部)部门:

根据人事部、公安部《关于进一步做好解决干部夫妻两地分居问题工作的通知》(人发[1999]80号)精神,结合本市实际,现就进一步解决专业技术人员和管理人员夫妻两地分居问题通知如下:

一、本市人员因夫妻两地分居,其在外地的配偶属专业技术人员或管理人员(含聘用制干部,下同),在沪有接收单位,且本人或在外地配偶符合下列条件之一的,可以申请将其在外地的配偶调入本市工作。未成年子女可随迁进沪。
1、获得省(市)部级以上荣誉称号者、获得省(市)部级以上科研成果奖的主要完成者、有突出贡献的中青年专家、被聘任为高级专业技术职务的人员、博士学位获得者、处级以上管理人员,以及在外地配偶符合本市引进人才政策规定条件的人员。
2、被聘任为中级专业技术职务满三年的人员、工作满三年的硕士学位获得者;以及被聘任为中级专业技术职务或获得硕士学位,夫妻两地分居满三年的人员。
3、夫妻两地分居满五年的人员。
4、家庭有特殊困难的人员。

二、专业技术人员和管理人员解决夫妻两地分居,由在上海的一方提出申请,既可由工作单位申报,也可以个人申报。
由工作单位申报的,市直属单位向市人事局申报;委托市人才服务中心人事代理的单位通过市人才服务中心向市人事局申报;其他单位向注册地所在区(县)人事局申报。
由个人申报的,通过户籍所在地的街道(乡、镇)向区(县)人事局申报。

三、本市建立解决夫妻两地分居计算机网上政务服务系统。市、区(县)人事局可通过该系统在网上办理调沪手续。市、区(县)人事部门承办调沪事宜时,应将在沪一方和调沪人员基本信息及办理情况及时输入该系统,符合系统内设条件的,即可核发市人事局的落户证明。

四、各公安派出所(警察署)凭市人事局出具的《申报户口证明信》和公安部门签发的《准予迁入证明》和《户口迁移证》办理落户手续。因解决夫妻两地分居调沪的人员及其随迁子女,一律免征城市建设费。

五、本通知自下发之日起执行,原沪人[1986]113号文同时废止。

一九九九年九月十五日

夫妻分居调沪政策问答

夫妻分居调沪政策问答


--------------------------------------------------------------------------------

问:夫妻分居两地,主要是指哪种情况?

答:指夫妻双方中,在沪一方是本市常住户口人员,另一方户籍关系、人事行政关系在外地的,我们称之为夫妻分居两地。



问:解决夫妻分居两地,对从外地调到上海来工作的一方有什么要求?

答:根据本市《关于进一步解决专业技术人员或管理人员夫妻两地分居问题的通知》规定,从外地调到上海的一方必须是专业技术人员或管理人员(含聘用制干部),且在沪有接收单位,如未自行落实接收单位的,不能提出申请。



问:夫妻一方现是本市常住户口人员,另一方是外地户口,现在上海找到了接收单位,请问外地一方要调到上海,目前本市有哪些具体的政策规定?

答:本市关于解决夫妻分居两地调沪的政策,主要作了三个方面的规定。

1、取得高级专业技术职称的;取得博士学位的;获得有突出贡献的中青年专家称号的;获得省(部)级以上荣誉称号的;获得省(部)级以上科研成果奖的主要完成者;处级以上管理人员;在外地的配偶符合本市人才引进政策规定条件的人员。

2、被聘任为中级专业技术职务满三年的人员、工作满三年的硕士学位获得者;被聘任为中级专业技术职务或获得硕士学位,夫妻两地分居满三年的人员。

3、夫妻两地分居满五年的人员。

4、家庭有特殊困难的人员。调沪人员的年龄掌握在距法定退休年龄五年以上。



问:夫妻分居两地的时间如何计算?

答:一般根据在沪一方的户口进沪的时间起计算。①一方先进沪后结婚的,按登记结婚的时间起计算;②一方先结婚后进沪的,按进沪一方报入本市户口的时间起计算。



问:夫妻分居两地调沪,具体怎样申请办理调动的手续?

答:解决夫妻分居两地调沪,政策规定由在沪的一方提出申请,既可由在沪一方的单位申请,也可由在沪一方以个人名义提出申请。

1、以在沪一方单位申请的:市直属单位向市人事局申报;委托市人才服务中心人事代理的单位通过市人才服务中心向市人事局申报;其他单位向注册地所在区(县)人事局申报。

2、在沪一方以个人名义申请的:通过户籍所在地的街道(乡、镇)组织人事科向区(县)人事局申报。



问:夫妻分居两地调沪的手续在街道能办理吗?

答:卢湾区人事局为方便社区居民和用人单位,目前在四个街道办事处都设立了接待“窗口”,具体接待的地点和时间:

打浦街道社会保障服务中心(南塘浜路103号)周一、周三上午8:45-11时;

五里街道社会保障服务中心(瞿溪路758号)周一、周三上午8:45-11时;

淮海街道社会保障服务中心(浏河口路88号)周二、周四下午1:30-4时;

瑞金街道社会保障服务中心(复兴中路553弄97号)周二、周四下午1:30-4时;

卢湾区人事局政策咨询电话:63262020-81026


--------------------------------------------------------------------------------

2009年3月21日土曜日

拥抱iPhone,拥抱软件开发的未来

拥抱iPhone,拥抱软件开发的未来

  知道过去半年以来,什么软件开发技术最火吗?不是Java、.NET,甚至也不是Ajax、Flex和Silverlight……是iPhone!

  2008年10月,第一部iPhone软件开发的图书《The iPhone Developer's Cookbook》出版(中文版《iPhone开发秘籍》即将由人民邮电出版社推出),迅速跃至Amazon计算机类榜首。不久,受众面更广的《Beginning iPhone Development》出版(中文版《iPhone开发基础教程》由人民邮电出版社引进),不仅立马夺取Amazon计算机类图书榜首宝座,而且一度逼近图书总排榜榜首,至今仍然时常进入总排榜Top100,在技术图书一片黯淡之中,可谓惊艳。而更多的后续竞争者也在不断涌现,且基本上呈现出一本、火一本的状况……

  熟悉技术图书市场的读者可能会感到奇怪,iPhone虽然很酷,但是即使在手机中,它的市场份额也不过百分之几而已,这样一本专的书,怎么会以如此热度热销这么久呢?

  若干年后我们再来回顾的话就会发现,2007年1月iPhone的火爆推出,尤其是2008年7月iPhone和iPod Touch软件网上专卖店App Store的开门营业,实际上不仅标志了智能手机正式开始成为主流计算平台,有了取代PC的可能,而且更深刻地改变了软件业和软件开发乃至整个信息产业的模式。

  根据苹果公司网站上的信息,仅2008年最后一个季度,iPhone的销量就达到了400多万部,而App Store里的软件数量更是每个月都要增加5000个左右,每月软件下载量过亿(也就是每天500万次左右),收入很快也到了亿元门槛,而且仍然在不断增长。一般App Store里的收费软件定价在几美元,用户购买后,苹果与软件开发者按3:7分成,由于潜在用户数量巨大,营销成本低,软件开发者的收入可能极具诱惑力。

  这期间,媒体也不断传出一些程序员在家里没事儿干做出来的iPhone软件居然成了巨大摇钱树的故事。Steve Demeter在业余时间开发了类似于俄罗斯方块的Trism游戏,使他在2个月内一举收入25万美元,并获得了《连线》杂志“2008最佳iPhone应用”称号。还有年初程序员High Gross用20分钟、10行代码编写了一个极为简单的音效程序,放到App Store上面免费下载,不料这个他自己称为垃圾程序的东西,居然很快进入总排榜的前3位,一天就下载了10万次。他决定在程序中加入广告,充足的点击量就给他带来了每小时200美元的收入……

  开发iShoot游戏的Ethan Nicholas更加传奇:身为Sun公司程序员的他,因为奖金减少、家里又出了一大笔医疗费用,决定利用业余时间,开发一个游戏,赚点外快,弥补家用。他在六周业余时间内,坐在沙发里、手里一手拿着饮料开发出来一个坦克大战游戏iShoot。起初,销售平平。但是在他发布了一个免费下载的简版之后,奇迹出现了,免费版本和收费版本都跃居App Store下载榜前列——这个游戏成了聚宝盆,最高日收入居然达到3万7千美元!最后一个月就挣了60万美元。巨大的潜力,已经是资深工程师的Nicholas毅然从Sun公司辞职,开始专门从事iPhone开发工作。

  与此同时,iPhone+App Store也标志着手机市场进入了新一轮群雄逐鹿时代。这一次竞争与以往最大的不同,是软件和互联网服务的深深融入。我们已经看到,受iPhone+App Store模式大热的启发,Google Android联盟发布了Android Market,Palm为还未上市的Palm Pre发布了App Catalog,黑莓推出了App World,仅仅在2009年的移动通信世界大会上,就有诺基亚发布Ovi Store,微软发布Windows Marketplace for Mobile。甚至中移动也基于Android开发了OMS手机系统,并将推出自己的软件商店“Mobile Market”。

  作为一种移动计算平台,手机市场的竞争重点,已经从单纯的硬件配置和外观设计,转到了软件操作系统,这将是Apple(依靠iPhone)和Google及其联盟(依靠Android)这样从其他市场转来的强劲的后起之秀,和原来的霸主Nokia(采用Symbian)以及老竞争者微软(Windows Mobile)之间的生死决战。而且,在“软件即服务”的大背景下,由于手机只是一个客户端,各种服务和软件还与App Store、Android Market等等网站相连,这种竞争的结果,很可能把此前在多个不同领域井水不犯河水的移动运营商、手机制造商、软件厂商、信息服务商和互联网厂商等都卷进来,最后融合为几个大型的信息服务提供商,就好像今天为数不多但个个巨大无比的电力供应商。就这个意义上,我们仿佛已经看到了云计算正在变为现实……

  熟悉苹果公司发展过程的人也会发现,历史竟然如此惊人地相似。当年,苹果公司在PC机和图形界面操作系统上都发挥了类似的作用,只不过后来逐渐被竞争力更强的IBM和微软+Intel所取代。而iPod的传奇故事,则已经是21世纪的事情了。这一次,由于iPod+iTunes的创新模式牢不可破,不仅iPod在媒体播放器上的地位无人能及,iTunes网站也成了美国乃至世界最大的音乐销售渠道,销售的音乐早已超过50亿首,而且潜力仍然充满想象空间。可以想象,如果iPhone+App Store能够复制iPod+iTunes的成功,其威力是非常可怕的。看看App Store,你会发现苹果不仅在卖俄罗斯方块之类的小游戏,它也卖企业用软件(比如,用来在现场给客户演示产品),还有健康软件、生活软件、工作效率软件、教育软件、音乐甚至图书……事实上,奥巴马总统竞选团队就开发了一个叫“奥巴马08”的程序,帮助用户与奥巴马的其他投票者保持联系,接收来自奥巴马团队的新闻、视频和照片资料,提醒用户如何参加大选,为奥巴马投票,并提醒自己的朋友为奥巴马助威。

  可以想象,iPhone+App Store以及更多后续的模仿者、改进者,将使信息技术更深地渗透到整个社会生活之中,有可能完全改变人类社会的面貌。

  而对于全世界也包括中国的软件开发人员而言,iPhone与App Store的绝配也为他们提供了一个施展才华的全新大舞台,一个前所未有的方便、低成本的宣传和销售平台。只要你的创意点子足够新颖,加上简单易行的营销手段,你完全有可能像开发iShoot游戏的Ethan Nicholas(日收入2万多美元)和Trism游戏的Steve Demeter(月收入超过10万美元)那样,仅凭单枪匹马就赢得全球市场,成功创业,改变自己的人生。由于与手机绑定,而且收费低廉,此前最为头疼的盗版、收费等等问题,可能都不再需要考虑。而App Store这类服务和各类社会化网络服务的融合,更可以为程序员架设起一座直通用户的桥梁,更直接地听取用户声音,更好地为用户服务。

  正是因为这样的无限可能,iPhone开发技术以及相关图书才会突然站在潮流之巅。事实上,由于苹果公司一直采取独特的软硬件平台,iPhone开发技术为出版界也开创了一片蓝海。iPhone(实际上包括iPhone手机和iPod Touch媒体播放器两个目标平台)开发有两种方式:一是基于浏览器的Web程序,也就是用HTML、CSS、JavaScript甚至Flash等开发,在浏览器(iPhone内置Safari浏览器)里使用;一是使用iPhoneSDK开发独立程序。而iPhone SDK中包含众多我们不熟悉的组件:首先是语言Objective-C,然后是用于GUI开发的Cocoa Touch框架,多媒体方面有OpenAL、Quartz、Core Animation,开发工具有Xcode、Interface Builder,数据存储方面有Core Data、SQLite等……而且,iPhone的操作系统本质上是基于BSD(Darwin)的,属于Unix家族,这个基础层本身就需要广大Windows里泡大的程序员熟悉一会儿。

  可喜的是,人民邮电出版社图灵公司已经引进了国外最畅销的几部i鄄Phone方面的图书《iPhone开发基础教程》、《iPhone开发秘籍》、《iPhone开发实战》和《Objective-C基础教程》,将以“图灵程序设计丛书·移动开发系列”为名陆续出版。

  其中,《iPhone开发基础教程》即Amazon超级畅销书《Beginning iPhone Development》中文译本,刚刚出版上市,预计将在国内掀起新的热潮。值得注意的是,这本书由于作者权威(是苹果开发图书的顶级作者),内容全面深入,讲解透彻易懂,实例丰富,而且提供了许多实用的技巧和来之不易的经验,对于初学者和中级读者都完全适用。所以,问世不久就已经被公认为经典。到今天,Amazon上的评论已经累积70多篇,几乎全是五星,也是近来少见的。几个月以来,同类图书不断出现,但是它不仅成功地击退了后续的竞争者,还将许多本来大红大紫的iPhone使用教程死死压在后面,堪称奇迹。

  同系列中的《Objective-C基础教程》也出自《iPhone开发基础教程》作者之手,问世以来也是势头逼人,已经与另外一本多年的经典《Programming in Objective-C2.0》不相伯仲。由于iPhone开发的主力语言就是Objective-C,所以此书可以说是《iPhone开发基础教程》的绝配。

  《iPhone开发秘籍》即Amazon榜首图书《The iPhone Developer's Cookbook》的中文译本,此书属于中高级层次,适合已有一定苹果开发经验的程序员,以实例方式较深入地讲解了iPhone开发平台的各种特性,包括一些没有纳入SDK中的API,与《基础教程》恰好形成互补。虽然门槛较高,但书中大量实战技巧保证了自己较长的生命力。

  《iPhone开发实战》原版出自著名技术出版社Manning的in Action系列名门,功力当然也不同凡响,从内容上看,书中涵盖了iPhoneWeb开发以及Web与GUI混合编程等重要知识点,很好地配合了系列中其他图书。

  据悉,图灵公司还将出版Android开发方面的优秀著作,比如《Professional Android》中文版。

  iPhone、Android开发仍然是一个持续的热点,国外出版社仍然在不断跟进,相信还会有更多好书出现。希望国内更多有识的出版人和作译者投入其中,为国内技术人员奉献更多优秀的引进和原创图书。别忘了,投身智能手机平台,你也就抢先拥抱了软件开发的未来。

2009年3月10日火曜日

Windows Install XML详细教程

wix介绍(1)
什么是wix?
Windows Installer XML(wix)平台是一套工具和规范,协助你轻松创建MSI和MSM类型的安装包。wix工具集可以模拟传统的编译和链接,从源代码创建可执行的安装包。对于wix,源代码就是XML格式的文件。这些文件经schema(wix.xsd)验证后,通过预处理、编译、连接来创建安装包。通过wix平台,可以很容易地从一套短的源代码创建多个Windows Installer安装包。

Windows Installer XML 概述 (2)

Windows Installer XML, 或者wix, 提供一个描述Windows Installer database (MSI or MSM)的模式, 以及将XML描述文档转化成安装包的一套工具包。第二版的模式(wix.xsd) 增加额外的内容, 使得由一套XML文档创建多个Windows Installer安装包更加容易。wix工具集可以模拟传统的编译和链接,从源代码创建可执行的安装包。

本文介绍如何使用工具包编译和链接wix源代码来生成Windows Installer安装包。

注:本文假设你知道Windows Installer安装包的格式。

.wxs & .wixobj – Windows Installer Xml Files

在一个Windows Installer XML系统中,所有源文件的扩展名都是 .wxs 。就如同 .cpp 之于 C++ 或者.cs 之于 C#。.wxs 文件经过预处理和编译生成扩展名为.wixobj的文件。当所有源文件编译成.wixobj文件后,就可以链接这些 .wixobj文件生成Windows Installer安装包。

.wxs 文件结构

所有的 .wxs文件都是XML格式的文档,以作为根节点。 在预处理之前,文档的其余部分可能匹配 WiX schema,也可能不匹配。不管怎样,预处理之后,所有源文件必须符合WiX schema的要求,否则编译通不过。根元素 最多只能包含 , 其中的一个作为子元素。包含 的个数不受限制。当源文件编译生成.wixobj文件后,这3个子元素产生了3个新的节元素。因此,这三个子元素常常称为节元素。

需要特别注意的是,每个源文件只能有一个 或者 节元素,这是因为他们将被编译生成称作入口节的特殊节元素。入口节是链接处理的起始点。有关节,入口节和全部的链接处理会在后面作详细描述。

节元素的子元素定义了Windows Installer安装包的内容。 对应Property表, 对应Directory表。
大多数元素包含"ID"属性,该属性将作为Windows Installer文件行的主键。 注意,WiX schema的第一版中,主键以元素的文本表示。由于一些原因,该方式并非合适,已经改为"ID"属性。大多数情况下,当源文件编译生成.wixobj文件后,"ID"属性用来确定了一个标记号。

Symbols and references

.wixobj文件中的每一个标记号由元素名加"ID"属性的唯一码组成。标记号非常重要,因为他们可以被其他任何源文件中节元素作为引用对象。例如, 可以包含在一个源文件的里,包含在另一个源文件的里。在下,通过添加创建一个显性的引用对象,直接引用第一个文件中定义的标记号。链接程序负责将标记号和引用链接。某些情况下,编译程序在处理源文件时会产生隐性引用。隐性引用具有与显性引用相同的行为。

除了上面提到的简单引用,WiX还支持复杂引用。复杂引用是在链接程序必须生成额外的信息来链接标记号和引用时使用。复杂引用最好的例子是 Windows Installer中Feature/Component的关系。当通过显性引用时,链接程序必须读取的标记号和的标记号,然后在FeatureComponents表中增加一个入口。

Feature/Component的关系甚至可以更复杂。因为中的某些元素,比如,包含指向与Component关联的Feature的引用。这种来自子元素的引用,被称作反向引用,有时也称作feature backlinks。复杂引用和反向引用的处理,可能是链接程序必须实现的最难的工作。

注意,标记号的定义和引用是WiX工具包第二版的新内容。之前的做法是必须将Components打包成Merge Modules,然后用merge进行标记号链接。新的系统在定义标记号方面更加灵活,避免了为确保每个Merge Module唯一的过多开销。

Windows Installer XML 概述 (3)

Structure of the .wixobj file

编译程序为每个源文件生成相应的.wixobj文件, .wixobj文件是XML格式的文档,遵照WiX项目中objects.xsd schema的定义。.wixobj文件包含一个或多个节,每个节包含标记号和指向其他标记号的引用。

虽然标记号和引用是.wixobj文件中最重要的数据,但他们只是信息中很少的一部分。大多数.wixobj文件的主要内由, and 组成,这些元素提供了Windows Installer安装包的原始数据。许多情况下,链接程序不但处理标记号和引用,还要使用和更新.wixobj中的原始数据。

但有趣的是,.wixobj文件schema (objects.xsd)使用了camel外壳,而源文件schema (wix.xsd)使用了Pascal外壳。这种选择明确表示.wixobj文件不能被用户编辑。事实上,对于只能用Wix工具包处理的数据,用来定义这些数据的schema都使用了camel外壳。

candle - Windows Installer XML Compiler

candle.exe是Windows Installer XM的编译程序,candle.exe的任务是将作为输入的.wxs源文件预处理生成有效的符合WiX schema( wix.xsd )的XML文档。然后,每一个经后处理的源文件被编译成.wixobj文件。

编辑过程是比较简单的。WiX schema遵从简单的递归降序解析。编译程序处理每个单元,依次为.wixobj文件创建新新的标记号,计算必要的引用和生成原始数据。

第二版candle.exe与第一版基本相同。仅有的变更包括:使新的标记号和引用可以链接以及基于客户反馈的改变。两个版本不同的地方包括:新的.wixobj文件的格式由MSI改为XML格式,主键的创建放在了链接时间,二进制文件的导入放在了链接时间。

light - Windows Installer XML Linker

light.exe是Windows Installer XM的链接程序。light的任务是处理一个或多个.wixobj文件,从多个外部文件中提取元数据,创建Windows Installer安装包(MSI or MSM)。必要时,light还将创建.cab文件和嵌入到Windows Installer安装包的数据流。

链接程序开始搜索从命令行输入的.wixobj文件以找到入口节。若发现一个以上的入口节,light返回错误。错误返回是必要的,因为入口节定义了准备创建什么类型的Windows Installer安装包,MSI () 或者 MSM ()。 不可能从一个链接操作创建出两个安装包。

当链接程序确定入口节时,将定义在每个.wixobj文件中的标记号储存在标记号表中。当找到入口节后,链接程序通过寻找标记号表中的所有标记号试图处理该节中的所有引用。当发现标记号在不同的节中,链接程序递归连处理新节中的标记号。这个过程要搜索必要的节以解决引用的全部嵌套,直到所有的引用都满足为止。如果一个标记号在所有提供的.wixobj文件都没有找到,链接程序退出处理,并返回一个错误表示未定义的标记号。

所有的节找到之后,将处理复杂引用和反向引用。这个过程出现在以下场合:Components 和 Merge Modules 准备挂接其上一级的 Features,或者准备添加Merge Modules, Components到ModuleComponents表中。反向引用处理是为象Shortcut, Class, 和 TypeLib这类元素添加合适的Feature ID。

一旦所有的引用解决后,链接程序处理所有的行,检索引用文件的语言、版本号和hash值,计算媒体布局,以及包括一些必要的标准流程,以确保成功安装顺序。这部分的处理,结束前通常生成附加的行,以获得额外的与入口节的联系,来确保它们被打包到最终的Windows Installer安装包。

最后,light产生IDT文件,并将他们导入到Windows Installer安装包。 数据库创建完成后,最后一步是合并Merge Modules,如果需要,创建.cab文件。输出结果是一个完整功能的Windows Installer安装包。

WiX Help - Authoring (1)

Authoring是撰写WiX源文件来创建Windows Installer安装包的过程。WiX源文件的扩展名通常为.wxs,是XML格式的文档,格式必须符合WiX schema的要求。(可以在 wix.xsd 找到)

WiX Help - Authoring (2)

WiX可以创建的Windows Installer数据库类型包括:Windows Installer 安装包 (MSI 文件)和 Merge Modules (MSM 文件)。

编写第一个 .wxs File

选择一个你最喜欢的XML编辑器,来编写全部范例。我使用notepad,创建一个新文件“product.wxs”。虽然取什么文件名都无所谓,但.wxs扩展名可以让我们知道这是一个Windows Installer Xml源文件。现在,让我们添加只有3行代码的.wxs文件:





这构成源文件的轮廓。你可以把这个空文件作为candle.exe的输入,来输出一个空的object文件。按照下面的步骤,你可以看到非常简单的输出:

C:\test> candle product.wxs
Microsoft (R) Windows Installer Xml Compiler version 1.0.1220.15022
Copyright (C) Microsoft Corporation 2003. All rights reserved


C:\test> type product.wixobj
xmlns="http://schemas.microsoft.com/wix/2003/04/objects"
src="C:\test\product.wxs" />

C:\test>

在继续往下之前,先注意两件事情。
(1)当没有任何错误时,除了文件头,candle不输出其他文本。事实上,你可以在命令行中指定-nologo来抑制文件头的输出。这种情况下,candle不输出任何信息,除非有错误产生。
(2)源文件的路径被储存在.wixobj文件中。这有助于追踪错误的来源。事实上,当错误产生时,链接程序使用"src"属性输出更多信息的错误消息。

现在我们已经看到了一个空的源文件产生了一个空的object文件。让我们创建一个可安装的Windows Installer安装包。添加下面的代码到你的product.wxs文件:



Version='1.0.0.0' Manufacturer='Microsoft Corporation' UpgradeCode='12345678-1234-1234-1234-123456789013' >
Description='My first Windows Installer package'
Comments='This is my first attempt at creating a Windows Installer database'
Manufacturer='Microsoft Corporation' InstallerVersion='200' Compressed='yes' />











这可以让我们创建一个MSI文件,该文件的ProductCode是 {12345678-1234-1234-1234-123456789012},ProductLanguage 是"1033",ProductVersion是"1.0.0.0"。所有这些信息都取自元素。元素定义了所有加入到MSI的摘要信息。最后的树与一起创建。这可以让我们的MSI在机器上注册成功。

按照下面的步骤,让我们编译,链接,安装,并看一下已经注册的文件包。

注意:MSI需要管理员权限,如果不是以管理员权限安装,将退出而无错误提示。

C:\test> candle product.wxs
Microsoft (R) Windows Installer Xml Compiler version 1.0.1220.15022
Copyright (C) Microsoft Corporation 2003. All rights reserved

product.wxs

C:\test> light product.wixobj
Microsoft (R) Windows Installer Xml Linker version 1.0.1220.15022
Copyright (C) Microsoft Corporation 2003. All rights reserved

C:\test> msiexec /i product.msi

C:\test> \\delivery\tools\msiconfig.exe
.
.
.
{12345678-1234-1234-1234-123456789012} Test Package
.
.
.

在列出的所有你的机器上已经安装的Windows Installer包中,你应该看到"Test Package"。你也可以到Control Panel中的Add/Remove Programs,"Test Package"已经出现在那里。再把它卸载掉。

现在我们已经有了一个可以正确安装和卸载的包。让我们真正安装一些东西。创建一个名为"readme.txt"的文本文件,随便输入一些文本。"Hello, World!"是最讨人喜欢的。接下来,要修改product.wxs:



Version='1.0.0.0' Manufacturer='Microsoft Corporation' UpgradeCode='12345678-1234-1234-1234-123456789013'>
Description='My first Windows Installer package'
Comments='This is my first attempt at creating a Windows Installer database'
Manufacturer='Microsoft Corporation' InstallerVersion='200' Compressed='yes' />



















编译,链接,然后安装那个 MSI后,你应该可以看到在你系统的"Program Files"下有个目录名叫"Test Program"。在那个"Test Program"下应该有你创建的"readme.txt"文件以及你输入的文字。最后别忘了卸载这个MSI,以便将来重新安装。

相信与否,这就是创建Windows Installer安装包的全部过程。


WiX Help - Authoring (3)

创建Merge Modules
创建Merge Module与创建Windows Installer package非常相似。让我们先创建一个名为"module.wxs"的文本文件。如下输入标准的框架:




然后创建Merge Module,我们添加元素以及所需的属性:




Comments='This is my first attempt at creating a Windows Installer Merge Module'
Manufacturer='Microsoft Corporation' InstallerVersion='200' Compressed='yes' />




编译和链接这个代码。你将得到一个非常小的.msm文件。让我象前面给Windows Installer package添加代码一样,给Merge Module添加一个文本文件。首先创建一个名为"readme2.txt"的文本文件,并输入一些不同的文字。然后,更新源码来包含这个文件:




Comments='This is my first attempt at creating a Windows Installer Merge Module'
Manufacturer='Microsoft Corporation' InstallerVersion='200' Compressed='yes' />











现在,你有了一个Merge Module,它可以和其他团队共享来安装你的"readme2.txt"文件。让我们在Windows Installer package实际应用它。

将Merge Module合成到一个 .wxs 文件

Merge Modules只能合并到Windows Installer package。幸运的是,我们有了一个.wxs 文件用来创建Windows Installer package。因此让我们添加2行代码(是的,只有2行)来合并你的新模块。打开"product.wxs",添加:



Version='1.0.0.0' Manufacturer='Microsoft Corporation'>
Comments='This is my first attempt at creating a Windows Installer database'
Manufacturer='Microsoft Corporation' InstallerVersion='200' Compressed='yes' />






















现在当你编译"product.wxs",它将包含来自Merge Module的安装逻辑和文件。当你再次安装"product.msi"时,你应该在"Test Program"目录下看到两个文本文件。

WiX Help - Authoring (4)

Adding Custom Actions

你已经熟悉了创建Windows Installer安装包的基本方法,让我们进入下一阶段来添加用户自定义功能。Windows Installer XM的每一个发行版都包含一套WiX Server CustomActions,因此我们将用他们来创建范例。现在打来 wix\bin\ca 目录,拷贝 "sca*.dll" 到 "product.wxs"的目录下。

不同于将CustomAction库文件拷贝到源文件目录下,让我们先练习编写一个名为"sca.wxs"的小模块来定义CustomActions。添加可以直接读取CustomActions Server表和调度不同行为的CustomAction。




Return='check'/>
Return='check'/>





这段代码可以编译,但不可以链接。记住,链接需要有一个入口节,而不是一个入口节。我们需要与一个包含的源文件来链接该文件。在正确处理链接问题之前,先让我们添加其他的CustomActions,这些 CustomActions与已经添加的直接CustomActions一样重要。




Return='check'/>
Return='check'/>

Return='check'/>

DllEntry='StartMetabaseTransaction' Execute='deferred' Return='check'/>
DllEntry='RollbackMetabaseTransaction' Execute='rollback' Return='check'/>
DllEntry='CommitMetabaseTransaction' Execute='commit' Return='check'/>

DllEntry='CreateMetabaseKey' Execute='deferred' Return='check'/>
DllEntry='DeleteMetabaseKey' Execute='deferred' Return='check'/>
DllEntry='CreateAspApp' Execute='deferred' Return='check'/>
DllEntry='WriteMetabaseValue' Execute='deferred' Return='check'/>
DllEntry='WriteMetabaseMultiString' Execute='deferred' Return='check'/>
DllEntry='DeleteMetabaseMultiString' Execute='deferred' Return='check'/>

DllEntry='CreateDatabase' Execute='deferred' Return='check'/>
DllEntry='DropDatabase' Execute='deferred' Return='check'/>
DllEntry='ExecuteSqlStrings' Execute='deferred' Return='check'/>
DllEntry='ExecuteSqlStrings' Execute='rollback' Return='check'/>






好了。我们已经完成了"sca.wxs"的编辑。我们已经成功定义了全部的针对WiX Server CustomActions的入口点。那么,怎样在product.wxs中添加代码来调用WiX Server CustomActions?让我们添加一段测试代码来调用CustomAction以在安装过程中产生一个错误。这就是"ErrorOut" CustomAction。




Version='1.0.0.0' Manufacturer='Microsoft Corporation'>
Description='My first Windows Installer package'
Comments='This is my first attempt at creating a Windows Installer database'
Manufacturer='Microsoft Corporation' InstallerVersion='200' Compressed='yes' />


























这3行就是调用"ErrorOut" CustomAction所需要的全部代码。现在用light.exe来链接两个文件。以下是编译,链接和安装的步骤。

C:\test> candle product.wxs module.wxs sca.wxs
Microsoft (R) Windows Installer Xml Compiler version 1.0.1256.19889
Copyright (C) Microsoft Corporation 2003. All rights reserved.

product.wxs
module.wxs
sca.wxs

C:\test> light module.wixobj
Microsoft (R) Windows Installer Xml Linker version 1.0.1256.19889
Copyright (C) Microsoft Corporation 2003. All rights reserved.

C:\test> light product.wixobj sca.wixobj ut product.msi
Microsoft (R) Windows Installer Xml Linker version 1.0.1220.15022
Copyright (C) Microsoft Corporation 2003. All rights reserved

C:\test> msiexec /i product.msi

当MSI开始回滚安装时,没有任何警告。记着,当安装完文件之后,"ErrorOut" CustomAction被调用,迫使安装失败。MSI于是删除已安装的文件并悄无声息地退出。对于有兴趣的读者,可以添加成功和失败的对话框作为练习。

翻译 Msi Tables to WiX Schema

对于WiX schema,并非总是明显地对应于Windows Installer schema。下面是一些有关二者之间关系的帮助信息。

DuplicateFile Table
对应于File节点内的CopyFile节点。你只需设置Id, DestinationFolder, 和 DestinationName的属性值。

LaunchCondition Table
对应于Fragment 或 Product下的Condition节点。你只需设置Message的属性值。

LockPermissions Table
对应于Permission。

MoveFile Table
对应于Component下的CopyFile节点。你需要腰设置除了Delete之外的所有属性值。设置Delete为'yes'是为了使用msidbMoveFileOptionsMove选项。

PublishComponent Table
对应于Category。这里有一个范例说明如何在MSI中实现PublishComponent。

MSI
ComponentId Qualifier Component_ AppData Feature_
{11111111-2222-3333-4444-5555555555555} 1033 MyComponent Random Data MyFeature

WiX

Qualifier='1033'/>

.
.
.






RemoveIniFile
对应于IniFile。仅需要设置Action的属性值为'removeLine' 或 'removeTag'。

RemoveRegistry Table
对应于Registry。仅需要设置Action的属性值为'remove' 或 'removeKey'。

Windows Installer XML Standard CustomActions

WiX 工具包提供一些CustomActions用来负责配置资源,如IIS(Internet Information Services)网站和虚拟路径,SQL Server数据库和scripts,用户帐号,文件共享等。这些CustomActions由两个.wixlibs库提供,sca.wixlib 和 wixca.wixlib。前者包含了"Server CustomActions",而后者提供更多通用的安装CustomActions。将来,这来两个库有可能合并,但现在由于历史原因,他们是分开的。

sca.wixlib - Server CustomActions

Internet Information Services (IIS) CustomAction - 创建和配置网站、虚拟目录、Web应用等。
SQL Server CustomAction - 创造数据库和执行的SQL脚本和报表。
FileShare CustomAction - 创造和配置文件共享(SMB).
Performance Counter CustomAction - 安装和卸载性能计数器。

wixca.wixlib - General CustomActions

Secure Objects CustomAction - 标准的LockPermission标没有提供的安全对象。(ACLs) 详情见中的Extended属性。
Service Configuration CustomAction - 配置ServiceInstall表没有提供的Windows Service属性。
Quiet Execution CustomAction - 运行不显示窗口的控制台应用程序。
XmlFile CustomAction - 允许你配置XML文件作为安装包的一部份,详情见

新的CustomActions一直在开发中。我们的目标是建立一个标准的满足任何需求的CustomActions。如果你有任何CustomAction需求,请告诉我们。


Using the Server Custom Actions

wix 工具包包括了一个用户定制的库。该库的核心是对服务器的用户化设置。服务器定制扩展了MSI可安装网站、共享文件和用户帐号等的整套资源。这些定制正确建立组件和资源的关联,遵照所有规则来正确安装、卸载和回滚相关联的部分组件的安装或者卸载。本文将举例概述这些应用。

本文假设读者已经理解MSI用户定制的类型,并已经阅读了"WiX Overview" 和 "Writing in WiX"。

Server Custom Action building blocks

scasched.dll, scaexec.dll 和 sca.wixlib随wix工具包一起发布。这两个DLL文件导出服务器定制功能所需的全部接口。当你创建一个使用服务器定制功能的MSI安装包时,他们在MSI的Binary表的结尾。sca.wixlib包含一系列wix fragments,你可以通过链接来确保所有的错误消息、定制记录以及二进制文件记录被链接到你最终的MSI中。

将服务器定制功能加入到MSI中的最简单的方法,是拷贝sca.wixlib和两个DLL(scasched.dll and scaexec.dll)到你的编译环境的路径下。该路径在哪并不重要,重要的是wixlib和两个DLL在同一路径下。当你用light.exe链接 MSI时,你只需在要链接的wixobjs 和 wixlibs列表中包含sca.wixlib的全路径。

Basic Example
首先让我们做一个练习,当MSI安装时创建一个用户帐号。
















这个简单范例将在目标机上创建一个新的用户,名为"testName1",密码为"pa$$word"。为了编译这个NSI,首先将代码拷贝到文件中,记着将"PUT-GUID-HERE"更换成实际的GUID,然后运行"candle.exe yourfile.wxs"编译,运行'light.exe yourfile.msi yourfile.wixout sca.wixlib'链接(sca.wixlib前加全路径)。现在用Orca打开生成的msi文件,查看Error表和Binary表,你会发现所有用户管理的数据都被连接到了MSI中。这之所以会产生,是因为你已经完成了两件关键的事。首先你在下使用了元素,这意味着一个用户将作为MSI的一部分被安装。其次,你连接了sca.wixlib,编译器确保在 sca.wixlib中的一系列fragments中,只有与wxs中使用元素关联的数据才被连接到MSI中。

The server custom action elements

在上面的例子中,你学习了通过在WiX中使用,与sca.wixlib连接后,错误消息和binary表的数据就自动添加完成。当你使用如这样的特定元素时,WiX编译器支持自动引用sca.wixlib中的对应的元素。如前所述,服务器定制增加了安装许多新类型资源的功能。每一个资源类型有一个或多个元素允许和MSI一起被安装。如果你使用sca.wixlib,你唯一需要知道的是与你想安装资源相符的符号。下面列出了一些服务器定制支持的资源类型和控制其安装的元素。

Web Sites -
Web Applications -
Certificates -
SQL databases -
SQL scripts -
SQL strings -
Users -
FileShares -
Perfmon Counter registration -

通过在你的wix文件中使用表中的合适元素以及链接sca.wixlib,你可以正确使用wix server custom actions。

Performance Counter CustomActions

PerfCounter元素能够让你利用Windows API注册自己的性能计数器。这里有几个部分一起使用来完成注册:

Your performance DLL -
DLL必须输出Open, Collect, 和 Close 方法。更多细节请参考MSDN。

Performance registry values -
注册表必须包含键值指向你的DLL及其Open, Collect, and Close方法。这些键值由Registry元素创建。

Perfmon INI and H text files -
这些文件包含显示于界面的文字描述。更多细节请参考MSDN。

The RegisterPerfmon custom action -
你可以链接sca.wixlib来确保在MSI中包含定制功能。定制功能调用(Un)LoadPerfCounterTextStrings来注册你的计数器。为了调用自定义功能,在Perfmon.INI文件 File元素里创建PerfCounter元素。PerfCounter元素包含唯一一个属性:Name,Name属性应该与注册表、WIX源文件 fragment节和PerfCounter.ini文件中的名称匹配。

























--------------------------------------------------------------------------------

Sample PerfCounters.ini:
[info]
drivername=MyApplication
symbolfile=PerfCounters.h

[languages]
009=English
004=Chinese

[objects]
PERF_OBJECT_1_009_NAME=Performance object name
PERF_OBJECT_1_004_NAME=Performance object name in Chinese

[text]
OBJECT_1_009_NAME=Name of the device
OBJECT_1_009_HELP=Displays performance statistics of the device
OBJECT_1_004_NAME=Name of the device in Chinese
OBJECT_1_004_HELP=Displays performance statistics of the device in Chinese

DEVICE_COUNTER_1_009_NAME=Name of first counter
DEVICE_COUNTER_1_009_HELP=Displays the current value of the first counter
DEVICE_COUNTER_1_004_NAME=Name of the first counter in Chinese
DEVICE_COUNTER_1_004_HELP=Displays the value of the first counter in Chinese

DEVICE_COUNTER_2_009_NAME=Name of the second counter
DEVICE_COUNTER_2_009_HELP=Displays the current rate of the second counter
DEVICE_COUNTER_2_004_NAME=Name of the second counter in Chinese
DEVICE_COUNTER_2_004_HELP=Displays the rate of the second counter in Chinese

PERF_OBJECT_1_009_NAME=Name of the third counter
PERF_OBJECT_1_009_HELP=Displays the current rate of the third counter
PERF_OBJECT_1_004_NAME=Name of the third counter in Chinese
PERF_OBJECT_1_004_HELP=Displays the rate of the third counter in Chinese
Sample PerfCounters.h:
#define OBJECT_1 0
#define DEVICE_COUNTER_1 2
#define DEVICE_COUNTER_2 4
#define PERF_OBJECT_1 8

Quiet Execution CustomAction

Quiet Execution CustomAction,即wixca.dll中的QtExec自定义动作,他可以执行命令行。

Immediate execution



.
.
.




这将导致及时执行命令行。如果命令行的退出码出错(非0),由于Return值设为"check",将导致安装失败。如果你不想引起安装失败,可以修改该值为"ignore"。(还会记录日志)

如果你想及时运行多条命令行,在执行每一个命令行之前,你需要多次派配QtExec以及设置QtExecCmdLine的属性值。

Deferred execution
你也可以通过设置自定义动作的data属性来延期运行命令行。如果代码以及时模式运行,它将试图执行 QtExecCmdLine的value,如果代码以延期模式运行,它将试图执行custom action data的value。custom action data是与custom action同名的属性。下面是一个延期执行命令行的例子:




.
.
.




Extensions

WiX支持以下3类扩展。

介绍
预处理扩展(Preprocessor Extensions)允许客户编译之前修改源文件。
编译器扩展(Compiler Extensions)允许客户在文件被编译成二进制之前将已编辑的XML编译成内表格式。
打包扩展(Binder Extensions)允许客户提供图象处理和数据生成的交错能力。

通过这些扩展,可以扩展WiX的功能以支持自定义处理、XML语法编译或者为一些特殊生成要求的流程创建语义环境。

一般要求
虽然从源代码开始是如何使用每一类扩展的最好方法,但是他们有一些东西是通用的。
1. 必须在与WiX其他部分相同的.NET 1.1环境下实现。
2. 构造一个合适扩展对象的子类并给他起一个容易区分的名字。
3. 构建一个合适语法的schema来提供对可能之处的检查验证。
4. 构造内表定义并用编译器注册他们。
5. 为可扩展的方法和虚拟成员构造overrides。
6. 创建扩展为一个DLL。
7. 将扩展DLL与WiX EXEs放在一起。
8. 通过WiX编译器的命令行注册。


注意事项
在购买一个扩展之前,应该先评估一下该外部工具及其包含的语法是否能够提供满足你的技术需求灵活性。多重扩展和扩展类型可以被支持,但不能保证特殊类型的扩展被处理的顺序。因此,在同一个扩展类中,扩展之间不应存在次序依赖关系。

Using the WixUI dialog library

WixUI对话框库包含一套"stock"多话框,用来提供wizard风格的安装用户界面。用一个UIRef为安装包添加一个界面。WixUI可以自定义,比如在界面上显示图片或者添加、删除某个界面。

Note: WixUI 对话框库现在还处在技术评估期。请在WiX-devs邮件列表上提供使用回馈。WixUI对话框库是否有用?你有其他建议吗?需要其他界面吗?基于回馈的信息,WixUI库可能有一些非兼容的改变。


使用WixUI对话框组件库

WixUI对话框组件支持4种通用对话框序列:

1. WixUI_Mondo
包括整套对话框:welcome, license agreement, setup type (typical, custom, and complete), feature customization, directory browse, 和 disk cost。也包括Maintenance-mode对话框。当产品的某些特性在默认情况下不安装,或者典型安装和完全安装有很大不同时,使用 WixUI_Mondo。
Note:当用户选择Typical 或 Complete安装选项时,WixUI_Mondo使用SetInstallLevel控制事件来设置安装等级。对于Typical,安装级别设为 3,Complete设为100。feature和安装级别的详情,请参见INSTALLLEVEL 属性。

2. WixUI_FeatureTree
是WixUI_Mondo的简单版本。它去掉了setup type对话框。用户直接从许可协议界面进入feature customization界面。当默认情况下安装全部特性时,WixUI_FeatureTree比WixUI_Mondo更合适。

3. WixUI_InstallDir
不允许用户选择features, 但允许选择安装路径。
Note: 要使用WixUI_InstallDir,你必须为WIXUI_INSTALLDIR设置一个值,该值由Directory的Id确定,作为初始的安装路径。例如:




...



...



4. WixUI_Minimal
最简单的WixUI组件类型。唯一的界面组合了welcome 和 license-agreement对话框,去掉了feature customization对话框。当产品没有可选特性时,WixUI_Minimal是合适的。


如何为安装包添加WixUI界面

假设你有一个现成的安装包,功能具备但没有界面。你应该遵循下面的步骤来使用WixUI对话框。

1. 在你的安装源代码中添加一个UIRef元素,使用上面对话框套件的一种作为Id的属性值。例如:





为light命令行添加wixui.wixlib和适当的WixUI本地化文件名。例如:

light Mondo.wixobj %WIXUI_PATH%\WixUI.wixlib -loc %WIXUI_PATH%\WixUI_en-us.wxl -out Mondo.msi

请参见doc/examples/wixui 路径下的.wxs文件。

2. 指定许可文件

WixUI界面组件中有一个界面显示最终用户的许可协议。要指定你的产品许可协议,需要在运行light之前包含一个License.rtf文件在当前目录下。如果没有该文件,light将使用ui路径下的License.rtf文件。

3. 使用翻译过的错误和进度文字

默认情况下,WixUI 不包含任何翻译过的错误和进度文字。你可以通过引用WixUI_ErrorProgressText UI元素来包含他们。




4. 自定义对话框组件

通过拷贝、修改已有组件,你可以很容易地从WixUI组件中添加、删除对话框。
范例请参见doc/examples/wixui/custom路径下的工程。下表描述了该工程文件:

CustomDialogSet.build
NAnt build文件,用于创建自定义对话框组件。如果需要,创建WixUI通用对话框元素,然后build CustomDialogSet.wxs 和 CustomDlg.wxs来生成CustomDialogSet.wixlib。

CustomDialogSet.wxs
自定义对话框组件定义文件。拷贝WixUI_FeatureTree组建,并在WelcomeDlg初始化完成之后添加CustomDlg对话框。

CustomDlg.wxs
简单的自定义对话框。

TestCustom.wxs
WiX源代码,调用自定义的CustomDialogSet.wixlib库。

5. 更换图像
WixUI对话框库包含的图片用于welcome、installation-complete界面背景和其他界面的banner。你可以重载这些图片,以使用自己商标的目的。为了更换图片,需要在WiX源文件Bitmaps子目录下添加以下图片文件。

bannrbmp.bmp
顶端banner 493 × 58

dlgbmp.bmp
welcome 和 install-complete 对话框的背景图片 493 × 312

exclamic.ico
wait-for-costing对话框的Exclamation图标 32 × 32

info.ico
cancel 和 error对话框的Information图标 32 × 32

New.ico
directory-browse 对话框的Button图标 16 × 16

Up.ico
directory-browse 对话框的Button图标 16 × 16

2009年3月1日日曜日

35岁以前要做好的十件事

35岁是青春的后期,35岁以后是收获的季节,如果你没有资格说这句话,你将会憎恨自己。所以在35岁以前,在烂漫蓬勃的青春年华里,你最好把下面十件事做好:
  
  第一,学会本行业所需要的一切知识并有所发展。已故零件大王布鲁丹在他35岁时,已经成为零件行业的领袖,并且组建了年收入达千万美元的海湾与西部工业公司。每个人在年轻时都可能有过彻夜不眠、刻苦攻读,这在20岁甚或30岁都没有问题,但到了35岁,就不应该再为学习基本技能而大伤脑筋了。35岁之前是一个人从事原始积累的阶段,35岁之后就应该勃发了。
  
  第二,养成个人风格。在35岁以前,找出你所喜欢的,不论是衣着或是爱好,哪怕是与众不同的小习惯也好。20岁、30岁时你可以不断尝试、不断改变,但是到了35岁,你便要明确地建立个人风格。一位男士或女士在事业中途改变自己的形象,就会让人觉得很不可靠。你喜欢穿西装吗?好!就把西装当作你的商标吧!办公桌上摆些鲜花会令你工作更有效率吗?那就每天都摆些鲜花吧!
  
  第三,在感情生活方面平和安定。在攀登事业的高峰时,如果私人生活不愉快,陷入感情危机,对你会产生很大的干扰,甚至会逐渐令你对别的事物失去兴趣。那些在35岁之前私人生活已经平和安定的人,一般都比生活动荡不安的人有更大的机会获得成功。因此,如果你想结束一段没有结果的恋情,或者你想和女友结婚,那就赶快行动吧,免得把问题拖到生命的第35个春秋。在35岁以后,你应该专注地看着你对事业的投资开始获利。
  
  第四,明白自己的短处。承认有些事情你的确做不好,或者不愿做。如果你讨厌数字而喜欢创作,那就不要因为待遇高或顺从别人的期望而强迫自己做数字工作。在35岁之前,一定要投入你所喜爱、所擅长的那种工作。否则,35岁之后必然会有一段郁郁不乐的日子。而且,真正的成功可能因为活力的消退而丧失。
  
  第五,知道自己的长处。你应该知道自己擅长什么,并且清楚你所喜欢做而又做得比别人好的事情。不管你目前担任什么样的角色,知道自己的长处对成功都很重要。
  
  第六,储备辞职另谋生路的钱。在这个多变的职业世界里,你也许不会永远在一个地方工作,或者永远在一个位置上淋漓尽致地发挥自己,当你感到无法施展时,你很可能会想到辞职,或者开辟第二职业,如果你事先储蓄了足够的钱,你便有了一个安全的后盾。
  
  第七,建立人际关系网。如果到了35岁你仍未建立起牢固的人际关系网,那你就有麻烦了。这个人际关系网包括你的朋友、亲人,最低限度包括所有可以互相帮助的人。这些人有的是你的同事,有的受过你的恩惠,有的你倾听过他们的问题,有的你和他有着相同的爱好。人际关系网不是一朝一夕就能建立起来的,它需要几年甚至十几年的培养。一个人在事业上、生活上的成功其实如同一个政党的成功,你要有许多人散布在适当的地方,你可以依赖他们,他们也可以依赖你。
  
  第八,学会授权他人。许多人不肯或不能这样做,因此始终被钉在从属的职位上。授权他人是成功的一半,一个事无巨细,不能将工作授权别人的人,注定会遇到极大的障碍。到了35岁,你最好已成为这方面的专家。换言之,你懂得挑选合适的人并信任他们。
  
  第九,学会在什么时候三缄其口。因说话不小心而自毁前程的人,比因为任何其他原因丧失成功的人都多。要学会保持沉默而且看起来机智--别人自然以为你知道的比实际还多。别讲别人的闲话,别谈论你自己的大计,守口如瓶所赢得的声誉,远比讲人闲话所带来的东西更加珍贵。你在事业上越成功,这一点就越重要。
  
  第十,对人要忠诚。如果你到了35岁仍未能建立起坚如磐石的忠诚信誉,这一缺点将会困扰你一生。不忠诚的恶名必然会使你在事业上到处不受欢迎。你不能靠暗箭伤人爬到事业的顶峰,而要靠在早期树立起来的真诚刚直和不可动摇的声誉。35岁以前,忠诚只是投资;35岁以后,你会作为一个可以信赖的人收到忠诚的回报。