Friday, June 29, 2007

常见Framework架构风格


· ConfigurationcPramework


· Plug-IncFramework


· CachecFramework


· BusinesscLogiccFramework


· FactorycFramework


· DaracConnectioncFramework


· SettingscFramework


· SerialcFramework


· SecuritycFramework


· ProfilecFramework


· Context Framework


Tuesday, June 26, 2007

我所关注的


我所关注的:


行业,业务,市场:PDM\PLMERPBISearch


经济,财经,股市,基金。


专家的blog,他们是如何观察,思考,表达的。


Job:关注职场需求。


人物: IT,CEO,...



连着两天把前年剩下的年假给休完了。也没去哪,就呆家里睡足了觉。结果就是现在凌晨一点了还睡不着,索性爬起来写点东西。

 

工作满两年了,越来越浮躁了,压力也多了起来。过去的两年里,晚上经常梦见自己又坐在高三的教室里奋力读书,准备参加高考,这样的梦好像不下十次吧。也不知道这究竟意味着什么?是因为平时压力太大了然后借用高三来折射呢,还是冥冥中告诫自己应该继续读书呢?我不知道。

 

 

Friday, June 22, 2007

开始关注业务

开始关注企业计算级业务了,主要领域有:

 

1.       CADCAX

2.       PDM/PLM

3.       ERP,CRM

4.       BI.

 

 

Thursday, June 21, 2007

const

 

pointer

T const * p ó const T * p;

 

T * const p;

 

T const * const p ó const T * const p;

 

 

Character array literal.

Char* p = "kaven";

P[1] = 'A';            //no compile time error! But will got runtime error!!

 

Const char* cp = "kaven";

Cp[1] = 'A';         //compile error!!

 

Char ap[] = "kaven";

Ap[1] = 'A';         //ok!

 

         Character array literals "kaven" is created by the compiler as a constant character array .

         So suggest that use const char* if you don't want it to be changed.

         If you want to be able to modify the string ,put it in array: char ap[];

 

Assignment and type cheching

Int d = 1;

Const int e = 2;

Int* pd = &d;                     //ok!

Int* pe = &e;                     //error!  Can not assign the address of a const object to a non-const pointer.

                                                //because you are saying you might change the object via the pointer.

Int const *cp = &e;          //ok now.

Int * const cp = &e;         //error too.

Int* pe = (int*)&e;          //illegal but bad practice.

 

         You can assign the address of a non-const object to a const pointer.

         But you can't assign the address of a const object to a non-const pointer!

 

Function

Const T func( const T& a) const;

 

让函数返回一个常量值经常可以在不降低安全性和效率的情况下减少用户出错的几率,防止对返回值进行赋值或修改.

const T& a) const;

 

 

 

const成员函数的目的当然是为了指明哪个成员函数可以在const对象上被调用

 

个成员函数为const的确切含义是什?

bitwise constness的坚持者认为,当且仅当成员函数不修改对象的任何数据成员(静态数据成员除外)时,即不修改对象中任何一个比特(bit)时,这个成员函数才是const

 

 

 

尽量用"传引用"而不用"传值"

消除一些对拷贝构造函数的调

避免了所谓的"切割问题(slicing problem"

引用几乎都是通过指针来实现的,所以通过引用传递对象实际上是传递指针。因此,如果是一个很小的对象――例如int――传值实际上会比传引用更高效

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C++ ADO Database

 

http://www.yesky.com/image20010518/104525.gif

2 ADO对象模型组成

  与微软的其它数据访问模型DAORDO相比,ADO对象模型非常精炼,仅由三个主要对象ConnectionCommandRecordset和几个辅助对象组成,其相互关系如图所示。Connection对象提供OLE DB数据源和对话对象之间的关联,它通过用户名称和口令来处理用户身份的鉴别,并提供事务处理的支持;它还提供执行方法,从而简化数据源的连接和数据检索的进程。Command对象封装了数据源可以解释的命令,该命令可以是SQL命令、存储过程或底层数据源可以理解的任何内容。Record set用于表示从数据源中返回的表格数据,它封装了记录集合的导航、记录更新、记录删除和新记录的添加等方法,还提供了批量更新记录的能力。其它辅助对象则分别提供封装ADO错误、封装命令参数和封装记录集合的列

 

