CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
可用分押宝游戏火热进行中... 专题改版:Java Web 专题
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  VC/MFC >  基础类

常见问题总结

楼主yintongshun(左岸思雨)2003-12-02 11:49:59 在 VC/MFC / 基础类 提问

大家好,我在http://expert.csdn.net/Expert/topic/2485/2485955.xml?temp=.4244348上贴的太长,现在这整理一下我在那贴的内容,如果各位也遇到过类似问题,请大家看看吧。  
   
  回调函数,就是由你自己写的。你需要调用另外一个函数,而这个函数的其中一个参数,就  
  是你的这个回调函数名。这样,系统在必要的时候,就会调用你写的回调函数,这样你就可  
  以在回调函数里完成你要做的事。  
   
  capVideoStreamCallback   这个回调函数,我没有做过,看了一下Help,应该是通过发送消息  
  WM_CAP_SET_CALLBACK_VIDEOSTREAM,来设置的,或者调用宏capSetCallbackOnVideoStream  
  来完成的。这样设定之后,系统在进行图像捕捉的过程中,就会自动调用你写的回调函数。  
   
  这个回调函数的函数体需要你自已来写,然后在另一函数中调用,即是说:  
  LRESULT   CALLBACK   capVideoStreamCallback(HWND   hWnd,LPVIDEOHDR   lpVHdr)  
  {  
    ........  
  }  
  //在另一函数中调用它(即以capVideoStreamCallback的地址作为一参数)  
  Function(1,......,capVideoStreamCallback,.....);  
  这就好像我们用定时器一样,在设置定时器时需要为定时器设置一回调函数:  
  ::SetTimer(m_hWnd,1,1000,(TIMERPROC)TMProc);这里的TMProc就是回调函数  
   
   
  模块A有一个函数foo,它向模块B传递foo的地址,然后在B里面发生某种事件(event)时,通过从A里面传递过来的foo的地址调用foo,通知A发生了什么事情,让A作出相应反应。  
          那么我们就把foo称为回调函数。  
   
          “这个回调函数不是VFW.h中声明的么,“  
          ----那是声明了回调函数原型,是告诉你传递进来的回调函数必须和它定义的原型保持一致。  
   
          ”为什么要自己写函数体呢?“  
          ----比如在上面模块B里面,它只知道当event发生时,向模块A发出通知,具体怎么回应这个事件,不是B所关心的,也不是B所能预料到的。  
          你站在A的角度上思考,当然要你自己作出对event的反应,也就是你要自己写函数体。  
   
          你如果明白了C++里面的函数指针,就很容易理解回调函数了。  
   
  "不知道系统调用后有什么结果,或者我怎么利用这个结果啊"  
  ---如果你向系统传递一个回调函数地址,那么你的程序就相当于上面我说的模块A,系统就相当于模块B,系统只是调用你的函数,它根本不可能知道会有什么结果。  
        你怎么利用这个结果,看你是怎么定义这个回调函数的。              
  回调函数和回调机制是不同的概念,。,,函数是被调用的,但是回调机制在不同的语言中不都是以函数指针来实现的。。。。比如c#...一般的在windows   api   中,会调都是使用函数指针实现的。。。  
   
   
   
  函数调用方式  
     
  我们知道在进行函数调用时,有几种调用方法,主要分为C式,Pascal式.在C和C++中  
  C式调用是缺省的,类的成员函数缺省调用为_stdcall。二者是有区别的,下面我们用  
  实例说明一下:  
   
  1.   __cdecl   :C和C++缺省调用方式  
  例子:    
  void   Input(   int   &m,int   &n);/*相当于void   __cdecl   Input(int   &m,int   &n);*/  
  以下是相应的汇编代码:  
  00401068   lea   eax,[ebp-8]   ;取[ebp-8]地址(ebp-8),存到eax  
  0040106B   push   eax   ;然后压栈  
  0040106C   lea   ecx,[ebp-4]   ;取[ebp-4]地址(ebp-4),存到ecx  
  0040106F   push   ecx   ;然后压栈  
  00401070   call   @ILT+5(Input)   (0040100a);然后调用Input函数  
  00401075   add   esp,8   ;恢复栈  
     
          从以上调用Input函数的过程可以看出:在调用此函数之前,首先压栈ebp-8,然  
  后压栈ebp-4,然后调用函数Input,最后Input函数调用结束后,利用esp+8恢复栈。由  
  此可见,在C语言调用中默认的函数修饰_cdecl,由主调用函数进行参数压栈并且恢复  
  堆栈。  
  下面看一下:地址ebp-8和ebp-4是什么?  
          在VC的VIEW下选debug   windows,然后选Registers,显示寄存器变量值,然后在  
  选debug   windows下面的Memory,输入ebp-8的值和ebp-4的值(或直接输入ebp-8和-4),  
  看一下这两个地址实际存储的是什么值,实际上是变量   n   的地址(ebp-8),m的地址  
  (ebp-4),由此可以看出:在主调用函数中进行实参的压栈并且顺序是从右到左。另外,  
  由于实参是相应的变量的引用,也证明实际上引用传递的是变量的地址(类似指针)。  
  总结:在C或C++语言调用中默认的函数修饰_cdecl,由主调用函数进行参数压栈并且  
  恢复堆栈,实参的压栈顺序是从右到左,最后由主调函数进行堆栈恢复。由于主调用  
  函数管理堆栈,所以可以实现变参函数。另外,命名修饰方法是在函数前加一个下划  
  线(_).  
   
  2.   WINAPI   (实际上就是PASCAL,CALLBACK,_stdcall)  
  例子:    
  void   WINAPI   Input(   int   &m,int   &n);  
  看一下相应调用的汇编代码:  
  00401068   lea   eax,[ebp-8]  
  0040106B   push   eax  
  0040106C   lea   ecx,[ebp-4]  
  0040106F   push   ecx  
  00401070   call   @ILT+5(Input)   (0040100a)  
     
          从以上调用Input函数的过程可以看出:在调用此函数之前,首先压栈ebp-8,然  
  后压栈ebp-4,然后调用函数Input,在调用函数Input之后,没有相应的堆栈恢复工作  
  (为其它的函数调用,所以我没有列出)  
  下面再列出Input函数本身的汇编代码:(实际此函数不大,但做汇编例子还是大了些,  
  大家可以只看前和后,中间代码与此例子无关)    
  39:   void   WINAPI   Input(   int   &m,int   &n)  
  40:   {  
  00401110   push   ebp  
  00401111   mov   ebp,esp  
  00401113   sub   esp,48h  
  00401116   push   ebx  
  00401117   push   esi  
  00401118   push   edi  
  00401119   lea   edi,[ebp-48h]  
  0040111C   mov   ecx,12h  
  00401121   mov   eax,0CCCCCCCCh  
  00401126   rep   stos   dword   ptr   [edi]  
  41:   int   s,i;  
  42:  
  43:   while(1)  
  00401128   mov   eax,1  
  0040112D   test   eax,eax  
  0040112F   je   Input+0C1h   (004011d1)  
  44:   {  
  45:   printf("\nPlease   input   the   first   number   m:");  
  00401135   push   offset   string   "\nPlease   input   the   first   number   m"...   (004260b8)  
  0040113A   call   printf   (00401530)  
  0040113F   add   esp,4  
  46:   scanf("%d",&m);  
  00401142   mov   ecx,dword   ptr   [ebp+8]  
  00401145   push   ecx  
  00401146   push   offset   string   "%d"   (004260b4)  
  0040114B   call   scanf   (004015f0)  
  00401150   add   esp,8  
  47:  
  48:   if   (   m=   s   )  
  004011B3   mov   eax,dword   ptr   [ebp+8]  
  004011B6   mov   ecx,dword   ptr   [eax]  
  004011B8   cmp   ecx,dword   ptr   [ebp-4]  
  004011BB   jl   Input+0AFh   (004011bf)  
  57:   break;  
  004011BD   jmp   Input+0C1h   (004011d1)  
  58:   else  
  59:   printf("   m   <   n*(n+1)/2,Please   input   again!\n");  
  004011BF   push   offset   string   "   m   <   n*(n+1)/2,Please   input   agai"...   (00426060)  
  004011C4   call   printf   (00401530)  
  004011C9   add   esp,4  
  60:   }  
  004011CC   jmp   Input+18h   (00401128)  
  61:  
  62:   }  
  004011D1   pop   edi  
  004011D2   pop   esi  
  004011D3   pop   ebx  
  004011D4   add   esp,48h  
  004011D7   cmp   ebp,esp  
  004011D9   call   __chkesp   (004015b0)  
  004011DE   mov   esp,ebp  
  004011E0   pop   ebp  
  004011E1   ret   8  
     
     
     
          最后,我们看到在函数末尾部分,有ret   8,明显是恢复堆栈,由于在32位C++中,  
  变量地址为4个字节(int也为4个字节),所以弹栈两个地址即8个字节。  
  由此可以看出:在主调用函数中负责压栈,在被调用函数中负责恢复堆栈。因此不能  
  实现变参函数,因为被调函数不能事先知道弹栈数量,但在主调函数中是可以做到的,  
  因为参数数量由主调函数确定。  
  下面再看一下,ebp-8和ebp-4这两个地址实际存储的是什么值,ebp-8地址存储的是n    
  的值,ebp   -4存储的是m的值。说明也是从右到左压栈,进行参数传递。  
   
          总结:在主调用函数中负责压栈,在被调用函数中负责弹出堆栈中的参数,并且  
  负责恢复堆栈。因此不能实现变参函数,参数传递是从右到左。另外,命名修饰方法  
  是在函数前加一个下划线(_),在函数名后有符号(@),在@后面紧跟参数列表中的参数  
  所占字节数(10进制),如:void   Input(int   &m,int   &n),被修饰成:_Input@8  
  对于大多数api函数以及窗口消息处理函数皆用   CALLBACK   ,所以调用前,主调函数会  
  先压栈,然后api函数自己恢复堆栈。  
   
  如:    
  push   edx  
  push   edi  
  push   eax  
  push   ebx  
  call   getdlgitemtexta  
   
  问题点数:100、回复次数:8Top

