堆和栈的区别以及关于程序结束内存释放问题

一般认为在c中分为这几个存储区
1 栈 – 有编译器自动分配释放
2 堆 – 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收
3 全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
 
– 程序结束释放
4另外还有一个专门放常量的地方。 – 程序结束释放在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函数中的”adgfdf”这样的字符串存放在常量区。
比如:
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = “abc”;栈
char *p2; 栈
char *p3 = “123456”; 123456在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, “123456”); 123456放在常量区,编译器可能会将它与p3所指向的”123456″优化成一块。
}
还有就是函数调用时会在栈上有一系列的保留现场及传递参数的操作。栈的空间大小有限定,vc的缺省是2M。栈不够用的情况一般是程序中分配了大量数组和递归函数层次太深。有一点必须知道,当一个函数调用完返回后它会释放该函数中所有的栈空间。栈是由编译器自动管理的,不用你操心。
 
堆是动态分配内存的,并且你可以分配使用很大的内存。但是用不好会产生内存泄漏。并且频繁地malloc和free会产生内存碎片(有点类似磁盘碎片),因为c分配动态内存时是寻找匹配的内存的。而用栈则不会产生碎片。
在栈上存取数据比通过指针在堆上存取数据快些。
一般大家说的堆栈和栈是一样的,就是栈(stack),而说堆时才是堆heap.栈是先入后出的,一般是由高地址向低地址生长。
 
堆(heap)和栈(stack)是C/C++编程不可避免会碰到的两个基本概念。首先,这两个概念都可以在讲数据结构的书中找到,他们都是基本的数据结构,虽然栈更为简单一些。在具体的C/C++编程框架中,这两个概念并不是并行的。对底层机器代码的研究可以揭示,栈是机器系统提供的数据结构,而堆则是C/C++函数库提供的。
 
具体地说,现代计算机(串行执行机制),都直接在代码底层支持栈的数据结构。这体现在,有专门的寄存器指向栈所在的地址,有专门的机器指令完成数据入栈出栈的操作。这种机制的特点是效率高,支持的数据有限,一般是整数,指针,浮点数等系统直接支持的数据类型,并不直接支持其他的数据结构。因为栈的这种特点,对栈的使用在程序中是非常频繁的。对子程序的调用就是直接利用栈完成的。机器的call指令里隐含了把
返回地址推入栈,然后跳转至子程序地址的操作,而子程序中的ret指令则隐含从堆栈中弹出返回地址并跳转之的操作。C/C++中的自动变量是直接利用栈的例子,这也就是为什么当函数返回时,该函数的自动变量自动失效的原因(因为 颜换指戳说饔们暗 状态)。
 
和栈不同,堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的,而是由函数库提供的。基本的malloc/realloc/free函数维护了一套内部的堆数据结构。当程序使用这些函数去获得新的内存空间时,这套函数首先试图从内部堆中寻找可用的内存空间,如果没有可以使用的内存空间,则试图利用系统调用来动态增加程序数据段的内存大小,新分配得到的空间首先被组织进内部堆中去,然后再以适当的形式返回给调用者
。当程序释放分配的内存空间时,这片内存空间被返回内部堆结构中,可能会被适当的处理(比如和其他空闲空间合并成更大的空闲空间),以更适合下一次内存分配申请。这套复杂的分配机制实际上相当于一个内存分配的缓冲池(Cache),使用这套机制有如下若干原因:
 
1. 系统调用可能不支持任意大小的内存分配。有些系统的系统调用只支持固定大小及其倍数的内存请求(按页分配);这样的话对于大量的小内存分类来说会造成浪费。
 
2. 系统调用申请内存可能是代价昂贵的。系统调用可能涉及用户态和核心态的转换。
 
3. 没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片
 
 
堆和栈的对比
 
从以上知识可知,栈是系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活;而栈是函数库提供的功能,特点是灵活方便,数据适应面广泛,但是效率有一定降低。栈是系统数据结构,对于进程/线程是唯一的;堆是函数库内部数据结构,不一定唯一。不同堆分配的内存无法互相操作。栈空间分静态分配和动态分配两种。静态分配是编译器完成的,比如自动变量(auto)的分配。动态分配由alloca函数完成。栈的动态分配
无需释放(是自动的),也就没有释放函数。为可移植的程序起见,栈的动态分配操作是不被鼓励的!堆空间的分配总是动态的,虽然程序结束时所有的数据空间都会被释放回系统,但是精确的申请内存/释放内存匹配是良好程序的基本要素。
可以放一块思考堆和栈的生长方向恰好相反,
|————–| 低地址
| 堆 |
|————–|
| |  |
| I  |
| |  |
| ^  |
| 栈 | 高地址
—————–
所以计算机中的堆和栈经常时放一块讲的nod 一般不是必要就不要动态创建,最讨厌把new出来的东西当局部变量用,用万了马上delete 的做法.
理由
1.栈分配比堆快,只需要一条指令就呢给配所有的局部变量
2.栈不会出现内存碎片
3。栈对象好管理
 
