VC计算文件摘要

1、VC计算文件MD5摘要

//输入:文件路径
//输出:128位MD5摘要
#include <wincrypt.h>
CString szResult;

CString CMD5AndSHA1Dlg::CalcMD5(CString strFilePath)
{
  //读取文件
  CFile inFile;
  CFileException ex;
  DWORD dwLength=0;
  BYTE* pbContent=NULL;
  BOOL bRet;

  bRet=inFile.Open(strFilePath,CFile::modeRead | CFile::typeBinary | CFile::shareDenyWrite,&ex);
  if(bRet==FALSE)
  {
    return TEXT("Error opening file");;
  }

  dwLength = (DWORD)inFile.GetLength();
  pbContent = new BYTE[dwLength];
  if(pbContent==NULL)
  {
    return TEXT("Error not enough memory");;
  }
  inFile.Read(pbContent,dwLength);
  inFile.Close();

  //计算MD5编码
  HCRYPTPROV hCryptProv; 
  HCRYPTHASH hHash; 
  BYTE byteMD5[16]; 
  DWORD dwHashLen=16;
  CString szResult;

  if(CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) 
  {
    if(CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash)) 
    {
      if(CryptHashData(hHash, pbContent, dwLength, 0))
      {

        if(CryptGetHashParam(hHash, HP_HASHVAL, byteMD5, &dwHashLen, 0)) 
        {
          szResult.Format(TEXT("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"),
            byteMD5[0],byteMD5[1],byteMD5[2],byteMD5[3],byteMD5[4],byteMD5[5],byteMD5[6],byteMD5[7]
            ,byteMD5[8],byteMD5[9],byteMD5[10],byteMD5[11],byteMD5[12],byteMD5[13],byteMD5[14],byteMD5[15]);
        }
        else
        {
          szResult=TEXT("Error getting hash param");
        }

      }
      else
      {
        szResult=TEXT("Error hashing data");
      }
    }
    else
    {
      szResult=TEXT("Error creating hash");
    }
  }
  else
  {
    szResult=TEXT("Error acquiring context");
  }

  CryptDestroyHash(hHash); 
  CryptReleaseContext(hCryptProv, 0); 
  delete[] pbContent;
  pbContent=NULL;

  return szResult;
}

2、VC计算文件SHA1摘要

//输入:文件路径(文件必须小于2^64bit)
//输出:160位SHA1摘要
#include <wincrypt.h>
CString szResult;

CString CMD5AndSHA1Dlg::CalcSHA1(CString strFilePath)
{
  //读取文件
  CFile inFile;
  CFileException ex;
  DWORD dwLength=0;
  BYTE* pbContent=NULL;
  BOOL bRet;

  bRet=inFile.Open(strFilePath,CFile::modeRead | CFile::typeBinary | CFile::shareDenyWrite,&ex);
  if(bRet==FALSE)
  {
    return TEXT("Error opening file");;
  }

  dwLength = (DWORD)inFile.GetLength();
  pbContent = new BYTE[dwLength];
  if(pbContent==NULL)
  {
    return TEXT("Error not enough memory");;
  }
  inFile.Read(pbContent,dwLength);
  inFile.Close();

  //计算MD5编码
  HCRYPTPROV hCryptProv; 
  HCRYPTHASH hHash; 
  BYTE byteSHA1[20]; 
  DWORD dwHashLen=20;
  CString szResult;

  if(CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) 
  {
    if(CryptCreateHash(hCryptProv, CALG_SHA1, 0, 0, &hHash)) 
    {
      if(CryptHashData(hHash, pbContent, dwLength, 0))
      {

        if(CryptGetHashParam(hHash, HP_HASHVAL, byteSHA1, &dwHashLen, 0)) 
        {
          szResult.Format(TEXT("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"),
            byteSHA1[0],byteSHA1[1],byteSHA1[2],byteSHA1[3],byteSHA1[4],byteSHA1[5],byteSHA1[6],byteSHA1[7],
            byteSHA1[8],byteSHA1[9],byteSHA1[10],byteSHA1[11],byteSHA1[12],byteSHA1[13],byteSHA1[14],byteSHA1[15],
            byteSHA1[16],byteSHA1[17],byteSHA1[18],byteSHA1[19]);
        }
        else
        {
          szResult=TEXT("Error getting hash param");
        }

      }
      else
      {
        szResult=TEXT("Error hashing data");
      }
    }
    else
    {
      szResult=TEXT("Error creating hash");
    }
  }
  else
  {
    szResult=TEXT("Error acquiring context");
  }

  CryptDestroyHash(hHash); 
  CryptReleaseContext(hCryptProv, 0); 
  delete[] pbContent;
  pbContent=NULL;

  return szResult;
}

