Readfile vs include

It is not often that you can write a PHP script that does not need to include the contents of different files as part of it’s output. If these includes happen to be php scripts themselves you have no choice but to use require or include. However more often than not, the contents are static, usually html template component. With static includes you have many more options available. aerwear We will analyse some of these functions to find out which one is most suitable when dealing with files with static content. We use the term function loosely, because require and include are not real functions but language constructs.

 

























Function

Brief Description

string file_get_contents ( string filename [, int use_include_path])

Reads entire file into a string

int fpassthru ( resource handle)

Output all remaining data on a file pointer

string fgets ( resource handle [, int length])

Gets line from file pointer

array file ( string filename [, int use_include_path])

Reads entire file into an array

require(string filename)

include(string filename)

require_once(string filename)

include_once(string filename)

includes and evaluates the specific file.

int readfile ( string filename [, int use_include_path])

Outputs a file


We will now attempt to ‘include’ the contents of a 1 megabyte file into the output produced by our php script. How you can generate files of specific sizes is described elsewhere. The execution times and peak memory consumption, as reported by xdebug have been tabulated below.

We compensate for file caching and background processes by executing each script 4 times and taking the average (mean) of the result number 2-4. The first result is always rejected. Any result that appears to be outlier is rejected. The mean is rounded to 5 decimal places.

 












































Function

Sample Usage

Time (s)

Memory (b)

file_get_contents

echo file_get_contents($filename);

0.00564

1067856

fpassthru

fpassthru($fp);

0.00184

20032

fgets

$fp = fopen($filename,”rb”);

while(!feof($fp))

{

    echo fgets($fp);

}

0.07190

30768

file

echo join(“”,file($filename));

0.06464

2185624

require_once

require_once($filename);

0.08065

2067696

include

include($filename);

0.08202

2067696

readfile

readfile($filename);

0.00191

19208


What’s obvious from these results is that using fpassthru is far superior to all other methods. What’s not so obvious is that fpassthru and readfile are equally good. The fpassthru version runs 0.00007 seconds quicker than the readfile version. What that really means is that you need to run the script at least 100000 times to make significant saving. On memory consumption readfile seems to have use up around 1kb less than passthru. A kilo byte is a drop in the ocean for modern web servers with hundreds of megabytes if not gigabytes of memory.

 

The only conclusion that can be drawn from these studies is that fpassthru and readfile are equally good if you wish to include static content as part of the script’s output.

 

Before you rush off to change all your includes and requires into readfiles or fpassthrus let’s run the same test with a smaller (32Kb file). 32Kb is a more realistic size for an included file.

 
























































Function

Time (s)

Memory (b)

 

32Kb File

1Mb File

32Kb File

1Mb File

file_get_contents

0.00152

0.00564

52480

1067856

fpassthru

0.00117

0.00184

20016

20032

fgets

0.00195

0.07190

30760

30768

file

0.00157

0.06464

87344

2185624

require_once

0.00225

0.08065

67992

2067696

include

0.00222

0.08202

67928

2067624

readfile

0.00117

0.00191

19192

19208


readfile and fpassthru have once again tied for first place. This new set of results just confirms the fact that speed and scalability comes from your design and not from your code. The difference between the best performance and the worst is just 0.00108s too close to call.

 

The most significant feature of these results is that both fpassthru and readfile scale really well. In other words, memory consumption and execution time does not increase significantly with increase in file size. That does not always mean your script will be faster just because you use these functions instead of require or include.

 

 


This page was last modified 10:56, 3 Aug 2006.




Modified At 2008-05-23 23:16:26