当然,某些情况下也要那么写,比如
1.对象很大
2.对象需要在某个特定的时刻构造或析够
3.类只允许对象动态创建,比如VCL的大多数类
当然,必须用堆对象时也不能躲避
 
 
 
 
 
 
 
 
PS:
在Linux和Windows中用   c   语言开发程序,一些函数(例如   malloc   和   strdup   )动态分配的内存,有必要在程序中加入语句,在程序结束前释放么?操作系统会不会自动在程序结束的时候把这些内存释放而不用编程者关心?
 
 
1.你可以不关心,不过这样下去也许哪天你老板也不会关心你的工资了.
 
2.是的,理论上不需要释放,就像你不需要在程序结束时fclose()用fopen()打开的文件一样。但是,自己做这件事毕竟是个好习惯,它只会给你带来好处。
 
3.动态分配的内存,当然有必要在不用时释放掉,这样可以使所编的程序尽可能小的占用内存资源。尽管程序结束时这些内存会被自动释放,但及时的释放不用的动态内存,是程序员应该具备的好习惯。
 
4.如果你用的是JAVA,那么你不释放,老板都不会不关心你的工资的。
 
5.理论上是必须你自己释放的,如果你不释放,在程序结束的时候,系统会帮你释放,但我们不能依靠系统来做这件事,因为你不释放,在你的程序运行中,就会造成内存泄漏,当然,你的程序是一个小软件,每一次运行的时间不长,你可以不关心这个问题,但是这不是说,你可以这样做,因为成为习惯以后就很可怕,你不可能永远写小软件,一些大型的软件,比如电信系统用的软件,它可能一次就运行几年,甚至更长,这个时候任何一个小的内存泄漏,在长时间的情况下,都可能造成系统的死机,而且这样的错误很难除去,所以平时养成良好的习惯,才不会关键的时刻出错。如果你的习惯不好,你的经理在code   review的时候就会发现,这样对你的饭碗当然不利了。
 
6.C/C++中,内存问题是一个很特殊的地方,C++的创作者们也曾努力想使C++具有内存自动回收功能,但最后碍于效率等因素还是把它留给了程序员自己解决。在程序中动态分配的内存,除了你自己没人可以帮你摆平,所以自己分配的内存自己释放,不仅是一个习惯问题,而且是绝对必需的。不过你可以借助特殊的模版实现动态内存的自动回收。这在很多技术书籍里都有提到。
 
7.一定要释放,我现在的工作是电信软件方面,我们的软件系统一般都是7*24小时运行的,如果在一个循环里,malloc内存而忘了释放的话,最终很可能导致系统的内存被你的程序耗尽,当掉。所以一定要样成malloc,free和new,delete配对的习惯。
8.程序中没有释放内存不仅会有内存耗尽的危险,还有可能堆变量的内存空间挤占其他的变量空间,这样的现象有人遇到过,排除这样的错误及其困难.  
   
  不释放内存是十分危险的,因为它造成的后果”不确定”.  
  不确定的意思是:程序在你写的时候可以运行,测试的时候也不会发现问题,但是到了客户那里运行的时候,就崩溃了.
 
…… ……
 
相关网址:

ShellExecute用法

ShellExecute函数原型及参数含义如下:
 
ShellExecute(
HWND hwnd, //父窗口句柄
LPCSTR lpOperation, //操作类型
LPCSTR lpFile, //要进行操作的文件或路径
LPCSTR lpParameters, //当lpOperation为“explore”时指定要传递的参数,通常设为NULL
LPCSTR lpDirectory, //指定默认目录,通常设为NULL
INT nShowCmd //文件打开的方式,以通常方式还是最大化或最小化显示
)
例子如下:
 
//调用计算器
ShellExecute(NULL,”open”,”calc.exe”,NULL,NULL,SW_SHOWNORMAL);
//调用记事本
ShellExecute(NULL,”open”,”NOTEPAD.EXE”,NULL,NULL,SW_SHOWNORMAL);
 
●hWnd:用于指定父窗口句柄。当函数调用过程出现错误时,它将作为Windows消息窗口的父窗口。例如,可以将其设置为应用程序主窗口句柄,即Application.Handle,也可以将其设置为桌面窗口句柄(用GetDesktopWindow函数获得)。
 
●Operation:用于指定要进行的操作。其中“open”操作表示执行由FileName参数指定的程序,或打开由FileName参数指定的文件或文件夹;“print”操作表示打印由FileName参数指定的文件;“explore”操作表示浏览由FileName参数指定的文件夹。当参数设为nil时,表示执行默认操作“open”。
 
●FileName:用于指定要打开的文件名、要执行的程序文件名或要浏览的文件夹名。
 
●Parameters:若FileName参数是一个可执行程序,则此参数指定命令行参数,否则此参数应为nil或PChar(0)。
 
●Directory:用于指定默认目录。
 
●ShowCmd:若FileName参数是一个可执行程序,则此参数指定程序窗口的初始显示方式,否则此参数应设置为0。
 
若ShellExecute函数调用成功,则返回值为被执行程序的实例句柄。若返回值小于32,则表示出现错误。
上述仅仅是ShellExecute函数的标准用法,下面将介绍它的特殊用法。
 
 
2.特殊用法
如果将FileName参数设置为“http:”协议格式,那么该函数将打开默认浏览器并链接到指定的URL地址。若用户机器中安装了多个浏览器,则该函数将根据Windows 9x/NT注册表中http协议处理程序(Protocols Handler)的设置确定启动哪个浏览器。
 