函数调用约定

1.STDCALL
微软制定的c语言调用约定,也就是传说中的WINAPI
参数从右向左进行压栈(微软官方文档说从左向右压栈),返回值存在eax中,
被调用函数清理栈,不允许变长参数列表
函数名前增加_,函数名后增加@和参数个数(32位机器上一定被4整除)
ret指令中,有可选参数指定有多少字节需要弹栈

2.CDECL
c语言的默认调用约定
参数从右向左进行压栈,返回值存在eax中,
调用函数清理栈,可用变长参数列表
函数名前会增加_

3.FASTCALL
非跨编译器的c语言调用约定,
前2-3个4字节内的参数被存放到寄存器(edx,eax,ecx)中,其他参数,或大于4字节的参数,按从右到左的顺序被存放在栈中
多数情况下,调用函数负责清理栈
仅适用于参数少,而且实时性要求很高的情况下
函数名前增加@,函数名后增加@和参数个数

4.THISCALL
cpp的调用约定
参数从右向左进行压栈,返回值存在eax中,this指针存在ecx中
每种编译器对函数名称有不同的修饰方法,但基于重载的需要,一般都会增加参数类型和类名
当使用extern “C”的时候,cpp编译器会使用c语言的修饰方法来修饰函数名,这样就可以方便的供别人调用了。

VC中Socket通信基础

1.客户端程序
SocketClientBlock.c

#pragma comment(lib, "WS2_32")

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>

#define BUF_SIZE (1024)