3 VC 调用ADO基本方法

 

1 import导入ADO COM 文件msado15.dll

  例如:

#import "C:\Program Files\Common Files\System\ADO\msado15.dll"\no_namespace


  2COM 使用时初始化

HRESULT ComInit()
{
 HRESULT hr = S_OK; // 默认返回值
 if FAILED(CoInitialize(NULL)) // COM 初始化调用
 {
  CoUninitialize();
  hr = E_UNEXPECTED;
 }
 return hr;
}


  3、建立数据库连接

HRESULT ConnectToDB( LPSTR pUserId , // 用户名
 LPSTR pConnString, // 连接字串
 LPSTR pUserPassword , // 用户密码
 ConnectOptionEnum ConnectOption) // 连接参数
 {
  HRESULT hr = S_OK; // 默认返回值
  _ConnectionPtr ptrConn; // 定义Connection对象
  try
  {
   // 创建一个连接实体
   hr = ptrConn.CreateInstance(__uuidof(Connection));
   // 设定连接等待的最大秒数,默认是15
   ptrConn->ConnectionTimeout = 20
   // 打开连接
   hr = ptrConn->Open(pConnString,
   pUserId,
   pUserPassword,
   ConnectOption);
   return hr;
  }
  catch(_com_error &pComError)
  {
   …… // 错误处理
   return E_UNEXPECTED;
  }
 }

 

关闭一个库连接 如果连接状态有效,则用Close方法关闭它并赋于它空值。代码如下所示:

if(m_pConnection->State)
m_pConnection->Close();
m_pConnection= NULL;


  4.执行一个SQL 查询,得到数据集(recordset

_RecordsetPtr GetRecordSet(LPSTR strSql, _ConnectionPtr ptrConn)
{
 try
 {
  RecordsetPtr ptrRS // recordset 对象
  // 创建recordset 对象实体
  ptrRS.CreateInstance(__uuidof(Recordset));
  ptrRS->Open( strSql,
   ptrConn.GetInterfacePtr(),
   adOpenForwardOnly,
   adLockUnspecified,
   adCmdText);
 或者
  ptrRS = ptrConn ->Execute(m_ strSql,NULL, adCmdText);
  return ptrRS;
 }
 catch(_com_error &a_pComError)
 {
  ….// 错误处理
  return NULL;
 }
}


  5.通过数据集(recordset)得到列的名称

HRESULT GetColumnNames(
 _RecordsetPtr ptrRs, // recordset 对象
 char strColNames[][255],
 DataTypeEnum iColTypes[])
 {
  try
  { // 参数变量
   _variant_t l_vaIndex;
   l_vaIndex.vt = VT_I2;
   // COLUMNS总数
   long lColCount;
   lColCount = ptrRs ->Fields->Count;
   // 循环取得列的属性和名称
   for(int iIndex = 0 ; iIndex < lColCount; iIndex++)
   {
    l_vaIndex.iVal = iIndex; // 设置循环索引
    // 取得字段名称
    sprintf(strColNames[iIndex], "%s",(LPSTR)ptrRs ->Fields->GetItem(l_vaIndex)->Name);
    // 取得字段属性
    iColTypes = ptrRs ->Fields->GetItem(l_vaIndex)->Type;
   }
  }
  return S_OK;
 }
 catch(_com_error &a_pComError)
 {
  …. // 错误处理
  return E_UNEXPECTED;
 }
 catch(...)
 {
  …. // 错误处理
  return E_UNEXPECTED;
 }
}


  6.通过数据集(recordset)得到当前行记录