格式一:http://网站域名。
如:ShellExecute(handle, ‘open’, http:// ;
www.neu.edu.cn’, nil, nil, SW_SHOWNORMAL);
 
格式二:http://网站域名/网页文件名。
如:ShellExecute(handle, ‘open’, http:// ;
如果将FileName参数设置为“mailto:”协议格式,那么该函数将启动默认邮件客户程序,如Microsoft Outlook(也包括Microsoft Outlook Express)或Netscape Messanger。若用户机器中安装了多个邮件客户程序,则该函数将根据Windows 9x/NT注册表中mailto协议处理程序的设置确定启动哪个邮件客户程序。
 
格式一:mailto:
如:ShellExecute(handle,‘open’, ‘mailto:’, nil, nil, SW_SHOWNORMAL);打开新邮件窗口。
 
格式二:mailto:用户账号@邮件服务器地址
如:ShellExecute(handle, ‘open’,‘ mailto:who@mail.neu.edu.cn’, nil, nil, SW_SHOWNORMAL);打开新邮件窗口,并自动填入收件人地址。若指定多个收件人地址,则收件人地址之间必须用分号或逗号分隔开(下同)。
 
格式三:mailto:用户账号@邮件服务器地址?subject=邮件主题&body=邮件正文
如:ShellExecute(handle, ‘open’, ‘ mailto:who@mail.neu.edu.cn?subject=Hello&Body=This is a test’, nil, nil, SW_SHOWNORMAL);打开新邮件窗口,并自动填入收件人地址、邮件主题和邮件正文。若邮件正文包括多行文本,则必须在每行文本之间加入换行转义字符%0a。
 
例子(delphi):
 
在一个应用程序调用c:\Project1.exe;
ShellExecute(handle, ‘open’,’c:\Project1.exe’,’字串内容’,nil, SW_SHOWNORMAL);
在Project1.exe里可以调用:
procedure TForm1.FormCreate(Sender: TObject);
var i:integer;
begin
for i:=1 to paramcount do
if ParamStr(i)<>” then showmessage(ParamStr(i));
end;
 
最后的那个参数,为窗口指定可视性方面的一个命令。
请用下述任何一个常数
SW_HIDE 隐藏窗口,活动状态给令一个窗口
SW_MINIMIZE 最小化窗口,活动状态给令一个窗口
SW_RESTORE 用原来的大小和位置显示一个窗口,同时令其进入活动状态
SW_SHOW 用当前的大小和位置显示一个窗口,同时令其进入活动状态
SW_SHOWMAXIMIZED 最大化窗口,并将其激活
SW_SHOWMINIMIZED 最小化窗口,并将其激活
SW_SHOWMINNOACTIVE 最小化一个窗口,同时不改变活动窗口
SW_SHOWNA 用当前的大小和位置显示一个窗口,不改变活动窗口
SW_SHOWNOACTIVATE 用最近的大小和位置显示一个窗口,同时不改变活动窗口
SW_SHOWNORMAL 与SW_RESTORE相同
 

SHOpenFolderAnd-SelectItems的用法

[language=CPP]

   LPITEMIDLIST  pidl;
   LPCITEMIDLIST cpidl;
   LPCITEMIDLIST cpidl1;
   LPSHELLFOLDER pDesktopFolder;
   char          szPath[MAX_PATH];
   OLECHAR       olePath[MAX_PATH];
   ULONG         chEaten;
   ULONG         dwAttributes;
   HRESULT       hr;
   char          szPath1[MAX_PATH];

   //
   // Get the path to the file we need to convert.
   //
   //  GetCurrentDirectory(MAX_PATH, szPath);
   sprintf(szPath, “D:\\code管理\\Demo”);
   sprintf(szPath1, “D:\\code管理\\Demo\\HookManager_src.zip”);
   //
   // Get a pointer to the Desktop’s IShellFolder interface.
   //
   if (SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder)))
   {
       //
       // IShellFolder::ParseDisplayName requires the file name be in
       // Unicode.
       //
       MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szPath, -1,
                           olePath, MAX_PATH);

       //
       // Convert the path to an ITEMIDLIST.
       //
       hr = pDesktopFolder->ParseDisplayName(NULL,0,olePath,&chEaten,&pidl,&dwAttributes);
       if (FAILED(hr))
       {
     AfxMessageBox(“ERROR”);
           // Handle error.
       }
    cpidl = pidl;

       MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szPath1, -1,
                           olePath, MAX_PATH);

       //
       // Convert the path to an ITEMIDLIST.
       //
       hr = pDesktopFolder->ParseDisplayName(NULL,0,olePath,&chEaten,&pidl,&dwAttributes);
       if (FAILED(hr))
       {
     AfxMessageBox(“ERROR”);
           // Handle error.
       }
  cpidl1 = pidl;
    HRESULT RE = CoInitialize(NULL);
    int re = SHOpenFolderAndSelectItems(cpidl,1,&cpidl1,NULL);

       //
       // pidl now contains a pointer to an ITEMIDLIST for .\readme.txt.
       // This ITEMIDLIST needs to be freed using the IMalloc allocator
       // returned from SHGetMalloc().
       //

       //release the desktop folder object
         pDesktopFolder->Release();
   } 

 