int main(int argc,char **argv) 
{
  WSADATA wsadata;

  int  sockfd,n;
  char  receline[BUF_SIZE];
  struct  sockaddr_in serveraddr;

  //Winsows下启用socket
  if(WSAStartup(MAKEWORD(1,1),&wsadata)==SOCKET_ERROR)
  {
    printf("WSAStartup() fail\n");
    exit(0);
  }

  //建立socket
  if((sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
  {
    printf("socket() fail\n");
    exit(0);
  }

  //设置协议,IP及Port
  memset(&serveraddr,0,sizeof(serveraddr));
  serveraddr.sin_family = AF_INET;
  serveraddr.sin_port=htons(1024);
  serveraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
  /*
  //hostname转ip
  hostent *hostEnt = gethostbyname( strRemote );
  if( hostEnt != NULL )
  {
    lIPAddress = ((in_addr*)hostEnt->h_addr)->s_addr;
    serveraddr.sin_addr.s_addr = lIPAddress;
  }
  else
  {
    serveraddr.sin_addr.s_addr = inet_addr( strRemote );
  }
  */

  //连接
  if(connect(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr))==SOCKET_ERROR)
  {
    printf("connect() fail\n");
    exit(0);
  }

  //读取数据并输入到标准输出
  while((n=recv(sockfd,receline,BUF_SIZE,0))!=SOCKET_ERROR)
  { 
    receline[n]=0;
    if(fputs(receline,stdout)==EOF)
    {
      printf("fputs() error\r\n");
    }
  }

  //没有获取数据
  if(n<0) 
  {
    printf("read() fail\n");
  }

  //Winsows下关闭socket
  closesocket(sockfd);
  WSACleanup();

  exit(0);
}
&#91;/code&#93;

2.服务端程序,阻塞
SocketServerBlock.c
&#91;code lang="c"&#93;
#pragma comment(lib, "WS2_32")

#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define MAX_CONNECT_NUM (10)

int main(int argc,char** argv)
{
  WSADATA wsadata;
  struct  sockaddr_in serveraddr;
  int  socketflag,connectflag;

  time_t  time_tick;
  char  buff[1024];
  
  //Winsows下启用网络
  if(WSAStartup(MAKEWORD(1,1),&wsadata)==SOCKET_ERROR)
  {
    printf("WSAStartup() fail,%d\n", WSAGetLastError());
    exit(0);
  }
  
  //新建socket
  if(INVALID_SOCKET==(socketflag=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)))
  {
    printf("socket() fail\n");
    exit(0);
  }

  //清零,设置协议,设置IP,设置Port
  memset(&serveraddr,0,sizeof(serveraddr));
  serveraddr.sin_family=AF_INET;
  serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
  serveraddr.sin_port=htons(1024);

  //绑定端口,监听1024端口的任何请求
  if(SOCKET_ERROR==bind(socketflag,(struct sockaddr*)&serveraddr,sizeof(serveraddr)))
  {
    printf("bind() fail\n");
    exit(0);
  }

  //监听端口,最大并发数MAX_CONNECT_NUM
  if(SOCKET_ERROR==listen(socketflag,MAX_CONNECT_NUM))
  {
    printf("listen() fail\n");
    exit(0);
  }

  //接受请求,发送主机时间
  for(;;)
  {
    struct  sockaddr clientaddr;
    int iLen = sizeof(clientaddr);
    printf("Waiting for connection...\n");

    //接受连接
    connectflag=accept(socketflag,&clientaddr,&iLen);
    if(INVALID_SOCKET==connectflag)
    {
      printf("accept() fail\n");
      exit(0);
    }

    //获取时间,并格式化
    time_tick=time(NULL);
    sprintf(buff,"From mys:\n%s",ctime(&time_tick));
    //写入时间
    if(SOCKET_ERROR==send(connectflag,buff,strlen(buff),0))
    {
      printf("send() fail\n");
      exit(0);
    }

    //关闭连接
    closesocket(connectflag);
  }

  //Winsows下关闭socket及网络
  if(SOCKET_ERROR==closesocket(socketflag))
  {
    printf("closesocket() fail\n");
    exit(0);
  }
  WSACleanup();

  //退出
  exit(0);
}

3.服务端程序,阻塞,有Select
SocketServerBlockSelect.c

#pragma comment(lib, "WS2_32")

#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define MAX_CONNECT_NUM (10)

