Monday, December 11, 2006

项目管理的中庸

从人类社会的诞生开始,管理就进入了人们的生活中。从古至今,无数先哲不断的在实践中总结了很多管理经验,并著书立说,有众所周知的《资治通鉴》,还有不为人熟知的《反经》,他们从多个方面对管理进行阐述,激发人们对管理的思考。管理的系统化理论化的时间还很短,并且大多从西方引入,他们更多的强调量化和流程化,人性化的东西相对来说太少了。而我国古代帝王治理天下首先强调民心的重要性,然后才是治理的谋略。我们在日常的项目管理中是否可以融合国内外的管理方法,逐步形成自己的管理风格,这是值得我们思考的问题。
儒家思想源远流长,被国人所广泛接受。它主张中庸之道,程颐诠释中庸云:“不偏之谓中,不易之谓庸。”(朱熹《中庸章句》引)。中庸强调正,要按照自然规律做事,我们日常的工作生活又有几个人可以做到中庸呢,中庸只是我们追求的一个境界。在项目管理中中庸之道可以一定程度的弥补现代管理方法的一些不足,使管理更加符合中国的国情。下面我就借助我们完成的一个企业应用集成项目来阐述我对项目管理的一些理解,希望能和大家进行一下交流。
场景:国内某生产制造公司对企业内部的信息化很关注,几年内陆续部署了呼叫中心、财务系统、办公自动化系统、供应链系统和ERP,然而多个系统采用的是不同公司的产品,他们之间没有办法进行有效的信息沟通,形成了多个信息孤岛,该公司现在委托我们对企业的现有的应用信息系统进行整合,使企业的信息沟通更加有效,可以提供更好的决策支持。
在所有的合同签订后,我们项目组被指派完成该工作。项目的工期为6个月,我们的人员配备共六名,项目经理一名,系统分析员一名,开发人员3名,配置管理人员一名。
我担任项目经理,并且很明显我们采用了项目的组织结构,更加有机会把我们项目组缔造成为一个有高度凝聚力的团体。但是在项目刚刚启动的时候,项目组成员之间还不是很熟悉,需要进行磨合,项目经理需要发挥自己的领导才能,把大家的工作和思想统一起来,为以后项目的顺利进行奠定基础。古语说“乱世用重典”,所以我们制定一套了制度来规范个人的工作行为,并严格执行,一定程度上加快了项目组的磨合过程。比如:强制固定了工作时间和工作地点,促进内部交流。当然我们也采取了一些提高团队凝聚力的方法,如:我们为项目组定义了一个名字,增强各位成员的归属感;不推荐项目组成员加班,避免由于过度疲劳引起厌烦情绪,提高工作效率。
因为有更高凝聚力的团队内的冲突程度就会不足,所以项目就比较容易偏离到一个错误的路线,项目的风险更高。为了保证我们项目组沿着正确的方向发展,我们正式公开的确定我们的目标,并且通过定时的评审,广泛邀请项目外的人员参与。
我们的系统依照需求分析、总体设计、详细设计、编码、测试和产品化的过程进行。
在需求分析阶段,我们需要确定项目的工作范围,然后在此基础上制定相关的范围、人力资源、采购和风险计划。我们首先需要知道各个信息系统可以提供的信息和需要从其他系统获取的信息,以及客户对系统在决策方面的期望。需求的获取和确定由配置管理员协助系统分析员完成,我对系统分析员进行了充分的授权,使得系统分析员可以放手去做。按照他认为有效的方式和客户进行沟通,并且为了保证工作的质量,我们组织了多次评审,使得项目组高效并且可控的运行。同时我们的开发人员对可能使用的技术进行预研,了解技术风险。对于各项计划的制定我们采用从下到上的方法进行,使得整体的计划相对比较准确,并且得到所有组员的支持,增强项目组工作的协调性。
然后我们对整个系统进行总体设计,抽象的架构图显示系统主要由两个部分组成:MDE服务器和适配器。其中我们采用了我们自主开发的消息中间件作为MDE服务器。根据总体设计的结构,我们对WBS图进行进一步的细化,然后对风险、人力资源和采购计划重新分析,更新了这些计划,使计划和项目的进度尽量同步,提高项目管理的准确性。
我们的项目顺利进入详细设计阶段,成员之间的配合也逐渐比较的默契,大家都有一个共同的目标,随着项目的顺利进行每个人都很有成就感,工作积极性很高。然而世事难料,客户的一个高层坚持需要在4个月的时间内完成所有的工作,他们提出可以增加我们项目的费用,我们公司上层也觉得这样是可行的,就对整个项目的期限进行了修改。按照软件工程的工作量的计算方法来说增加开发人员就可以按时完成项目,故此我们公司决定给我们项目增加2名开发人员。
根据变化,我们对计划进行了调整,对工作也重新进行了分工,表面看来项目还是可以稳步前进的。事实并非如此,加入的新成员没有办法短时间内开始工作,我们需要指派一个老成员来指导他,同时他会向其他多个项目组成员了解系统的情况,项目的进度开始有了一些延时,我们并没有意识到问题。此时项目组再次进入磨合期,我又严格的执行项目组的制度。对老成员也按照制度进行管理,他们的工作效率有所下降。现在重新进行分析,发现统一严格执行制度看似符合中庸的思想,其实是错误的,中庸强调按照自然规律办事,而我没有看每个人的实际情况,而采用了对新成员的标准来管理老成员。我当时应该因人而异,不应该采用统一的标准进行管理。
随着项目的推进,我们发现项目的进度有了比较的大的延时。公司高层也意识到了问题的存在,规定项目组必须加班。项目的进度逐渐有了提高,好像一切逐步进入掌握之中。然而项目经过短暂的加速之后,进度再次明显落后,项目内漂浮疲劳和浮躁的空气,团队的合作不再是那么和谐。大家对项目目标的追求不再那么热情,疲劳的感觉冲淡了激情。有的成员认真的工作,有的成员有严重的抵触情绪。我国的儒家主张和谐,和谐的团体才有最高的工作效率,怎样才能再次把项目组团结起来呢?在大禹治水的故事中我找到了好的方法,筑堤防水不如疏通。我们组织了一个项目组内的交流会,大家通过不记名的便签提出自己的意见。根源在于大家对长期加班的厌烦情绪,所以我们集体放假一天,然后取消强制加班制度。团队的工作效率在经过调整之后又进入了高峰期。
我们项目在测试阶段系统分析员和开发人员全部参与,但是测试的效果并不好。在学习项目管理后进行思考,发现问题主要是没有对测试的内容进行详细的WBS分析,所以人员的分工不明确,大家的工作有很多重复并且还有部分遗漏,在测试开始的时候也没有制定完整的测试计划,使测试的过程失去了控制,以至于对测试的质量控制也没有办法进行。如果使用适当的项目管理工具,应该可以一定程度的避免问题。
最终项目并没有能够按时进行交付,我们的反思结果是对项目的变更管理不善,并且项目成员的工作量并不能按照人月的计算方法,不能忽视人员之间的交流所带来的消耗,增加人手并一定能够缩短项目的开发时间,以至于在缩短工期后项目计划中时间的估算严重偏离了真实的情况。
通过本次计算机信息系统集成项目经理培训,详细学习了项目管理方面的知识,特别是其中明确提出的九个知识点,再次对以前管理的项目进行思考。我总结了几点体会:
2????? 项目经理是领导者和管理者,必须具备多方面的能力,包括管理、谈判、财务。
2????? 项目管理是需要不断反思自己,不断完善的。
2????? WBS图可以比较全面的描述项目的所有工作,需要随着项目的推进不断的完善WBS图。完善的WBS图也提供了工作分配的依据。
2????? 项目的任何阶段都需要进行计划,使项目一直在可控的范围,就像九个知识点一样,每个知识点都有计划的部分。
2????? 在项目进行的过程中最重要的还是人的管理,不论我们在这个过程中采用多么先进的开发和管理工具,它永远不能掩盖人的重要性。
2????? 适当的压力可以产生工作成果,但是压力过度就会产生负面效果。
2????? 注意项目管理中的细节,就像左美云教授说得“管理在于悟性,管理在于细节”,注意人的差别,因人制宜,充分发挥个人优势。我个人对项目管理的一些浅显的认识就说到这里,希望可以和大家进行交流,不断提高我们的管理理论和实践水平。