编写可移植C/C++程序的要点

    分层设计,隔离平台相关的代码。就像可测试性一样,可移植性也要从设计抓起。一般来说,最上层和最下层都不具有良好的可移植性。最上层是GUI,大多数GUI都不是跨平台的,如Win32 SDK和MFC。最下层是操作系统…
 
    1.分层设计,隔离平台相关的代码。就像可测试性一样,可移植性也要从设计抓起。一般来说,最上层和最下层都不具有良好的可移植性。最上层是GUI,大多数GUI都不是跨平台的,如Win32 SDK和MFC。最下层是操作系统API,大多部分操作系统API都是专用的。
 
  如果这两层的代码散布在整个软件中,那么这个软件的可植性将非常的差,这是不言自明的。那么如何避免这种情况呢?当然是分层设计了:
 
  最底层采用Adapter模式,把不同操作系统的API封装成一套统一的接口。至于封装成类还是封装成函数,要看你采用的C还是C++写的程序了。这看起来很简单,其实不尽然(看完整篇文章后你会明白的),它将耗去你大量的时间去编写代码,去测试它们。采用现存的程序库,是明智的做法,有很多这样的库,比如,C库有glib(GNOME的基础类),C++库有ACE(ADAPTIVE Communication Environment)等等,在开发第一个平台时就采用这些库,可以大大减少移植的工作量。
 
  最上层采用MVC模型,分离界面表现与内部逻辑代码。把大部分代码放到内部逻辑里面,界面仅仅是显示和接收输入,即使要换一套GUI,工作量也不大。这同时也是提高可测试性的手段之一,当然还有其它一些附加好处。所以即使你采用QT或者GTK+等跨平台的GUI设计软件界面,分离界面表现与内部逻辑也是非常有用的。
 
  若做到了以上两点,程序的可移植性基本上有保障了,其它的只是技术细节问题。
 
  2.事先熟悉各目标平台,合理抽象底层功能。这一点是建立在分层设计之上的,大多数底层函数,像线程、同步机制和IPC机制等等,不同平台提供的函数,几乎是一一对应的,封装这些函数很简单,实现Adapter的工作几乎只是体力活。然而,对于一些比较特殊的应用,如图形组件本身,就拿GTK+ 来说吧,基于X Window的功能和基于Win32的功能,两者差巨大,除了窗口、事件等基本概念外,几乎没有什么相同的,如果不事先了解各个平台的特性,在设计时就精心考虑的话,抽象出来的抽口在另外一个平台几乎无法实现。
 
  3.尽量使用标准C/C++函数。大多数平台都会实现POSIX(Portable Operating System Interface)规定的函数,但这些函数较原生(Native) 函数来说,性能上的表现可能较次一些,用起来也不如原生函数方便。但是,最好不要贪图这种便宜而使用原生函数函数,否则搬起的石头最终会轧到自己的脚。比如,文件操作就用fopen之类的函数,而不要用CreateFile之类的函数等。
 
  4.尽量不要使用C/C++新标准里出现的特性。并不是所有的编译器都支持这些特性,像VC就不支持C99里面要求的可变参数的宏,VC对一些模板特性的支持也不全面。为了安全起见,这方面不要太激进了。
 
  5.尽量不要使用C/C++标准里没有明确规定的特性。比如你有多个动态库,每个动态库都有全局对象,而且这些全局对象的构造还有依赖关系,那你迟早会遇到麻烦的,这些全局对象构造的先后顺序在标准里是没有规定的。在一个平台上运行正确,在另外一个平台上可能莫明其妙的死机,最终还是要对程序作大量修改。
    6.尽量不要使用准标准函数。有些函数大多数平台上都有,它们使用得太广泛了,以至于大家都把它们当成标准了,比如atoi(把字符串转换成整数)、strdup(克隆字符串)、alloca(在栈分配自动内存)等等。不怕一万,就怕万一,除非明白你在做什么,否则还是别碰它们为好。
 
  7.注意标准函数的细节。也许你不相信,即使是标准函数,抛开内部实现不论,就其外在表现的差异也有时令人惊讶。这里略举几个例子:
 
  int accept(int s, struct sockaddr *addr, socklen_t *addrlen);addr/ addrlen本来是输出参数,如果是C++程序员,不管怎么样,你已经习惯于初始化所有的变量,不会有问题。如果是C程序员,就难说了,若没有初始化它们,程序可能莫名其妙的crash,而你做梦也怀疑不到它头它。这在Win32下没问题,在Linux下才会出现。
 
  int snprintf(char *str, size_t size, const char *format, ……);第二个参数size,在Win32下不包括空字符在内,在Linux下包括空字符,这一个字符的差异,也可能让你耗上几个小时。
 
  int stat(const char *file_name, struct stat *buf);这个函数本身没有问题,问题出在结构stat上,st_ctime在Win32下代表创建(create)时间,在Linux下代表最后修改 (change)时间。
 
  FILE *fopen(const char *path, const char *mode);在读取二进制文件,没有什么问题。在读取文本文件可要小心,Win32下自动预处理,读出来的内容与文件实际都长度不一样,在Linux则没有问题。
 
  8.小心数据标准数据类型。不少人已经吃过int类型由16位转变成32位带来的苦头,这已经是陈年往事了,这里且不谈。你可知道char在有的系统上是有符号的,在有的系统是无符号的吗?你可知道wchar_t在Win32下是16位的,在Linux 下是32位的吗?你可知道有符号的1bit的位域,取值是0和-1而不是0和1吗?这些貌合神离的东东,端的是神出鬼没,一不小心着了它的道。
 
  9.最好不要使用平台独有的特性。比如Win32下DLL可以提供一个DllMain函数,在特定的时间,操作系统的Loader会自动调用这个函数。这类功能很好用,但最好不要用,目标平台可不能保证有这种功能。
 
  10.最好不要使用编译器特有的特性。现代的编译器都做很人性化,考虑得很周到,一些功能用起非常方便。像在VC里,你要实现线程局部存储,你都不调用TlsGetValue /Tls TlsSetValue之类的函数,在变量前加一个__declspec( thread )就行了,然而尽管在pthread里有类似的功能,却不能按这种方式实现,所以无法移植到Linux下。同样gcc也有很多扩展,是在VC或者其它编译器里所没有的。
 
  11.注意平台的特性。比如:
 
  在Win32下的DLL里面,除非明确指明为export的函数外,其它函数对外都是不可见的。而在Linux下,所有的非static的全局变量和函数,对外全部是可见的。这要特别小心,同名函数引起的问题,让你查上两天也不为过。
 
  目录分隔符,在Win32下用‘\\’,在Linux下用‘/’。
 
  文本文件换行符,在Win32下用‘\r\n’,在Linux下用‘\n’,在MacOS下用‘\r’。
 
  字节顺序(大端/小端),不同硬件平台的字节顺序可能不一样。
 
  字节对齐,在有的平台(如x86)上,字节不对齐,无非速度慢一点,而有的平台(如arm)上,它完全用错误的方式去读取数据,而且不会给你一点提示。若出问题,可能让你一点头绪都没有。
 
  12.最好清楚不同平台的资源限制。想必你还记得DOS下同时打开的文件个数限制在几十个的情形吧,如今操作系统的功能已经强大多了,但是并非没有限制。比如Linux下的共享内存默认的最大值是4M。若你对目标平台常见的资源限制了然于胸,可能有很大的帮助,一些问题很容易定位
 