[/language]

 

 

 

打开文件夹并选定文件还有一种最简便的方法,就是使用ShellExecute:

 

[language=CPP]

BOOL OpenFolderAndSelectItem(LPSTR pFilePath)
{
 char szCmdLine[MAX_PATH]={0};  
 strcpy(szCmdLine,   “/Select,   “);  
 strcat(szCmdLine,   pFilePath);  
 ShellExecute(NULL,   NULL,   “Explorer”,   szCmdLine,   NULL,   SW_SHOWNORMAL);
 return TRUE;
}

[/language]




Modified At 2008-05-22 18:56:44

如何获取其它程序的命令行参数

    我们都知道,在程序里获取命令行参数很简单,WinMain函数会以参数的形式传递给我们,或者可以调用API GetCommandLine 获取。但是GetCommandLine函数不接受参数,获取的只是自己程序的命令行参数。那么如果我们想获取别的应用程序的命令行参数应该怎么办呢?

  有的同学说,既然GetCommandLine只能获取本程序的命令行参数,我们可以在其它进程里插入一个Dll,在那个进程的地址空间调用GetCommandLine函数,然后传回来就可以了。这样好像有点儿不太友好。让我们想想还有没有别的办法。

  我们想,自己的命令行参数既然随时都可以获取到,那么在该进程里一定有一个地方存放它。那么在哪儿呢?看一下GetCommandLine函数的反汇编代码,我们发现,原来世界是如此的美好!

  以下是WinXP系统的GetCommandLine函数反汇编代码:

.text:7C812C8D GetCommandLineA proc near
.text:7C812C8D mov eax, dword_7C8835F4   //dword_7C8835F4 就是命令行参数字符串的地址
            //该指令机器码为 A1 F4 35 88 7C,从第2个字节开始的4个字节就是我们要的地址
.text:7C812C92 retn
.text:7C812C92 GetCommandLineA endp既然知道了放在哪儿了,我们自己去拿就可以了。因为GetCommandLine函数的地址在各个进程内都是一样的,所以可以直接用我们进程里的地址。 win2000/xp系统很简单,98下稍微麻烦一点儿,需要进行一些简单的计算。 以下是GetCommandLine函数在win98下的汇编代码:.text:BFF8C907 GetCommandLineA proc near
.text:BFF8C907 mov eax, dword_BFFCADE4
.text:BFF8C90C mov ecx, [eax]
.text:BFF8C90E mov eax, [ecx+0C0h]
.text:BFF8C914 test eax, eax
.text:BFF8C916 jnz short locret_BFF8C91E
.text:BFF8C918 mov eax, [ecx+40h]
.text:BFF8C91B mov eax, [eax+8] //算到这儿,才是我们想要的地址
.text:BFF8C91E
.text:BFF8C91E locret_BFF8C91E: ; CODE XREF: GetCommandLineA+F.
.text:BFF8C91E retn这样,我们就可以调用OpenProcess函数打开其它进程,然后用ReadProcessMemory读取相应的数据即可。 示例代码:

 



 

用户自定义消息

    ClassWizard不允许增加用户自定义消息,所以你必须手工输入。输入后,ClassWizard就可以象处理其它消息一样处理你自定义的消息了。
    下面是增加自定义消息的步骤:
 
    第一步:定义消息。开发Windows95应用程序时,Microsoft推荐用户自定义消息至少是WM_USER+100,因为很多新控件也要使用WM_USER消息。
 
    第二步:实现消息处理函数。该函数使用WPRAM和LPARAM参数并返回LPESULT。
 
LPESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
           // TODO: 处理用户自定义消息
          …
            return 0;
}
 
    第三步:在类头文件的AFX_MSG块中说明消息处理函数:
 
class CMainFrame:public CMDIFrameWnd
{
    …
    // 一般消息映射函数
    protected:
        // {{AFX_MSG(CMainFrame)
        afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
        afx_msg void OnTimer(UINT nIDEvent);
        afx_msg LRESULT OnMyMessage(WPARAM wParam,  LPARAM lParam);
        //}}AFX_MSG
        DECLARE_MESSAGE_MAP()
}
 
    第四步:在用户类的消息块中,使用ON_MESSAGE宏指令将消息映射到消息处理函数中。
 
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
    //{{AFX_MSG_MAP(CMainFrame)
    ON_WM_CREATE()
    ON_WM_TIMER()
    ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()
 
    如果用户需要一个整个系统唯一的消息,可以调用SDK函数RegisterWindowMessage并使用ON_REGISTER_MESSAGE宏指令取代ON_MESSAGE宏指令,其余步骤同上。

如何获得指定进程加载的DLL

Enumerating All Modules For a Process
To determine which processes have loaded a particular DLL, you must enumerate the modules for each process. The following sample code uses the EnumProcessModules function to enumerate the modules of current processes in the system.

 



 

The main function obtains a list of processes by using the EnumProcesses function. For each process, the main function calls the PrintModules function, passing it the process identifier. PrintModules in turn calls the OpenProcess function to obtain the process handle. If OpenProcess fails, the output shows only the process identifier. For example, OpenProcess fails for the Idle and CSRSS processes because their access restrictions prevent user-level code from opening them. Next, PrintModules calls the EnumProcessModules function to obtain the module handles function. Finally, PrintModules calls the GetModuleFileNameEx function, once for each module, to obtain the module names.