Wednesday, December 06, 2006

开发语言互相调用

最近完成了一个项目,用到了多种开发语言间的相互调用,在此做一个总结,希望对大家有所帮助。
1 Java调用C++
Java调用C++的方法是在Java中声明native方法,而在C++动态链接库中实现该方法。
1) 在Java中把方法声明为native,传入参数和返回值建议采用简单类型,否则处理会比较麻烦;
2) 编译Java文件;
3) 用javah根据编译后的文件生成C++需要的头文件;
4) 在C++中实现该方法;
5) 编译C++的实现为动态链接库;
6) 在Java中用System.loadLibrary()方法加载生成的动态链接库文件。
我们就象调用一般的Java类一样调用该包含了本地方法的类了。
[注意]:
在使用过程中,发现有些API调用会失败,反复调用几次就成功了,没有找到很好的解决方法。
2 C++调用Java
在C++中调用Java的方法一般分为五个步骤:初始化虚拟机、获取类、创建类对象、调用方法和退出虚拟机。
1) 初始化虚拟机。
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
int res;
//设置参数
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=.";
options[2].optionString = "-verbose:jni";

vm_args.version = JNI_VERSION_1_4;
vm_args.nOptions = 3;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res >= 0)
{
//创建虚拟机成功
}
一个应用程序只需要一个虚拟机,但是每个线程需要自己的虚拟机运行环境。我们从一个虚拟机获取多个当前线程的运行环境,代码如下:
int result=0;
result=jvm->AttachCurrentThread( reinterpret_cast( &env ), 0 );
if(result>=0)
{
//获取运行环境成功
}
当线程退出时,需要释放本线程使用的运行环境。
jvm->DetachCurrentThread();
2) 获取类
在进行方法调用之前,需要先获取相应的类,类名称必须包括包名,其中的“.”用“/”代替。
jclass JavaClass;
JavaClass = env->FindClass("com/test/TestInterface");
if(JavaClass != 0)
{
//获取成功
}
3) 创建类对象
如果需要调用的方法静态方法,则可以跳过本步骤。反之,则需要构造该对象。构造对象是通过调用类的构造函数来实现的,构咱函数的方法声明为, GetMethodID方法的参数在下一步骤详细说明。
jobject obj;
jmethodID ctor;
ctor = env->GetMethodID(JavaClass,"","()V");
if(ctor != 0)//获取方法成功
{
obj = env->NewObject(JavaClass, ctor);
}
4) 调用方法
调用一个方法需要两个步骤:获取方法句柄和调用方法。
jmethodID methodID = env->GetMethodID( JavaClass, "setTest","(I)V");
if(methodID!=0)//获取方法成功
{
env->CallVoidMethod( obj, methodID,12);
}
GetStaticMethodID是用来获取静态方法的定义,GetMethodID则是获取非静态的方法定义。他们传入参数的参数依次为:类定义、方法名称和方法的定义,方法的定义可以用jdk中带的javap工具反编译class文件获取,其格式如下:
public void setTest(int inTest);
Signature: (I)V
Signature后面的内容就是方法的定义。
CallVoidMethod是对获取的方法进行调用,JNI接口中提供了一系列的同类方法,包括静态方法的调用函数(如:CallStaticXXXMethod)和非静态的方法(如:CallXXXMethod),其中XXX表示的不同方法返回类型,包括int、object等等。
5) 退出虚拟机
退出虚拟机调用方法如下:
jvm->DestroyJavaVM();
在JNI接口定义和众多文档中都说,只有最后一个线程退出时,该方法才会返回,但是我只用一个线程,调用该方法也无法返回。故此建议系统退出时执行该方法,或者整个程序退出时,让虚拟机自己释放。
[注意]:
Ø 在处理中文字符串时,需要注意Java的char是双字节的,采用Unicode编码,在和C++中的char转换时,需要用到系统API:WideCharToMultiByte和MultiByteToWideChar。
Ø 注意对运行环境中对象引用时的释放,以免引起内存泄漏。
jstring str;
wchar_t *w_buffer =(wchar_t *)env->GetStringChars(str,0);
env->ReleaseStringChars(str,(const unsigned short *)w_buffer);
3 Delphi调用C++
Delphi可以直接调用C++开发的动态链接库中的函数,但是主要调用时方法需要申明为stdcall,否则可能会引起传入的参数压栈顺序不正确。
在实际应用中我们,我们很可能会需要传递类对象或者返回类对象,我们可以通过一种变通的方式来实现。我们通过五个步骤完成这个调用过程:定义C++接口、实现C++类、编译、定义Delphi接口和调用接口。
1) 定义C++接口
接口中需要被Delphi的方法必须声明为纯虚方法,该类需要声明为导出类。
class _declspec(dllexport) BaseTest
{
public:
virtual void __stdcall seTest(char * inData)=0;
}
2) 实现C++类
class Test:public BaseTest
{
public :
void __stdcall seTest(char * inData);
}
3) 编译
在动态库中声明一个导出的函数,由该方法生成对象,返回给动态链接库调用程序。
extern "C" _declspec(dllexport) void * __stdcall CreateObject();
编译C++实现的动态链接库,以备Delphi调用。
4) 定义Delphi接口
声明与C++中定义的相同的接口,方法也需要声明为纯虚方法。
type
MyTest=class
Procedure seTest (inData:PChar); virtual;stdcall;abstract;
5) 调用接口
在Delphi中调用动态链接库有两种方式,一种是在应用程序装载时调用,另一种是在应用程序运行时调用。比较简单的方式是应用程序装载时调用,在Delphi源文件中如下声明:
function CreateObject:MyTest ; stdcall; external 'Test.dll';
调用就比较简单了了。
MyTest test:=CreateObject();
Test.seTest(‘test String’);
[注意]:
Ø 传递参数的参数最好是简单类型,Delphi的string可以用PChar类型来传递,用户需要注意字符串空间的释放。
Ø 在Delphi中释放该对象调用free会失败,所以建议在定义接口时定义一个对象释放的方法,当然也必须是纯虚的方法。