1 楼yintongshun(左岸思雨)回复于 2003-12-02 11:50:30 得分 100

 
  Win32程序函数调用时堆栈变化情况分析  
     
  2002-12-10   18:19:13       PCVC.NET       halk       阅读次数:   4804    
      在经典的汇编语言教程中,函数调用时堆栈的使用都是着重讲解的问题。如今随着高级语言的越来越完善,单纯使用汇编开发的程序已经不多了。但对函数调用时堆栈动向的了解仍有助于我们明晰程序的执行流程,从而在程序编写和调试的过程中有一个清晰的思路。  
   
  一.调用约定  
  在Win32中,有关函数的调用主要有两种约定。  
  1._stdcall  
                  以__stdcall方式调用的函数有以下特征:  
          &#8226;     参数由右至左压栈  
          &#8226;   调用返回时,堆栈由被调函数调整  
  2.__cdecl  
  __cdecl约定是C/C++函数的默认调用约定。它有以下特征:  
          &#8226;   参数由右至左压栈  
          &#8226;   调用返回时,堆栈由调用者调整  
   
  二.Win32函数调用过程  
   
  1.         压入参数  
  这里依据以上的调用方式将调用者给出的参数一一压入堆栈。  
   
  2.         压入断点  
  当程序执行到Call指令的时候,当前语句的地址作为断点地址压入堆栈。  
   
  3.         跳转  
  eip的值被重新设置为被调函数的起始地址。  
   
  4.         mov   ebp,   esp  
  这里ebp被用来在堆栈中寻找调用者压入的参数,同时作为调用者堆栈指针的一个备份。在此前还应该执行一条:  
  push   ebp  
  把ebp中原来的数值保存。  
   
  5.         sub   esp,N  
  这里N是函数内局部变量的总字节数加上一个整数,一般为40。此后esp即为被调函数的堆栈指针了。  
   
  6.         初始化esp   ~   esp-N之间的N字节空间  
  这是对堆栈中已分配给局部变量使用的内存空间的初始化,一般全部设置为0xcc。  
   
  7.         顺序执行函数内语句。  
  此时函数的堆栈位于所有局部变量的内存空间之后,二者之间一般有40字节的隔离带。  
   
  8.返回  
  为保障调用的正常返回,函数内应当保证规范使用堆栈,使即将返回的时候esp的值恢复为执行第一条语句前的状态。说明白点,就是每一条push都要有相应的pop。  
  调用返回的过程如下:  
  mov   esp,   ebp  
  执行后,esp恢复为调用者的堆栈指针,栈顶除断点地址外,还存有原ebp的值和调用时压入的参数。  
  然后依次弹出ebp的值和断点地址。如果是__cdecl约定则直接返回调用者,调用者将负责调整堆栈,丢弃调先前压入的参数。如果是__stdcall则这个工作由被调函数来执行。  
   
  程序样例如下:  
  ……  
  0040B8E8       push                 1                         ;压入参数  
  0040B8EA       call                 00401028                 ;调用函数  
  ……  
  00401028       jmp                   0040b7c0                 ;跳转到函数入口  
  ……  
  0040B7C0       push                 ebp                         ;保存ebp  
  0040B7C1       mov                   ebp,esp                  
  0040B7C3       sub                   esp,44h                 ;设置函数的堆栈指针,此函数中有4  
  ;字节的局部变量  
  0040B7C6       push                 ebx  
  0040B7C7       push                 esi                  
  0040B7C8       push                 edi  
  0040B7C9       lea                   edi,[ebp-44h]          
  0040B7CC       mov                   ecx,11h  
  0040B7D1       mov                   eax,0CCCCCCCCh  
  0040B7D6       rep   stos         dword   ptr   [edi]         ;初始化局部变量空间  
  0040B7D8       mov                   eax,dword   ptr   [ebp+8]  
  0040B7DB       mov                   dword   ptr   [ebp-4],eax  
  ……  
  0040B7DE       pop                   edi                         ;弹出曾压栈的数据  
  0040B7DF       pop                   esi  
  0040B7E0       pop                   ebx  
  0040B7E1       mov                   esp,ebp                 ;恢复调用者的堆栈  
  0040B7E3       pop                   ebp                         ;弹出原ebp值  
  0040B7E4       ret                   4                         ;返回并将堆栈向上调整4字节。  
  ;此处为__stdcall约定,所以由函数调  
  ;整堆栈  
   
  相应的C代码如下:  
   
  void   __stdcall   fun(int);  
   
  int   main(void)  
  {  
          ……          
  fun(1);  
  ……  
          return   0;  
  }  
   
  void   __stdcall   fun(int   para)  
  {  
          int   localpara   =   para;  
          ……  
  }  
     
   
   
  C   与C++的两种调用方法  
  (1)_stdcall:用于Win32   API函数和COM+接口。它从右向左将参数推入堆栈,被调函数在返回之前从堆栈中弹出自己的参数。从堆栈中弹出自己参数的函数不支持参数数目的变化。  
  (2)_cdecl:C和C++默认方式)。它也是从右向左传递参数。但是被调函数不负责从堆栈中弹出参数,调用函数将在函数调用返回后晴空堆栈。  
  CALLBACK采用方式(1)。它称为回调函数,即供系统调用的函数。  
  例如窗口函数、定时处理函数、线程处理函数等。  
   
  CALLBACK   是老式叫法,现在微软都改为WINAPI.  
   
   
   
  至于编译器的解释上。c与c++搞的很麻烦。  
   
  我也记不住每次碰到都去查一下.  
   
  我列个表:  
   
  语言                                     调用约定                         输出方式  
   
  c                                           stdcall                     _SomeFunctionname@Number  
   
   
  c                                           cdecl                         _SomeFunctionname  
       
   
   
   
  C++的烦的要死。  
  C++编译时函数名修饰约定规则:    
   
  __stdcall调用约定:    
  1、以“?”标识函数名的开始,后跟函数名;    
  2、函数名后面以“@@YG”标识参数表的开始,后跟参数表;    
  3、参数表以代号表示:    
  X--void   ,    
  D--char,    
  E--unsigned   char,    
  F--short,    
  H--int,    
  I--unsigned   int,    
  J--long,    
  K--unsigned   long,    
  M--float,    
  N--double,    
  _N--bool,    
  ....    
  PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复;    
  4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;    
  5、参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。    
   
  其格式为“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,例如    
  int   Test1(char   *var1,unsigned   long)-----“?Test1@@YGHPADK@Z”    
  void   Test2()   -----“?Test2@@YGXXZ”    
   
  __cdecl调用约定:    
  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YA”。    
   
  __fastcall调用约定:    
  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YI”。    
               
   
  编写DLL的时候输出函数老是不认就是编译器解释的不同造成的。EXTERN   "C"就是为了统一。全部用c方式解释。  
   
   
   
  两种声明是不同的,vc默认的调用是__cdecl   ,所以   extern   "C"   __declspec(dllimport)   int   mydllfunc(int);是extern   "C"   __declspec(dllimport)   int   __cdecl   mydllfunc(int);  
  因此和extern   "C"   __declspec(dllimport)   int   __stdcall   mydllfunc(int);是有区别的  
   
   
   
   
   
  Top