获取进程信息

我们在编写程序时,常常遇到的一件事情就是要准确列出系统中所有正在运行的程序或者进程。Windows 任务管理器就是这样的一个程序。它既能列出运行的桌面应用程序,又能列出系统中所有运行的进程。那么,我们在程序中如何实现这样的任务呢?本文下面将详细 讨论这个问题。


枚举顶层(top-level)窗口

枚举桌面顶层窗口相对于枚举进程来说可能要容易一些。枚举桌面顶层窗口的方法是用 EnumWindows() 函数。不要用 GetWindow()来创建窗口列表,因为窗口之间复杂的父子及同胞关系(Z-Order)容易造成混乱而使得枚举结果不准确。
EnumWindows()有两个参数,一个是指向回调函数的指针,一个是用户定义的 LPARAM 值, 针对每个桌面窗口(或者顶层窗口)它调用回调函数一次。然后回调函数用该窗口句柄做一些处理,比如将它添加到列表中。这个方法保证枚举结果不会被窗口复杂 的层次关系搞乱,因此,一旦有了窗口句柄,我们就可以通过 GetWindowText() 得到窗口标题。


枚举进程

建立系统进程列表比枚举窗口稍微复杂一些。这主要是因为所用的 API 函数对于不同的 Win32 操作系统有依赖性。在 Windows 9x、Windows Me、Windows 2000 Professional 以及 Windows XP 中,我们可以用 ToolHelp32 库中的 APIs 函数。但是在 Windows NT 里,我们必须用 PSAPI 库中的 APIs 函数, PSAPI 库是 SDK 的一部分。本文我们将讨论上述所有平台中的实现。附带的例子程序将对上述库中的 APIs 进行包装,以便包装后的函数能支持所有 Win32 操作系统。


使用 ToolHelp32 库枚举进程

ToolHelp32 库函数在 KERNEL32.dll 中,它们都是标准的 API 函数。但是 Windows NT 4.0 不提供这些函。
ToolHelp32 库中有各种各样的函数可以用来枚举系统中的进程、线程以及获取内存和模块信息。其中枚举进程 只需用如下三个的函数:CreateToolhelp32Snapshot()、Process32First()和 Process32Next()。
使用 ToolHelp32 函数的第一步是用 CreateToolhelp32Snapshot() 函数创建系统信息“快照”。这个函数可以让你选择存储在快照中的信息类型。如果你只是对进程信息感兴趣,那么只要包含 TH32CS_SNAPPROCESS 标志即可。 CreateToolhelp32Snapshot() 函数返回一个 HANDLE,完成调用之后,必须将此 HANDLE 传给 CloseHandle()。
接下来是调用一次 Process32First 函数,从快照中获取进程列表,然后重复调用 Process32Next,直到函数返回 FALSE 为止。这样将遍历快照中进程列表。这两个函数都带两个参数,它们分别是快照句柄和一个   PROCESSENTRY32 结构。
调用完 Process32First 或 Process32Next 之后,PROCESSENTRY32 中将包含系统中某个进程的关键信息。其中进程 ID 就存储在此结构的 th32ProcessID。此 ID 可以被传给 OpenProcess() API 以获得该进程的句柄。对应的可执行文件名及其存放路径存放在 szExeFile 结构成员中。在该结构中还可以找到其它一些有用的信息。
注意:在调用 Process32First() 之前,一定要记住将 PROCESSENTRY32 结构的 dwSize 成员设置成 sizeof(PROCESSENTRY32)。



CreateToolhelp32Snapshot函数为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程[THREAD])建立一个快照[snapshot]。

HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags,
DWORD th32ProcessID
);

参数:
dwFlags
[输入]指定快照中包含的系统内容,这个参数能够使用下列数值(变量)中的一个。

    TH32CS_INHERIT – 声明快照句柄是可继承的。
    TH32CS_SNAPALL – 在快照中包含系统中所有的进程和线程。
    TH32CS_SNAPHEAPLIST – 在快照中包含在th32ProcessID中指定的进程的所有的堆。
    TH32CS_SNAPMODULE – 在快照中包含在th32ProcessID中指定的进程的所有的模块。
    TH32CS_SNAPPROCESS – 在快照中包含系统中所有的进程。
    TH32CS_SNAPTHREAD – 在快照中包含系统中所有的线程。

th32ProcessID
[输入]指定将要快照的进程ID。如果该参数为0表示快照当前进程。该参数只有在设置了TH32CS_SNAPHEAPLIST或TH32CS_SNAPMOUDLE后才有效,在其他情况下该参数被忽略,所有的进程都会被快照。

返回值:
调用成功,返回快照的句柄,调用失败,返回INVAID_HANDLE_VALUE。

备注:
使用GetLastError函数查找该函数产生的错误状态码。
要删除快照,使用CloseHandle函数

 

 


使用 PSAPI 库枚举进程

在 Windows NT 中,创建进程列表使用 PSAPI 函数,这些函数在 PSAPI.DLL 中。这个文件是随 Platform SDK 一起分发的,最新版本的 Platform SDK 可以从这里下载:

 