HRESULT getOneRecord(
 _RecordsetPtr ptrRs,
 const long lNoOfColumns,
 _variant_t varValue[])
 {
  try
  {
   // 参数变量
   _variant_t l_vaIndex;
   l_vaIndex.vt = VT_I2;
   // 循环取得列的值
   for(long lIndex = 0; lIndex < lNoOfColumns; lIndex ++)
   {
    l_vaIndex.iVal = lIndex;
    // 取得字段值
    varValue[lIndex]= ptrRs->Fields->GetItem(l_vaIndex)->Value;
   }
   return S_OK;
  }
  catch(_com_error &a_pComError)
  {
   …. // 错误处理
   return E_UNEXPECTED;
  }
  catch(...)
  {
   …. // 错误处理
   return E_UNEXPECTED;
  }
 }


  7.出错情况下错误信息的取得

void ErrorFunc(_com_error &pComError, _ConnectionPtr ptrConn);
{
 // COM 错误取得
 // 当执行COM功能的时候,如果出错,可以捕捉到_com_error的异常
 char lpComErrorStr512];
 sprintf(lpComErrorStr512,"ErrorCode = %08lx \
  Error Message = %s \
  Source = %s \
  Description = %s ",
  pComError.Error(), // 错误编号
  pComError.ErrorMessage(),// 错误信息
  (LPCSTR) pComError.Source(),// 错误源
  (LPCSTR) pComError.Description());// 错误描述
  // 通过上面的代码我们可以看出,_com_error对象中可以得到COM所有出错的信息
  // ADO错误取得
  ErrorPtr pErr = NULL;
  if( (ptrConn ->Errors->Count) > 0)
  {
   long nCount = ptrConn ->Errors->Count;
   for(long i = 0; i < nCount; i++)
   {
    pErr = a_pConnPtr->Errors->GetItem(i);
    char l_pchErrorString[512];
    sprintf(l_pchErrorString,"Error:\n Error number: %x\t%s",
     pErr->Number, // 错误编号
     pErr->Description); // 错误描述
   }
  }
  // ADO 处理出错的情况下, connection对象里面都有记录,可以通过访问
  // connection 对象取得错误编号和错误信息。

插入记录 可以先用AddNew()方法新增一个空记录,再用PutCollect(字段名,)输入每个字段的值,最后再Update()更新到库中数据既可。其中变量m_Namem_Age分别为姓名及年龄编辑框的成员变量名。代码所下所示:

try
{
//
写入各字段值
m_pRecordset->AddNew();
m_pRecordset->PutCollect("Name", _variant_t(m_Name));
m_pRecordset->PutCollect("Age", atol(m_Age));
m_pRecordset->Update();

AfxMessageBox("
插入成功!");
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}


   移动记录指针 移动记录指针可以通过MoveFirst()方法移动到第一条记录、MoveLast()方法移动到最后一条记录、MovePrevious()方法移动到当前记录的前一条记录、MoveNext()方法移动到当前记录的下一条记录。但我们有时经常需要随意移动记录指针到任意记录位置时,可以使用Move(记录号)方法来实现,注意: Move()方法是相对于当前记录来移动指针位置的,正值向后移动、负值向前移动,如:Move(3),当前记录是3时,它将从记录3开始往后再移动3条记录位置。代码如下所示:

try
{
int curSel = m_AccessList.GetCurSel();
//
先将指针移向第一条记录,然后就可以相对第一条记录来随意移动记录指针
m_pRecordset->MoveFirst();
m_pRecordset->Move(long(curSel));

}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}


  改记录中字段值 可以将记录指针移动到要修改记录的位置处,直接用PutCollect(字段名,值)将新值写入并Update()更新数据库既可。可以用上面方法移动记录指针,修改字段值代码如下所示:

try
{
//
假设对第二条记录进行修改
m_pRecordset->MoveFirst();
m_pRecordset->Move(1); //
0开始
m_pRecordset->PutCollect("Name", _variant_t(m_Name));
m_pRecordset->PutCollect("Age", atol(m_Age));
m_pRecordset->Update();
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}


   删除记录 删除记录和上面修改记录的操作类似,先将记录指针移动到要修改记录的位置,直接用Delete()方法删除它并用Update()来更新数据库既可。代码如下所示:

try
{
//
假设删除第二条记录
m_pRecordset->MoveFirst();
m_pRecordset->Move(1); //
0开始
m_pRecordset->Delete(adAffectCurrent); //
参数adAffectCurrent为删除当前记录
m_pRecordset->Update();
}
catch(_com_error *e)
{
AfxMessageBox(e->ErrorMessage());
}


   关闭记录集 直接用Close方法关闭记录集并赋于其空值。代码如下所示:

m_pRecordset->Close();
m_pRecordset = NULL;


  3. CommandPtr智能指针,可以使用_ConnectionPtr_RecordsetPtr来执行任务,定义输出参数,执行存储过程或SQL语句。

   执行SQL语句 先创建一个_CommandPtr实例指针,再将库连接和SQL语句做为参数,执行Execute()方法既可。代码如下所示:

_CommandPtr m_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
m_pCommand->ActiveConnection = m_pConnection; //
将库连接赋于它
m_pCommand->CommandText = "SELECT * FROM DemoTable"; // SQL
语句
m_pRecordset = m_pCommand->Execute(NULL, NULL,adCmdText); //
执行SQL语句,返回记录集


   执行存储过程 执行存储过程的操作和上面执行SQL语句类似,不同点仅是CommandText参数中不再是SQL语句,而是存储过程的名字,如Demo。另一个不同点就是在Execute()中参数由adCmdText(执行SQL语句),改为adCmdStoredProc来执行存储过程。如果存储过程中存在输入、输出参数的话,需要使用到另一个智能指针_ParameterPtr来逐次设置要输入、输出的参数信息,并将其赋于_CommandPtrParameters参数来传递信息,有兴趣的读者可以自行查找相关书籍或MSDN。执行存储过程的代码如下所示:

_CommandPtr m_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
m_pCommand->ActiveConnection = m_pConnection; //
将库连接赋于它
m_pCommand->CommandText = "Demo";
m_pCommand->Execute(NULL,NULL, adCmdStoredProc);

 

Tuesday, June 19, 2007

湖北人没什么可牛的


今天端午节 , 转文赏一下


湖北人没什么可牛B的,只是出了2个国家主席而已.
  湖北人没什么可牛B的,只是共和国十大元帅中有3个而已。
  湖北人没什么可牛B的,只是很能吃吃香喝辣而已。
  湖北人没什么可牛B的,只是N次火车的终点站而已。
  湖北人没什么可牛B的,只是省会在武汉3个城市组成而已。
  湖北人没什么可牛B的,只是有时候会被人说9头鸟而已。
  湖北人没什么可牛B的,只是古代4大美女王昭君故乡而已
  湖北人没什么可牛B的,只是长江三峡世界闻名而已
  湖北人没什么可牛B的,只是光谷新区让中央和建设部的那帮人吓得乱开会而已。
  湖北人没什么可牛B的,只是武汉大学和华中科技进了前十而已。
  湖北人没什么可牛B的,只是悉尼奥运会上拿了中国金牌总数的四分之三而已。
  
  湖北人没什么可牛B的,只是一个湖北人在跳了河,全国人为他过节而已!
  
  ------预祝端午愉快......


Thursday, June 14, 2007

How to install and debug windows NT Services.

Support you have a Windows NT Service named YourServices.exe

 

How to install Windows NT Services?

C++:       yourService.exe �I [Parameter]

.Net:      InstallUtil yourService.exe

 

Notice: when the Windows services is installed, it will registry here:

HHEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services

 

How to uninstall Windows NT Services?

C++:     yourService.exe �u 

.Net:     InstallUtil /u yourService.exe

 

How to start Windows NT Services?

                Net start "Your Service"

 

Notice:

.Net:      "Your Service"ProjectInstaller类里 System.ServiceProcess.ServiceInstaller.ServiceName的值,它是你在Service管理器里可以看到的Service名称

C++:      

 

How to Debug Windows NT Services?

大致步骤