int main(int argc,char** argv)
{
  WSADATA wsadata;
  struct  sockaddr_in serveraddr;
  int  socketflag,connectflag;

  fd_set  fdR;
  struct  timeval timeout ={1,500};

  time_t  time_tick;
  char  buff[1024];
  
  //Winsows下启用网络
  if(WSAStartup(MAKEWORD(1,1),&wsadata)==SOCKET_ERROR)
  {
    printf("WSAStartup() fail,%d\n", WSAGetLastError());
    exit(0);
  }
  
  //新建socket
  if(INVALID_SOCKET==(socketflag=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)))
  {
    printf("socket() fail\n");
    exit(0);
  }

  //清零,设置协议,设置IP,设置Port
  memset(&serveraddr,0,sizeof(serveraddr));
  serveraddr.sin_family=AF_INET;
  serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
  serveraddr.sin_port=htons(1024);

  //绑定端口,监听1024端口的任何请求
  if(SOCKET_ERROR==bind(socketflag,(struct sockaddr*)&serveraddr,sizeof(serveraddr)))
  {
    printf("bind() fail\n");
    exit(0);
  }

  //监听端口,最大并发数MAX_CONNECT_NUM
  if(SOCKET_ERROR==listen(socketflag,MAX_CONNECT_NUM))
  {
    printf("listen() fail\n");
    exit(0);
  }

  //接受请求,发送主机时间
  for(;;)
  {
    printf("Waiting for connection...\n");

    FD_ZERO(&fdR);
    FD_SET(socketflag, &fdR);
        switch (select(0, &fdR, NULL, NULL,&timeout)) 
    {
    case SOCKET_ERROR:
      printf("Socket Error...\n");
      break;
    case 0:
      printf("Time Out Here...\n");
      break;
    default:
      printf("Connection is coming...\n");
      if (1)
      {
        struct  sockaddr clientaddr;
        int iLen = sizeof(clientaddr);

        //接受连接
        connectflag=accept(socketflag,&clientaddr,&iLen);
        if(INVALID_SOCKET==connectflag)
        {
          printf("accept() fail\n");
          exit(0);
        }

        //获取时间,并格式化
        time_tick=time(NULL);
        sprintf(buff,"From mys:\n%s",ctime(&time_tick));
        //写入时间
        if(SOCKET_ERROR==send(connectflag,buff,strlen(buff),0))
        {
          printf("send() fail\n");
          exit(0);
        }

        //关闭连接
        closesocket(connectflag);
      }
      break;
        }
  }

  //Winsows下关闭socket及网络
  if(SOCKET_ERROR==closesocket(socketflag))
  {
    printf("closesocket() fail\n");
    exit(0);
  }
  WSACleanup();

  //退出
  exit(0);
}

4.服务端程序,非阻塞
SocketServerNonBlock.c

#pragma comment(lib, "WS2_32")

#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define MAX_CONNECT_NUM (10)

int main(int argc,char** argv)
{
  WSADATA wsadata;
  struct  sockaddr_in serveraddr;
  int  socketflag,connectflag;

  time_t  time_tick;
  char  buff[1024];

  unsigned long flag=1;
  
  //Winsows下启用网络
  if(WSAStartup(MAKEWORD(1,1),&wsadata)==SOCKET_ERROR)
  {
    printf("WSAStartup() fail,%d\n", WSAGetLastError());
    exit(0);
  }
  
  //新建socket
  if(INVALID_SOCKET==(socketflag=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)))
  {
    printf("socket() fail\n");
    exit(0);
  }

  //非阻塞方式
  if(ioctlsocket(socketflag,FIONBIO,&flag)!=0)
  {
    printf("ioctlsocket() fail\n");
    exit(0);
  }

  //清零,设置协议,设置IP,设置Port
  memset(&serveraddr,0,sizeof(serveraddr));
  serveraddr.sin_family=AF_INET;
  serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
  serveraddr.sin_port=htons(1024);

  //绑定端口,监听1024端口的任何请求
  if(SOCKET_ERROR==bind(socketflag,(struct sockaddr*)&serveraddr,sizeof(serveraddr)))
  {
    printf("bind() fail\n");
    exit(0);
  }

  //监听端口,最大并发数MAX_CONNECT_NUM
  if(SOCKET_ERROR==listen(socketflag,MAX_CONNECT_NUM))
  {
    printf("listen() fail\n");
    exit(0);
  }

  //接受请求,发送主机时间
  for(;;)
  {
    struct  sockaddr clientaddr;
    int iLen = sizeof(clientaddr);
    printf("Waiting for connection...\n");

    //接受连接
    connectflag=accept(socketflag,&clientaddr,&iLen);
    if(INVALID_SOCKET==connectflag)
    {
      printf("no connection\n");
      Sleep(1500);
    }
    else
    {
      printf("Connection is coming...\n");
      //获取时间,并格式化
      time_tick=time(NULL);
      sprintf(buff,"From mys:\n%s",ctime(&time_tick));
      //写入时间
      if(SOCKET_ERROR==send(connectflag,buff,strlen(buff),0))
      {
        printf("send() fail\n");
        exit(0);
      }

      //关闭连接
      closesocket(connectflag);
    }
  }

  //Winsows下关闭socket及网络
  if(SOCKET_ERROR==closesocket(socketflag))
  {
    printf("closesocket() fail\n");
    exit(0);
  }
  WSACleanup();

  //退出
  exit(0);
}

