VC函数对象模板

  • 2005-10-26

    编程技巧 函数对象模板 - [C++]

    作者:rick1126
    email: [email protected]
    日期:2001-4-15 9:48:59
    FUNCTION OBJECT BASES
    要将书写函数对象的进程简单化, 标准库提供两个类模板作为这样的对象的基类: std::unary_function 和 std::binary_function. 它们都在头文件 < functional > 中声明. 根据其命名, unary_function 提供接收一个参数的基函数而 binary_function 提供一个接收两个参数的基函数.

    template < class Arg, class Res > struct
    unary_function
    {
    typedef Arg argument_type;
    typedef Res result_type;
    };

    template < class Arg, class Arg2, class Res >
    struct binary_function
    {
    typedef Arg first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Res result_type;
    };

    这些模板不提供任何有用的函数体, 它们只是确保参数和返回类型具有统一的命名. 在下面的例子里面, 谓词 is_vowel 表示一个参数, 继承自 unary_function:

    template < class T >
    class is_vowel: public unary_function< T, bool >
    {
    public:
    bool operator ()(T t) const
    {
    if ((t==’a’)||(t==’e’)||(t==’i’)||(t==’o’)||(t==’u’))
    return true;
    return false;
    }
    };
    -----------
    Danny Kalev
  • 2005-10-26

    编程技巧 使用一个模板类实现对于类成员函数的回调执行(编译自Topica) - [C++]

    作者:rick1126
    email: [email protected]
    日期:2001-4-13 9:23:39
    A GENERIC CALLBACK DISPATCHER
    可以创建一个通用的回调模板类进行成员函数的自动回调. 这样的一个模板将将类作为其成员函数的第一个参数, 第二个参数是一个指向类成员函数的指针. 关键在于将第二个参数基于第一个参数如下:

    template < class T, void (T::*F)() > class callback {/**/};

    该模板的实现十分简单: 它具备一个针对T的引用, 就是所需调用的成员函数的宿主类, 一个跟在其和一个称之为execute()的成员函数.:

    template < class T, void (T::*F)() >
    class callback
    {
    public:
    callback(T& t) : object(t) {}/*assign actual object to T*/
    void execute() {(object.*F)();}/*launch callback function*/
    private:
    T& object;
    };

    要通过一个成员指针调用成员函数, 你必须具有一个针对确定的对象的引用. 这就是为什么模板需要一个 T& 作为成员变量. 现在你需要使用回调模板来执行一个类 A 的成员函数:

    class A
    {
    public:
    void f();
    };

    你不能使用变量操作成员函数地址, 为此使用 & 操作来获得函数地址. 最后, 传递该成员函数的数组对象给模板对象.:

    int main()
    {
    A a; /*first, create an object*/
    callback < A, &A::f > c(a); /*instantiate template*/
    c.execute(); /*invoke a.f()*/
    }

    你可以使用回调模板类结合任意类型的类, 只要其成员函数具有同样的形式.
    -----------
    Danny Kalev
  • 2005-10-26

    拨号上网程序(转) - [VC专栏]

    作者:chache
    email: [email protected]
    日期:2000-10-24 1:58:04
    大家知道,在Netants、DownLoad Expert等软件中都带有定时拨号上网下载软件的功能。而
    一般用户的拨号上网,利用的是Windows的Remote Access Service(RAS,远程访问服务)。
    下面介绍一下其在Visual C++下的实现。
      Visual C++为我们提供了包含RAS API声明的“ras.h″头文件。要在程序中实现拨号
    上网功能,其大致过程如下:
      1. 利用Modem拨号进行连接,应使用RasDial函数。
      其声明如下:
      DWORD Ras Dial(LPRASDIALEXTENSIONS lpRas DialExtensions,LPCTSTR lpszPhoneboo
    k,LPRASDIALPARAMS lp Ras DialParams,DWORD dw Notifier Type,LPVOID lpv Notifier,
      LPHRASCONN lph Ras Conn )
      参数说明:
      lpRasDialExtensions和lpszPhonebook:仅在Windows NT下有效,在Windows 95下,这
    两个参数被忽略。
      lpRasDialParams:这个参数很重要,它指向一个RASDIALPARAMS结构,该结构包含以下
    几个成员:
      dwSize:应设定为sizeof(RASDIALPARAMS);
      szEntryName和szPhoneNumber:这两个参数有联系,szEntryName可以指定要建立的连接
    ,比方说“我的连接”等等,这是处理用户已经在“拨号网络”里建立的连接的。这时,Mo
    dem将拨打你在“我的连接”中设定的ISP号码,此时szPhoneNumber成员设为空字符串“”即
    可;如果你要在程序中自行指定要拨打的ISP号码的话,szEntryName可以设定为空字符串“
    ”,此时应设置szPhoneNumber为你的ISP号码(169,663等),特别的,对于用201电话卡来
    上网的情况,可以设为“201,,,账号,密码#,,ISP号码#”(其中“,”表示停顿一段时
    间(以等待确认账号,密码等),你可以根据自己所在位置的线路状况自行调节。
      SzCallBackNumber,szDomain:设为空串“”即可。
      SzUserName,szPassword:登录用户名和密码。如169公用账号guest,guest。
      其他成员不必设置。
      DwNotifierType:指定是由窗口还是由回调函数来处理确认消息。通过确认消息我们可
    以得到RasDial过程的当前状态。如“正在打开段口”,“正在验证用户名和密码”等。也可
    设为NULL。
      dwNotifier:指定处理确认消息的窗口或回调函数。也可设为NULL。
      LphRasConn:指向一个类型为HRASCONN的变量。在调用RasDial前必须指定为NULL,Ras
    Dial若成功返回,则将RAS连接的句柄存放于它所指向的变量中。我们也可以通过此句柄来断
    开连接。
      只要在程序中适当位置调用RasDial函数即可建立连接。
      2. 理确认消息以得到拨号过程的当前状态。
      我们以指定窗口来处理确认消息为例说明如何得到拨号过程的当前状态。
      在处理确认消息的对话框类(或视图类等)的实现代码中加入:
      const UINT WM_RASEVENT = ::RegisterWindowMessageA(RASDIALEVENT);
      在Message Map中手工加入消息映射:(****是你定义的对话框类名称)
      BEGIN_MESSAGE_MAP(****, CDialog)
      //AFX_MSG_MAP(****)
      ……
      ON_REGISTERED_MESSAGE(WM_RASEVENT, OnRasDialEvent)(<-加入此句)
      //AFX_MSG_MAP
      END_MESSAGE_MAP()
      加入成员函数处理消息:
      LRESULT CDialInfo::OnRasDialEvent(WPARAM wp, LPARAM lp)
      {
      RASCONNSTATE rasstate= (RASCONNSTATE)wp;
      CListBox *info =(CListBox *)GetDlgItem(IDC_INFOLIST);
      //用ListBox 控件(ID为IDC-INFOLIST)来显示状态)
      switch(rasstate)
      {
      case RASCS_OpenPort:
      info→AddString(_T(″打开端口……″));
      break;
      case RASCS_PortOpened:
      info→AddString(_T(″端口已打开.″));
      break;
      case RASCS_ConnectDevice:
      info→AddString(_T(″连接设备……″));
      break;
      case RASCS_DeviceConnected:
      info→AddString(_T(″设备已连接.″));
      break;
      case RASCS_Authenticate:
      info→AddString(_T(″验证用户及密码″));
      break;
      case RASCS_Authenticated:
      info→AddString(_T(″通过″));
      break;
      case RASCS_Connected:
      info->AddString(_T(″已连接″));
      reak;
      case RASCS_Disconnected:
      info->AddString(_T(″连接已断开″));
      m_hRasConn=NULL;
      //可定义类型为HRASCONN的成员变量m_hRasConn来保存RAS连接的句柄。
      //在调用RasDial时用指向m_hRasConn的指针作为lphRasConn参数。
      //既然用m_hRasConn来保存连接句柄,连接断开后应重置为NULL.
      break;
      default:
      return (LRESULT)0;
      }
      return (LRESULT)0;
      }
      3. 断开连接:
      if (m_hRasConn != NULL)
      {
      RasHangUp(m_hRasConn);
      m_hRasConn = NULL;
      m_OnDial=TRUE;
      :Sleep(2000);
      }
      注意 :
  • 2005-10-26

    词法分析的C实现(做编译器的时候很有用) - [C语言]

    作者:skyhorsebj
    email: [email protected]
    日期:2001-7-3 12:41:52


    这里以开始定义的PASCAL语言子集的源程序作为词法分析程序的输入数据。
    在词法分析中,自文件头开始扫描源程序字符,一旦发现符合"单词"定义的源程
    序字符串时,将它翻译成固定长度的单词内部表示,并查填适当的信息表。经过
    词法分析后,源程序字符串(源程序的外部表示)被翻译成具有等长信息的单词
    串(源程序的内部表示),并产生两个表格:常数表和标识符表,它们分别包含
    了源程序中的所有常数和所有标识符。

    下面就词法分析程序中的主要变量进行说明:
    FILE fp :文件指针,用于指向要分析的PASCAL源程序;
    char *key[8]:指针数组,用于指向关键字表;
    char *border[6]:指针数组,用于指向符号表;
    char *arithmetic[4]:指针数组,指向算术运算符表;
    char *relation[6]:指针数组,指向关系运算符表;
    char *consts[20]:指针数组,指向常数表;
    char *label[20]:指针数组,指向标识符表;
    int constnum,labelnum:整型变量,分别用于存放当前常数个数和标
    识符个数。

    程序中的主要子函数:
    alphaprocess:关键字和标识符处理子函数;
    digitprocess:数字处理函数;
    otherprocess:其他字符处理函数;
    search:查找子函数;

    下面简要分析一下词法分析程序的运行流程:

    主函数main():
    打开要分析的PASCAL源程序,若不能正确打开,则报错。
    先从源程序中读入一个字符ch,然后进行如下处理:
    1、ch是字符:转入关键字和标识符处理子函数;
    2、ch是数字:转入数字处理函数;
    3、ch是其他字符:转入其他字符处理子函数;
    结束。

    关键字和标识符处理子函数alphaprocess(char buffer);
    1、将buffer送入临时数组alphatp[0],再读入一个字符至buffer;
    2、判断buffer是否为字符或数字,若是,则alphatp[1]=buffer;
    3、重复1,2,直到2判断为假;在alphatp末尾添加’/0’;
    4、调用search()子函数,在关键字表中匹配alphatp,若匹配成功,则返回序号;
    5、调用search,在标识符表中匹配alphatp,若匹配成功,则返回序号;
    6、在标识符表中添加alphatp,并返回序号;

    其余子函数的处理方式于alphaprocess类似,此处不再赘述。

    [程序调试]

    现有PASCAL程序清单如下:
    BEGIN
    IF I=1 THEN
    I:=I+1 #
    ELSE *&^
    IF I=2 THEN
    I:=I+11;
    END.

    运行词法分析程序后,显示如下结果:

    BEGIN (1,1)
    IF (1,4)
    I (6,0)
    = (4,2)
    1 (5,0)
    THEN (1,5)
    I (6,0)
    := (2,2)
    I (6,0)
    + (3,0)
    1 (5,0)
    # error,not a word
    ELSE (1,2)
    * (3,2)
    & error,not a word
    ^ error,not a word
    IF (1,4)
    I (6,0)
    = (4,2)
    2 (5,1)
    THEN (1,5)
    I (6,0)
    := (2,2)
    I (6,0)
    + (3,0)
    11 (5,2)
    ; (2,1)
    END (1,3)
    . (2,3)
    over

    结果完全正确。

    源程序:
    #include
    #include
    #include
    #include
    #include
    #define NULL 0

    FILE *fp;
    char cbuffer;
    char *key[8]={"DO","BEGIN","ELSE","END","IF","THEN","VAR","WHILE"};
    char *border[6]={",",";",":=",".","(",")"};
    char *arithmetic[4]={"+","-","*","/"};
    char *relation[6]={"<","<=","=",">",">=","<>"};
    char *consts[20];
    char *label[20];
    int constnum=0,labelnum=0;

    int search(char searchchar[],int wordtype)
    {
    int i=0;
    switch (wordtype) {
    case 1:for (i=0;i<=7;i++)
    {
    if (strcmp(key[i],searchchar)==0)
    return(i+1);
    };

    case 2:{for (i=0;i<=5;i++)
    {
    if (strcmp(border[i],searchchar)==0)
    return(i+1);
    };
    return(0);
    }

    case 3:{for (i=0;i<=3;i++)
    {
    if (strcmp(arithmetic[i],searchchar)==0)
    {
    return(i+1);
    };
    };
    return(0);
    };

    case 4:{for (i=0;i<=5;i++)
    {
    if (strcmp(relation[i],searchchar)==0)
    {
    return(i+1);
    };
    };
    return(0);
    };

    case 5:{for (i=0;i<=constnum;i++)
    {
    if (strcmp(consts[i],searchchar)==0)
    {
    return(i+1);
    };
    }
    consts[i-1]=(char *)malloc(sizeof(searchchar));
    strcpy(consts[i-1],searchchar);
    constnum++;
    return(i);
    };

    case 6:{for (i=0;i<=labelnum;i++)
    {
    if (strcmp(label[i],searchchar)==0)
    {
    return(i+1);
    };
    }
    label[i-1]=(char *)malloc(sizeof(searchchar));
    strcpy(label[i-1],searchchar);
    labelnum++;
    return(
  • 2005-10-26

    从C到C++看对象的产生 - [C++]

    作者:rick1126
    email: [email protected]
    日期:8/8/2001 4:20:33 PM
    1. 对象解决了数据和方法的封装以及变量的名字空间问题
    1) 名字空间
    以前的C里面, 所有变量包括DLL都共享同一个名字空间, 为此你不能保证每一个第三方提供的DLL都是互不冲突的.
    2) 封装, 抽象, 继承, 多态
    [封装]
    封装是针对对象而言的, 以前我们封装数据和相应操作的方法是利用DLL, 现在我们可以使用对象将相关数据封装起来. 对象比较DLL更加符合我们显示世界的认知单位
    [抽象]
    抽象就是将现实世界中的对象进行一般化, 为此我们通常在进行系统分析和设计的时候, 将最最特殊的问题抽象成为类型 -- 注意, 特殊就是我们所关心的元素量最少, 最简单, 由简到繁的设计方式使得我们的系统更具扩展性.
    [继承]
    继承使得对象之间有了联系和层次. 通过向类型添加需要关心的元素, 新的类型派生出来.
    [多态]
    多态是现实世界的一种对待问题的方式

    2. 从结构的角度看待对象
    1) 类就是具有生命力的结构
    结构是来自C的用户自定义类型方式, 加上方法就使得结构实例有了自己的"行为"能力, 成为"活生生"的对象. 这使得它具备了逻辑上的一种认识层次. 从计算机角度看, 结构就是2进制数据的结构化存储, 是对于内存空间的一种单元化使用, 添加了方法, 就是附加了相对应的函数指针, 有了这种认识, 对于我们以后学习COM涉及的虚函数和VTable都是很有帮助的. class和structure的区别仅仅在于出现的前后和默认存取类型的不同(class--private, structure--public), 所以我们以后接触到的COM的interface就是structure的宏定义而已

    上面就是目前我对于类和对象的粗浅认识, 希望各位网友指正.
  • 2005-10-26

    鼠标屏幕取词原理 - [VC专栏]

    作者:独孤九剑
    email: [email protected]
    日期:6/21/2001 10:21:49 AM
    鼠标屏幕取词 原理
    “鼠标屏幕取词”技术是在电子字典中得到广泛地应用的,如四通利方和金山词霸等软件,这个技术看似简单,其实在windows系统中实现却是非常复杂的,总的来说有两种实现方式:
    第一种:采用截获对部分gdi的api调用来实现,如textout,textouta等。
    第二种:对每个设备上下文(dc)做一分copy,并跟踪所有修改上下文(dc)的操作。
    第二种方法更强大,但兼容性不好,而第一种方法使用的截获windowsapi的调用,这项技术的强大可能远远超出了您的想象,毫不夸张的说,利用windowsapi拦截技术,你可以改造整个操作系统,事实上很多外挂式windows中文平台就是这么实现的!而这项技术也正是这篇文章的主题。
    截windowsapi的调用,具体的说来也可以分为两种方法:
    第一种方法通过直接改写winapi 在内存中的映像,嵌入汇编代码,使之被调用时跳转到指定的地址运行来截获;第二种方法则改写iat(import address table 输入地址表),重定向winapi函数的调用来实现对winapi的截获。
    第一种方法的实现较为繁琐,而且在win95、98下面更有难度,这是因为虽然微软说win16的api只是为了兼容性才保留下来,程序员应该尽可能地调用32位的api,实际上根本就不是这样!win 9x内部的大部分32位api经过变换调用了同名的16位api,也就是说我们需要在拦截的函数中嵌入16位汇编代码!
    我们将要介绍的是第二种拦截方法,这种方法在win95、98和nt下面运行都比较稳定,兼容性较好。由于需要用到关于windows虚拟内存的管理、打破进程边界墙、向应用程序的进程空间中注入代码、pe(portable executable)文件格式和iat(输入地址表)等较底层的知识,所以我们先对涉及到的这些知识大概地做一个介绍,最后会给出拦截部分的关键代码。
    先说windows虚拟内存的管理。windows9x给每一个进程分配了4gb的地址空间,对于nt来说,这个数字是2gb,系统保留了2gb到 4gb之间的地址空间禁止进程访问,而在win9x中,2gb到4gb这部分虚拟地址空间实际上是由所有的win32进程所共享的,这部分地址空间加载了共享win32 dll、内存映射文件和vxd、内存管理器和文件系统码,win9x中这部分对于每一个进程都是可见的,这也是win9x操作系统不够健壮的原因。win9x中为16位操作系统保留了0到4mb的地址空间,而在4mb到2gb之间也就是win32进程私有的地址空间,由于 每个进程的地址空间都是相对独立的,也就是说,如果程序想截获其它进程中的api调用,就必须打破进程边界墙,向其它的进程中注入截获api调用的代码,这项工作我们交给钩子函数(setwindowshookex)来完成,关于如何创建一个包含系统钩子的动态链接库,《电脑高手杂志》在第?期已经有过专题介绍了,这里就不赘述了。所有系统钩子的函数必须要在动态库里,这样的话,当进程隐式或显式调用一个动态库里的函数时,系统会把这个动态库映射到这个进程的虚拟地址空间里,这使得dll成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈,也就是说动态链接库中的代码被钩子函数注入了其它gui进程的地址空间(非gui进程,钩子函数就无能为力了),
    当包含钩子的dll注入其它进程后,就可以取得映射到这个进程虚拟内存里的各个模块(exe和dll)的基地址,如:
    hmodule hmodule=getmodulehandle(“mypro.exe”);
    在mfc程序中,我们可以用afxgetinstancehandle()函数来得到模块的基地址。exe和dll被映射到虚拟内存空间的什么地方是由它们的基地址决定的。它们的基地址是在链接时由链接器决定的。当你新建一个win32工程时,vc++链接器使用缺省的基地址0x00400000。可以通过链接器的base选项改变模块的基地址。exe通常被映射到虚拟内存的0x00400000处,dll也随之有不同的基地址,通常被映射到不同进程
    的相同的虚拟地址空间处。
    系统将exe和dll原封不动映射到虚拟内存空间中,它们在内存中的结构与磁盘上的静态文件结构是一样的。即pe (portable executable) 文件格式。我们得到了进程模块的基地址以后,就可以根据pe文件的格式穷举这个模块的image_import_descriptor数组,看看进程空间中是否引入了我们需要截获的函数所在的动态链接库,比如需要截获“textouta”,就必须检查“gdi32.dll”是否被引入了。说到这里,我们有必要介绍一下pe文件的格式,如右图,这是pe文件格式的大致框图,最前面是文件头,我们不必理会,从pe file optional header后面开始,就是文件中各个段的说明,说明后面才是真正的段数据,而实际上我们关心的只有一个段,那就是“.idata”段,这个段中包含了所有的引入函数信息,还有iat(import address table)的rva(relative virtual address)地址。
    说到这里,截获windowsapi的整个原理就要真相大白了。实际上所有进程对给定的api函数的调用总是通过pe文件的一个地方来转移的,这就是一个该模块(可以是exe或dll)的“.idata”段中的iat输入地址表(import address table)。在那里有所有本模块调用的其它dll的函数名及地址。对其它dll的函数调用实际上只是跳转到输入
  • 2005-10-26

    VC++菜鸟学习手记 - [VC专栏]

    作者:pp.boy
    email: [email protected]
    日期:2000-10-24 0:41:11
    今天加入chinaasp的c/c++版,成立了c版菜鸟帮,也自己把自己选为了鸟帮帮主。嘻嘻...开始学习vc++了.
    说自己有好菜,可能还没有人相信,pp.boy只是稍稍懂点c语言,写过一些dos程序,对于windows程序嘛,嘻嘻....了解的实在是太少了. 那为什么要选vc++呢?好象是在那本书上看到过这样的话:业余的程序员用vb;聪明的程序员用delphi;真正的程序员用vc++. pp从很早前就想成为一名程序员,又怪pp天生愚笨,想了好久还是就看上vc++了! 虽然听说vc难了点,可pp是不畏惧这些的!....嘻嘻...又说了好多废话.
    pp可是做事情有三分的冲动的,刚刚加入C鸟帮,马上就真的干了起来,瞧..从大堆光盘上找来了久违的Y版vc++光盘,迫不及待的就点了setup,...嘎吱...嘎吱..PP的3.2G的硬盘又在叫嚣了,哎!找到工作就让你下岗!
    VC到是装好了,可要学习什么了?听说VC可以有很多用处的啊!见上连上qq向各位大哥大嫂问了问,得知其中的MFC可算是VC的最大特点,也能轻车熟路...哈哈...看中你了!
    目标锁定!开始找资料!PP告诉大家一个的消息,自从前周被"程序员大本营2000"骗去了半月生活费后PP的锅都不能揭了,哪里有钱买书啊!....只好连上网到什么yesky,vckbase也下了写东东...一个字...看!
    看了良久,PP也是个丈二和尚,干脆就听chache的教导来个实际的.实践实践.
    打开VC编辑器,呵呵还算是不陌生,和以前的tc的菜单都差不多,和interdev也很相试,ok没有问题!打开FILE,在new选项卡上选projects毫不忧郁的用MFC APPWIZARD(EXE)就新建了一个工程.看见屏幕的左边,出现了三个标签。InfoView / ClassView / FileView 。ClassView 标签列出了好多看不懂的类来,FileView 标签给出了项目中文件的列表。看文件列表把PP下了一跳,有到几个文件,一个一个打开文件看看...O...我的天啦!...大哥你在哪里?...怎么像main()或是winmain()的都看不到!怎么一回事.这个时候就只有看当下来的电子图书了!书上说: "用VC++的MFC库编程与传统上使用 C 语言直接访问 Windows API不同是 MFC 已经包含和压缩了所有标准的"样板文件"代码,这些代码是所有用 C 编写的 Windows 程序所必需的." 嘻嘻...难道说MFC里面的"样板文件"里就包含了winmain(),我们不需要在写这样的程序了?现在就姑且认为是吧.
    在看代码,发现好多从来都没有见过的标注,用msdn一个一个的查它是做什么的,搞了半天,msdn和英文词典都被PP翻拦了也看不懂一二.哎!放弃这条路.
    再次连上QQ好容易找才了个好友给了段最简单的代码:

    1 //hello.cpp

    2 #include

    3 // Declare the application class
    4 class CHelloApp : public CWinApp
    5 {
    6 public:
    7 virtual BOOL InitInstance();
    8 };

    9 // Create an instance of the application class
    10 CHelloApp HelloApp;

    11 // Declare the main window class
    12 class CHelloWindow : public CFrameWnd
    13 {
    14 CStatic* cs;
    15 public:
    16 CHelloWindow();
    17 };

    18 // The InitInstance function is called each
    19 // time the application first executes.
    20 BOOL CHelloApp::InitInstance()
    21 {
    22 m_pMainWnd = new CHelloWindow();
    23 m_pMainWnd->ShowWindow(m_nCmdShow);
    24 m_pMainWnd->UpdateWindow();
    25 return TRUE;
    26 }

    27 // The constructor for the window class
    28 CHelloWindow::CHelloWindow()
    29 {
    30 // Create the window itself
    31 Create(NULL,
    32 "Hello World!",
    33 WS_OVERLAPPEDWINDOW,
    34 CRect(0,0,200,200));
    35 // Create a static label
    36 cs = new CStatic();
    37 cs->Create("hello world",
    38 WS_CHILD|WS_VISIBLE|SS_CENTER,
    39 CRect(50,80,150,150),
    40 this);
    41 }

    嘻嘻....怎么像gwbasic一样有行好啊!..哦..原来是为了好看. 怎么又是一个hello world!这样的程序?
    官它3721啊!先把它编译连接运行后在说!再次打开VC编辑器,用new -> projects -> win32 application 键一个空的工程(an emply project).ok! 存盘后有四个文件(HELLO.OPT、HELLO.NCB、HELLO.DSP 和 HELLO.DSW)这下好了文件比上次少了很多,连*.CPP和*.HPP都没有! 分别打开这四个文件得知HELLO.DSW是这个工程的工程文件,其它文件有什么用嘛?PP还不知道! 不管了!PP又打开"File"菜单中选择"New"建了一个文件名为hello.cpp的"Text File".然后将上面的这代码除开行号后粘贴了进去,然后就开始编译了!PP的硬盘又好好的响了一阵,哎,看来PP的32M内存也该下课了!..开始连接了...哦..我的天啦..怎么有好多错误,说是什么标识没有定义..怎么搞的????又只好问问在线的大哥了..原来还没有告诉项目要使用MFC库。按照joker2000说的选择"Project"菜单的"Settings"。在出现的对话框中选择"General"标签。在"Microsoft Foundation Classes"组合框中,选择"Use MFC in a Shared DLL"后! 重新连接..good !..能够运行了...好有成就感啊!PP还在沾沾自喜....旁边一个室友来了一句:这样的东西用VB就需要
  • 2005-10-26

    vc开发的域名查询组件 - [VC专栏]

    作者:小牧
    email: [email protected]
    日期:7/31/2001 12:57:55 AM
    [#FF0000]转[/#]


    一个用vc开发的域名查询组件(源代码及说明)

    关键词:Visual C++

    最近经常见到有人问如何在asp中查询域名是否被注册,所以写了这个组件,主要原理就是向gopher站点的whois服务器发送whois请求,由于没有太多时间,所以很简陋,目前只能实现向cnnic查询,并且返回的信息没有进行处理,如果你要用的话,清在asp里自己处理一下吧。以后如果有时间将加上过及域名的查询功能。其实最主要的目的还是给大家做组件提供一点参考,毕竟组件的写法有些特殊。

    下载地址:
    组件:http://homepage.qdcatv.com.cn/bigeagle/whois.zip
    源代码:http://homepage.qdcatv.com.cn/bigeagle/whoiscode.zip

    组件介绍
    域名查询组件0.01版

    组件名称:WhoIS

    描述: 查询域名是否已注册,如果注册,则显示该域名的信息,包括注册者的详细信息。

    版本:0.01 (之所以称为0.01版是因为是在是太简陋了,目前只能查询国内注册的站点,也就是以cn结尾的站点。另外输出未做处理,需要在外部进行再包装。实在没有时间。)

    日期:2000/9/15日

    作者:bigeagle


    使用方法(以asp调用为例)

    <%
    dim m_objDomainInfo

    ’创建whois对象
    set m_objDomainInfo = server.CreateObject ("WhoIs.DomainInfo")

    ’查询新浪,你可以改变,但不能加前面的www
    m_objDomainInfo.GetDomainInfoCn ("cctv.com.CN")


    if m_objDomainInfo.IfSuccess <> 0 then ’如果成功
    Response.Write replace( m_objDomainInfo.DomainInfo , vbcrlf , "
    ")
    else ’如果失败
    Response.Write "失败原因:" & m_objDomainInfo.DomainInfo
    end if
    set m_objDomain = nothing
    %>
  • 2005-10-26

    Visual C++实现文件间批量转换功能 - [VC专栏]

    作者:skyhorsebj
    email: [email protected]
    日期:2001-7-3 18:07:19
    全部代码用Visual C++6.0在Windows95/98/2000下编译通过。


      首先用MFC AppWizard生成一个SDI风格的应用程序test,生成过程中全部使用缺省设置。

      其次,利用资源编辑器,在主菜单“文件”下增加一个菜单项“转换”,属性为:

       ID:ID_CONVERT

       Caption: 转换

       Prompt: 在不同格式文件之间进行转换/n转换文件

      然后用“CTRL-W”热键激活MFC ClassWizard,为CmainFrame类增加响应ID_CONVERT消息的命令函数OnConvert()。加入转换功能的代码如下所示:

       void CMainFrame::OnConvert()
        {
         LPMALLOC pMalloc;//利用shell扩展功能
         BROWSEINFO bi;
         if (SUCCEEDED(SHGetMalloc(&pMalloc)))//为生成目录选择对话框分配自由内存
          {
           ZeroMemory(&bi,sizeof(bi));//清零分配的空间
           char pszDirName[MAX_PATH];//存放选择的目录名
           LPITEMIDLIST pidl;
           bi.hwndOwner = GetSafeHwnd();
           bi.pidlRoot = NULL;
           bi.pszDisplayName = pszDirName;
           bi.lpszTitle = _T("选择要批量转换文件所在的目录");
           bi.ulFlags = BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS;
           bi.lpfn = NULL;
           bi.lParam = 0;
           if ((pidl = ::SHBrowseForFolder(&bi)) != NULL)//调用选择目录对话框
            {
             if (::SHGetPathFromIDList(pidl, pszDirName))//获得所选择的目录
              {
               file://设置选择的目录为当前目录,以便查找
                SetCurrentDirectory(pszDirName);
                file://定义一个查找
                CFileFind findch1;
                CString strconv;
                CString strsour;
              if(findch1.FindFile("*.CH1"))//在当前目录进行查找
               {
                CFile SourceFile;
                CStdioFile TargetFile;
                BOOL bfindresult;
                do
                {
                 file://查找下一个符合条件的文件
                  bfindresult= findch1.FindNextFile();
                  file://获得查找到的文件名
                  strsour=findch1.GetFilePath();
                  strconv=strsour;
                  file://把文件名转换为小写
                  strconv.MakeLower();
                  file://把*.ch1类型的文件转换为*.txt
                  strconv.Replace(".ch1",".txt");
                  file://打开*.ch1类型的文件作为源文件
                  SourceFile.Open(strsour,CFile::modeRead);
                  file://打开*.txt类型的文件作为目标文件
                  TargetFile.Open(strconv,CFile::modeCreate|CFile::modeWrite);

                  file://此处调用*.ch1类型的文件的解码函数
                  file://此处调用转换成文本文件的函数
                  file://文件使用完毕,要关闭
                  SourceFile.Close();
                  TargetFile.Close();
                 }while(bfindresult);
                 MessageBox("转换完毕!","转换完毕!",MB_OK);
                }
              else
               {
                MessageBox("没找到CH1文件","没找到",MB_OK);
               }
              findch1.Close();//关闭这个搜索
             }
            pMalloc->Free(pidl);//释放使用完的资源
            }
           pMalloc->Release();//释放使用完的资源
          }
         }

      编译并运行程序,选择“文件”菜单下的“转换”命令, 选择一个目录就完成了对此目录下所有具有.ch1扩展名的文件的转换工作。

  • 2005-10-26

    Visual C++的程序设计技巧(转) - [VC专栏]

    作者:skyhorsebj
    email: [email protected]
    日期:2001-7-3 18:12:19
    Microsoft Visual C++是一种可视化编程语言,因功能强大而受到广大程序设计人员的青睐。但是,由于VC++的应用程序框架结构非常复杂,使得许多初学者望而却步。本文通过对设置视图背景颜色和改变对话框标题的几种实现方法的分析研究,揭示了VC++程序代码执行时的一些本质特征和有关的程序设计技巧,对理解MFC库的结构和Windows操作系统的内部工作方式提供了一定的帮助。
    设置视图背景颜色
    对于VC++文档、视结构中的视图,从用户的角度来看,只是可以改变大小、位置的普通窗口,同其他基于Windows应用程序的窗口是一样的;从程序员的角度来看,视图并不是普通的窗口,而是从MFC库中CView类派生的类对象。像任何VC++对象一样,视图对象的行为由类的成员函数(数据成员)决定,包括派生类中应用程序定义的函数和从基类继承来的函数。
    提出问题
    视图的背景一般来说是白色的,在缺省情况下,它和系统定义的颜色COLOR_WINDOW是一致的。设计者一般会希望自己的程序可以让用户轻松地改变窗口背景颜色,或是用漂亮的图片来充填背景。我们可以用Windows函数SetSysColors来重新指定COLOR_WINDOW所对应的实际颜色,来达到改变视图背景颜色的目的。但这样会同时改变其他应用程序的视图窗口背景,使得整个Windows系统的颜色设置产生混乱。另外,我们可能会用以下方法来设置视图的背景颜色,即在CView的OnDraw函数中添写如下一段程序代码:
    void CTestView::OnDraw(CDC* pDC)
    {
    CTestDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    CRect rectClient;
    CBrush brushBkColor;
    GetClientRect(rectClient);
    brushBkColor.CreateSolidBrush(RGB(255,0,0));
    pDC->DPtoLP(rectClient);
    pDC->FillRect(rectClient,&brushBkColor);

    }
    这样可以达到改变当前应用程序的视图背景的目的,但同时也产生了一些不良影响,使得程序运行效果不尽如人意。
    分析问题
    我们知道,在VC++的文档、视结构中,CView的OnDraw函数用于实现绝大部分图形绘制的工作。如果用户改变窗口尺寸,或者显示隐藏的区域,OnDraw函数都将被调用来重画窗口。并且,当程序文档中的数据发生改变时,一般必须通过调用视图的Invalidate(或InvalidateRect)成员函数来通知Windows所发生的改变,对Invalidate的调用也会触发对OnDraw函数的调用。正因为OnDraw函数被频繁调用,所以在其执行时,每次都刷新填充一次视图客户区域,便会使屏幕不稳定,产生闪烁现象。
    笔者通过对VC++应用程序框架结构和Windows消息映射系统的仔细研究,找到另外一种改变视图背景的方法,其执行效果比上述两种方法都好。其实在程序调用OnDraw函数之前,会触发一个Windows消息:WM_ERASEBKGND,以擦除视图刷新区域。在缺省情况下,Windows系统使用视图窗口注册时窗口类中的成员hbrBackground所描述的画刷来擦除屏幕,这一般会将屏幕刷新成COLOR_WINDOW所对应的颜色。因此,在OnDraw函数中设置背景颜色的执行过程是这样的:先将屏幕刷新成COLOR_WINDOW所对应的颜色,接着又在OnDraw函数中填充其他颜色,这正是产生屏幕闪烁的根本原因。
    解决问题
    通过上述分析,我们应将视图背景颜色填充移到Windows消息:WM_ERASEBKGND所对应的消息映射函数中,而不是在OnDraw函数中。我们可以通过下列步骤实现这一过程:在文档类中增加一成员变量m_viewBkColor保存当前背景颜色,同时增加两个成员函数GetViewBkColor和SetViewBkColor对其进行读写操作。这样做的好处是可以对m_viewBkColor成员进行序列化,将其和文档联系在一起,打开某一文档时,其背景将和上一次程序操作该文档时的背景保持一致。在视图类中为视图的Windows消息WM_ERASEBKGND增加消息映射函数OnEraseBkgnd,代码如下:
    BOOL CTestView::OnEraseBkgnd(CDC* pDC)
    {
    CRect rect;
    CBrush brush;
    brush.CreateSolidBrush(GetDocument()->GetViewBkColor());
    pDC->GetClipBox(rect);
    pDC->FillRect(rect,&brush);
    return true;
    }
    在该函数中不需要对客户区域矩形进行设备坐标到逻辑坐标的转换,并且Windows在调用该函数时会自动进行裁剪区域的计算,使得需要刷新的屏幕面积达到最小。这样我们可以在程序中通过设计下列菜单函数轻松地改变视图背景的颜色,而且运行效果相当令人满意。
    void CTestView::OnChangeViewBkcolor()
    {
    CColorDialog cdlg;
    if(cdlg.DoModal()==IDOK)
    {
    GetDocument()->SetViewBkColor
    (cdlg.GetColor());
    InvalidateRect(NULL);
    }
    }
    改变对话框标题
    提出问题
    在VC++程序设计过程中经常会遇到这样的情况:执行程序的多个地方需要调用同一个对话框,但在不同的情况下希望给对话框加上不同的标题。开始我们可能会用下面的一段程序以达到这一目的:
    CTestDialog dlg;
    dlg.SetWindowText(“标题-1");
    dlg.DoModal();
    利用上述办法,我们本希望在程序不同的地方,通过设置函数SetWindowText不同的参数,以达到使同一对
  • 2005-10-26

    Winsocket网络编程谈 - [VC专栏]

    作者:五一
    日期:00-7-7 下午 02:01:48
    (作者:李其 2000年07月07日 13:34)

      Winsocket编程是非常复杂的,这令一般人望而生畏。但如果你想编写这样的程序,又不懂得相关知识,怎么办呢?Delphi的网络组件库中为我们提供了关于实现网络通信的组件,其中ClientSocket和ServerSocket组件使我们能够很方便地编写出自己的网络通信和资源共享程序。

      一、设置Winsocket属性

      在Delphi 4.0中,将Winsocket细分为两种组件:ClientSocket和ServerSocket,它们分别作为客户端和服务器端的组件。通过这两种组件之间的通信,再加上辅助的应用程序代码,就可以实现一个简单的通信程序。当然,如果你想在客户端程序中再引入ServerSocket的话,那么客户端程序就可以充当服务器了,可以对其他的客户端程序的请求进行响应。

      如果正在编写服务器端程序,就必须设置ServerSocket组件的Port属性。设置此参数主要是因为在同一台计算机上可能运行着多个服务器程序,而它们可能总在不停地接受来自于远程客户端程序的连接请求。也可以设置Service属性,它指示了ServerSocket所提供的服务类型,比如:FTP、HTTP等,然后设置Active属性为True。

      如果正在编写客户端程序,则设置ClientServer组件的属性就多一些。Port属性应设置成和服务器端的Port属性值一致,另外Host的属性必须正确设置,它是一个只读属性,在设计时不可用。Host指示了客户程序所要连接的远程服务器的主机名。也可以设置Address属性,也就是远程主机的IP地址。

      二、建立与远程计算机的连接

      要在远程计算机系统之间进行数据传输,首先必须在通信的两台主机之间建立连接。

      服务器端的ServerSocket组件调用Open方法初始化Socket连接,同时也就设置了Active属性为True,将ServerSocket组件设置成侦听模式,随时侦测是否有连接请求。

      如果服务器接受了客户程序的连接请求,则触发OnAccept事件,如下代码就是处理接受连接后服务器程序所要做的工作。

      procedure Myform..ServerSocketAccept(Sender: TObject,Socket: TCustomWinSocket);

      begin

      IsServer := True;

      end;

      在客户端程序中,ClientSocket组件则设置Port、Host等必须的属性,然后设置Active属性为True,提出连接请求。

      三、计算机之间的数据传输

      一旦服务器端接受了客户机方面的连接请求,客户机就可以发送数据。这时,在客户机和服务器之间就拥有了一个Socket,通过此Socket双方实现通信。所以Socket属性很重要,它又拥有很多的方法,用其中的几个简单的方法,就可以实现数据的发送和接收。

      客户机端用如下形式:ClientSocket1.socket.sendtext(’string you want to send’);

      在服务器端采用如下形式:ServerSocket1.socket.recievetext( str: string);

      此函数返回接收到的字符串的长度,将字符串存储在变量str中。

      上述是数据传输的最简单的例子,你还可以采用Socket属性所提供的其他方法来实现复杂的数据传输。





  • 2005-10-26

    WINDOWS高级窗口的客户区域拖动技术及其应用(转载) - [VC专栏]

    作者:chache
    email: [email protected]
    日期:2000-10-23 17:30:50

    作者:宋立波


      WINDOWS应用程序窗口一般包括两种:普通窗口和常居顶层的无标题条高级窗口。前者是
    由WINDOWS内部功能定制的,它具有WINDOWS应用程序窗口的所有普通特性:具有标题条、窗
    口边框、最大化按钮、最小化按钮和系统默认的快捷键及鼠标支持功能等,利用鼠标左键拖
    动该种窗口的标题条可以在屏幕上任意移动窗口,当鼠标光标停在窗口边框上时可以改变窗
    口大小;后者是一种定制的高级窗口,它不具有普通窗口的任何属性,整个窗口的控制必须
    由编程者来一一确定,使用这种窗口的典型实例有WINDOWS中的IME输入法应用程序、UCWIN4
    .0平台、各种浮动工具箱、OFFICE中的桌面工具栏和第三方开发的汉字输入平台等。

      WINDOWS 这种无标题条常居顶层高级窗口的一个显著特点是,不需改变窗口大小但必须
    具有窗口的客户区域拖动功能。由于普通窗口的拖动功能是由系统来完成的,编制普通的应
    用程序根据无须考虑客户区域拖动问题,因此一般编程人员很难遇到这个问题,更谈不上如
    何实现这一功能了。开发者往往希望自己开发出来的软件具有经典软件中的窗口客户区域拖
    动功能,笔者曾经利用模仿系统鼠标点击标题条拖动窗口和WINDOWS系统内部提供的API发送
    函数发送内部拖动命令来实现无标题常居顶层高级窗口的客户拖动功能,结果都不理想。后
    来只好在窗口函数中通过直接处理WM_LBUTTONDOWN、WM_MOUSEMOVE和WM_LBUTTONUP消息,自
    行控制窗口拖动的客户命令区、拖动开始、窗口移动、拖动虚框绘制、虚框移动和拖动结束
    等过程,来实现高级顶层窗口的客户区域拖动方案。下面就自己实践经验详细介绍实现该方
    案的具体方法和主要技巧。

      一、WINDOWS检测客户拖动命令及鼠标光标动态提示的实现方法

      WINDOWS 无标题条常居顶层高级窗口的客户区域一般分为两种:特定客户命令区域和非
    特定客户命令区域。特定客户命令区域是指利用"RECT"定义的特定子矩形区域,窗口函数对
    发生在该区域内的鼠标命令进行检测并处理;非特定客户命令区域是指没有明确定义的窗口
    客户区域部分,即所有特定客户命令区域之外的部分,窗口函数根据实际需要来确定是否对
    该区域内发生的鼠标命令进行处理。实现常居顶层高级窗口拖动功能的首要问题,是如何检
    测和处理特定客户命令区域和非特定客户命令区域内的鼠标命令,以及如何利用鼠标光标来
    动态提示用户此时可以进行窗口的拖动操作。

      1、在特定客户区域检测鼠标命令的方法

      当窗口中设置了实现拖动功能的图标命令按钮时,就必须在资源文件中定义命令按钮的
    特定客户区域,该区域一般也就是显示命令按钮中图标的矩形区域,这个区域的定义方法为
    "RECT DragRT",其中DragRT为定义的检测鼠标命令矩形区域,它用DragRT.LEFT、DragRT.T
    OP、DragRT.RIGHT和DragRT.BOTTOM四个参数来描述矩形区域相对于窗口客户区域左上角的相
    对坐标值,这四个参数必须事先定义具体的数值,也可以利用"SETRECT"函数直接填充。

      窗口函数在处理鼠标消息WM_LBUTTONDOWN时,在接收系统传递的鼠标位置参数lParam后
    ,通过MAKEPOINT( )函数将其转换为窗口坐标值,利用判断某坐标点是否位于特定矩形区域
    内的函数PtInRect(),就可以判断鼠标指针是否点击在拖动命令按钮之内,从而完成窗口拖
    动功能的启动任务。其描述性功能代码示例如下:

      case WM_LBUTTONDOWN://鼠标光标点击处理

      POINT pt;//鼠标在屏幕上位置指针,包括pt.X和pt.Y两个参数,

       //该指针值利用MAKEPOINT通过lParam参数转换而来

      pt=MAKEPOINT(lParam); //获取鼠标当前屏幕位置指针

      if(PtInRect(&DragRT,pt)){//判断鼠标是否点击在拖动按钮内

       //实现鼠标拖动窗口方案的启动功能

      } else {

       //进行其它特定或非特定命令客户区域判断处理

      }

      break;

      2、在非特定客户区域检测鼠标命令的方法

      当窗口应用程序中采取了非特定客户区域拖动方法时,必须在资源文件中事先确定各个
    特定客户区域的矩形坐标,这时非特定客户区域是不规则的区域,它需要根据实际的应用程
    序窗口及各个命令按钮矩形区域来确定,也就是各个命令按钮相对于窗口矩形区域的“非”
    子集。窗口函数在处理鼠标消息WM_LBUTTONDOWN时,首先利用函数PtInRect()判断当前鼠标
    指针是否点击在各个命令按钮矩形区域内,如果未点击在任何命令按钮区域内,则可确定鼠
    标点击在非特定客户区域内,从而实现窗口拖动功能的启动。其描述性功能代码示例如下:


      case WM_LBUTTONDOWN: //鼠标光标点击处理

      POINT pt; //定义鼠标在屏幕上的位置指针

      pt=MAKEPOINT(lParam); //取得鼠标光标当前位置指针

      for(i=0;i

       if(PtInRect(&DragRT[i],pt)){//DragRT[i]为按钮矩形数组

       break; //鼠标点击在其它按钮上中断

       }

      }

      if(i

       //鼠标点击在其它特定客户区域内则处理其它按
  • 2005-10-26

    常量const的使用(编译自Topica) - [C语言]

    作者:rick1126
    email: [email protected]
    日期:2001-4-13 6:27:23
    CONST AND PASS-BY-VALUE
    使用一个常量前缀(const)可以避免传址变量的修改:

    void f(const string & s);

    一些开发者即使针对传值变量也用 const :

    void f(const int n); /*n is passed by value, why const?*/

    const 是否真的必要? 不, 不需要. 记住, 在你使用传值变量的时候, 调用函数不会修改变量值而仅仅复制它. 进一步讲, 根据 C++ 标准, Top-level cv-qualification 前缀是被忽略的. 让我们解释这个术语: "cv-qualification" 指常量和非稳定. "Top-level" 意味着参数不是一个组合或者非完整的类型, 比如: 不是指针, 引用或者数组. 这样:

    void f(int const param1);

    还是作为下列方式对待:

    void f(int param1);

    为此, 传递一个类型为 ’const int’ 类型的参数给 void f(int param1); 是允许的:

    void f(int n); /*non-const parameter*/
    int main()
    {
    f(1); /*const int; fine*/
    }

    相反, 下列对于常量的使用则有影响:

    void f(int *p1);
    void f(const int *p2);

    这里, 常量被用于一个组合类型, 称之为 int *. 这样, 不能传递类型为"一个指向常量整数的指针" 给第一个函数 f().

    void f(int *p1); /*non const version*/
    int n=0;
    const int *p=&n;
    f(p); /*error, cannot convert ’const int *’ to ’int *’*/

    作为一个规则, 如果变量以传值方式传递或者返回不能声明为常量, 只有针对组合类型使用常量类型.
    -----------
    Danny Kalev
  • 2005-10-26

    非法探取密码的原理及其防范(转) - [VC专栏]

    作者:九流
    email: [email protected]
    日期:8/8/2001 11:59:25 AM
    一、非法获取Password的原理:
    Edit控件是Windows的一个标准控件,当把其Password属性设为True时,就会将输入的内容屏蔽为星号,从而达到保护的目的。虽然我们看来都是星号,但程序中的Edit控件实际仍是用户输入的密码,应用程序可以获取该控件中的密码,其他应用程序也可以通过向其发送WM_GETTEXT或EM_GETLINE消息来获取Edit控件中的内容。黑客程序正是利用Edit控件的这个特性,当发现当前探测的窗口是Edit控件并且具有ES_PASSWORD属性时,则通过SendMessage向此窗口发送WM_GETTEXT或EM_GETLINE消息,这样Edit框中的内容就一目了然了。
    二、黑客软件工作方法:
    首先要取得当前的窗口,并判断是否是Edit控件,一般多通过鼠标来指定要探测的窗口,例如在WM_MOUSEMOVE消息的响应函数中进行判断,现列举代码片段如下:
    //将客户坐标转换成屏幕坐标
    ClientToScreen(&point);
    //返回一个包含指定屏幕坐标点的窗口
    CWnd* pWnd = CWnd::WindowFromPoint(point);
    if (pWnd)
    {
    //获取窗口句柄
    HWND hwndCurr = pWnd->GetSafeHwnd();
    if ((::GetWindowThreadProcessId (GetSafeHwnd(), NULL)) != (::GetWindowThreadProcessId (hwndCurr, NULL)))
    {
    char lpClassName[255];
    //获取类名
    if (::GetClassName(hwndCurr, lpClassName, 255))
    {
    //判断是否是Edit控件
    if (0 == m_strWndClass.CompareNoCase("EDIT"))
    {
    //获取窗口风格
    LONG lStyle = ::GetWindowLong(hwndCurr, GWL_STYLE);
    //如果设置了ES_PASSWORD属性
    if (lStyle & ES_PASSWORD)
    {
    char szText[255];
    //通过掌握的句柄hwndCurr向此控件发送WM_GETTEXT消息
    ::SendMessage(hwndCurr, WM_GETTEXT, 255, (LPARAM)szText); //密码已保存在szText中
    m_strPassword = szText;
    }
    }
    }
    }
    }
    上述代码中值得注意的有以下几个关键地方:
    ClientToScreen(&point);
    CWnd* pWnd = CWnd::WindowFromPoint(point);
    HWND hwndCurr = pWnd->GetSafeHwnd();
    这三句代码可以获取当前鼠标位置所在窗口的窗口句柄,在SendMessage中要用到的。
    ::SendMessage(hwndCurr, WM_GETTEXT, 255, (LPARAM)szText);
    这便是真正起作用的SendMessage了,其第一个参数指定了要接收消息的窗口句柄,我们已经通过上面的代码获取到了,第二个参数就是让Edit控件返回字符的WM_GETTEXT消息了,并将得到的内容保存在szText中。
    三、防范措施
    既然我们搞清除了黑客软件普遍采取的手法,那我们自然能制订出一套防范其攻击的措施来。下面我们就要对Password进行保护。
    从以上分析我们可以看出:Edit控件的漏洞主要在于没有对发送WM_GETTEXT或EM_GETLINE消息者的身份进行检查,只要能找到Edit窗口句柄,任何进程都可获取其内容。所以必须要对发送消息者的身份进行验证,这里给出一种方法来验证发送消息者的身份是否合法:
    1.创建新CEdit类
    从CEdit继承一个子类CPasswordEdit,申明全局变量g_bSenderIdentity表明消息发送者的身份:
    BOOL g_bSenderIdentity;
    然后响应CWnd的虚函数DefWindowProc,在这个回调函数中进行身份验证:
    LRESULTCPasswordEdit::DefWindowProc (UINTmessage,WPARAMwParam,LPARAMlParam)
    {
    //对Edit的内容获取必须通过以下两个消息之一
    if((message==WM_GETTEXT) ||(message==EM_GETLINE))
    {
    //检查是否为合法
    if(!g_bSenderIdentity)
    {
    //非法获取,显示信息
    AfxMessageBox(_T ("报告:正在试图窃取密码!"));
    return 0;
    }
    //合法获取
    g_bSenderIdentity=FALSE;
    }
    return CEdit::DefWindowProc (message,wParam,lParam);
    }
    2.在数据输入对话框中做些处理
    在对话框中申明一个类成员m_edtPassword:
    CpasswordEdit m_edtPassword;
    然后在对话框的OnInitDialog()中加入下列代码:
    m_edtPassword.SubclassDlgItem(IDC_EDIT_PASSWORD,this);
    将控制与新类做关联。
    之后要在对话框的数据交换函数中将身份设为合法:
    void CDlgInput::DoDataExchange (CDataExchange*pDX)
    {
    //如果获取数据
    //注意:对于CPropertyPage类这里不需要 if (pDX->m_bSaveAndValidate)条件
    if(pDX->m_bSaveAndValidate)
    {
    g_bSenderIdentity=TRUE;
    }
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CDlgInput)
    DDX_Text (pDX,IDC_EDIT_PASSWORD,m_sPassword);
    //}}AFX_DATA_MAP
    }
    这样,Password输入框就拥有了合法身份,会受到保护。
    结论:
    以上的方法仅针对VC程序,对于其他语言如VB、Delphi等语言,需要借助VC做一个Password的ActiveX控件,实现方法与上述方法基本类似。以上程序均用VisualC++6.0编制调试通过。
  • 2005-10-26

    构建稳定的服务器端组件的七个步骤(转) - [VC专栏]

    作者:poolnet
    email: [email protected]
    日期:2001-6-19 8:36:36
    实现健壮性能的规则

    Hank Marquis
    来自于Enterprise Solutions for Microsoft BackOffice and Windows NT Magazine

    在你的服务器上安装了微软IIS(Internet Information Server),你就可以发挥ASP(Active Server Pages)的优势了,ASP利用ActiveX组件来为你的网络应用完成所有种类的工作。尽管你可以在HTML和有ASP页面的IIS里面使用许多ActiveX组件,服务器端组件也不是运行在一台服务器上的普通组件。它在运行时不会告诉你同需要特别关照的产品服务器有关的任何信息。你将无法做任何事情去改变其对服务器性能、安全和稳定性的影响。对服务器端的组件的不恰当选择可能会导致一些问题,包括速度的明显下降,安全漏洞或者其它更恶劣的问题。

    客户端的组件在用户计算机上执行。客户端组件包括绝大多数我们现在已经了解的一些流行组件:标签控件,文本框,命令按钮,格子等。这些组件可以通过标签和(或)HTML对象语法来包含在客户端HTML代码中。

    多数的有用的客户端组件会提供特定种类的用户界面。记住,使用客户端组件就意味着真实组件已经被传到客户计算机上。寻常的做法就是把组件下载到客户计算机上。当然,用户不得不等待下载过程,而且客户计算机必须被配置为允许下载。

    与此形成对比的是,服务器端的组件在服务器计算机上执行。服务器端组件也为用户做一些工作,但却是在服务器上运行的。你必须认识到这个差异并且相应地编制代码。服务器端组件为你的整个应用程序封装了一些逻辑或功能。

    当一个用户使用应用程序时,他将不会真正看到服务器端的组件。这些组件大多数都可以通过需要使用组件的ASP脚本中的标识来被包含。你同样可以通过服务器的CreateObject语法来包含服务器端的组件。

    建造健壮的组件

    用于创建健壮组件的好材料并不多。但是,我在这里向大家推荐七个关键步骤,它可以帮助你创建稳定和安全的服务器端组件,可以很优雅地缩放并且维持性能。在创建一个服务器端网络应用时,你需要把稳定,安全和性能放在你心目中的首要位置。

    服务器端组件不应该具有GUI(图形用户界面)。因为服务器端组件是在服务器上运行,网络应用的用户是看不到可能弹出的任何对话的。你的组件需要能够同脚本和其它组件进行交流,却无需同用户交流。避免所有的消息框和其它任何图形的用户界面单元。你必须开发利用返回结果来同状态和其它模块信息进行交流的代码。如果什么东西出问题,不要抛出一个错误消息或者使用一个消息框,可以返回一个状态变量。你需要做的最后一件事是锁定忙碌服务器等待OK按钮被按下。


    服务器端组件不得被传递引用或者传递引用给对象。普遍的做法是把控制作为一个参数传递给其它过程或组件。这包括其它对象的引用,比如RecordSets。尽管如此,向网络中的组件传递引用可能导致速度明显下降,使一个繁忙的服务器更加缓慢,网络应用程序在响应用户需求时也表现得更慢。


    服务器端组件应该尽可能地少含方法和属性。每一个方法或属性的调用都需要大量处理。因此,一个编写的好的服务器端组件应该几乎不含明显的方法和属性。组件含有的那些方法和属性会带来更多的参数。具有很多参数的调用越少,性能就越好,尤其是你的网络应用程序需要支持许多用户时。这个技巧和许多开发人员的经验是相反的。尽量少的使用带有许多参数的调用也会带来另外一些问题,这使得编码和调试更加困难,但是速度上的改进是与付出的努力相当的。


    服务器端组件必须实现恰当的线程模型。利用单线程组件可能导致服务器限制一个线程的会话,这将带来速度的明显下降。应该选择VB的房间模型线程选项并且努力避免单线程组件。但是,VB不能创建你可以在Visual C++里发现的具有线程选择范围的组件,。这一点也表明VB不是一种很合适这项工作的开发语言。


    服务器端组件应该使用早期绑定。如果你的服务器应用程序要扩容,这显得特别重要。早期绑定的对象在编译时就拥有引用解析,可以节省不少执行时间。


    服务器端组件不能使用在应用程序或会话作用域的声明中。请注意你的控件是如何被限定作用域的。作用域描述了如何创建一个组件的实例,这对你的服务端组件的成功起着关键作用。正如上月所讨论的那样,存在三种级别的作用域:页面级,会话级和应用程序级。页面级作用域对象可以用页面本身的HTML和ASP脚本代码来创建。页面级作用域组件的最佳性能可以通过使用房间线程来得到。而对应用程序级和会话级作用域组件来说,可以通过使用ATL组件的双线程模型来得到。同作用域结合的线程模型也影响服务器的安全性能。例如,一个利用应用程序级作用域的房间线程组件在系统安全环境里运行,但并不是当前用户的安全环境。这对那些具有安全意识的人来说可能是一个问题。


    为了速度,服务器端组件应该是进程中组件;为了稳定,则应该是进程外组件。有两种方式去创建COM(OLE)服务器--进程中和进程外。在VB里,你用EXE或DLL扩展名去编译服务器。具有DLL扩展名的OLE服务器就被称为进程中服务器;而具有EXE扩展名
  • 2005-10-26

    关于PostMessage函数的中文帮助文件 - [VC专栏]

    作者:hxfwsk
    email: [email protected]
    日期:6/18/2001 2:54:50 PM
    关于PostMessage函数的中文帮助文件

    PostMessage函数放置(发送)一个消息到一个随着创建窗体而创建的消息队列,而且不会等待线程处理这个消息就返回。消息队列中的消息会通过调用GetMessage或PeekMessage函数取得。

    BOOL PostMessage(

    HWND hWnd, // 目标窗口句柄
    UINT Msg, // 要发送的消息
    WPARAM wParam, // 第一个消息参数
    LPARAM lParam // 第二个消息参数
    );


    参数

    hWnd

    Identifies the window whose window procedure is to receive the message. Two values have special meanings:
    接收消息的窗口标识。两个值有特定的意义。

    值         意义
    HWND_BROADCAST  消息被发送到所有系统顶级窗口,包含不可用的和不可视的独立的窗口,重叠窗口,弹出         窗口。不会被传递到子窗口。
    NULL      设置为此参数时,函数作用类似于PostThreadMessage函数使用dwThreadId参数,代表当前         线程 

    Msg

     要发送的消息

    wParam
     附属的消息信息  
    lParam
     附属的消息信息  

    返回值

    成功返回非零值
    失败返回0,可通过GetLastError函数取得出借信息
  • 2005-10-26

    关于ShellExecute函数的中文说明 - [VC专栏]

    作者:hxfwsk
    email: [email protected]
    日期:6/18/2001 2:49:35 PM
    关于ShellExecute函数的中文说明


    ShellExecute 函数 打开或打印一个指定的文件。文件可以是可执行文件也可以是一个文档。请查看关于ShellExecuteEx的帮助。
    HINSTANCE ShellExecute(

    HWND hwnd, // 主窗口句柄
    LPCTSTR lpOperation, // 字符串指针,指定要执行的操作
    LPCTSTR lpFile, // 字符串指针,指定文件名或目录名
    LPCTSTR lpParameters, // 字符串指针,指定传给可执行文件的参数   LPCTSTR lpDirectory, // 字符串指针,指定缺省目录
    INT nShowCmd // 文件显示模式
    );


    参数

    hwnd

    指定一个主窗体。这个窗体与应用程序产生的message boxes。例如,

    lpOperation
    一个非空的字符串指针,指定操作方式。有以下操作方式可用
    字符串意义
    "open"
    该函数打开由lpFile指定的文件,文件可以是一个可执行文件,也可以是文档文件,也可以是一个要打开的目录。
    "print"
    该函数打印由lpFile指定的文件。文件应该是一个文档文件。如果是一个可执行文件则运行这个文件就象指定用"opne"操作方式一样。
    "explore"
    函数打开浏览由lpFile指定的目录窗口。

    如果该参数为NULL,则相当于使用"open"操作方式。

    lpFile

    一个非空字符串指定要打开或打印的文件,或者是要打开浏览的目录名。该函数可以打开一个可执行文件或一个文档文件,也可以打印一个文件。


    lpParameters

    如果lpFile指定一个可执行文件,则lpParameters 是一个指向非空字符串的指针,代表要传给这个应用程序的参数。
    如果lpFile指定一个文档文件,则其应该为空。

    lpDirectory

    非空字符串指定缺省目录
  • 2005-10-26

    回调方式的事件实现 - [VC专栏]

    作者:rick1126
    email: [email protected]
    日期:7/7/2001 6:37:45 PM
    服务端 -- IDL:

    // Chapter7_Server.idl : IDL source for Chapter7_Server.dll
    //

    // This file will be processed by the MIDL tool to
    // produce the type library (Chapter7_Server.tlb) and marshalling code.

    import "oaidl.idl";
    import "ocidl.idl";

    [
    object,
    uuid( 6E8087CE-C144-4eea-B48A-2D9FD376E8FA ),
    helpstring( "ICallBack Interface" ),
    pointer_default(unique)
    ]
    interface ICallBack: IUnknown //回调接口
    {
    //事件对应的方法
    [helpstring("method ComputationComplete")] HRESULT ComputationComplete( long lResult );
    };

    [
    object,
    uuid(CE4BD5E1-B808-4617-91F0-2B4728290D4F),

    helpstring("IMath Interface"),
    pointer_default(unique)
    ]
    interface IMath : IUnknown
    {
    //组件自身方法, 如果需要触发事件, 需要在适当的地方添加
    /*
    if ( m_pCallBack )
    m_pCallBack->ComputationComplete( lResult );
    */
    [helpstring("method Add")] HRESULT Add([in]long lOp1, [in]long lOp2 );
    [helpstring("method Subtract")] HRESULT Subtract([in]long lOp1, [in]long lOp2 );
    [helpstring("method Multiply")] HRESULT Multiply([in]long lOp1, [in]long lOp2 );
    [helpstring("method Divide")] HRESULT Divide([in]long lOp1, [in]long lOp2 );
    //回调接口维护方法
    // 其实其中的ICallBack只是一个ATL的公开的回调接口, 需要组件提供一个成员变量保存这个ICallBack实例
    // 下列函数的相应实现:
    /*
    //ICallBack
    STDMETHODIMP CMath::Advise( ICallBack *pCallBack )
    {
    m_pCallBack = pCallBack;
    m_pCallBack->AddRef();
    return S_OK;
    }

    STDMETHODIMP CMath::Unadvise()
    {
    m_pCallBack->Release();
    m_pCallBack = 0;
    return S_OK;
    }

    */
    [helpstring("method Advise")] HRESULT Advise([in]ICallBack* pCallBack);
    [helpstring("method Unadvise")] HRESULT Unadvise();
    };

    [
    uuid(E208EDBD-30BF-4868-9D46-133E66C60C87),
    version(1.0),
    helpstring("Chapter7_Server 1.0 Type Library")
    ]
    library CHAPTER7_SERVERLib
    {
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");

    [
    uuid(F257585E-5190-4E7C-B5B2-990B5FD3DA5C),
    helpstring("Math Class")
    ]
    coclass Math
    {
    [default]interface IMath;
    interface ICallBack;
    };
    };


    事件的好处就是异步...
  • 2005-10-26

    基于MFC的大型数据文件处理方法 - [VC专栏]

    作者:SKYHORSEBJ
    email: [email protected]
    日期:2001-7-4 17:30:36
    在Visual C++中,MFC(微软基础类库)提供了CFile和CStdio
    File两个类来进行程序中的文件输入输出操作。Cfile类提供了基于二
    进制流的文件操作,功能类似于C语言中的fread()和fwrite()函
    数。CStdioFile提供了基于字符串流的文件操作,功能类似于C语言中
    的fgets()和fputs()函数。但是,使用这两个类进行文件操作时
    ,对于一次文件读写的数据量的大小必须限制在65535字节以内。其原
    因是在VC中访问大于65535字节的缓冲区需要Huge型指针,而在CFile
    和CStdioFile类中,使用的是Far型的指针。由于Far型指针不具有跨
    段寻址的能力,因此限制了一次文件读写的长度小于65535字节。如果
    传递给CFile和CStdioFile两个类的成员函数的数据缓冲区的大小大于
    65535字节的时候,VC就会产生ASSERT错误。
    笔者在使用Visual C++进行多媒体程序设计的时候,由于程序
    处理的数据量非常大,所以需要频繁地读写大于65535字节的数据。在
    使用CFile和CStdioFile类处理巨型数据的时候一般是分段读写,笔者
    感到这样的处理方法非常地繁琐,同时容易导致程序编制错误。笔者
    在查阅了相关的文献以后,找到了使用Visual C++直接读写巨型数
    据的方法。
    在MFC的CFile类中提供了两个未载入文档的函数,其原型声明在
    AFX.H中。函数原型如下:
    DWORD CFile::ReadHuge(void FAR *lpBuffer,DWORD dwCo
    unt);
    void CFile::WriteHuge(const void FAR*lpBuffer,DWORD
    dwCount);
    在这两个函数内部使用的都是Huge型指针来对传递的缓冲区进行
    寻址,因此可以读写大于65535字节的巨型数据。
    对于ReadHuge()和WriteHuge()函数需要的巨型缓冲区可以使
    用Windows的API函数GobalAlloc()来创建。
    作为一个例子,下面的程序段演示了通过使用Read
  • 2005-10-26

    利用键盘钩子在Windows平台下捕获键盘动作 - [VC专栏]

    作者:skyhorsebj
    email: [email protected]
    日期:2001-7-3 18:10:43
    一。我们可以在应用程序中毫不费力的捕获在本程序窗口上所进行的键盘操作,但如果我们想要将此程序作成一个监控程序,捕获在Windows平台下任意窗口上的键盘操作,就需要借助于全局钩子来实现了。
    二、系统钩子和DLL
    钩子的本质是一段用以处理系统消息的程序,通过系统调用,将其挂入系统。钩子的种类有很多,每种钩子可以截获并处理相应的消息,每当特定的消息发出,在到达目的窗口之前,钩子程序先行截获该消息、得到对此消息的控制权。此时在钩子函数中就可以对截获的消息进行加工处理,甚至可以强制结束消息的传递。
    在本程序中我们需要捕获在任意窗口上的键盘输入,这就需要采用全局钩子以便拦截整个系统的消息,而全局钩子函数必须以DLL(动态连接库)为载体进行封装,VC6中有三种形式的MFC DLL可供选择,即Regular statically linked to MFC DLL(标准静态链接MFC DLL)、Regular using the shared MFC DLL(标准动态链接MFC DLL)以及Extension MFC DLL(扩展MFC DLL)。 在本程序中为方便起见采用了标准静态连接MFC DLL。
    三、键盘钩子程序示例
    本示例程序用到全局钩子函数,程序分两部分:可执行程序KeyHook和动态连接库LaunchDLL。
    1、首先编制MFC扩展动态连接库LaunchDLL.dll:
    (1)选择MFC AppWizard(DLL)创建项目LaunchDLL;在接下来的选项中选择Regular statically linked to MFC DLL(标准静态链接MFC DLL)。
    (2)在LaunchDLL.h中添加宏定义和待导出函数的声明:
    #define DllExport __declspec(dllexport)
    ……
    DllExport void WINAPI InstallLaunchEv();
    ……
    class CLaunchDLLApp : public CWinApp
    {
    public:
    CLaunchDLLApp();
  • 2005-10-26

    连接点的事件实现 - [VC专栏]

    作者:rick1126
    email: [email protected]
    日期:7/7/2001 6:41:45 PM
    服务端
    1. 首先保证你创建ATL对象的时候选中了IConnectionPoint的支持

    // Chapter7_CPServer.idl : IDL source for Chapter7_CPServer.dll
    //

    // This file will be processed by the MIDL tool to
    // produce the type library (Chapter7_CPServer.tlb) and marshalling code.

    import "oaidl.idl";
    import "ocidl.idl";
    [
    object,
    uuid(76E00670-8C03-458D-8A36-AB7E179346CF),
    dual,
    helpstring("IMath Interface"),
    pointer_default(unique)
    ]
    interface IMath : IDispatch
    {
    [helpstring("method Add")] HRESULT Add([in]long lOp1, [in]long lOp2, [out] long* plResult );
    [helpstring("method Subtract")] HRESULT Subtract([in]long lOp1, [in]long lOp2, [out] long* plResult );
    [helpstring("method Multiply")] HRESULT Multiply([in]long lOp1, [in]long lOp2, [out] long* plResult );
    [helpstring("method Divide")] HRESULT Divide([in]long lOp1, [in]long lOp2, [out] long* plResult );
    };

    [
    uuid(503AB931-AF9A-4CE0-8D34-D106C6341A58),
    version(1.0),
    helpstring("Chapter7_CPServer 1.0 Type Library")
    ]
    library CHAPTER7_CPSERVERLib
    {
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");

    [
    uuid(FC5CB2DB-6011-4431-80AA-01E7B60461EC),
    helpstring("_IMathEvents Interface"),
    ]
    dispinterface _IMathEvents//事件接口, 看到了吧_前缀需要注意, 而且它是一个纯Dispatch接口
    {
    properties:
    methods:
    [id(1),helpstring("method ComputationComplete")] HRESULT ComputationComplete([in]long lResult);
    };

    [
    uuid(7A11D9AD-52ED-4A3C-92E4-7AA579713C11),
    helpstring("Math Class")
    ]
    coclass Math
    {
    [default] interface IMath;
    [default,source] dispinterface _IMathEvents;
    };
    };


    客户端:

    class CMathEvents: public CComObjectRoot, public _IMathEvents
    {
    public:
    CMathEvents(){}

    BEGIN_COM_MAP( CMathEvents )
    COM_INTERFACE_ENTRY(_IMathEvents)
    END_COM_MAP()

    //IMathEvents
    public:
    STDMETHODIMP GetTypeInfoCount( UINT* )
    {
    return E_NOTIMPL;
    }

    STDMETHODIMP GetTypeInfo( UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo )
    {
    return E_NOTIMPL;
    }

    STDMETHODIMP GetIDsOfNames( REFIID riid, LPOLESTR* rgsNames, UINT cNames, LCID lcid, DISPID *rgDispId )
    {
    return E_NOTIMPL;
    }

    STDMETHODIMP Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams,
    VARIANT* pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr )
    {
    switch ( dispIdMember)
    {
    case 0x1:
    if ( pDispParams->cArgs!=1 )
    return DISP_E_BADPARAMCOUNT;

    if ( pDispParams->cNamedArgs )
    return DISP_E_NONAMEDARGS;

    HRESULT hr;
    VARIANTARG var;
    VariantInit( &var );
    hr = VariantChangeTypeEx( &var, &(pDispParams->rgvarg[0]), lcid, 0, VT_I4 );
    if ( FAILED( hr ) )
    return DISP_E_BADVARTYPE;

    ComputationComplete( var.lVal );
    break;
    default:
    DisplayMessage( "Error" );
    break;
    }
    return S_OK;
    }

    STDMETHODIMP ComputationComplete( long lResult )
    {
    char szMsg[128];
    sprintf( szMsg, "The result is %d", lResult );
    DisplayMessage( szMsg );
    return S_OK;
    }
    };
  • 2005-10-26

    让CC++图形程序独立运行 - [C++]

    作者:star2002
    日期:2000-8-18 8:36:41
    C/C++语言提供了十分丰富的图形函数,图形函数文件为Graphics.h,使用图形函数前须先将屏幕设置为图形模式,C/C++语言提供了下面的函数:
      void far initgraph(int far *GD,int far *GM,char *P);
      其中,GD和GM分别表示图形驱动程序和图形模式,P指图形驱动程序所在的目录路径。
      图形驱动程序由Borland公司(对于Turbo C和Borland C++)提供,同时C/C++语言还提供了退出图形状态的函数closegraph(),格式为:
      void far closegraph(void);
      许你经常在用C/C++语言编写一些图形程序,但是总不能脱离C/C++语言环境独立运行,我们怎样来解决呢?下面是实现图形程序独立运行的具体步骤:
      1.将驱动程序EGAVGA.BGI转换成目标文件EGAVGA.OBJ:
      C:/TC>BGIOBJ EGAVGA
      按同样的办法,将字体文件*.CHR转换成目标文件*.OBJ:
      C:/TC>BGIOBJ TRIP
      C:/TC>BGIOBJ LITT
      C:/TC>BGIOBJ SANS
      C:/TC>BGIOBJ GOTH
      2.将上述建立的OBJ文件加入到GRAPHICS.LIB库文件中,具体方法如下:
      C:/TC>TLINK C:/TC/LIB/GRAPHICS.LIB+EGAVGA
      C:/TC>TLINK C:/TC/LIB/GRAPHICS.LIB+TRIP
      C:/TC>TLINK C:/TC/LIB/GRAPHICS.LIB+LITT
      C:/TC>TLINK C:/TC/LIB/GRAPHICS.LIB+SANS
      C:/TC>TLINK C:/TC/LIB/GRAPHICS.LIB+GOTH
      也可以使用TLIB、PRJ程序代替TLINK。
      3.在程序中调用initgraph()函数前,应加上如下语句:
      registerbgidriver(EGAVGA-driver);
      它通知连接程序把EGAVGA驱动程序装入用户的执行程序中,同样在装入字体文件之前要加上如下语句:
      registerbgifont(字体文件名);
      4.通过上述处理后,编译连接后的执行程序就可以在任何目录下运行了。这时,将屏幕初始化为图形模式的函数可改写为:
      void InitGra(void)
      {int GD=DETECT,GM;
      registerbgidriver(EGAVGA_driver);
      registerbgifont(triplex_font);
      registerbgifont(small_font);
      registerbgifont(sansserif_font);
      registerbgifont(gothic_font);
      initgraph(&GD,&GM,"");
      }
      按照以上步骤,就能实现图形程序的独立运行
  • 2005-10-26

    让VC中的非模式属性表PropertySheet出现OKCancelApply按钮 - [VC专栏]

    作者:茶馆主人
    日期:2001-6-3 20:55:08
    让VC中的非模式属性表PropertySheet出现OK/Cancel/Apply按钮

    北京商即通数码科技有限公司 张宏

    很多VC程序员都遇到过这个问题,当建立一个非模式的属性表(不是向导模式)时,此时正常用DoModal()调用时可以出现的OK/Cancel/Apply等按钮全都不见了,真让人郁闷!笔者查找了很多资料,均没有正确的答案,最后,笔者自己分析,在CPropertySheet调用初始化对话框CPropertySheet::OnInitDialog()时由于m_psh中自动设置了PSH_MODELESS属性,导致自动调整对话框大小,隐藏了对话框中的OK按钮部分,因此,笔者考虑以下解决办法:
    1.从CPropertySheet派生一个自己的属性表类,将要加入的页面作为其成员变量:
    class CMySheet : public CPropertySheet
    {
    DECLARE_DYNAMIC(CMySheet)

    // Construction
    public:
    CMySheet(UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0);
    CMySheet(LPCTSTR pszCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0);

    // Attributes
    public:

    // Operations
    public:

    // Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CMySheet)
    public:
    virtual BOOL OnInitDialog();
    //}}AFX_VIRTUAL

    // Implementation
    public:
    virtual ~CMySheet();

    // Generated message map functions
    protected:
    //{{AFX_MSG(CMySheet)
    // NOTE - the ClassWizard will add and remove member functions here.
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
    private:
    void AddPages(); //添加属性页
    CPage2 page2; //定义属性页变量
    CPage1 page1;
    };

    2.添加属性页:
    void CMySheet::AddPages()
    {
    AddPage(&page1);
    AddPage(&page2);
    }
    CMySheet::CMySheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
    :CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
    {
    AddPages();
    }

    CMySheet::CMySheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
    :CPropertySheet(pszCaption, pParentWnd, iSelectPage)
    {
    AddPages();
    }

    3.在属性页的初始化对话框中拉大对话框高度,并且将OK/Cancel/Apply按钮显示、激活。
    BOOL CMySheet::OnInitDialog()
    {
    BOOL bResult = CPropertySheet::OnInitDialog();

    RECT rc;

    // 调整属性页对话框的大小
    GetWindowRect (&rc);
    rc.bottom += 30; //窗口向下拉30点,让OK按扭可以显示出来
    MoveWindow (&rc); //调整窗口
    GetDlgItem(IDOK)->ShowWindow(SW_SHOW); //显示隐藏的OK按钮
    GetDlgItem(IDOK)->EnableWindow(); //激活OK按钮
    GetDlgItem(IDCANCEL)->ShowWindow(SW_SHOW); //显示隐藏的Cancel按钮
    GetDlgItem(IDCANCEL)->EnableWindow(); //激活Cancel按钮
    GetDlgItem(ID_APPLY_NOW)->ShowWindow(SW_SHOW); //显示Apply按钮
    GetDlgItem(ID_APPLY_NOW)->EnableWindow(); //激活Apply按钮

    return bResult;
    }
    好了,在主对话框中加入一个测试按钮,加入一个CmySheet *sh指针成员变量,并且加入以下代码:
    CTestPropertySheetDlg::CTestPropertySheetDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CTestPropertySheetDlg::IDD, pParent)
    {
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    sh=NULL;
    }
    void CTestPropertySheetDlg::OnButton1()
    {
    sh=new CMySheet("测试对话框");
    sh->Create(this);
    }

    void CTestPropertySheetDlg::OnDestroy()
    {
    CDialog::OnDestroy();

    if (sh) delete sh;
    }

    编译工程并运行,你看到了什么?OK/Cancel/Apply全都出来了!
    可是,点击OK按钮看看?怎么?不会关闭对话框!!! >:-(((
    怎么办?是非模式对话框没有响应OK按钮吗?不是,当前的各属性页已经得到了OK按钮事件,但是属性表没有对OK按钮响应,加上对事件的处理以后就可以了:
    BOOL CMySheet::OnCommand(WPARAM wParam, LPARAM lParam)
    {
    if (HIWORD (wParam) == BN_CLICKED)
    {
    switch (LOWORD (wParam))
    {
    case IDOK:
    PressButton (PSBTN_OK);
    DestroyWindow ();
    return (TRUE);
    case ID_APPLY_NOW: // Apply
    PressButton (PSBTN_APPLYNOW);
    return (TRUE);
    case IDCANCEL:
    PressButton (PSBTN_CANCEL);
    DestroyWindow ();
    return (TRUE);
    case IDHELP:
    PressButton (PSBTN_HELP);
    return (TRUE);
    }
    }
    return CPropertySheet::OnCommand(wParam, lParam);
    }

    现在再编译试试看,一切OK了。

  • 2005-10-26

    如何更改用户密码 - [VC专栏]

    作者:wgenry
    email: [email protected]
    日期:2000-8-24 8:19:29
    如果你使用ADSI的话
    大概应该是这样的
    Set obja = GetObject path
    ’path 的格式为WINNT:///Users/,如果用户在Users下的话
    obja.ChangePassword OldPwd,newPwd

    如果用ISAPI或者ASP Component
    STDMETHODIMP CPwdMgr::ChangePassword(BSTR Domain, BSTR UserName, BSTR OldPwd, BSTR NewPwd, VARIANT_BOOL *RetVal)
    {
    HRESULT hr;
    LPWSTR wUserName;
    LPWSTR wComputerName = NULL; // default to local machine
    LPWSTR wDomain;
    LPWSTR wOldPassword;
    LPWSTR wNewPassword;
    USER_INFO_1003 pi1003;
    NET_API_STATUS nas;

    _bstr_t bstrDomain,bstrUser,bstrOldPwd,bstrNewPwd;
    bstrDomain = Domain;
    bstrUser = UserName;
    bstrOldPwd = OldPwd;
    bstrNewPwd = NewPwd;

    wDomain = (wchar_t*)bstrDomain;
    wUserName = (wchar_t*)bstrUser;
    wOldPassword = (wchar_t*)bstrOldPwd;
    wNewPassword = (wchar_t*)bstrNewPwd;

    nas = NetGetDCName(
    NULL,
    wDomain,
    (LPBYTE *)&wComputerName
    );

    if(nas != NERR_Success)
    {
    if (nas == NERR_DCNotFound)
    AtlReportError(CLSID_PwdMgr,"Could not find the domain controller for the domain",IID_IPwdMgr,nas);
    if (nas == ERROR_INVALID_NAME)
    AtlReportError(CLSID_PwdMgr,"The name could not be found",IID_IPwdMgr,nas);
    *RetVal = VARIANT_FALSE;
    return E_FAIL;
    }

    if(wOldPassword == NULL)
    {
    pi1003.usri1003_password = wNewPassword;

    nas = NetUserSetInfo(
    wComputerName, // computer name
    wUserName, // username
    1003, // info level
    (LPBYTE)&pi1003, // new info
    NULL
    );
    if (nas != NERR_Success)
    {
    switch(nas)
    {
    case ERROR_ACCESS_DENIED:
    AtlReportError(CLSID_PwdMgr,"The user does not have access to the requested information",IID_IPwdMgr,nas);
    break;
    case ERROR_INVALID_PARAMETER:
    AtlReportError(CLSID_PwdMgr,"One of the function parameters is invalid. For more information, see the following Remarks section",IID_IPwdMgr,nas);
    break;
    case NERR_InvalidComputer:
    AtlReportError(CLSID_PwdMgr,"The computer name is invalid",IID_IPwdMgr,nas);
    break;
    case NERR_NotPrimary:
    AtlReportError(CLSID_PwdMgr,"The operation is allowed only on the primary domain controller of the domain",IID_IPwdMgr,nas);
    break;
    case NERR_SpeGroupOp:
    AtlReportError(CLSID_PwdMgr,"The operation is not allowed on specified special groups, which are user groups, admin groups, local groups, or guest groups",IID_IPwdMgr,nas);
    break;
    case NERR_LastAdmin:
    AtlReportError(CLSID_PwdMgr,"The operation is not allowed on the last administrative account",IID_IPwdMgr,nas);
    break;
    case NERR_BadPassword:
    AtlReportError(CLSID_PwdMgr,"The share name or password is invalid",IID_IPwdMgr,nas);
    break;
    case NERR_PasswordTooShort:
    AtlReportError(CLSID_PwdMgr,"The password is shorter than required. (The password could also be too long, be too recent in its change history, not have enough unique characters, or not meet another password policy requirement.)",IID_IPwdMgr,nas);
    break;
    case NERR_UserNotFound:
    AtlReportError(CLSID_PwdMgr,"The user name could not be found",IID_IPwdMgr,nas);
    break;
    }
    }
    }
    else
    {
    nas = NetUserChangePassword(
    wComputerName,
    wUserName,
    wOldPassword,
    wNewPassword
    );
    if (nas != NERR_Success)
    {
    switch(nas)
    {
    case ERROR_ACCESS_DENIED:
    AtlReportError(CLSID_PwdMgr,"The user does not have access to the requested information",IID_IPwdMgr,nas);
    break;
    case ERROR_INVALID_PASSWORD :
    AtlReportError(CLSID_PwdMgr,"The user has entered an invalid password",IID_IPwdMgr,nas);
    break;
    case NERR_InvalidComputer:
    AtlReportError(CLSID_PwdMgr,"The computer name is invalid",IID_IPwdMgr,nas);
    break;
    case NERR_NotPrimary:
    AtlRepor
  • 2005-10-26

    如何用VC++和VFP进行ActiveX数据通讯 - [VC专栏]

    作者:SKYHORSEBJ
    email: [email protected]
    日期:2001-7-4 17:31:33
    在进行软件开发的过程中,如何在不同的编程工具之间进行数据交换和通讯,需要进行不断的探索和总结。我们在开发机械CAD仿真软件的过程中,遇到了在VC++中读取和修改Visual Foxpro中数据的问题。经过许多次的试验,我们通过采用自动服务器(ActiveX Automation)的方法圆满解决了这个问题。

    ---- 自动服务器,以前称为OLE Automation,后来称为ActiveX OLE Automation,就是编写能被其他程序调用的代码。其他程序不是以DLL的孤立方式而是直接调用自动服务器的用户代码。这其中比较难理解的概念是:自动服务器用户代码向其他应用程序揭示了属性(变量)和方法(函数)。以下将以整型和双精度(对浮点型同样适用)的数据传递为例,讲述如何用ActiveX Automation在VC++和Visual Foxpro之间进行数据通讯,例子中的编程工具为VC++ 5.0和Visual Foxpro 5.0。

    ---- 一.在Visual Foxpro中创建自动服务器

    ---- 1. 在Visual Foxpro中定义服务器类(此例中为CDATA类)首先在某目录下新建一工程,在工程管理器中选择代码栏,同一目录下新建一程序(如MyServer.prg),并在此程序文件中定义服务器类。

    *File Name: MyServer.prg
    DEFINE CLASS CData AS Custom OLEPUBLIC
    *对VC++而言, para1为整型, para2为double型
    para1=123
    para2=123.123
    PROCEDURE ChangeData
    this.para1=this.para1*2
    this.para2=this.para2*2
    RETURN
    ENDPROC
    ENDDEFINE

    ---- 2. 将上述程序联编为可执行程序(如MyServer.exe)

    ---- 选中程序MyServer,点击“连编”按钮,选择“连编可执行程序”选项,并按确定,便可生成可执行程序。

    ---- 二.在VC++中对自动服务器进行测试

    ---- 1.关闭Visual Foxpro,在VC++中选择菜单项“File-New”,再选择“Projects”中的“MFC AppWizard (exe)”选项,随后按默认方式生成“Dialog Based”的工程项目(此例中工程名为MyTest)。

    ---- 2.在MyTest.cpp中的APP类的InitInstance()函数开头加入OLE使能

    BOOL CMyTestApp::InitInstance()
    {
    BOOL OleEnable=AfxOleInit();
    if(!OleEnable) return FALSE;
    ……
    }

    ---- 3.在ClassWizard中选“Automation”中的“Add Class-From a type library”。在“Import from type library”对话框中找到刚才所创建的Visual Foxpro工程目录下的tlb文件(如MyServer.tlb)并选择“打开”按钮,会有提示说明将要从Lib中生成CDATA类,点击OK按钮加以确认,将自动在项目中加入与CDATA类有关的文件MyServer.cpp和MyServer.h。在ClassView中可查看CDATA类的函数,如GetPara1()、SetPara1()和CHANGEDATA()等。这里要注意类名CDATA和函数名CHANGEDATA()的大小写请参看具体的头文件MyServer.h。

    ---- 4.在对话框类(此例中为CMyTestDlg)的头文件MyTestDlg.h的开头部分,将CDATA类的头文件MyServer.h包含进来。随后定义CDATA类的实例m_data作为对话框类的成员变量。

    // MyTestDlg.h : header file
    #include "myserver.h"
    ……
    class CMyTestDlg : public CDialog
    {
    // Construction
    public:
    CMyTestDlg(CWnd* pParent = NULL);
    // standard constructor
    private:
    CDATA m_data; //定义CDATA类的实例m_data
    ……
    }
    ……

    ---- 5.在对话框的初始化部分(如InitDialog()函数中)加入

    m_data.CreateDispatch(“MyServer.CDATA);

    ---- 6.使用自动服务器的通讯编程

    ---- 此例中,我们在对话框中设置一命令按钮“Test”,通过点击该按钮来对自动服务器进行测试。

    void CMyTestDlg::OnButtonTest()
    {
    //首先利用CDATA类的GetPara1()
    等取值函数取出Foxpro中的变量值,
    //再利用CDATA类SetPara1()
    等赋值函数来修改Foxpro中的这些变量值
    //也可以在VC++中调用CDATA
    类的函数CHANGEDATA()来修改变量值
    //定义tagVARIANT型变量,
    请参看有关tagVARINAT的帮助
    tagVARIANT mypara1, mypara2;
    mypara1=m_data.GetPara1();
    //读取Foxpro中的变量值para1
    mypara2=m_data.GetPara2();
    //读取Foxpro中的变量值para2
    //检验读取数据是否正确(Foxpro设定为123和123.123)
    if(mypara1.iVal==123)
    MessageBox("mypara1.iVal=123");
    if
  • 2005-10-26

    什么是C++ - [C++]

    作者:rick1126
    email: [email protected]
    日期:8/20/2001 7:20:10 PM
    C++ 是脱胎自 C 语言的一种中级语言. 从计算机角度看, 它可以嵌入ASM等低端语言; 从面向对象的程序设计角度看, 它有具备OOP的三个基本特征 -- 抽象, 封装和继承; 同时从市场角度来看, 它又不是纯面向对象, 其实那些纯粹的面向对象语言的阵地只是在实验室.

    比较C语言. C++ 的几个显著变化或者解决的问题就是
    1. 名字空间的问题, 原始的C语言使用公共的名字空间, 这样无论是开发本人还是第三方团队都面临变量名字耗尽的问题. 而C++提供独立的名字空间, 而且对象的引入也为名字空间提供了进一步划分

    2. 代码复用的问题, C语言使用函数库的方式或者DLL方式实现代码复用, 在接口稳定的前提下实现内部修改和数据及其实现的封装. C++提供了类库机制实现了具有层次的代码复用, 和多种继承机制, 同时重载等各种机制提供了进一步的复用实现. 使得类库和代码更加容易维护, 虽然建立类库在人员, 组织等各个方面还是比较麻烦的.

    3. 安全机制. 因为有了类机制, 有一些初始化操作可以自动实现

    4. 效率问题. 因为C语言本质上是站在计算机立场的非常注重效率的问题, 但是事物总是具备矛盾的两面, 过于偏重效率和软件危机的出现, 反而增加了程序设计的难度. 而OOA的现实世界角度的考虑问题更加贴近自然, 使得代码或者程序更加具备稳定性, 可扩展性和可维护性.

    为此, 和经典物理同量子物理一样. C/C++ 今天在不同领域各自发挥着最大的效率.

    BTW: 希望各位多多跟贴. 注意主题, 不要跑火车.
  • 2005-10-26

    隐藏鼠标 - [VC专栏]

    作者:hxfwsk
    email: [email protected]
    日期:8/2/2001 4:41:09 PM
    如何实现象“超级解霸”中的自动隐藏鼠标功能呢?当我们欣赏VCD时,屏幕中的鼠标常常会影响观赏效果。当一段时间内,用户没有任何输入的情况下就自动隐藏鼠标,一旦用户进行任何输入立即重新显示出鼠标即可。程序段如下:
    请先添加一个Timer组件,再设置Timer组件的Interval属性为1000,

    //----------------------------------------------------------
    #include
    #pragma hdrstop

    #include "Unit1.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    bool MouseHide;
    void MouseDo();
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------

    void __fastcall TForm1::FormCreate(TObject *Sender)
    {
       MouseHide=true;
    }
    //---------------------------------------------------------------------

    void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,
       TShiftState Shift, int X, int Y)
    {
       MouseDo(); 
    }
    //---------------------------------------------------------------------
    void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift,
       int X, int Y)
    {
       MouseDo(); 
    }
    //---------------------------------------------------------------------
    void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,
       TShiftState Shift, int X, int Y)
    {
       MouseDo(); 
    }
    //---------------------------------------------------------------------
    void __fastcall TForm1::Timer1Timer(TObject *Sender)
    {
      MouseHide=true;

      Screen->Cursor=crNone;// 隐藏鼠标

      Timer1->Enabled=false;


    }
    //---------------------------------------------------------------------
      void MouseDo()
      {
       if(MouseHide)
       {
       Screen->Cursor=crDefault;//显示鼠标
       MouseHide=false;
       }
      else
       Form1->Timer1->Enabled=true;


  • 2005-10-26

    用c写cgi(转) - [C语言]

    作者:chache
    email: [email protected]
    日期:2000-10-24 2:03:05
    三、From输入的分析和解码

      1.分析名字/值对

      当用户提交一个HTML Form时,Web浏览器首先对Form中的数据以名字/值对的形式进行编
    码,并发送给Web服务器,然后由Web服务器传递给CGI程序。其格式如下:
      name1=value1&name2=value2&name3=value3&name4=value4&...
      其中名字是Form中定义的INPUT、SELECT或TExtAREA等标置(Tag)名字,值是用户输入或选
    择的标置值。这种格式即为URL编码,程序中需要对其进行分析和解码。要分析这种数据流,C
    GI程序必须首先将数据流分解成一组组的名字/值对。这可以通过在输入流中查找下面的两个
    字符来完成。
      每当找到字符=,标志着一个Form变量名字的结束;每当找到字符& ,标志着一个Form变量
    值的结束。请注意输入数据的最后一个变量的值不以&结束。
      一旦名字/值对分解后,还必须将输入中的一些特殊字符转换成相应的ASCII字符。这些特
    殊字符是:
      +:将+转换成空格符;
      %xx:用其十六进制ASCII码值表示的特殊字符。根据值xx将其转换成相应的ASCII字符。
      对Form变量名和变量值都要进行这种转换。下面是一个对Form数据进行分析并将结果回
    送给Web服务器的CGI程序。


      #include
      #include
      #include
      int htoi(char *);
      main()
      {
       int i,n;
      char c;
      printf (″Contenttype: text/plain/n/n″);
      n=0;
      if (getenv(″CONTENT-LENGTH″))
       n=atoi(getenv(″CONTENT-LENGTH″));
      for (i=0; i   int is-eq=0;
      c=getchar();
      switch (c){
       case ′&′:
        c=′/n′;
        break;
       case ′+′:
        c=′ ′;
        break;
       case ′%′:{
        char s[3];
        s[0]=getchar();
        s[1]=getchar();
        s[2]=0;
        c=htoi(s);
        i+=2;
       }
       break;
      case ′=′:
       c=′:′;
       is-eq=1;
       break;
      };
      putchar(c);
      if (is-eq) putchar(′ ′);
      }
      putchar (′/n′);
      fflush(stdout);
      }
      /* convert hex string to int */
      int htoi(char *s)
      {
       char *digits=″0123456789ABCDEF″;
      if (islower (s[0])) s[0]=toupper(s[0]);
      if (islower (s[1])) s[1]=toupper(s[1]);
      return 16 * (strchr(digits, s[0]) -strchr (digits,′0′)
    )
      +(strchr(digits,s[1])-strchr(digits,′0′));
      }

      上面的程序首先输出一个MIME头信息给Web服务器,检查输入中的字符数,并循环检查每一
    个字符。当发现字符为&时,意味着一个名字/值对的结束,程序输出一个空行;当发现字符为+
    时,将它转换成空格; 当发现字符为%时,意味着一个两字符的十六进制值的开始,调用htoi()
    函数将随后的两个字符转换为相应的ASCII字符;当发现字符为=时,意味着一个名字/值对的名
    字部分的结束,并将它转换成字符:。最后将转换后的字符输出给Web服务器。
      四、产生HTML输出

      CGI程序产生的输出由两部分组成:MIME头信息和实际的信息。两部分之间以一个空行分
    开。我们已经看到怎样使用MIME头信息″Cont enttype:text/plain/n/n″和printf()、put
    char()等函数调用来输 出纯ASCII文本给Web服务器。实际上,我们也可以使用MIME头信息″
    C ontenttype:text/html/n/n″来输出HTML源代码给Web服务器。请注意任何MIME头信息后必
    须有一个空行。一旦发送这个MIME头信息给We b服务器后,Web浏览器将认为随后的文本输出
    为HTML源代码,在HTML源代码中可以使用任何HTML结构,如超链、图像、Form,及对其他CGI程
    序的调用。也就是说,我们可以在CGI程序中动态产生HTML源代码输出 ,下面是一个简单的例
    子。

      #include
      #include
      main()
      {
       printf(″Contenttype:text/html/n/n″);
      printf(″/n″);
      printf(″an HTML Page From a CGIt/n″);
      printf(″
    /n″);
    printf(″

    This is an HTML page generated from with i n a CGI program..  
     .

    /n″);
      printf(″

    /n″);
      printf(″ Go back to out put.html pa
    ge <
      /b>
    /n″);
      printf(″/n″);
      printf(″/n″);
      fflush(stdout);
      }


      上面的CGI程序简单地用printf()函数来产生HTML源代码。请注意在输出的字符串中如果
    有双引号,在其前面必须有一个后斜字符/, 这是因为整个HTML代码串已经在双引号内,所以H
    TML代码串中的双引号符必须用一个后斜字符/来转义。


      五、结束语

      本文详细分析了用C语言进行CGI程序设计的方法、过程和技巧。C语言的CGI程序虽然执
    行速度快、可靠性高,但是相对于Perl语

  • 2005-10-26

    用vc6.0编服务器与客户机互相传送消息的程序 - [VC专栏]

    作者:SKYHORSEBJ
    email: [email protected]
    日期:2001-7-4 17:34:53
    首先介绍服务器程序:

    ---- 1.创建一个名为"server"的项目,单文档界面.

    ---- 2.在serverview.h中加入代码:

    #include "winsock.h"
    添加变量:
    CSize sizeTotal;//控制滚动条
    intcount;//信息条数
    CString m_data[1000];//信息存放
    char Hostname[260];
    char Hostaddress[20];//主机IP地址
    SOCKET m_sock;
    HANDLE m_hListenThread;//线程
    BOOL m_bInitialized;//是否初始化
    WSADATAWSAData;
    BOOL flag;
    SOCKADDR_IN saClnt;
    int saClntLen;
    BOOL Isconnect;//是否连接

    ---- 3.在serverview.cpp中重载CServerView()构造器,创建并绑定嵌套字:

    CServerView::CServerView()
    {
    // TODO: add construction code here
    Isconnect=FALSE;
    flag=FALSE;
    sizeTotal.cy=350;
    sizeTotal.cx=300;
    m_hListenThread;
    count=5;
    int status;
    WSADATA wsaData;
    m_data[0]="initializing Windows Sockets DLL....";
    if((status=WSAStartup(0x0101,&wsaData))==0)
    {
    m_data[0]+="Succeeded";
    m_bInitialized=TRUE;
    }
    else
    {
    m_bInitialized=FALSE;
    }
    m_sock=socket(AF_INET,SOCK_DGRAM,0);
    m_data[1]="Creating socket....";
    if(m_sock==INVALID_SOCKET)
    {
    m_data[1]+="Failed";
    }
    m_data[1]+="Succeeded";
    m_data[2]="Binding socket....";
    sockaddr_in sa;
    sa.sin_family=AF_INET;
    sa.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
    sa.sin_port=htons(5050);
    if(bind(m_sock,(PSOCKADDR)&sa,sizeof
    (sa))==SOCKET_ERROR)
    {
    m_data[2]+="Failed";
    closesocket(m_sock);
    }
    m_data[2]+="Succeeded";
    m_data[3]="Creating listener thread....";
    unsigned long idThread;
    m_hListenThread=CreateThread(NULL,0,
    (LPTHREAD_START_ROUTINE)Listen,(void *)
    this,0,&idThread);
    if(m_hListenThread)
    {m_data[3]+="Succeeded";
    m_data[4]+="Listening....";
    }
    else
    m_data[4]+="Failed";

    }

    ---- 4.在析构函数中完成必需的清除操作:

    CServerView::~CServerView()
    {
    if(m_bInitialized)
    WSACleanup();
    closesocket(m_sock);
    if(m_hListenThread)
    ::TerminateThread(m_hListenThread,0);
    }

    ---- 5.定义接收和处理消息的线程:

    long WINAPI Listen(CServerView *pView)
    {

    char msg[2000]="";
    intnchar;
    SOCKADDR_IN saClnt;
    int saClntLen;
    while(1)
    {
    saClntLen=sizeof(saClnt);
    nchar=recvfrom(pView->m_sock,msg,1024,0,
    (PSOCKADDR)&saClnt,&saClntLen);
    if(nchar<0) { pView->m_data[pView->count++]+
    ="Error in recvfrom/n";
    pView->InvalidateRect(NULL);
    }
    else
    {

    switch(msg[0])
    {
    case’A’:
    wsprintf(msg,"A: Client from %s
    attached/n",inet_ntoa(saClnt.sin_addr));
    pView->m_data[pView->count++]=msg;
    pView->flag=TRUE;
    pView->InvalidateRect(NULL);
    pView->Isconnect=TRUE;
    pView->saClnt=saClnt;
    pView->saClntLen=saClntLen;
    sendto(pView->m_sock,msg,1024,0,
    (PSOCKADDR)&saClnt,saClntLen);
    break;

    case ’D’:
    wsprintf(msg,"D: Client form %s
    detached/n",inet_ntoa(saClnt.sin_addr));
    pView->m_data[pView->count++]=msg;
    pView->flag=TRUE;
    pView->InvalidateRect(NULL);
    pView->Isconnect=FALSE;
    sendto(pView->m_sock,msg,1024,0,
    (PSOCKADDR)&saClnt,saClntLen);
    break;


    case ’R’:
    saClntLen=sizeof(saClnt);
    pView->m_data[pView->count++]=msg;
    pView->flag=TRUE;
    pView->InvalidateRect(NULL);
    break;

    default:
    break;
    }


    }

    }
    return(0);
    }

    ---- 6.在程序菜单项中添加"本机IP地址":

    void CServerView::OnIp()
    {

    int WSAReturn;
    WSAReturn=WSAStartup( 0x0101, &WSAData );
    if( WSAReturn == 0 ){
    gethostname( Hostname, 260 );
    struct hostent *pHostEnt;
    pHostEnt = gethostbyname( Hostname);
    if( pHostEnt != NULL ){
    wsprintf( Hostaddress, "%d.%d.%d.%d",
    ( pHostEnt->h_addr_list[0][0] & 0x00ff ),
    ( pHostEnt->h_addr_list[0][1] & 0x00ff ),
    ( pHostEnt->h_addr_list[0][2] & 0x00ff ),
    ( pHostEnt->h_addr_list[0][3] & 0x00ff ) );
    CString out;
    out.Format(Hostaddress);
    AfxMessageBox(out);
    }
    }
    }


    ---- 7.在程序菜单中添加"发送消息":

    void CServerView::OnSendmessage()
    {
    // TODO: Add your command handler code here
    char msg[2000];
    Csend Sendmessage;
    if(Sendmessage.DoModal()
    ==IDOK&&!Sendmessage.m_Message.IsEmpty())
    {
    wsprintf(msg,"R: "+Sendmessage.m_Message);
    sendto(m_sock,msg,1024,0,
    (PSOCKADDR)&saClnt,saClntLen);
    m_data[count++]=Sendmessage.m_Message;
    flag=TRUE;
    InvalidateRect(NULL);
    }

    }

    ---- 8.为发送消息项添加一
  • 2005-10-26

    有关ActiveX控件安全注册的例子 - [VC专栏]

    作者:rick1126
    email: [email protected]
    日期:2001-3-26 11:56:16
    #include
    #include
    #include
    ...

    /////////////////////////////////////////////////////////////////////////////
    // DllRegisterServer - Adds entries to the system registry
    /* 原来的代码(被注释)
    STDAPI DllRegisterServer(void)
    {
    // registers object, typelib and all interfaces in typelib
    return _Module.RegisterServer(TRUE);
    }

    /////////////////////////////////////////////////////////////////////////////
    // DllUnregisterServer - Removes entries from the system registry

    STDAPI DllUnregisterServer(void)
    {
    return _Module.UnregisterServer(TRUE);
    }
    */

    //用途: 注册组件分类
    //说明: 组件安全性种类就是通过组件分类的注册才得以设置组件的安全性
    HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription){
    //初始化ICatRegister对象实例指针
    ICatRegister* pcr = NULL ;
    HRESULT hr = S_OK ;

    //获得ICatRegister对象实例
    //说明: 这个接口原来注册组件分类
    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
    NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
    if (FAILED(hr))
    return hr;

    //初始化分类信息
    //说明: 确保注册 HKCR/Component Categories/{..catid...}
    CATEGORYINFO catinfo;
    catinfo.catid = catid;
    catinfo.lcid = 0x0409 ; // english

    //说明: 确保提供的描述不会超长, 仅仅复制前127个字符
    int len = wcslen(catDescription);
    if (len>127)
    len = 127;
    wcsncpy(catinfo.szDescription, catDescription, len);
    // 确保描述使用"/0"结束
    catinfo.szDescription[len] = ’/0’;

    hr = pcr->RegisterCategories(1, &catinfo);
    pcr->Release();

    return hr;
    }


    //用途: 在已经存在的组件分类中进行接口类的注册
    HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid) {
    ICatRegister* pcr = NULL ;
    HRESULT hr = S_OK ;

    //获得CCM的ICatRegister接口
    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
    NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
    if (SUCCEEDED(hr))
    {
    //在组件分类之中注册接口类
    CATID rgcatid[1] ;
    rgcatid[0] = catid;
    hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);
    }

    if (pcr != NULL)
    pcr->Release();

    return hr;
    }

    //用途: 反注册已存在组件分类中的接口类
    HRESULT UnRegisterCLSIDInCategory(REFCLSID clsid, CATID catid){
    ICatRegister* pcr = NULL ;
    HRESULT hr = S_OK ;

    //获得CCM的ICatRegister接口
    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
    NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
    if (SUCCEEDED(hr))
    {
    //注销已存在的组件分类中的接口类
    CATID rgcatid[1] ;
    rgcatid[0] = catid;
    hr = pcr->UnRegisterClassImplCategories(clsid, 1, rgcatid);
    }

    if (pcr != NULL)
    pcr->Release();

    return hr;
    }

    //注册服务器
    STDAPI DllRegisterServer(void){
    HRESULT hr; // return for safety functions

    AFX_MANAGE_STATE(_afxModuleAddrThis);
    /* 原来的代码
    if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
    return ResultFromScode(SELFREG_E_TYPELIB);

    if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
    return ResultFromScode(SELFREG_E_CLASS);
    */
    if (FAILED(_Module.RegisterServer(TRUE))){
    AfxMessageBox("注册类型库失败");
    }

    //创建初始化安全组件分类
    hr = CreateComponentCategory(CATID_SafeForInitializing,
    L"Controls safely initializable from persistent data!");
    if (FAILED(hr))
    return hr;
    //在上面的分组之中注册接口类
    hr = RegisterCLSIDInCategory(CLSID_psSub, CATID_SafeForInitializing);
    if (FAILED(hr))
    return hr;

    //创建脚本编程安全组件分类
    hr = CreateComponentCategory(CATID_SafeForScripting,
    L"Controls safely scriptable!");
    if (FAILED(hr))
    return hr;
    //在上面的分组之中注册接口类
    hr = RegisterCLSIDInCategory(CLSID_psSub, CATID_SafeForScripting);
    if (FAILED(hr))
    return hr;

    return NOERROR;
    }

    //用途: 反注册服务器
    STDAPI DllUnregisterServer(void) {
    HRESULT hr; // HResult used by Safet
  • 你可能感兴趣的:(VC函数对象模板)