使用这个库所需的 PSAPI.h 和 PSAPI.lib 文件也在该 Platform SDK 中。
为了使用 PSAPI 库中的函数,需将 PSAPI.lib 添加到代码项目中,同时在所有调用 PSAPI API 的模块中包含 PSAPI.h 文件。记住一定要随可执行文件一起分发 PSAPI.DLL,因为它不随 Windows NT 一起分发。你可以点击这里单独下载 PSAPI.DLL 的可分发版本(不用完全下载 Platform SDK)。


与 ToolHelp32 一样,PSAPI 库也包含各种各样有用的函数。由于篇幅所限,本文只讨论与枚举进程有关函数:EnumProcesses()、EnumProcessModules ()、GetModuleFileNameEx()和 GetModuleBaseName()。


创建进程列表的第一步是调用 EnumProcesses()。该函数的声明如下:

 

BOOL EnumProcesses( DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded );


EnumProcesses()带三个参数,DWORD 类型的数组指针 lpidProcess;该数组的大小尺寸 cb;以及一个指向 DWORD 的指针 cbNeeded,它接收返回数据的长度。DWORD 数组用于保存当前运行的进程 IDs。cbNeeded 返回数组所用的内存大小。下面算式可以得出返回了多少进程:nReturned = cbNeeded / sizeof(DWORD)。


注意:虽然文档将返回的 DWORD 命名为“cbNeeded”,实际上是没有办法知道到底要传多大的数组的。EnumProcesses()根本不会在 cbNeeded 中返回一个大于 cb 参数传递的数组值。结果,唯一确保 EnumProcesses()函数成功的方法是分配一个 DWORD 数组,并且,如果返回的 cbNeeded 等于 cb,分配一个较大的数组,并不停地尝试直到 cbNeeded 小于 cb
现在,你获得了一个数组,其元素保存着系统中每个进程的ID。如果你要想获取进程名,那么你必须首先获取一个句柄。要想从进程 ID 得到句柄,就得调用 OpenProcess()。


一旦有了句柄,则需要得到该进程的第一个模块。为此调用 EnumProcessModules() API:


EnumProcessModules( hProcess, &hModule, sizeof(hModule), &cbReturned );


调用之后,hModule 变量中保存的将是进程中的第一个模块。记住进程其实没有名字,但进程的第一个模块既是该进程的可执行模块。现在你可以用 hModule 中返回的模块句柄调用 GetModuleFileNameEx() 或 GetModuleBaseName() API 函数获取全路径名,或者仅仅是进程可执行模块名。两个函数均带四个参数:进程句柄,模块句柄,返回名字的缓冲指针以及缓冲大小尺寸。
用 EnumProcesses() API 返回的每一个进程 ID 重复这个调用过程,你便可以创建 Windows NT 的进程列表。


16位进程的处理方法

在 Windows 95,Windows 98 和 Windows ME 中,ToolHelp32 对待16位程序一视同仁,它们与 Win32 程序一样有自己的进程IDs。但是在 Windows NT,Windows 2000 或 Windows XP 中情况并不是这样。在这些操作系统中,16位程序运行在所谓的 VDM 当中(也就是DOS机)。
为了在 Windows NT,Windows 2000 和 Windows XP 中枚举16位程序,你必须使用一个名为 VDMEnumTaskWOWEx()的函数。在源代码模块中必须包含 VDMDBG.h,并且 VDMDBG.lib 文件必须与项目链接。这两个文件都在 Platform SDK 中。该函数的声明如下:
INT WINAPI VDMEnumTaskWOWEx( DWORD dwProcessId, TASKENUMPROCEX fp,LPARAM lparam );
  此处 dwProcessId 是 NTVDM 中拟枚举的16位任务进程标示符。参数 fp 是回调枚举函数的指针。参数 lparam 是用户定义的值,它被传递到枚举函数。枚举函数应该被定义成如下这样:


BOOL WINAPI Enum16( DWORD dwThreadId,

WORD hMod16, 

WORD hTask16, 

PSZ pszModName,

 PSZ pszFileName,

LPARAM lpUserDefined );


该函数针对每个运行在 NTVDM 进程中的16位任务调用一次,NTVDM 进程ID将被传入 VDMEnumTaskWOWEx()。如果想继续枚举则返回 FALSE,终止枚举则返回 TRUE。注意这是与 EnumWindows()相对的。


关于代码

本文附带的代码例子将 PSAPI 和 ToolHelp32 封装到一个名为 EnumProcs() 的函数中。该函数的工作原理类似 EnumWindows(),有一个指向回调函数的指针,并要对该函数进行重复调用,针对系统中的每个进程调用一次。另一个参数是用户定义的 lParam。下面是该函数的声明:
BOOL WINAPI EnumProcs( PROCENUMPROC lpProc, LPARAM lParam );
使用该函数时,要象下面这样声明回调函数:

 

BOOL CALLBACK Proc( DWORD dw, WORD w16, LPCSTR lpstr, LPARAM lParam );


  参数 dw 包含 ID,“w16”是16位任务的任务号,如果为32位进程则为0(在 Windows 95 中总是0),参数lpstr 指向文件名,lParam 是用户定义的,要被传入 EnumProcs()。