1. 
建立windows服务程序,不管你是用win32 API,还是ATL,还是.NET,都可以实现的。
2.
将代码写好,编译,保证没有错误。
3.
现在到了调试的过程了,首先,在IDE中,找到你要调试的地方,加上断点,然后安装好服务。
服务的安装,其实就是设计到注册表的操作。在.NET平台下,我一般习惯用installutil这个程序来安装服务。安装好之后,我们打开控制面板的"Administrator Tools",找到"Services"选项,双击打开。在服务列表中,我们能找到我们安装的服务。然后运行这个服务。
4.
现在到了关键的地方,在服务开始的时候,就会执行你的代码。为了调试的方便,在服务的代码开始处(i.e: CMyService::OnInit()) 让程序先Sleep20,这样的好处看后面你就知道了。
5.
切换回我们的IDE,在Debug菜单,里面有个Process(进程)选项。点击打开,在对话框的进程列表中,选择我们的服务相对应的进程,然后点击旁边的Attach(附加)按钮。这样,IDE就开始加载我们要调试的进程了。加载完毕之后,程序会停顿一下。(为什么?因为我们在服务的开始代码处,Sleep20秒,喝口水等一下吧)。之后,黄色的光标,跳动到你的断点处,现在就可以像以往一样,来调试这个windows服务了。
之所以在程序开始的地方Sleep20秒,是为我们在IDE附加这个Service进程的时候,留下一段缓冲的时间,不然当你的服务开始运行的时候,你的断点处的代码早就被执行过了,你断点自然不起作用了。明白了吧?呵呵。

还有一些调试windows服务小的窍门,就是写日志文件。这也是调试windows service的一个比较好的方法,虽然比较笨,但是通过阅读你自己留下的日志,也能起到不小的作用的。所以在编写程序的时候,要在关键的地方,记录日志,也是为以后程序的维护提供方便

 

总结如下:

1.       可以用Attach Process的方式调试windows service程序。

2.       对于初始化部分的代码(Init()),应为在Attach process前就已经执行了,所以可以用延时的方法Sleep()调试。

3.       如果想debug安装部分的代码,可以在Property /Debuging/Command Argument 里输入i ,再将windows services 工程设置为启动工程即可。

4.       最后就是写log的方法调试。

 

 

Monday, June 11, 2007

How to write BOM(Byte of Marker) into file using fstream

The Byte Order Marker (BOM) is Unicode character U+FEFF. (It can also represent a Zero Width No-break Space.) The code point U+FFFE is illegal in Unicode, and should never appear in a Unicode character stream. Therefore the BOM can be used in the first character of a file (or more generally a string), as an indicator of endian-ness. With UTF-16, if the first character is read as bytes FE FF then the text has the same endian-ness as the machine reading it. If the character is read as bytes FF FE, then the endian-ness is reversed and all 16-bit words should be byte-swapped as they are read-in. In the same way, the BOM indicates the endian-ness of text encoded with UTF-32.

Note that not all files start with a BOM however. In fact, the Unicode Standard says that text that does not begin with a BOM MUST be interpreted in big-endian form.

The character U+FEFF also serves as an encoding signature for the Unicode Encoding Forms. The table shows the encoding of U+FEFF in each of the Unicode encoding forms. Note that by definition, text labeled as UTF-16BE, UTF-32BE, UTF-32LE or UTF-16LE should not have a BOM. The endian-ness is indicated in the label.

For text that is compressed with the SCSU (Standard Compression Scheme for Unicode) algorithm, there is also a recommended signature.

Encoding Form

BOM Encoding

UTF-8

EF BB BF

UTF-16
(big-endian)

FE FF

UTF-16
(little-endian)

FF FE

UTF-16BE, UTF-32BE
(big-endian)

No BOM!

UTF-16LE, UTF-32LE
(little-endian)

No BOM!

UTF-32
(big-endian)

00 00 FE FF

UTF-32
(little-endian)

FF FE 00 00

SCSU
(compression)

0E FE FF

 

 

Solutions:

 

Case 1: using ANSI std::ofstream:

 

wchar_t BOM = 0xFEFF;
std::ofstream outFile("filename.dat", std::ios::out | std::ios::binary);
outfile.write((char *) &BOM,sizeof(wchar_t));

 

Case 2: using ANSI std::wofstream:

 

  const wchar_t BOM = 0xFEFF;

  const char *fname = "abc.txt";

  std::wofstream wfout;

  wfout.open(fname,ios_base::binary);
 

   //S1:

   testFile << BOM;

 

   //S2:

   //testFile.put(BOM);