2 楼yintongshun(左岸思雨)回复于 2003-12-02 11:50:46 得分 0

 
   
  调用约定  
   
  调用约定(Calling   convention)决定以下内容:函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法。MFC支持以下调用约定:  
   
   
  _cdecl  
   
  按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于“C”函数或者变量,修饰名是在函数名前加下划线。对于“C++”函数,有所不同。  
   
  如函数void   test(void)的修饰名是_test;对于不属于一个类的“C++”全局函数,修饰名是?test@@ZAXXZ。  
   
  这是MFC缺省调用约定。由于是调用者负责把参数弹出栈,所以可以给函数定义个数不定的参数,如printf函数。  
   
   
  _stdcall  
   
  按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。对于“C”函数或者变量,修饰名以下划线为前缀,然后是函数名,然后是符号“@”及参数的字节数,如函数int   func(int   a,   double   b)的修饰名是_func@12。对于“C++”函数,则有所不同。  
   
  所有的Win32   API函数都遵循该约定。  
   
   
  _fastcall  
   
  头两个DWORD类型或者占更少字节的参数被放入ECX和EDX寄存器,其他剩下的参数按从右到左的顺序压入栈。由被调用者把参数弹出栈,对于“C”函数或者变量,修饰名以“@”为前缀,然后是函数名,接着是符号“@”及参数的字节数,如函数int   func(int   a,   double   b)的修饰名是@func@12。对于“C++”函数,有所不同。  
   
  未来的编译器可能使用不同的寄存器来存放参数。  
   
   
  thiscall  
   
  仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压栈。thiscall不是关键词,因此不能被程序员指定。  
   
   
  naked   call  
   
  采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked   call不产生这样的代码。  
   
  naked   call不是类型修饰符,故必须和_declspec共同使用,如下:  
   
  __declspec(   naked   )   int   func(   formal_parameters   )  
   
  {  
   
  //   Function   body  
   
  }  
   
   
  过时的调用约定  
   
  原来的一些调用约定可以不再使用。它们被定义成调用约定_stdcall或者_cdecl。例如:  
   
  #define   CALLBACK   __stdcall  
   
  #define   WINAPI   __stdcall  
   
  #define   WINAPIV   __cdecl  
   
  #define   APIENTRY   WINAPI  
   
  #define   APIPRIVATE   __stdcall  
   
  #define   PASCAL   __stdcall  
   
   
  表7-1显示了一个函数在几种调用约定下的修饰名(表中的“C++”函数指的是“C++”全局函数,不是成员函数),函数原型是void   CALLTYPE   test(void),CALLTYPE可以是_cdecl、_fastcall、_stdcall。  
   
  表7-1   不同调用约定下的修饰名  
   
   
  调用约定  
    extern   “C”或.C文件  
    .cpp,   .cxx或/TP编译开关  
     
  _cdecl  
    _test  
    ?test@@ZAXXZ  
     
  _fastcall  
    @test@0  
    ?test@@YIXXZ  
     
  _stdcall  
    _test@0  
    ?test@@YGXXZ  
     
   
  函数调用的几个概念:_stdcall,_cdecl....  
  (剑灵时空-www.SwordElf.com)  
     
  原作者:   csdn,sinman  
  来自于:   不详  
  共有213位读者阅读过此文    
   
  内容:  
  左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。  
   
          _stdcall是Pascal程序的缺省调用方式,通常用于Win32   Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。  
   
          2、C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。  
   
          _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。  
   
          3、__fastcall调用约定是“人”如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。  
   
          _fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。          
   
          4、thiscall仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。  
   
          5、naked   call采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked   call不产生这样的代码。naked   call不是类型修饰符,故必须和_declspec共同使用。  
   
          关键字   __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前,也可以在编译环境的Setting...\C/C++   \Code   Generation项选择。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd,即__cdecl。  
   
          要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是WINAPI宏,Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为__stdcall。使用WINAPI宏可以创建自己的APIs。  
   
  2)名字修饰约定  
   
  1、修饰名(Decoration   name)  
   
  “C”或者“C++”函数在内部(编译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如在模块定义文件里头指定输出“C++”重载函数、构造函数、析构函数,又如在汇编代码里调用“C””或“C++”函数等。  
   
  修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。  
   
  2、名字修饰约定随调用约定和编译种类(C或C++)的不同而变化。函数名修饰约定随编译种类和调用约定的不同而不同,下面分别说明。  
   
          a、C编译时函数名修饰约定规则:  
   
  __stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_functionname@number。  
   
  __cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。  
         
  __fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@functionname@number。  
   
          它们均不改变输出函数名中的字符大小写,这和PASCAL调用约定不同,PASCAL约定输出的函数名无任何修饰且全部大写。  
   
          b、C++编译时函数名修饰约定规则:  
   
  __stdcall调用约定:  
                      1、以“?”标识函数名的开始,后跟函数名;  
                      2、函数名后面以“@@YG”标识参数表的开始,后跟参数表;  
                      3、参数表以代号表示:  
                            X--void   ,  
                            D--char,  
                            E--unsigned   char,  
                            F--short,  
                            H--int,  
                            I--unsigned   int,  
                            J--long,  
                            K--unsigned   long,  
                            M--float,  
                            N--double,  
                            _N--bool,  
                            ....  
                            PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复;  
                      4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;    
                      5、参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。  
   
          其格式为“?functionname@@YG*****@Z”或“?functionname@@YG*XZ”,例如  
                      int   Test1(char   *var1,unsigned   long)-----“?Test1@@YGHPADK@Z”  
                      void   Test2()                                               -----“?Test2@@YGXXZ”  
   
  __cdecl调用约定:  
  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YA”。  
   
  __fastcall调用约定:  
  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@YI”。  
   
          VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用.  
   
  CB在输出函数声明时使用4种修饰符号  
  //__cdecl  
  cb的默认值,它会在输出函数名前加_,并保留此函数名不变,参数按照从右到左的顺序依次传递给栈,也可以写成_cdecl和cdecl形式。  
  //__fastcall  
  她修饰的函数的参数将尽肯呢感地使用寄存器来处理,其函数名前加@,参数按照从左到右的顺序压栈;  
  //__pascal  
  它说明的函数名使用Pascal格式的命名约定。这时函数名全部大写。参数按照从左到右的顺序压栈;  
  //__stdcall  
  使用标准约定的函数名。函数名不会改变。使用__stdcall修饰时。参数按照由右到左的顺序压栈,也可以是_stdcall;      
   
   
   
   
  Top

3 楼yintongshun(左岸思雨)回复于 2003-12-02 11:51:20 得分 0

最高窗口的实现  
  VC++中对基于SDI、MDI的运用程序,要实现最高窗口,只要在框架窗口类CMainFrame中的PreCreateWindow()函数中加入“cs.dwExStyle   =WS_EX_TOPMOST;”即可。  
          而对基于对话框的运用程序,如何实现最高窗口却很少论及,以下便是一种实现方法。  
          重载要实观最高窗口的对话框的OnInitDialog()函数,方法是进入ClassWizard,在Object   ID列表框中选择该对话框的ID,在Message列表框中选择WM_INITDIALOGG,单击Add   Function按钮后,即对onlnitDialog函数进行了重载。再按下Edit   code按钮,加入以下语句:  
  const   CWnd   *   pWndInsertAfter;  
  pWndInsertAfter   =   &wndTopMost;  
  SetWindowPos(pWndInsertAfter,0,0,0,0,SWP_NOSIZE   |   SWP_NOMOVE);  
  函数SetWindowPos原型为BOOL   SetWindowPos(const   CWnd   *   pWndInsertAfter,int   x,int   y,int   cx,int   cy,UINT   nFlags);  
  pWndInsertAfter为指向标识窗口类型的CWnd对象的指针。  
  x,y为窗口左上角的坐标。  
  cx,cy为窗口的宽与高。  
  nFlags确定窗口的大小及位置。当为SWP_NOSIZE时,忽略cx,cy。当为SWP_NOMOVE时,忽略x,y。  
   
   
  改变程序的窗口设置  
  可以在视图类的PRECREATEWINDOW中加入语句改变程序的窗口设置。程序在生成窗口之前调用这个函数。此函数接受CREATESTRUCT结构的引用,这个结构的字段存放生成窗口时MFC指定的窗口特性。如果赋值这个结构的一个或几个字段,则MFC用所赋值而不是缺省值。CREATESTRUCT结构的IpszClass字段存放WINDOWS窗口类名,这是WINDOWS系统维护的数据结构,存放窗口生成时一组一般特性。加入的AfxRegisterWndClass生成新的WINDOWS窗口类,然后将类名赋予结构的IpszClass字段,使视图窗口用存放在这个WINDWOS窗口类中的用户化特性生成。AfxRegisterWndClass是MFC提供的全局函数。  
  BOOL   CminiDrawView::PreCreateWindow(CREATESTRUCT   &cs)  
  {  
  M_Class=AfxRegisterWndClass  
  (CS_HREDRAW|CS_VREDRAW,  
  0,  
  (HBRUSH)::GetStockObject(WHITE_BRUSH);  
  0);  
  cs.lpszClass=m_ClassName;  
  returen   cview::precreatewindow(cs);  
  }  
  第一个参数指定类样式。第二个参数指定WINDOWS在窗口内自动显示的鼠标光标,赋值为0则是不准备显示光标。第三个参数提供标准的白色刷笔。最后一个参数指定窗口图标,由于视图窗口不显示图标,他赋值为0。  
   
   
  纯资源DLL的编写  
            下面的例子是一个纯资源DLL的源程序    
  纯资源的DLL就是只包含资源的DLL,例如:图标,位图,字符串,声音,视频,对话框等。使用纯资源DLL可以节约可执行文件的大小,可以被所有的应用程序所共享,从而提高系统性能。纯资源DLL的编写比普通的DLL要简单的多,首先创建一个WIN32    
  DLL工程,不是MFC的DLL,然后创建一个资源文件   *.RC,添加到资源DLL的工程中去。然后添加一个初始化DLL的原文件。  
  #include   <windows.h>  
  extern   "C"  
  BOOL   WINAPI   DllMain(   HINSTANCE   hInstance,   DWORD   dwReason,   LPVOID   )  
  {  
          return   1;  
  }  
  这是纯资源DLL所必须需的代码,保存这个文件为*.CPP。编译这个资源DLL。  
   
  在应用程序显示的调用这个DLL,使用LoadLibrary函数装入资源DLL,FindResource和LoadResource来装入各种资源,或者使用下列的特定的资源装入函数:    
      FormatMessage    
      LoadAccelerators    
      LoadBitmap    
      LoadCursor    
      LoadIcon    
      LoadMenu    
      LoadString    
  当资源使用结束,你的应用程序须调用FreeLibrary函数来释放资源。  
  下面就讲一下如何调用编写好的资源DLL  
  首先在应用程序中声明一个DLL的句柄,HINSTANCE   m_hLibrary;在OnCreate(   )函数中调用LoadLirbrary(    
  ),在OnDestory(   )中调用FreeLibrary()。    
   
   
  空心字  
  在开始一个路径前,我们先调用CDC类的成员函数BeginPath,然后调用一系列的输出函数,在完成绘制之后,我们可以调用CDC类的成员函数EndPath。在完成一个路径之后,我们可以调用StrokePath来绘制该路径。为了简单起见,我们仅给出应用程序的OnPaint成员函数如下:  
  //   应用程序主窗口的重绘函数  
  void   CMyWnd::OnPaint()  
  {  
  //   获得窗口的客户区设备上下文句柄  
  CPaintDC   dc(this);  
  //   更改当前字体  
  LOGFONT   lf;  
  dc.GetCurrentFont()->GetLogFont(&lf);  
  CFont   font;  
  CFont   *pOldFont;   //   保存设备上下文最初使用的字体对象  
  lf.lfCharSet=134;  
  lf.lfHeight=-150;  
  lf.lfWidth=0;  
  strcpy(lf.lfFaceName,   "隶书");  
  font.CreateFontIndirect(&lf);  
  pOldFont=dc.SelectObject(&font);  
  dc.SetBkMode(TRANSPARENT);  
  //   更改当前画笔  
  CPen   pen(PS_SOLID,   1,   RGB(255,   0,   0));  
  CPen   *pOldPen;  
  pOldPen=dc.SelectObject(&pen);  
  //   开始一个路径  
  dc.BeginPath();  
  dc.TextOut(10,   10,   "空心字");  
  dc.EndPath();  
  //   绘制路径  
  dc.StrokePath();  
  //   恢复设备上下文的原有设置  
  dc.SelectObject(pOldFont);  
  dc.SelectObject(pOldPen);  
  }  
  函数FillPath可以使用当前刷子填充路径的内部。按下面的代码修改前面的OnPaint成员函数:  
  //   应用程序主窗口的重绘函数  
  void   CMyWnd::OnPaint()  
  {  
  //   获得窗口的客户区设备上下文句柄  
  CPaintDC   dc(this);  
  //   更改当前字体  
  LOGFONT   lf;  
  dc.GetCurrentFont()->GetLogFont(&lf);  
  CFont   font,   *pOldFont;  
  lf.lfCharSet=134;  
  lf.lfHeight=-150;  
  lf.lfWidth=0;  
  strcpy(lf.lfFaceName,   "隶书");  
  font.CreateFontIndirect(&lf);  
  pOldFont=dc.SelectObject(&font);  
  dc.SetBkMode(TRANSPARENT);  
  //   更改当前画笔  
  CPen   pen(PS_SOLID,   1,   RGB(255,   0,   0)),   *pOldPen;  
  pOldPen=dc.SelectObject(&pen);  
  //   更改当前刷子  
  CBrush   br(HS_DIAGCROSS,   RGB(0,   255,   255)),   *pOldBrush;  
  pOldBrush=dc.SelectObject(&br);  
  //   开始一个路径  
  dc.BeginPath();  
  dc.TextOut(10,   10,   "空心字");  
  dc.EndPath();  
  //   绘制路径  
  dc.StrokeAndFillPath();  
  //   恢复设备上下文的原有设置  
  dc.SelectObject(pOldFont);  
  dc.SelectObject(pOldPen);  
  dc.SelectObject(pOldBrush);  
  }  
   
   
  渐变字  
  在完成一个路径之后,如前所述,我们可以调用CDC类的成员函数FillPath、StrokePath或StrokeAndFillPath来绘制和填充路径。这只是最初级的技巧。更进一步,我们可以使用成员函数SelectClipPath将路径选入当前剪辑区域,这样,所有的绘制操作都将只作用于这个剪辑区域。  
  使用此技巧可以绘制具有渐变颜色效果的字体。如下面的OnPaint成员函数所示:  
  //   应用程序主窗口的重绘函数  
  void   CMyWnd::OnPaint()  
  {  
  //   获得窗口的客户区设备上下文句柄  
  CPaintDC   dc(this);  
  //   更改当前字体  
  LOGFONT   lf;  
  dc.GetCurrentFont()->GetLogFont(&lf);  
  CFont   font,   *pOldFont;  
  lf.lfCharSet=134;  
  lf.lfHeight=-150;  
  lf.lfWidth=0;  
  strcpy(lf.lfFaceName,   "隶书");  
  font.CreateFontIndirect(&lf);  
  pOldFont=dc.SelectObject(&font);  
  dc.SetBkMode(TRANSPARENT);  
  //   更改当前画笔为空  
  CPen   pen(PS_NULL,   1,   RGB(255,   0,   0)),   *pOldPen;  
  pOldPen=dc.SelectObject(&pen);  
  //   更改当前刷子  
  CBrush   br(0),   *pOldBrush;  
  pOldBrush=dc.SelectObject(&br);  
  //   开始一个路径  
  dc.BeginPath();  
  dc.TextOut(10,   10,   "渐变字");  
  dc.EndPath();  
  //   绘制渐变效果  
  dc.SelectClipPath(RGN_COPY);  
  for   (int   i=255;   i>0;   i--)  
  {  
  int   iRadius=(600*i)/255;  
  dc.SelectObject(pOldBrush);  
  br.DeleteObject();  
  br.CreateSolidBrush(RGB(255,   i,   0));  
  dc.SelectObject(&br);  
  dc.Ellipse(-iRadius,   -iRadius/3,   iRadius,   iRadius/3);  
  }  
  //   恢复设备上下文的原有设置  
  dc.SelectObject(pOldFont);  
  dc.SelectObject(pOldPen);  
  dc.SelectObject(pOldBrush);  
  }  
  在上面的示例中,我们以RGN_COPY方式将路径选作当前剪辑区域,然后,在该剪辑区域上进行一系列的绘制操作,这些绘制操作以不同的颜色绘制了一系列的同心椭圆,这些同心椭圆有视觉上给用户以渐变的感觉。上面的程序的运行结果如图。  
   
   
  测试ALT键是否按下:  
  GetKeyState(VK_MENU);  
  GetAlt();  
   
   
  元文件  
  用于记录和回放GDI函数调用。首先通过CMetaFileDC:Create来创建新的对象,如果有文件名,则保存到文件内,如果没有则建立在内存中。完毕后,用Close()关闭。  
  可以调用PlayMetaFile()来回放元文件。还可以用CopyMetaFile()将文件存盘。当结束元文件的时候,用DeleteMetaFile()从内存中删除。  
  另外有增强型元文件。  
  CMetaFileDC         dc;  
  BOOL   bCreate=dc.CeeateEnblanced(pDC,”metaest,wmf\0”,0,vectosd\0   this   is   l\0\0”);  
  if(!bCreated)   return;  
  (具体的绘制内容……)  
  //关闭文件,并返回文件句柄  
  HENHMETAFILE         hemf=dc.CloseEnhanced();  
  //播放文件  
  pDC->SaveDC();  
  pDC->SetMapMode();  
  pDC->PlayMetaFile(hemf,&rc);  
  pDC->RestoreDC(-1);  
  //当文件不再需要  
  if(hemf)  
          ::DeleteEnhMetaFile(hemf);  
   
   
  如何将窗口的大小限制为特定值:  
  Windows发送一个WM_GETMAXMININFO消息来确定窗口的位置、大小以及跟踪尺寸,下面将窗口的追踪尺寸限制为1/4屏幕大小:  
  void   CMainFrame::OnGetMinMaxInfo(MINMAXINFO   FAR   *LPmmI)  
  {  
  lpMMI->ptMaxTrackSize.x=GetSystemMetrics(SM_CXSCREEN)/2;  
  lpMMI->ptMaxTrackSize.Y=GetSystemMetrics(SM_CYSCREEN)/2;  
  CMDIFrameWnd::OnGetMinMaxInfo(lpMMI);  
  }  
   
   
  Top

4 楼yintongshun(左岸思雨)回复于 2003-12-02 11:51:44 得分 0

字符串太长的时候如果用省略号显示  
  调用CDC::DrawText并指定DT_END_ELLIPSIS标志,就可以用省略号代替末尾的字符来适合指定的边界矩形,如果要显示路径信息,指定DT_PATH_ELLIPSIS标志来取代中间的字符。  
          CClientDC   dc(this);  
          dc.DrawText(CString("This   is   a   long   string."),  
                  CRect(10,10,80,30),DT_LEFT|DT_END_ELLIPSIS);  
          dc.DrawText(AfxGetApp()->m_pszHelpFilePath,  
                  CRect(10,40,200,60),DT_LEFT|DT_PATH_ELLIPSIS);  
   
   
  WINDOWS异常分支  
  异常分支是WINDOWS消息处理路径中的点,子例程根据这一路径被注入或附加,以在某些类型的消息到达某目的地之前将其过滤。一旦这些消息被捕获了,就可以被修改,记录或者简单的放弃。子例程本身被称为过滤器。他们按照正在过滤的事件的类型进行分类。  
  为了设置一个异常分支,必须调用SetWindowsHookEx()函数。  
  SetWindowsHookEX(int   idHook,HOOKPROC   lpfn,HINSTANCE   hMod,DWORD   dwThreadld);  
  第一个参数,代表了一个常量,标识了正要设置的异常分支的类型。  
  第二个参数,指向一个函数类型为HOOKPROC的指针。对于键盘异常分支,必须要如下的形式:  
  LRESULT   CALLBACK   KeyboardProc(int   code   ,WPARAM,LPARAM);  
  第三个参数,是包含过滤函数的DLL的HINSTANCE。  
  第四个参数允许程序指定要为之设置异常分支的线程。  
  为了把一个异常分支从链中去掉,必须调用函数:  
  BOOL         UnhookWindowsHookEx(HHOOK   hHook);  
   
   
  文档模板字符串处理  
  文档类包含了GetDocString(),可以接受CString对象的引用以及一个索引,该函数可以用来查询已经注册的字符串资源的值。方式如下:  
  virtual   BOOL   GetDocString(CString   &rString,enum   DocStringIndex   intex)const;  
  具体参数索引的有效值:  
  CDocTemplate::windowTitle   应用程序窗口标题栏的文本,只对SDI程序很重要  
  CDocTemplate::docName  
  CDocTemplate::fileNewName  
  CDocTemplate::filterName  
  CDocTemplate::filterExt                      
  CDocTemplate::regFileTypeId       注册表数据库内部名称,由OLE和FILE   MANAGER使用  
  CDocTemplate::regFileTypeName   注册表数据库文档名称,由所有OLE应用程序使用并且对用户可见  
   
   
  简单的打印  
  void   Print2()  
  {  
  HDC   hDC;  
  DOCINFO   di;  
  hDC=CreateDC(“WINSPOOL”,”HP   Laser   Printer”,NULL,NULL);  
  memset(&di,sizeof(DOCINFO));  
  di.cbSize=sizeof(DOCINFO);  
  di.lpszDocName=”Sample   Document”;  
  if(StartDoc(hDC,&di)!=SP_ERROR)  
  {  
  StartPage(hDC);  
  TextOut(hDC,10,10,”This   is   on   Page   1.”,18);  
  EndPage(hDC);  
  StartPage(hDC);  
  TextOut(hDC,10,10,”This   is   on   Page   2.”,18);  
  EndPage(hDC);  
  EndDoc(hDC);  
  }  
  DeleteDC(hDC);  
  }  
   
   
  时间控制函数  
  传统的方法使用WM_TIMER(),精度最低。  
  在要求不大于1毫秒的情况下,可以采用GetTickCount()函数,该函数的返回值是DWORD型,表示以毫秒为单位的计算机启动后经历的时间间隔。实现50毫秒精确定时方法:  
  DWORD   dwstart,dwstop;  
  dwstop=GetTickCount();  
  while(TRUE)  
  {  
  dwstart=dwstop;  
          do{  
                  dwstop=GetTickCount();  
                  }while(dwstop-50<dwstart);  
  }  
  仅供WIN9X使用的高精度定时器:QueryPerformanceFrequency()和QueryPerformanceCounter(),         要求计算机从硬件上支持高精度定时器。函数的原形是:BOOL   QueryPerformanceFrequency(LARGE_INTEGER   *lpFrequency);  
  BOOL   QueryPerformanceCounter   (LARGE_INTEGER   *lpCount);  
  数据类型LARGEINTEGER既可以是一个作为8字节长的整数,也可以是作为两个4字节长的整数的联合结构,其具体用法根据编译器是否支持64位而定。该类型的定义如下:typeef   union   _   LARGE_INTEGER  
  {  
          struct  
          {  
                  DWORD   LowPart;  
                  LONG     HighPart;  
          };  
          LONGLONG   QuadPart;  
  }   LARGE_INTEGER;  
  在定时前应该先调用QueryPerformanceFrequency()函数获得机器内部计时器的时钟频率。接着在需要严格计时的事件发生前和发生之后分别调用QueryPerformanceCounter(),         利用两次获得的计数之差和时钟频率,         就可以计算出事件经历的精确时间。测试函数SLEEP(100)的精确持续时间方法:  
  LARGE_INTEGER   litmp;  
  LONGLONG   qt1,qt2;  
  double   dft,dff,dfm;  
  QueryPerformanceFrequency(&litmp);//获得时钟频率  
  dff=(double)litmp.QuadPart;  
  QueryPerformanceCounter(&litmp);//获得初始值  
  qt1=litmp.QuadPart;Sleep(100);  
  QueryPerformanceCounter(&litmp);//获得终止值  
  qt2=litmp.QuadPart;  
  dfm=(double)(qt2-qt1);  
  dft=dfm/dff;//获得对应的时间值  
  需要注意的是DFT计算的结果单位是秒。  
   
   
  文本绘制函数  
  DrawText()             在文本被绘制时,提供一些文本格式编排。将一个字符串放入一个矩形区域使之格式化。  
  CRect   rectangle;  
  GetClientRect(rectangle);  
  pDC->DrawText("Hello",-1,&rectangle,  
  DT_CENTER|DT_SINGLELINE|DT_VCENTER);  
  ExtTextOut()                 使用三个新增特性绘制一行文本。首先可以指定一个剪取矩形以将文本限制在矩形内。其次可以指定一个不透明的矩形,它涉及使用背景文本颜色填充矩形。第三,可以通过提供一组字符单元宽度值,控制字符间距。  
  GrayString()                 创建虚化的文本。类似于使用在菜单中显示被禁用的菜单项的文本。  
  TabbedTextOUt()     绘制一行文本,类似TextOut()。区别是支持制表符。  
  TextOut()               绘制单行文本。其坐标只能识别16位,在NT中才可以识别32。  
   
   
  文本坐标的计算  
  GetCharWidth(0       为当前选入DC中的字体的一系列字符获取默认字符宽度值的副本。  
  GetDeviceCaps()     获取特定于具体设备的数据的各种位。  
  GetTextExtent()     当使用当前选入DC中的字体绘图时,获得给定字符串将占据的空间宽度和高度。  
  GetTabbedTextExtent()类似于GetTextExtent(),当使用当前选入DC中的字体绘图时,此函数获取字符串将占据的空间宽度和高度。也考虑了制表设置。  
  GetTextMetrics()   为当前选入DC中的字体获取数据副本。包含基本的字体测量信息。  
   
   
  使用定时器  
  定时器的时间单位是毫秒,数值是1-32位整数,最大的数字是49.5天左右。虽然可以设定间隔,但是因为硬件定时器是54.9毫秒为基准,所以设定的定时器都是每55秒发送一个定时消息,有时定时就是近似的,并不完全精确。  
   
   
  鼠标操作  
  1.非客户鼠标事件:  
  非客户区域包括客户区域以外的所有窗口部分,包括标题栏、菜单栏、滚动条等。  
  在客户鼠标的事件前面加上NC的内容是非客户事件,行为之间的差别是根据鼠标按钮被单击时的鼠标位置决定的。例如:WM_NCLBUTTONDOWN  
  2.俘获和释放鼠标  
  调用CWND成员函数SETCAPTURE抓住鼠标,即所有后续鼠标信息都发送到视图窗口,直到放开命令::ReleaseCapture(),此函数是WIN32   API函数。  
  3.限制鼠标移动区域  
  将鼠标光标限制在视图窗口内,使用户不会到窗口外去画线,调用CWND::GETCLENTRECT()取得视图窗口的当前坐标,然后调用CWND::CLIENTTOSCREEN将坐标换算成屏幕坐标,最后调用::CLIPCURSOR将鼠标光标限定在指定的屏幕坐标内,从而保证光标在视图窗口内,使用命令::CLIPCURSOR(NULL)则可以使鼠标移动到屏幕的任何位置。  
  ::ClipCursor是WIN32   API提供的函数,而不是MFC提供的。  
  4.鼠标处理函数的参数NFLAGS的位的位屏蔽:  
  MK_CONTROL             按下CTRL键时设置  
  MK_LBUTTON             按下鼠标左键时设置  
  MK_MBUTTON         按下鼠标中键时设置  
  MK_RUBUTTON         按下鼠标右键时设置  
  MK_SHIFT                 按下SHIFT时设置  
  使用下面的语句检测SHIFT键是否按下:  
  if(nFlags&MK_SHIFT)  
  {  
          ...  
  }  
  5.更改鼠标光标:  
  ::SetCursor(::LoadCursor(AfxGetInstanceHandle(),IDC_ARROW));  
   
   
  指针数组:  
  CTypedPtrArray<CObArray,CLine*>m_linearray;  
  新数据成员m_LineArray是MFC类样板CTyped   PrtArray的实例。CTyped   PtrArray生成一族类,各由第一个样板参数指定的派生,各用于存放第二个参数指定类型的数据项。这样m_LineArray是CObArray派生的类的对象,存放CLine对象的指针。使用派生类的原因是他的编译器能进行更广泛的类型检查,帮助减少错误,并减少使用类对象时需要的校正操作。  
  为了使用CTypedPtrArray或者其他的任何MFC类样板,必须包含AfxTempl.h头文件,将其加入到StdAfx.h文件中。  
  删除数据的时候,必须先用DELETE命令删除对象,然后使用类的删除命令,删除对象的指针。  
   
   
  如何使程序保持极小状态?  
  在恢复程序窗体大小时,   Windows会发送WM_QUERY-OPEN消息,   用   ClassWizard设置成员函数   OnQueryOpen()   ,add   following   code:  
  Bool   CMainFrame::   OnQueryOpen(   )  
  {  
  Return   false;  
  }  
   
   
  如何获取有关窗口正在处理的当前消息的信息  
  调用CWnd:   :   GetCurrentMessage可以获取一个MSG指针。例如,可以使用ClassWizard将几个菜单项处理程序映射到一个函数中,然后调用GetCurrentMessage来确定所选中的菜单项。  
  viod   CMainFrame   :   :   OnCommmonMenuHandler   (   )  
  {  
  //Display   selected   menu   item   in   debug   window   .  
  TRACE   ("Menu   item   %u   was   selected   .   \n"   ,  
  GetCruuentMessage   (   )   ->   wParam   );  
  }  
   
   
  检查是否按下鼠标左键  
  if((nFlags&MK_LBUTTON)==MK_LBUTTON)  
   
   
  检查键盘输入    
  1.在OnKeyDown中的参数nChar是一个数值,当显示的时候,需要转换成字符,使用如下的命令:  
  char   lsChar;  
  lsChar=char(nChar);  
  if(lsChar=='A');  
  {  
  .......  
  }  
  2.调用另一个函数::GetKeyState(),用一个特定的键代码来确定法键是否被按下。如果::GetKeyState函数的返回值是负的,表示该键被按下。如果返回值是非负的,表示该留未被按下。例如:如果要确定shift键是否被按下,可以使用下面的代码:  
  if(::GetKeyState(VK_SHIFT)<O)  
  {  
          AfxMessageBox("shift   is   pressed");  
  }  
   
  Top

5 楼yintongshun(左岸思雨)回复于 2003-12-02 11:51:58 得分 0

WMI使用技巧集  
   很多的朋友对WMI可能见过但理解不深,我也是十分想了解关于WMI的知识,可一直找不对太合适的资料,在网上的一些资料不是有很多错误,就是讲解不清,我有空的时候将关于WMI的知识集中一下,放在这里便于大家学习。本贴会不断增加。  
  1、   什么是WMI  
  WMI是英文Windows   Management   Instrumentation的简写,它的功能主要是:访问本地主机的一些信息和服务,可以管理远程计算机(当然你必须要拥有足够的权限),比如:重启,关机,关闭进程,创建进程等。  
  2、   如何用WMI获得本地磁盘的信息?  
  首先要在VS.NET中创建一个项目,然后在添加引用中引用一个.net的装配件:System.Management.dll,这样你的项目才能使用WMI。代码如下:  
  using   System;  
  using   System.Management;    
   
  class   Sample_ManagementObject  
  {  
    public   static   int   Main(string[]   args)    
    {  
      SelectQuery   query=new   SelectQuery("Select   *   From   Win32_LogicalDisk");  
      ManagementObjectSearcher   searcher=new   ManagementObjectSearcher(query);  
      foreach(ManagementBaseObject   disk   in   searcher.Get())  
      {  
        Console.WriteLine("\r\n"+disk["Name"]   +"   "+disk["DriveType"]   +   "   "   +   disk["VolumeName"]);  
      }  
   
   
      Console.ReadLine();  
   
      return   0;  
   
    }  
   
  }  
   
  disk["DriveType"]   的返回值意义如下:  
   
  1   No   type    
  2   Floppy   disk    
  3   Hard   disk    
  4   Removable   drive   or   network   drive    
  5   CD-ROM    
  6   RAM   disk  
   
   
  3、如何用WMI获得指定磁盘的容量?  
  using   System;  
  using   System.Management;  
   
  //   This   example   demonstrates   reading   a   property   of   a   ManagementObject.  
  class   Sample_ManagementObject  
  {  
    public   static   int   Main(string[]   args)    
    {  
      ManagementObject   disk   =   new   ManagementObject(  
        "win32_logicaldisk.deviceid=\"c:\"");  
      disk.Get();  
      Console.WriteLine("Logical   Disk   Size   =   "   +   disk["Size"]   +   "   bytes");  
      Console.ReadLine();    
      return   0;  
    }  
  }  
   
   
  4、   如何列出机器中所有的共享资源?  
  using   System;  
  using   System.Management;  
   
  class   TestApp   {  
    [STAThread]  
    static   void   Main()  
    {  
      ManagementObjectSearcher   searcher   =   new   ManagementObjectSearcher(  
        "SELECT   *   FROM   Win32_share");  
      foreach   (ManagementObject   share   in   searcher.Get())  
      {  
        Console.WriteLine(share.GetText(TextFormat.Mof));  
      }  
    }  
   
   
  }  
  别忘记在引用中把System.Management添加进来。  
   
   
  5、   怎样写程控制让系统中的某个文件夹共享或取消共享.?  
  首先,这需要以有相应权限的用户登录系统才行。然后,试试下面的代码:  
  using   System;  
  using   System.Management;  
   
  class   CreateShare  
  {  
    public   static   void   Main(string[]   args)  
    {  
      ManagementClass   _class   =   new   ManagementClass(new   ManagementPath("Win32_Share"));  
   
      object[]   obj   =   {"C:\\Temp","我的共享",0,10,"Dot   Net   实现的共享",""};  
   
      _class.InvokeMethod("create",obj);  
    }  
  }  
  将  
  object[]   obj   =   {"C:\\Temp","我的共享",0,10,"Dot   Net   实现的共享",""};  
  改为  
  object[]   obj   =   {"C:\\Temp","我的共享",0,null,"Dot   Net   实现的共享",""};  
  就可以实现授权给最多用户了。  
   
   
  6、   如何获得系统服务的运行状态?  
  private   void   getServices()  
  {  
    ManagementObjectCollection   queryCollection;  
    string[]   lvData   =     new   string[4];  
       
    try  
    {  
      queryCollection   =   getServiceCollection("SELECT   *   FROM   Win32_Service");  
      foreach   (   ManagementObject   mo   in   queryCollection)  
      {  
        //create   child   node   for   operating   system  
        lvData[0]   =   mo["Name"].ToString();  
        lvData[1]   =   mo["StartMode"].ToString();  
        if   (mo["Started"].Equals(true))  
          lvData[2]   =   "Started";  
        else  
          lvData[2]   =   "Stop";  
          lvData[3]   =   mo["StartName"].ToString();  
             
          //create   list   item  
          ListViewItem   lvItem   =   new   ListViewItem(lvData,0);  
          listViewServices.Items.Add(lvItem);  
      }  
    }  
    catch   (Exception   e)  
    {  
      MessageBox.Show("Error:   "   +   e);  
    }  
  }  
   
   
  7、   通过WMI修改IP,而实现不用重新启动?  
  using   System;  
  using   System.Management;  
  using   System.Threading;  
   
  namespace   WmiIpChanger  
  {  
    class   IpChanger  
    {  
      [MTAThread]  
      static   void   Main(string[]   args)  
      {  
        ReportIP();  
        //   SwitchToDHCP();  
        SwitchToStatic();  
        Thread.Sleep(   5000   );  
        ReportIP();  
        Console.WriteLine(   "end."   );  
      }  
   
      static   void   SwitchToDHCP()  
      {  
        ManagementBaseObject   inPar   =   null;  
        ManagementBaseObject   outPar   =   null;  
        ManagementClass   mc   =   new   ManagementClass("Win32_NetworkAdapterConfiguration");  
        ManagementObjectCollection   moc   =   mc.GetInstances();  
        foreach(   ManagementObject   mo   in   moc   )  
        {  
          if(   !   (bool)   mo["IPEnabled"]   )  
            continue;  
   
          inPar   =   mo.GetMethodParameters("EnableDHCP");  
          outPar   =   mo.InvokeMethod(   "EnableDHCP",   inPar,   null   );  
          break;  
        }  
      }  
   
      static   void   SwitchToStatic()  
      {  
        ManagementBaseObject   inPar   =   null;  
        ManagementBaseObject   outPar   =   null;  
        ManagementClass   mc   =   new   ManagementClass("Win32_NetworkAdapterConfiguration");  
        ManagementObjectCollection   moc   =   mc.GetInstances();  
        foreach(   ManagementObject   mo   in   moc   )  
        {  
          if(   !   (bool)   mo[   "IPEnabled"   ]   )  
            continue;  
   
          inPar   =   mo.GetMethodParameters(   "EnableStatic"   );  
          inPar["IPAddress"]   =   new   string[]   {   "192.168.1.1"   };  
          inPar["SubnetMask"]   =   new   string[]   {   "255.255.255.0"   };  
          outPar   =   mo.InvokeMethod(   "EnableStatic",   inPar,   null   );  
          break;  
        }  
      }  
   
      static   void   ReportIP()  
      {  
        Console.WriteLine(   "******   Current   IP   addresses:"   );  
        ManagementClass   mc   =   new   ManagementClass("Win32_NetworkAdapterConfiguration");  
        ManagementObjectCollection   moc   =   mc.GetInstances();  
        foreach(   ManagementObject   mo   in   moc   )  
        {  
          if(   !   (bool)   mo[   "IPEnabled"   ]   )  
            continue;  
   
          Console.WriteLine(   "{0}\n   SVC:   '{1}'   MAC:   [{2}]",   (string)   mo["Caption"],  
            (string)   mo["ServiceName"],   (string)   mo["MACAddress"]   );  
   
          string[]   addresses   =   (string[])   mo[   "IPAddress"   ];  
          string[]   subnets   =   (string[])   mo[   "IPSubnet"   ];  
   
          Console.WriteLine(   "   Addresses   :"   );  
          foreach(string   sad   in   addresses)  
            Console.WriteLine(   "\t'{0}'",   sad   );  
   
          Console.WriteLine(   "   Subnets   :"   );  
          foreach(string   sub   in   subnets   )  
            Console.WriteLine(   "\t'{0}'",   sub   );  
        }  
      }  
    }  
  }  
   
   
   
  Top

6 楼yintongshun(左岸思雨)回复于 2003-12-02 11:52:13 得分 0

8、   如何利用WMI远程重启远程计算机?  
  using   System;  
  using   System.Management;      
  namespace   WMI3  
  {  
              ///   <summary>  
              ///   Summary   description   for   Class1.  
              ///   </summary>    
              class   Class1  
              {  
                          static   void   Main(string[]   args)  
                          {  
                                      Console.WriteLine("Computer   details   retrieved   using   Windows   Management   Instrumentation   (WMI)");  
                                      Console.WriteLine("mailto:Written%2002/01/02%20By%20John%20O'Donnell%20-%20csharpconsulting@hotmail.com");  
                                      Console.WriteLine("========================================  
  =================================");    
                                        //连接远程计算机  
                          ConnectionOptions   co   =   new   ConnectionOptions();  
                          co.Username   =   "john";  
                          co.Password   =   "john";  
                          System.Management.ManagementScope   ms   =   new   System.Management.ManagementScope("\\\\192.168.1.2\\root\\cimv2",   co);              
                                      //查询远程计算机  
                        System.Management.ObjectQuery   oq   =   new   System.Management.ObjectQuery("SELECT   *   FROM   Win32_OperatingSystem");  
                                       
                        ManagementObjectSearcher   query1   =   new   ManagementObjectSearcher(ms,oq);  
                        ManagementObjectCollection   queryCollection1   =   query1.Get();                          
                                      foreach(   ManagementObject   mo   in   queryCollection1   )    
                                      {  
                                                  string[]   ss={""};  
                                                  mo.InvokeMethod("Reboot",ss);  
                                                  Console.WriteLine(mo.ToString());  
                                      }  
                          }  
              }  
  }      
   
   
  9、   利用WMI创建一个新的进程?  
  using   System;  
  using   System.Management;  
   
  //   This   sample   demonstrates   invoking   a   WMI   method   using   parameter   objects  
  public   class   InvokeMethod    
  {          
    public   static   void   Main()    
    {  
   
      //Get   the   object   on   which   the   method   will   be   invoked  
      ManagementClass   processClass   =   new   ManagementClass("Win32_Process");  
   
      //Get   an   input   parameters   object   for   this   method  
      ManagementBaseObject   inParams   =   processClass.GetMethodParameters("Create");  
   
      //Fill   in   input   parameter   values  
      inParams["CommandLine"]   =   "calc.exe";  
   
      //Execute   the   method  
      ManagementBaseObject   outParams   =   processClass.InvokeMethod   ("Create",   inParams,   null);  
   
      //Display   results  
      //Note:   The   return   code   of   the   method   is   provided   in   the   "returnvalue"   property   of   the   outParams   object  
      Console.WriteLine("Creation   of   calculator   process   returned:   "   +   outParams["returnvalue"]);  
      Console.WriteLine("Process   ID:   "   +   outParams["processId"]);  
    }  
  }  
   
   
  10、   如何通过WMI终止一个进程?  
  using   System;    
  using   System.Management;    
   
  //   This   example   demonstrates   how   to   stop   a   system   service.    
  class   Sample_InvokeMethodOptions    
  {    
          public   static   int   Main(string[]   args)   {  
                  ManagementObject   service   =    
                          new   ManagementObject("win32_service=\"winmgmt\"");  
                  InvokeMethodOptions   options   =   new   InvokeMethodOptions();  
                  options.Timeout   =   new   TimeSpan(0,0,0,5);    
   
                  ManagementBaseObject   outParams   =   service.InvokeMethod("StopService",   null,   options);  
   
                  Console.WriteLine("Return   Status   =   "   +   outParams["Returnvalue"]);  
   
                  return   0;  
          }  
  }  
   
   
  11、   如何用WMI   来获取远程机器的目录以及文件.比如如何列出一个目录下的所有文件,或者所有子目录;如何删除,舔加,更改文件?  
  using   System;  
   
                          using   System.Management;  
   
                          //   This   example   demonstrates   reading   a   property   of   a   ManagementObject.  
   
                          class   Sample_ManagementObject  
   
                          {  
   
                                  public   static   int   Main(string[]   args)   {  
   
                                          ManagementObject   disk   =   new   ManagementObject(  
   
                                                  "win32_logicaldisk.deviceid=\"c:\"");  
   
                                          disk.Get();  
   
                                          Console.WriteLine("Logical   Disk   Size   =   "   +   disk["Size"]   +   "   bytes");  
   
                                          return   0;  
   
                                  }  
   
                          }  
   
  12、   参考资料  
  可以参考下列内容:  
   
  msdn   WMI   SDK:  
  http://msdn.microsoft.com/library/default.asp?url=/library/en-     us/wmisdk/wmi/wmi_start_page.asp  
   
  WMI使用说明(csdn):  
  http://www.csdn.net/develop/article/15/15346.shtm  
   
  WMI技术的应用   for   .net(csdn):  
  http://www.csdn.net/develop/article/16/16419.shtm  
   
  在.NET中轻松获取系统信息(1)   -WMI篇(csdn):  
  http://www.csdn.net/develop/article/15/15744.shtm  
   
   
    :)  
   
   
  13、   一些技巧  
  我使用WMI可以取出网卡的MAC地址,CPU的系列号,主板的系列号,其中主板的系列号已经核对过没有错的,其余的有待于验证,因为我使用的是笔记本,笔记本背面有一个主板的系列号,所以可以肯定主板系列号没有问题  
   
  网卡的MAC地址  
   
  SELECT   MACAddress   FROM   Win32_NetworkAdapter   WHERE   ((MACAddress   Is   Not   NULL)   AND   (Manufacturer   <>   'Microsoft'))  
   
  结果:08:00:46:63:FF:8C  
   
   
  CPU的系列号  
   
  Select   ProcessorId   From   Win32_Processor  
   
  结果:3FEBF9FF00000F24  
   
   
  主板的系列号  
   
  Select   SerialNumber   From   Win32_BIOS  
   
  结果:28362630-3700521  
  获取硬盘ID  
  String   HDid;  
  ManagementClass   cimobject   =   new   ManagementClass("Win32_DiskDrive");  
  ManagementObjectCollection   moc   =   cimobject.GetInstances();  
  foreach(ManagementObject   mo   in   moc)  
  {  
    HDid   =   (string)mo.Properties["Model"].value;  
   
    MessageBox.Show(HDid     );    
  }  
   
   
  14、   一个使用WMI后的异常处理的问题  
  下面是我整理的一段代码.  
     
    ManagementObjectCollection   queryCollection1;  
      ConnectionOptions   co   =   new   ConnectionOptions();  
      co.Username   =   "administrator";  
      co.Password   =   "111";  
      try  
      {  
        System.Management.ManagementScope   ms   =   new   System.Management.ManagementScope(@"\\csnt3\root\cimv2",   co);  
        System.Management.ObjectQuery   oq   =   new   System.Management.ObjectQuery("SELECT   *   FROM   Win32_OperatingSystem");  
        ManagementObjectSearcher   query1   =   new   ManagementObjectSearcher(ms,oq);  
   
        queryCollection1   =   query1.Get();  
        foreach(   ManagementObject   mo   in   queryCollection1   )  
        {  
          string[]   ss={""};  
          mo.InvokeMethod("Reboot",ss);  
          Console.WriteLine(mo.ToString());  
        }  
      }  
      catch(Exception   ee)  
      {  
   
        Console.WriteLine("error");  
   
      }  
   
   
  Top

7 楼yintongshun(左岸思雨)回复于 2003-12-02 11:52:36 得分 0

 
  15、Windows   管理规范   (WMI)   是可伸缩的系统管理结构,它采用一个统一的、基于标准的、可扩展的面向对象接口。WMI   为您提供与系统管理信息和基础   WMI   API   交互的标准方法。WMI   主要由系统管理应用程序开发人员和管理员用来访问和操作系统管理信息。  
  WMI   可用于生成组织和管理系统信息的工具,使管理员或系统管理人员能够更密切地监视系统活动。例如,可以使用   WMI   开发一个应用程序,用于在   Web   服务器崩溃时呼叫管理员。  
  将   WMI   与   .NET   框架一起使用  
  WMI   提供了大量的规范以便为许多高端应用程序(例如,Microsoft   Exchange、Microsoft   SQL   Server   和   Microsoft   Internet   信息服务   (IIS))实现几乎任何管理任务。管理员可以执行下列任务:    
  &#8226;   监视应用程序的运行状况。    
  &#8226;   检测瓶颈或故障。    
  &#8226;   管理和配置应用程序。    
  &#8226;   查询应用程序数据(使用对象关系的遍历和查询)。    
  &#8226;   执行无缝的本地或远程管理操作。    
  WMI   结构由以下三层组成:    
  &#8226;   客户端    
  使用   WMI   执行操作(例如,读取管理详细信息、配置系统和预订事件)的软件组件。    
  &#8226;   对象管理器    
  提供程序与客户端之间的中间装置,它提供一些关键服务,如标准事件发布和预订、事件筛选、查询引擎等。    
  &#8226;   提供程序    
  软件组件,它们捕获实时数据并将其返回到客户端应用程序,处理来自客户端的方法调用并将客户端链接到所管理的基础结构。    
  通过定义完善的架构向客户端和应用程序无缝地提供了数据和事件以及配置系统的能力。在   .NET   框架中,System.Management   命名空间提供了用于遍历   WMI   架构的公共类。  
  除了   .NET   框架,还需要在计算机上安装   WMI   才能使用该命名空间中的管理功能。如果使用的是   Windows   Millennium   Edition、Windows   2000   或   Windows   XP,那么已经安装了   WMI。否则,将需要从   MSDN   下载   WMI。  
  用   System.Management   访问管理信息  
  System.Management   命名空间是   .NET   框架中的   WMI   命名空间。此命名空间包括下列支持   WMI   操作的第一级类对象:    
  &#8226;   ManagementObject   或   ManagementClass:分别为单个管理对象或类。    
  &#8226;   ManagementObjectSearcher:用于根据指定的查询或枚举检索   ManagementObject   或   ManagementClass   对象的集合。    
  &#8226;   ManagementEventWatcher:用于预订来自   WMI   的事件通知。    
  &#8226;   ManagementQuery:用作所有查询类的基础。    
  System.Management   类的使用编码范例对   .NET   框架环境很适合,并且   WMI   在任何适当的时候均使用标准基框架。例如,WMI   广泛利用   .NET   集合类并使用推荐的编码模式,如   .NET   异步操作的“委托”模式。因此,使用   .NET   框架的开发人员可以使用他们的当前技能访问有关计算机或应用程序的管理信息。  
  请参见  
  使用   WMI   管理应用程序   |   检索管理对象的集合   |   查询管理信息   |   预订和使用管理事件   |   执行管理对象的方法   |   远程处理和连接选项   |   使用强类型对象  
     
  获取CPU序列号代码  
  string   cpuInfo   =   "";//cpu序列号  
        ManagementClass   cimobject   =   new   ManagementClass("Win32_Processor");  
        ManagementObjectCollection   moc   =   cimobject.GetInstances();  
        foreach(ManagementObject   mo   in   moc)  
        {  
          cpuInfo   =   mo.Properties["ProcessorId"].value.ToString();  
          Console.WriteLine(cpuInfo);  
          Console.ReadLine();  
        }  
  获取网卡硬件地址  
  using   System.Management;  
  ...  
  ManagementClass   mc   =   new   ManagementClass("Win32_NetworkAdapterConfiguration");  
  ManagementObjectCollection   moc   =   mc.GetInstances();  
  foreach(ManagementObject   mo   in   moc)  
  {  
  if((bool)mo["IPEnabled"]   ==   true)  
  Console.WriteLine("MAC   address\t{0}",   mo["MacAddress"].ToString());  
  mo.Dispose();  
  }  
  }  
  获取硬盘ID  
  String   HDid;  
  ManagementClass   cimobject   =   new   ManagementClass("Win32_DiskDrive");  
  ManagementObjectCollection   moc   =   cimobject.GetInstances();  
  foreach(ManagementObject   mo   in   moc)  
  {  
    HDid   =   (string)mo.Properties["Model"].value;  
    MessageBox.Show(HDid     );    
  }  
   
   
  16、在.NET中轻松获取系统信息(1)   -WMI篇  
  Montaque  
  申明:  
          1、个人的一点心得,仅供参考  
          2、转载时候,请保留原本。  
   
  概述:  
    不知道大家有没有这种体会?有时候为了获取系统一点点信息,比如考虑一下操作系统的版本号,或者当前屏幕的分辨率。其实说到底就是读操作系统某个方面的一个属性值而已,然后就看到我们的程序中密密麻麻的Win32   API申明,调用,代码的可读性和维护性不言而喻。到了.NET,微软提供了更为丰富的类,有很多以前要调用API的方法可以在.NET中轻而易举的调用实现。今天简单介绍一个在.NET中如何通过与WMI(Windows   管理规范)的通讯,从而得到获取信息的目的。  
  主要思路:  
    举一个获取操作系统共享目录和获取主板号的例子,介绍如何利用System.Managment下面的类获取系统相关的信息:  
   
  正文:  
    WMI(Windows管理规范:Windows   Management   Instrumentation)是Microsoft基于Web的企业管理(WBEM)的实现,同时也是一种基于标准的系统管理接口。WMI最早出现在Microsoft   Windows   2000系统上,但它同样可以安装在Windows   NT   4和Windows   9x计算机上。WMI是一种轻松获取系统信息的强大工具。  
    在.NET中,有一个System.Management名空间(系统默认没有引用,我们可以手动添加引用),通过下面的Class的操作,可以查询系统软硬件的信息,先看一个简单的例子:  
   
  Imports   System.Management  
  Dim   searcher   As   New   ManagementObjectSearcher("SELECT   *   FROM   Win32_share")  
  Dim   share   As   ManagementObject  
    For   Each   share   In   searcher.Get()  
                MessageBox.Show(share.GetText(TextFormat.Mof))  
    Next   share  
  运行的结果是列出了所有系统当前共享的目录、以及描述等等。  
   
  分析一下上面的代码,可以看到一下几点:  
  1、似乎是在进行数据库操作,有点像SQL语句。其实就是SQL操作,这种语句被成WQL(WMI   Query   Language),实际上是标准SQL的一个子集加上了WMI的扩展.  
  2、WQL是个只读的查询语言,我们只能查询响应的数据,不能用UPDATE,INSERT等更新操作  
  3、代码很简单、通俗易懂  
  4、我们采用了一种MOF(托管对象格式)的显示。  
   
  例子二:获取当前主板的信息  
    上面的例子是一个软件方面的信息,下面看一个获取硬件信息的例子,获取主板的序列号以及制造商:  
  Dim   searcher   As   New   ManagementObjectSearcher("SELECT   *   FROM   Win32_BaseBoard")  
  Dim   share   As   ManagementObject  
        For   Each   share   In   searcher.Get()  
              Debug.WriteLine("主板制造商:"   &   share("Manufacturer"))  
              Debug.WriteLine("型号:"   &   share("Product"))  
              Debug.WriteLine("序列号:"   &   share("SerialNumber"))  
        Next   share  
  总结以及补充:  
    WMI类也是分层次的,具体可以参考msdn中的WMI;转向.NET平台开发的时候,最好能多看一些关于.NET新特性的介绍,这样可以大幅度的提升代码的开发效率以及运行效率。  
   
   
  Top

8 楼bluebohe(薄荷)回复于 2003-12-02 12:30:10 得分 0

谢谢!  
  这个贴子一会我结掉吧  
  Top

相关问题

  • SQL SERVER中一些常见性能问题的总结
  • 请高手总结一下常见的Web服务器有哪些?谢谢
  • 大家能帮我总结一下m_pMainWnd的一些常见用法吗??谢谢!
  • 再当次义工:VB高手搜集-常见问题总结(立华软件园)
  • 年终总结
  • mfc总结
  • ***2005年总结***
  • 常见错误!!!!!!
  • VB常见问题?
  • VB常见问题?

关键词

  • .net
  • c++
  • win32
  • win32 api
  • 函数