EnumProcs() 函数通过显示链接使用 ToolHelp32 和 PSAPI,而非通常所用的隐式链接。之所以要这样做,主要是为了让代码能够在二进制一级兼容,从可以在所有 Win32 操作系统平台上运行。

 

 

 

 

相关代码例子:

一、使用PSAPI:



 

 

 

 

 

二、使用WtsApi32:



 

相关连接:(四种方法实现VC枚举系统当前进程 )






Modified At 2008-05-02 10:49:13

LIB与DLL的区别

1.dll是一个可执行文件,而lib则是对dll中引出函数提供索引地址功能的文件。在隐式链接dll(静态)的时候,linker可以根据lib文件提供的索引地址进行链接。动态是指在运行中在需要的时候加载dll文件,能过查询dll的导出函数地址来调用的dll中的函数,典型的调用为LoadLibrary,QueryProcAddress,FreeLibrary.


所以,lib文件主要是提供给linker进行静态连接使用的。而dll则存放程序真正的可执行的代码。无论动态还是静态。dll都是必须的,而动态调用下,lib不是必须的!

 

2.上面提到的把自己的代码编译成lib+.h的方式提供给别人是静态链接,这里需要声明一下概念的问题,程序进行链接的时候,lib中是否包含执行代码要看他的类型:


在静态库情况下,函数和数据被编译进一个二进制文件(通常扩展名为*.LIB),编译器在处理程序代码时将从静态库中恢复这些函数和数据并把他们和应用程序中的其他模块组合在一起生成可执行文件。这个过程称为”静态链接”,此时因为应用程序所需的全部内容都是从库中复制了出来,所以静态库本身并不需要与可执行文件一起发行。


在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。





Modified At 2008-04-25 21:29:10

如何破解Skin++

[点评:值得注意的是这里的破解方法还是比较独特的.程序使用线程,一旦发现他的提示对话框则关闭掉,不让他出现,蛮有意思的.]


1.先到 www.uipower.com下载skin++的试用版本.其实就三个文件

 

         SkinPPWTL.h

         SkinPPWTL.lib

         SkinPPWTL.dll

 

我就不长篇大论叽叽歪歪了,写程序的一看这三个文件就明白是干什么的.下面分析一下如何将试用版本变为正式版本.Skin++在换肤的时候需要调用 skinppLoadSkin函数.该函数负责完成皮肤的切换并弹出一个注册对话框来让你注册.其实只要把这个讨厌的注册对话框干掉就一切OK了.嗯,想法不错,下面就动手吧….

 

2.在App里面封装几个函数,或者你挺懒的话可以直接把下面几个函数拷过去,改改.



一看就明白了.其中m_strPath是程序路径,在App的InitInstance函数里面得到就行.



 

3.在你工程的InitInstance里面调用 theApp.SetSkin(皮肤文件);可以看到没有弹出Skin++的注册窗口,这样就可以正常使用了。

 

另:网上有相关破解的DLL下载,本站Resource有下载   ——   SkinPPWTL.dll





Modified At 2008-04-25 21:41:24

CButton按钮设计相关

virtual void CButton::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct );
 
Parameters:
lpDrawItemStruct
A long pointer to a DRAWITEMSTRUCT structure. The structure contains information about the item to be drawn and the type of drawing required.
 
Remarks:
Called by the framework when a visual aspect of an owner-drawn button has changed. An owner-drawn button has the BS_OWNERDRAW style set. Override this member function to implement drawing for an owner-drawn CButton object. The application should restore all graphics device interface (GDI) objects selected for the display context supplied in lpDrawItemStruct before the member function terminates.
 
The DRAWITEMSTRUCT structure has the following form:
 
typedef struct tagDRAWITEMSTRUCT {
    UINT   CtlType;
    UINT   CtlID;
    UINT   itemID;
    UINT   itemAction;
    UINT   itemState;
    HWND   hwndItem;
    HDC    hDC;
    RECT   rcItem;
    DWORD  itemData;
} DRAWITEMSTRUCT;
 
The DRAWITEMSTRUCT structure provides information the owner window must have to determine how to paint an owner-drawn control or menu item. The owner window of the owner-drawn control or menu item receives a pointer to this structure as the lParam parameter of the WM_DRAWITEM message.
 
CtlType
The control type. The values for control types are as follows:
ODT_BUTTON   Owner-drawn button
ODT_COMBOBOX   Owner-drawn combo box
ODT_LISTBOX   Owner-drawn list box
ODT_MENU   Owner-drawn menu
ODT_LISTVIEW   List view control
ODT_STATIC   Owner-drawn static control
ODT_TAB   Tab control
 
CtlID
The control ID for a combo box, list box, or button. This member is not used for a menu.
 
 
itemID
The menu-item ID for a menu or the index of the item in a list box or combo box. For an empty list box or combo box, this member is a negative value, which allows the application to draw only the focus rectangle at the coordinates specified by the rcItem member even though there are no items in the control. The user can thus be shown whether the list box or combo box has the input focus. The setting of the bits in the itemAction member determines whether the rectangle is to be drawn as though the list box or combo box has input focus.
 
 
itemAction
Defines the drawing action required. This will be one or more of the following bits:
ODA_DRAWENTIRE   This bit is set when the entire control needs to be drawn.
ODA_FOCUS   This bit is set when the control gains or loses input focus. The itemState member should be checked to determine whether the control has focus.
ODA_SELECT   This bit is set when only the selection status has changed. The itemState member should be checked to determine the new selection state.
 