密码破解小程序的编写

对于一些没有对密码输入次数进行限制的程序,可以通过简单的重复尝试密码来进行暴力破解。更加优秀的方法是对其密码算法进行破解,当然这个方法的难度比较大。我在这里介绍的就是一种暴力破解的方法,实现也比较简单,为大家提供一些帮助。
这种破解方式一般有三个步骤:输入新密码、点击确定按钮和检测是否密码正确。
1) 输入密码
查找密码校验的对话框,通过系统的FindWindow方法查找对应的窗口。
FindWindow("TForm4",NULL);
该API的第一个参数是需要查找的对话框的类名,第二个参数是对话框的标题。如果第二个参数为空,该API不对标题进行匹配。
然后通过GetWindow方法获取到密码的输入框。
GetWindow(MyHandle,GW_CHILD);
该API的第一个参数是原始窗口的句柄,第二个参数为需要找的窗口与原始窗口的关系,其值包括GW_CHILD,GW_HWNDFIRST,GW_HWNDLAST,GW_HWNDNEXT,GW_HWNDPREV,GW_OWNER等。
最后我们就可以对该密码的输入框进行操作,可以通过向该句柄发送事件,输入新的密码和清除密码。
SendMessage(MyHandle1,WM_CHAR,0x8,0); //输入回退键,清楚密码字符
SendMessage(MyHandle1,WM_CHAR,’a’,0) ;//输入密码字符
通过循环调用SendMessage可以输入需要的字符串。
2) 点击按钮
获取按钮的句柄的方法和获取密码输入框的方法相同。获取按钮的句柄后,我们需要重新定位鼠标,然后发送鼠标点击事件到该按钮
定位鼠标的API是SetCursorPos,它的两个参数就是你需要定位的鼠标的位置。
SetCursorPos(x,y);
当然这个位置是整个屏幕的位置,可以通过ClientToScreen把窗口的相对坐标转换为屏幕的坐标。
ClientToScreen(MyHandle,&aa);
第一个参数为窗口的句柄,第二个参数为LPPOINT类型的坐标,处理结果在该LPPOINT中返回。
然后发送点击按钮事件,点击按钮事件由两个事件组成。
SendMessage(MyHandle,WM_LBUTTONDOWN,MK_LBUTTON,param);
SendMessage(MyHandle,WM_LBUTTONUP,0,param);
其中param是鼠标的X和Y坐标的值。
param=(x<<16)+y;
3) 检测结果
由于各个程序对密码正确的处理方式各不相同,大家可以根据事件情况来进行密码的正确性校验。
当然前面介绍的这些技巧我们也可以用来完成其他功能的程序,比如一个鼠标自动点击程序、文本自动输入等。
注:
对于窗口的类名、相关信息和各个窗口间的关系,我们可以通过VC带的SPY++的工具方便的进行查看。

Friday, December 01, 2006

终于可以在国内访问了~~

以后会常来看看,努力更新!:)

This page is powered by Blogger. Isn't yours?