VC判断UTF-8与ANSI

大家知道,如果只有英文的话,UTF-8与ANSI是一样的
但有了中文以后,情况就很不一样了,
在ANSI中,比如GBK,中文占两字节,
在UTF-8中,中文占三字节,
当中英文混合时,情况就更复杂一些了。
下面一段是在以前项目中,先判断是UTF-8还是GBK然后转为UNICODE的代码

//要判断内容
char *s1="....";
//字符编码
UINT CodePage=0;
//字符串长度
int nLen=strlen(s1);

//判断是否为UTF-8
//至少要3字节
if(nLen>=3)
{
    unsigned char U1,U2,U3;
    int nNow=0;
    while(nNow<nLen)
    {
        U1=(unsigned)s1&#91;nNow&#93;;
        if((U1&0x80)==0x80)
        {
            //中文字符,则要三个字符
            if(nLen>nNow+2)
            {
                U2=(unsigned)s1[nNow+1];
                U3=(unsigned)s1[nNow+2];
                //中文三字节为0xE0 0xC0 0xC0
                if(((U1&0xE0)==0XE0) && ((U2&0xC0)==0x80) && ((U3&0xC0)==0x80))
                {
                    //有可能是UTF-8
                    CodePage=65001;
                    nNow=nNow+3;
                }
                else
                {
                    //不是UTF-8
                    CodePage=0;
                    break;
                }
            }
            else
            {
                //不是UTF-8
                CodePage=0;
                break;
            }
        }
        else
        {
            //非中文字符
            nNow++;
        }
    }
}

DWORD dwNum;
dwNum=MultiByteToWideChar(CodePage,0,s1,-1,NULL,0);
if(dwNum)
{
    wchar_t *pwText;
    pwText=new TCHAR[dwNum];
    if(pwText)
    {
        MultiByteToWideChar(CodePage,0,s1,-1,pwText,dwNum);
    }
    szPatientName=pwText;
    delete []pwText;
}

CDatabase中Open与OpenEx

昨天帮同事调试一段Windows服务代码,
其实很简单,但诡异的是,在服务中一直无法Catch到数据库无法连接的错误。

一开始,我以为他没有加载资源,少了:

AfxSetResourceHandle(GetModuleHandle(NULL));

这句话

后来发现,错误根本无法catch到,运行后直接向死了一样,等了几分钟还是这样
后来仔细看了下代码

db.OpenEx(szConnect);

天啊
直接换成

db.Open(szConnect);

问题解决。

Service里弹什么对话框啊,晕。

一个有趣的VC6题目

在VC6 Debug环境下,要求填补一段代码,使输入与输出一致。

    #Include <Stdio.h>  
    void test()  
    {  
        int t;  
        scanf("%d", &t);  
       /*  
       在这里填写代码…… 
       */  
    }  
    int main()  
    {  
        int m;  
        test();  
        printf("%d", m);  
    }  

方法一:

    int* pt = &t;  
    pt += 22;  
    *pt = t  

方法二:

    #Include <windows.h>  
    DWORD addrEbp;  
    _asm  
    {  
        mov addrEbp,ebp;  
    }  
    DWORD *pm=(DWORD *)(addrEbp + 0x80-0x28 -4);  
    *pm=t;  

两种方法都是直接修改了内存,呵呵,还是蛮有意思的啦。