关于VS2008中的一个编译错误的解决办法

     VC2008中,使用了很多标准的C函数,比如fopen,strcpy,strcat之类的。编译时会出现警告,比如这个:


     d:\xxxx.c(1893) : warning C4996: “strcpy”被声明为否决的

 

     紧接着IDE有提示说:“This function or variable may be unsafe. Consider using strcpy_s instead… 可是我的代码只是借VS2008的IDE环境开发而已,以后是要移植到别的ANSI C平台的,所以不想用strcpy_s,舍弃此法。

 

    接着看提示, “To disable deprecation, use _CRT_SECURE_NO_DEPRECATE“。原来加个编译选项就可以了。于是按 Alt+F7打开项目属性,找到“配置属性” -> “C/C++” -> “命令行”,在右侧的“附加选项”文本编辑框里加入 【/D “_CRT_SECURE_NO_DEPRECATE” 】 (注意加入的是【】符号里面的文本),点确定。再次编译,就没有这种警告了。

关于获取进程信息(完整+经验+总结)

最近写了一个进程管理的程序,下面是对这次编程的总结和笔记:
、关于进程的枚举获取:
距我所知,枚举进程总共有4种方法,也包括四种不同的库,我采用用了前面的三种来获取进程的详细信息,当然是对自己关心的信息。
它们分别采用了:
包含库: psapi.lib WtsApi32.lib;
头文件: #include <TLHELP32.H>,#include <WtsApi32.h>,#include <psapi.h>;
 
还有一种方法是由ntdll.dll提供的函数来获取进程信息的,我没有采用,因为前面的三种方法一起能获取很多的进程信息了,对我来说已经够了。
另外为了方便我也写了一个进程管理的类,把相应的功能封装在一起。
主要的技术有:
1.权限的提升:
 

 
 
其中涉及到几个API函数,是在编写系统相关函数常用到的,看代码应该能理解了。
 
2.为了获得自己感兴趣的信息,定义了两个结构体,把他们分开也是为了方便管理,因为有的信息不可能是同一块获得的。
 

 
 
获得相关信息的代码如下:
 

 
其中大量使用了动态分配内存,主要是为了尽可能地有效利用内存,经过两天的测试,这个类没有任何内存泄露。
 
3.动态分配内存的释放:
 

 
 
其中内存是分别释放的,我也没办法,而且这里必须注意内存释放问题:
如果结构体本身是new出来的,但是里面的内容也含有new出来的数据、指针什么的,必须先将里面的释放完了再把结构体所占的内存释放掉!!
 
4.由于NT遗留下来的问题,进程的路径获得中会出现一些小问题:
 

 
 
5.通过进程ID获得进程窗口的句柄:
 

6.获得进程的命令行:


 
7.获得进程所调用的模块信息:
 

 
8.获得创建进程的用户名:
 

 
以上是获得进程信息的一些技术,下面是其他的一些技术,有的是我的原创。
 
 
 
二、关于TabCtrl的另类利用:
 
上面是实现后的效果,按钮放在左边,按不同的按钮出现不同的对话框在右面,有人说那是OutLook风格,我不知道,总之这样看起来比属性表更好看,管理起来也更方面,相关的代码如下。
 

 
其中CMyTabSheet是从管理控件TabCtrl的类派生的,其实那个管理对话框的TabCtrl控件被前面的对话框遮住了,呵呵。
三、关于弹出菜单:

 
 
弹出菜单的三个三钮分别对应三个功能:复制文件的路径到剪贴板、打开文件所在的文件夹并选中文件、关掉进程。
其中复制文件的路径到剪贴板的代码如下:
 

 
关掉进程的代码为:

 
其实这种关进程的方法不太好,因为这是强制行的,很容易造成数据丢失以及其他问题,有时间的话我会改进一下。
 
打开文件所在的文件夹并选中文件的代码如下:
 

 
四、在列表框中显示数据的代码如下,我觉得有必要写下来,其中最重要的是图标的输出问题:
 

 
五、另外还定义了一些全局的函数:
 

 
就写这么多笔记了,如果还有什么的我下次再写,我想写的一个小软件还没完成,进程管理只是其中的一个部分。
另外我还写了一个获取文件版本信息的类,恩,主要的一些代码如下:
 

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

一般认为在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.