itemState
Specifies the visual state of the item after the current drawing action takes place. That is, if a menu item is to be dimmed, the state flag ODS_GRAYED will be set. The state flags are as follows:
ODS_CHECKED   This bit is set if the menu item is to be checked. This bit is used only in a menu.
ODS_DISABLED   This bit is set if the item is to be drawn as disabled.
ODS_FOCUS   This bit is set if the item has input focus.
ODS_GRAYED   This bit is set if the item is to be dimmed. This bit is used only in a menu.
ODS_SELECTED   This bit is set if the item’s status is selected.
ODS_COMBOBOXEDIT   The drawing takes place in the selection field (edit control) of an ownerdrawn combo box.
ODS_DEFAULT   The item is the default item.
 
hwndItem
Specifies the window handle of the control for combo boxes, list boxes, and buttons. Specifies the handle of the menu (HMENU) that contains the item for menus.
 
 
hDC
Identifies a device context. This device context must be used when performing drawing operations on the control.
 
 
rcItem
A rectangle in the device context specified by the hDC member that defines the boundaries of the control to be drawn. Windows automatically clips anything the owner draws in the device context for combo boxes, list boxes, and buttons, but it does not clip menu items. When drawing menu items, the owner must not draw outside the boundaries of the rectangle defined by the rcItem member.
 
 
itemData
For a combo box or list box, this member contains the value that was passed to the list box by one of the following:
 
 
 
 
 
 
 
 
The GetSysColor function retrieves the current color of the specified display element. Display elements are the parts of a window and the display that appear on the system display screen.
 
 
DWORD GetSysColor(
  int nIndex   // display element
);
 
Parameters:
nIndex
Specifies the display element whose color is to be retrieved. This parameter must be one of the following values:
COLOR_3DDKSHADOW      
Dark shadow for three-dimensional display elements.
COLOR_3DFACE, COLOR_BTNFACE         
Face color for three-dimensional display elements.
COLOR_3DHILIGHT, COLOR_3DHIGHLIGHT, COLOR_BTNHILIGHT, COLOR_BTNHIGHLIGHT    
Highlight color for three-dimensional display elements (for edges facing the light source.)
COLOR_3DLIGHT          
Light color for three-dimensional display elements (for edges facing the light source.)
COLOR_3DSHADOW, COLOR_BTNSHADOW       
Shadow color for three-dimensional display elements (for edges facing away from the light source).
COLOR_ACTIVEBORDER    
Active window border.
COLOR_ACTIVECAPTION   
Active window title bar.
Windows NT 5.0 and Windows 98: Specifies the left side color in the color gradient of an active window’s title bar if the gradient effect is enabled.
 
COLOR_APPWORKSPACE
Background color of multiple document interface (MDI) applications.
 
COLOR_BACKGROUND, COLOR_DESKTOP
Desktop.
COLOR_BTNTEXT
Text on push buttons.
COLOR_CAPTIONTEXT
Text in caption, size box, and scroll bar arrow box.
COLOR_GRADIENTACTIVECAPTION
Windows NT 5.0 and Windows 98: Right side color in the color gradient of an active window’s title bar. COLOR_ACTIVECAPTION specifies the left side color. Use SPI_GETGRADIENTCAPTIONS with the SystemParametersInfo function to determine whether the gradient effect is enabled.
COLOR_GRADIENTINACTIVECAPTION
Windows NT 5.0 and Windows 98: Right side color in the color gradient of an inactive window’s title bar. COLOR_INACTIVECAPTION specifies the left side color. 
COLOR_GRAYTEXT
Grayed (disabled) text. This color is set to 0 if the current display driver does not support a solid gray color.
COLOR_HIGHLIGHT Item(s)
selected in a control.
COLOR_HIGHLIGHTTEXT
Text of item(s) selected in a control.
COLOR_HOTLIGHT
Windows NT 5.0 and Windows 98: Color for a hot-tracked item. Single clicking a hot-tracked item executes the item.
COLOR_INACTIVEBORDER
Inactive window border.
COLOR_INACTIVECAPTION
Inactive window caption.
Windows NT 5.0 and Windows 98: Specifies the left side color in the color gradient of an inactive window’s title bar if the gradient effect is enabled.
 
COLOR_INACTIVECAPTIONTEXT
Color of text in an inactive caption.
COLOR_INFOBK
Background color for tooltip controls.
COLOR_INFOTEXT
Text color for tooltip controls.
COLOR_MENU
Menu background.
COLOR_MENUTEXT
Text in menus.
COLOR_SCROLLBAR
Scroll bar gray area.
COLOR_WINDOW
Window background.
COLOR_WINDOWFRAME
Window frame.
COLOR_WINDOWTEXT
Text in windows.
 
Return Values:
The return value is the red, green, blue (RGB) color value that specifies the color of the given element.
If the function fails, the return value is zero. To get extended error information, callGetLastError.