nati"l ION_MODI兀ED、nLE—ACllON RENAMED_OLD—NAME来区veAPI可以译做“原生API”就是最直接的API像fopen,CreC++里的函数可以用CreateFileateFile这些函数,其实是封装过的API,他们最终都调用了NtCreateFile。NtCreateFile这样的函数就是nativeAPI
createfile函数_createfont函数
createfile函数_createfont函数
createfile函数_createfont函数
createfile函数_createfont函数
用360系统修复,我刚刚就误打误撞修好了……
win7对安全的管理比较严格,可能你存取的位置有限制,你先把他放到C:盘你的帐户中,我的文档的位置试试,或者在你所要创建的文件的位置,设置比较低的安全等级,比如ryone都能完全控制的权限
physicaldrive0表示本机的物理驱动器0。
一、由于""是C/C+中转义符, "."就相当于.在Windows中 . 前缀用于标识设备,其中的"."表示本地计算机。比如.PhysicalDrive0表示本机的物理驱动器0(一般是主硬盘),
.COM1表示本机的1号串行口。
二、comr01PhysicalDrive1表示网络中计算机comr01的物理驱动器1,可以用CreateFile函数打开.PhysicalDrive0,然后用ReadFile, WriteFile来直接读写本机硬盘扇区用DevIoControl来获取硬盘的信息(扇区数,磁头数,柱面数)等。亦可用CreateFile打开卷,如.C:即是C盘。
三、on Microsoft Press, 1999.OPEN_ALWAYS改成OPEN_EXISTING,在Vista/win7上用权限启动编译好的程序,或者以权限启动VS。
四、事实上.就相当于NT对象目录中的??目录其中包含了所有提供给Win32程序的设备对象,可用winobj来查看。
VC++中检测文件是否存在有很多种方法,比较通用的是GetFileAttributes函数,在控制台下增加#include
if(GetFileAttributes(FileName)<0)//==-1
{//文件不存在!
//存在这种方法是windows下使用的,并可以用于目录(文件夹)的判断(返值为FILE_ATTRIBUTE_DIRECTORY),不会因为文件访问权限等问题造成误判。
此外还有很多判断方法,各有优势和适用范围,需要注意的是,各个方法判断的“文件存在和不存在”可能包括同名目录、用户权限、文件夹不存在或禁止访问等问题,应该根据具体需求使用适合的方法:
1.
CFile::Open或OpenFile,无法以读取方式打开
2.
_access函数,通过判断文件是否能够访问,返回文件是否存在
3.
CFileFind类或FindFirstFile函数。这个也是很不错的方法,不但能够获取比较详细的文件存在信息,还能使用递归搜索子目录。
4.
使用Shell函数SHGetFileInfo判断文件是否存在,这个方法可以获取更完整的文件信息,不仅能判断是否存在,还能直接获取文件的shell信息,方便进一步处理。
vc++中检测文件是否存在有很多种方法,比较通用的是getfileattributes函数,在控制台下增加#include
,mfc和其它窗口程序下可以直接使用:
if(getfileattributes(filename)<0)//==-1
{//文件不存在!
//存在这种方法是windows下使用的,并可以用于目录(文件夹)的判断(返值为file_attribute_directory),不会因为文件访问权限等问题造成误判。
此外还有很多判断方法,各有优势和适用范围,需要注意的是,各个方法判断的“文件存在和不存在”可能包括同名目录、用户权限、文件夹不存在或禁止访问等问题,应该根据具体需求使用适合的方法:
1.
cfile::open或openfile,无法以读取方式打开
2.
_access函数,通过判断文件是否能够访问,返回文件是否存在
3.
cfilefind类或findfirstfile函数。这个也是很不错的方法,不但能够获取比较详细的文件存在信息,还能使用递归搜索子目录。
4.
使用shell函数shgetfile判断文件是否存在,这个方法可以获取更完整的文件信息,不仅能判断是否存在,还能直接获取文件的shell信息,方便进一步处理。
给你一个函数:
#include
//文件是否存在
bool FileExist(const char FileName)
return (stat(FileName, &my_stat) == 0);
}这样调用:
if(FileExist("这里写文件名"))
{//这里写如果文件存在的处理代码
}
VC++ 判断文件是否存在的方法有:
1. 使用_access函数,函数原型为:
int _access( const char path, int mode );
2. 使用CreateFile函数,函数原型为:
HANDLE CreateFile( LPCTSTR lpFileName, //pointer to name of the file DWORD dwDesiredAccess, // access (readwrite) modeDWORD dwShareMode, // share mode LPSECURITY_ATTRIBUTESlpSecurityAttributes, // pointer to security attributes DWORD dwCreationDisition, //how to create DWORD dwFlagsAndAttributes, // file attributes HANDLE hTemplateFile //handle to file with attributes to // copy );
3. 使用FindFirstFile函数,函数原型为:
HANDLE FindFirstFile( LPCTSTR lpFileName, //pointer to name of file to search for LPWIN32_FIND_DATA lpFindFileData // pointer to returned rmation );
4. 使用GetFileAttributes函数,函数原型如下:
DWORD GetFileAttributes( LPCTSTRlpFileName // pointer to the name of a file or directory );
5. 使用Shell Lightweight Utility APIs函数。
PathFileExists()专门判断文件和目录时否存在的函数文件名可读性比较强还可以判断目录是否存在Header: Declared in Shlwapi.h ,Import Library: Shlwapi.lib 。
方法1:
#include
#include
#include
void main( void ) {
if( (_access( "D:a.txt", 0 )) != -1 ) {
printf( "File ACCESS.C existsn" );
if( (_access( "ACCESS.C", 2 )) != -1 )
printf( "File ACCESS.C has write permissionn" ); } }
方法2:
if (INVALID_HANDLE_VALUE != CreateFile("D:a.txt", GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) {
AfxMessageBox("File A{struct stat my_stat;CCESS.C existsn"); }
方法3:
#define _WIN32_WINNT 0x0400
#include
#include
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
printf ("Target file is %s.n", argv[1]);
hFind = FindFirstFile(argv[1], &FindFileData);
if (hFind == INVALID_HANDLE_VALUE) {
printf ("Invalid File Handle. GetLastError reports %dn", GetLastError ()); return (0); }
else {
printf ("The first file found is %sn", FindFileData.cFileName); FindClose(hFind);
return (1); } }
方法4:
if (GetFileAttributes("c:1.txt") == -1)
MessageBox(0."Invalid File ","hehe",0)
else
MessageBox(0."The first file found ","haha",0)
方法5:
if (INVALID_HANDLE_VALUE != CreateFile("D:a.txt", 0, 0, NULL, OPEN_EXISTING, 0, NULL)) {
AfxMessageBox("File existsn");
}
用于串口通信的函数和结构在Winbase.h头文件中定义。
函数 描述
CreateFile 打开串行口
GetCommState 用指定通信设备的当前控制设置填充设备控制块(DCB结构)
SetCommState 按照DCB结构的说明配置通信设备。这个函数重新初始化所有硬件
和控制设备,但不清空I/O队列
GetCommTimeouts 获得指定通信设备上所有读/写作的超时参数
SetCommTimeouts 设置指定通信设备上所有读/写作的超时参数
WriteFile 向串行口写数据,这样将把数据传送给串行连接另一端的设备
ReadFile 从串行口读数据,这样将从串行连接另一端的设备接收数据
SetCommMask 指定为通信设备监视的一组
GetCommMask 获得指定通信设备的掩码值
WaitCommEvent 等待指定通信设备的的发生。WaitCommEvent函数监视的
包含在与设备句柄相关联的掩码中
EscapeCommFunction 指导指定通信设备执行扩展功能。通常用于将串行口设置为IR模式
ClearCommBreak 恢复指定通信设备的字符传输,并设置传输线路为不可中断状态
ClearCommError 获得通信错误数据,并报告指定通信设备的当前状态
打开端口
CreateFile函数用于打开串行口,因为硬件供应商和设备驱动程序开发者可以随意命名端口,所以应用程序应该列出所有可用端口,从而使用户能够指定要打开的端口。如果端口不存在,则CreateFile函数返回ERROR_FILE_NOT_FOUND,而且应该通知用户端口不可用。
打开串行口
1 在个参数lpzPortName指向的通信口后插入一个冒号。例如,指定“COM1:”为通信端口。
2 指定dwShareMode参数为0。通信端口不能像文件一样被共享。
3 在dwCreationDisition参数中指定OPEN_EXISTING。这个标志是必须的。
4 指定dwFlagsAndAttributes参数为0。Windows CE只支持非重叠I/0.
下面的代码段说明了如何打开串行通信端口。
hPort=CreateFile(lpszPortName, //指出通信端口
GENERIC_READ|GENERIC_WRITE, //读写模式
0, //共享模式
NULL, //安全属性
OPEN_EXISTING, //如何打开服务端口
NULL); //端口属性句柄的拷贝
配置串行口
打开串行口后,一般情况下,应用程序需要改变缺省设置。用GetCommState函数可以获得缺省设置,用SetCommState函数可以设置新的端口设置。
另外,端口配置还包括用COMMTIMEOUTS结构设置读/写作的超时值。当发生超时时,ReadFile或WriteFile函数返回成功传输的具体字符数。
配置串行口
DCB PortDCB;
PortDCB.DCBlength=sizeof(DCB);
GetCommState(hPort,&PortDCB);
PortDCB.BaudRate=9600; //波特率
PortDCB.fBinary=TRUE; //只支持二进制串行传输模式
PortDCB.fParity=TRUE; //启用奇偶校验
PortDCB.fOutxCtsFlow=FALSE; //TRUE是由CTS线来控制端口的输出
PortDCB.fOutxDsrFlow=FALSE; //TRUE是由DSR线来控制端口的输出
PortDCB.fDtrControl=DTR_CONTROL_ENABLE; //DTR_CONTROL_DISABLE:
禁用DTR(Data Terminal Ready)线并保持此状态;
DTR_CONTROL_ENABLE;
启用DTR(Data Terminal Ready)线
DTR_CONTROL_HANDSHAKE
根据接收缓冲区数据的数量告诉串行驱动程序切换DTR线状态
PortDCB.fDsrSensitivity=FALSE; //TRUE为端口将忽略任何输入的字节,除非端口DSR线被启用
PortDCB.fTXContinueOnXoff=TRUE; //TRUE为如接收缓冲区已满且驱动程序已传送XOFF字符,
将使驱动程序停止传输字符
PortDCB.fOutX=FALSE; //TRUE为指定XON/XOFF控制被用于控制串行输出
PortDCB.fInX=FALSE; //TRUE为指定XON/XOFF控制由输入串行流使用
PortDCB.fErrorChar=FALSE; //Windows CE串行驱动程序默认忽略该字段
PortDCB.fNull=FALSE; //TRUE为使串行驱动程序忽略接收到的空字节
PortDCB.fRtsControl=RTS_CONTROL_ENABLE; //RTS_CONTROL_DISABLE表示当端口打开时RTS(Request and Send)行
将禁用
//RTS_CONTROL_ENABLE表示当端口打开时RTS行将启用
//RTS_CONTROL_HANDSHAKE表示RTS线由驱动程序控制,输入缓冲区
不到半满,则RTS线将被启用,否则将被禁用
//RTS_CONTROL_TOGGLE表示如果在输出缓冲区有字节要被传输,则
驱动程序将启用RTS线,否则将禁用该线
PortDCB.fAbortOnError=FALSE; //出现读/写错误并不终止
PortDCB.ByteSize=8; //指定每字节的位数
PortDCB.Parity=NOPARITY; //奇偶字段,EVENPARITY、MARKPARITY、NOPARITY、ODDPARITY、SPACERITY
PortDCB.StopBits=ONESTOPBIT; //停止位,每字节一位(ONESTOPBIT)、一位半(ONE5STOPBITS)、
两位(TWOSTOPBITS)。
if(!SetCommState(hPort,&PortDCB))
{MessageBox(hMainWnd,TEXT("Unable to configure the serial port"),TEXT("Error"),MB_OK);
dwError=GetLastError();
return FALSE;
每次打开通信端口时应用程序都必须用COMMTIMEOUTS结构设置通信超时值。如果不配置这个结构,则端口使用驱动程序提供的缺省超时值,或者使用上一个通信应用程序的超时值。通过定特殊的与实际设置不同的超时设置,应用程序可以执行永远不能完成的读/写作,或者执行完成过于频繁的读/写作。
当读/写作发生超时时,作结束。而且ReadFile和WriteFile函数并不返回错误值。若想确定某个作是否发生了超时,可以验证实际传输的字节数是否小于请求的字节数。例如,如果ReadFile函数返回TRUE,但读的字节数小于请求的字节数,则这个作发生了超时。
配置串行口的超时值
1 调用GetCommTimeouts函数或者手工设置成员来初始化COMMTIMEOUTS结构。
2 用ReadIntervalTimeout成员指定在不发生超时的情况下两个字符间允许经过的毫秒数。
3 用ReadTotalTimeoutMultiplier成员指定读超时乘子。对于每个读作,这个乘子被乘以读作期望接收到的字节数。
4 用ReadTotalT√SeekimeoutConstant成员指定读超时常数。这个成员表示的毫秒数被加到读取的总字节数与 ReadTotalTimeoutMultiplier的乘积上。得到的结果是读作发生超时前必须经过的毫秒数。
5 用WriteTotalTimeoutMultiplier成员指定写超时乘子。对于每个写作,这个乘子被乘以写作期望接收的字节数。
6 用WriteTotalTimeoutConstant成员指定写超时常数。这个成员表示的毫秒数被加到总字节数与WriteTotalTimeoutMultiplier的 乘积上。得到的结果是写作发生超时前必须经过的毫秒数。
7 调用SetCommTimeouts函数激活端口超时设置。
为了有助于多任务处理,通常需要配置COMMTIMEOUTS结构,以便ReadFile函数能够立即返回读到的字符。要做到这一点,可以设置ReadIntervalTimeout为MAXWORD,设置ReadTotalTimeoutMultiplier和ReadTotalTimeoutMultiplier为0。
写串行口
因为Windows CE不支持重叠I/O(也称为异步I/O),所以主线程或者任何创建窗口的线程都不应该试图向串行口写大量数据。这样的线程将被阻塞,因而不能管理消息队列。应用程序可以通过创建多个线程处理读/写作以模拟重叠I/O。为了协调多个线程,应用程序可以调用WaitCommEvent函数阻塞线程,直至发生特定的通信。
写串行口
1 用CreateFile函数返回的句柄。
2 用lpBuffer参数指定要写的数据的指针。这一数据通常是二进制数据或者字符数据。
3 用nNumberOfBytesToWrite参数指定要写的字符数。对于基于Windows CE的设备,通常写一个字符,因为应用程序必须将Unicode 字符转换为ASCII字符,以便将文本传输到串行连接另一端的设备。
4 用lpNumberOfBytesWritten参数指定实际写的字节数的指针。WriteFile函数填充这个变量,以便应用程序能够确定数据是否已 被传输。
5 lpOverlapped参数必须是NULL。
读串行口
ReadFile函数从串行连接另一端的设备获取数据。参数与WriteFile相同。
通常情况下,读作是一个的线程,他总是随时准备处理到达串行口的数据。通信通知读线程串行口有数据可读。读线程通常一次读一个字节(每读一个字节调用一次ReadFile函数),直至读完所有数据,然后读线程等待下一个通信。
使用通信
通信是当发生重要时Windows CE向应用程序发送的通知:应用程序可以用WaitCommEvent函数阻塞线程,直至特定发生;用SetCommMask函数可以指定继续处理前必须发生的。如果指定了多个,则任何一个指定的发生将导致WaitCommEvent函数返回。
例如,这种机制使应用程序能够知道数据何时到达串行口。通过等待表示数据到达的通信,应用程序可以避免因调用ReadFile函数等待数据到达而阻塞串行口。只有当有数据可读时才应该调用ReadFile函数。
描述
EV_BREAK 输入中发生中断
EV_CTS CTS信号状态发生变化
EV_DSR DSR信号状态发生变化
EV_ERR 发生线路状态错误,线路状态错误包括CE_FRAME,CE_OVERRUN和CE_RXPARITY
EV_RING 检测到振铃指示
EV_R 接收线路信号检测的信号状态发生变化
EV_RXCHAR 接收到字符,并置于输入缓冲区中
EV_RXFLAG 接收到字符,并置于输入缓冲区中
EV_TXEMPTY 输出缓冲区中的一个字符已被发送
使用通信
1 调用SetCommMask函数指定要查找的。
2 调用WaitCommEvent函数,并指定导致这个函数返回的。当应用程序指定多个时,lpEvtMask参数指向表示导致 WaitCommEvent函数返回的的变量。
3 WaitCommEvent函数返回后,循环调用ReadFile函数,直至读完所有接收到的数据。
4 再次调用SetCommMask函数,指定要查找的。
SetCommMask函数通常是应用程序在监视串行口和读数据的循环中个调用的函数。下面的代码说明了如何将通信用于这个目的。
BYTE Byte;
DWORD dwBytesTransferred;
SetCommMask(hPort,EV_RXCHAR|EV_CTS|EV_DSR|EV_R|EV_RING);
while(hPort!=INVALID_HANDLE_VALUE)
{WaitCommEvent(hPort,&dwCommModemStatus,0);
SetCommMask(hPort,EV_RXCHAR|EV_CTS|EV_DSR|EV_RING);
if(dwCommModemStatus & EV_RXCHAR)
{do
{ReadFile(hPort,&Byte,1,&dwBytesTransferred,0);
if(dwBytesTransferred==1)
ProcessChar(Byte);
}while(dwBytesTransferred==1);
}}
关闭串行口
CloseHandle(hPort);
一.内核对象和地址空间
为了更好地理解本文后面的内容,在介绍内存映像文件之前我们先简单回顾一下Window
s中内核对象和地址空间的有关概念。在Windows中有各种内核对象,如、文件、进
程、旗语、互斥体等。内核对象是由系统内核分配管理的一段内存块,只有系统内核能
够直接访问这一段内核对象数据,而应用程序只能通过Windows提供的一系列函数按规定
的方式去作这些内核对象。我们通过调用有关Windows API函数创建内核对象(这些函
数通常带有Create前缀)。当我们调用一个函数创建内核对象的时候,该函数通常返回
一个标识该对象的句柄(HANDLE)。我们把标识该内核对象的句柄传递给有关windows函数
,告诉系统去作哪一个内核对象。内核对象属于系统内核而不是进程,内核对象可被
多个进程访问使用,也即可在多个进程间共享。每个内核对象自身有一个引用计数值,
它记录有多少进程使用该对象,当引用计数值为零时由系统负责销毁内核对象。在多个
进程间共享内核对象有三种方法:句柄继承、命名、句柄。在本文后面将要提到的
文件映像对象亦是内核对象。
在Windows中每一个进程都有自己私有的地址空间,对32位的进程来说地址空间的大小是
从0x00000000 到 0xFFFFFFFF共4GB范围。这个空间仅仅是内存地址的范围,而不是计算
机的物理存储RAM空间,因而又称为虚拟内存空间。使用某段虚拟内存空间需要经过保留
和提交两个过程,将物理存储分配映像到地址空间的该段区域上。物理存储不应理解为
RAM,实际上应理解为来自于磁盘上(通常为硬盘)的系统内存分页文件,在必要时(读
写访问时)其内容才会被系FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);统写到RAM中或从RAM中写回来。磁盘上的这种系统内存分页
二. 内存映像文件
几乎每个应用程序都要和文件打交道。比如,应用程序有时需要打开、读取、再关闭文
件;而有时需要打开文件,将数据读到一个缓冲区再写回到文件中另一个位置。通常实
现起来都显得有点繁琐。Microsoft Windows提供了满足这两方面要求的解决途径:
内存映像文件。
象使用虚拟内存一样,使用内存映像文件同样需要经过保留和提交两个过程,首先在进
程内存空间保留一块区域,然后提交物理存储给这段区域。不同的是物理存储来自于磁
盘上的文件,而不是系统的分页文件。也即将磁盘上指定的数据文件作为虚拟内存,这
个实现过程被称为文件映像,可以将文件全部或部分映像到进程的地址空间中。文件映
像过以后,可以把文件映像的部分当作已全部被载入内存一样的去访问它,这时又称它
为内存映像文件。
内存映像文件通常有三个方面的应用:
1. 系统使用内存映像文件载入和执行.EXE和.DLL文件。一方面节省了系统分页文件空
间,另一方面缩短了加载应用程序开始执行所需的时间。
2. 使用内存映像文件访问磁盘上的数据文件。绕开对文件实行I/O作和对文件内容的
缓冲,交由作系统内核去完成。
3. 使用内存映像文件可以实现在多个进程间彼此共享数据。Windows提供了在进程间进
行数据通信的其它多种方法。但这些方法也是通过内存映像文件来实现的,所以内存映
像文件是实现进程间通信最的方法。
三. 内存映像文件使用步骤
要使用内存映像文件,可以按以下步骤:
(1) 调用Windows API 函数CreateFile()创建或是打开一个文件,得到一个标识该文
件(内核对象)的句柄,它确定了哪一个磁盘文件将要作为内存映像文件。
(2) 将(1)中得到的文件对象句柄作为个参数调用Windows API 函数CreateFileM
apping()创建一个文件映像对象。通知系统该文件的大小及对该文件的访问方式,同
时也得到一个标识该文件映像对象的句柄。
(3) 将(2)中得到的文件映像对象句柄作为个参数调用Windows API 函数MapView
OfFile()通知系统映像文件全部或部分内容到进程的某一段地址空间,并将此段空间
首地址通过该函数返回。这一步也就是使用虚拟内存的保留和提交过程,此后就可以利
用此首地址实现对文件内容的读写了。
也可以不做步,直接从第二步开始,但这时要用INVALID_HANDLE_VALUE为参数作为
标识文件对象的句柄,这时系统以其分页文件作为内存映像文件而不用指定磁盘上的哪
一个磁盘文件。当你使用完内存映像文件后,需要做以下几步清除动作。
(1) 调用Windows API 函数UnmapViewOfFile(),通知系统释放文件映像对象在进程地
址空间中占用的区域。
(2) 调用Windows API 函数CloseHandle(),分别关闭文件映像对象和文件对象。
四. 内存映像文件应用举例
当一个.EXE文件被载入准备运行时,系统要执行以下几步:
(1) 系统定位.EXE文件在磁盘的位置。
(2) 系统创建一个新的内核对象——进程。
(4) 系统保留地址空间中足够大的一块区域容纳.EXE文件。而这块区域的基地址在.EXE
文件中有所指定,通常在0x00400000处。
(5) 系统记下支持这块区域的物理存储是.EXE文件,而不是在系统内存分页文件。
这之后系统访问.EXE文件有关信息项,确定所要载入哪些.DLL(动态连接库)文件。系
统调用LoadLibrary函数载入.DLL文件,执行类似上述(4)、(5)步的作。
(1) 系统保留地址空间中足够大的一块区域容纳.DLL文件。同样其基地址在.DLL件中有
所指定。 Visual C++下制作DLL,默认情况下是 0x10000000处。
(2) 如果从该基地址开始的区域已被另一个.DLL或.EXE所占,或从该基地址开始的区域
不够大,系统将在地址空间寻找另一块区域,保留给.DLL文件。
(3) 系统记下支持这块区域的物理存储是在.DLL文件,而不是在系统内存分页文件。
在所有的.EXE和.DLL文件被映像到进程的地址空间中后,系统开始执行.EXE文件的入口
点代码。
2.应用二 —— 进程间共享数据
Windows的确提供了很多快速、方便的机制用于应用程序间共享数据和信息。这些机制包
括RPC、COM、OLE、DDE、WINDOWS消息(特别是WM_COPYDATA),剪贴板,管道,套节字
等等。然而在Windows中,同一台机器上多进程间共享数据层的机制是内存映像文件
。也就是说在同一台机器上,采用上述提到的方法实现进程间通信,最终全都是通过内
存映像文件分别实现各自特定的任务。因此使用内存映像文件是提高这类程序性能的一
种极好途径。
多进程间共享数据是通过将同一个文件映像对象的同一块区域映像到各进程地址空间中
来实现的。映像到在各个进程中的地址空间区域可能有所不同,但是各进程的这段区域
共享相同的物理存储分页文件,如图1所示。因此,当其中一个进程向文件映像对象的这
一区域写入数据时,其他进程在它们自身的地址空间中就能看到这一区域的变化。
让我们接着看系统加载.EXE启动一个应用程序这个例子。当启动一个程序时,系统调用
CreateFile函数打开磁盘上的.EXE文件。然后系统调用CreateFileMapping函数创建一个
文件映像对象,调用MapViewOfFileEx (用SEC_IMAGE 标志)将.EXE文件映像到进程
中的首地址是存储在.EXE文件中指定的位置。然后系统创建进程的主线程,把此映像区
域可执行代码区的个字节的地址赋给线程的执行指针,然后让CPU开始执行代码。
如果用户执行同一个程序第二个实例时,系统会发现所要创建.EXE文件的文件映像对象
在内核中已经存在,因而不会再创建新的文件对象或文件映像对象。取而代之的是,系
统再次映像文件,这次是在新创建进程的地址空间中。可见系统同时映像同一个文件到
两个地址空间中。显然这更有效使用了内存,因为两个进程共享同一个包含正在执行代
码部分的物理存储文件。
象所有(3) 系统为此进程创建其私有地址空间。共享内核对象方法一样,可以使用句柄继承、命名、句柄三种方法在多个进
程间共享文件映像对象。下面列出了用命名方法共享内核对象,实现在两个进程间共享
数据的部分具体代码。用命名方法实现多个进程共享同一个文件映像对象时,所有进程
只需要对共享的文件映像对象使用完全相同的名称。
进程1:
//创建一个物理存储来自系统分页文件的文件映像对象,
//其大小为4 KB 命名为MMFSharedData.
s_hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, 4 1024, TEXT("MMFSharedData"));
PVOID pView = MapViewOfFile(s_hFileMap,
进程2:
// 打开名称为MMFSharedData 的文件映像对象
HANDLE hFileMapT = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE,
FALSE, TEXT("MMFSharedData"));
PVOID pView = MapViewOfFile(hFileMapT,
这两个进程在其各自pView指针开始处4KB内容的物理存储来自同一个分页文件的同一段
区域,因而具有相同的内容,也即它们共享同一段数据。
参考文献:
1. Jeffrey Richter. Programming Applications for Microsoft Windows. Washingt
2. MSDN library –January 2001 Platform SDK/Base Servs/Memory/File Mappin
g.
3. Did J. Kruglinski. Programming Visual C++. Microsoft Press, 1998.
--
※ 来源:·瀚海星云 bbs.ustc.edu·[FROM: 210.45.209.130]
驱动程序是一个分层的结构,一个硬件设备并不是只由一个驱动程序来管理,在它相关联的物理设备驱动程序之上,还有很多过滤驱动程序。与这些过滤驱动程序相关联的,就是这个物理设备对象的过滤器设备对象。那么,一个用户模式的请求,必须通过上层的过滤器设备对象,一层一层的往下传,最终才能到达物理设备对象。这有点像TCP/IP分层结构模型,一个应用层的数据包必须通过传输层、网络层这样一层一层的往下传,最终才能达到物理层并传递到网络中。而设计这样的分层模型的目的,我想应该是为了方便扩展,比如如果想对某个设备加入新的管理作,那么不需要修改其已有的物理设备驱动程序和过滤器驱动程序,而只需要加入新的过滤器设备对象以及相应的驱动程序,在这里加入新的作就行了。
}else另外,需要指出的一点是,在很多内核模式编程中,驱动程序并不一定要与某一个实际存在的物理设备相关联,它可以仅创建一个虚拟的设备对象,而这个设备对象不与任何实际的物理设备相关联。因为在很多情况下,用户编写驱动的目的仅仅是要让自己的代码执行在系统的内核态中。
编写WDM驱动程序
驱动程序入口点 - DriverEntry函数(相当于c语言的main函数), 它在驱动程序被加载进内存的时候调用。
DriverEntry函数有两个参数,其中个参数PDRIVER_OBJECT pDriverObj是指向该驱动程序对应的驱动程序对象的指针。
一个重要的任务就是要设定驱动程序对象的几个函数指针,这样,该驱动程序对象关联的设备对象在接收到上层的IRP的时候,就会通过驱动程序对象中设置的函数指针,找到相应的函数来做处理:
pDriverObj->DriverUnload = DriverUnload;
pDriverObj->MajorFunction[IRP_MJ_CREATE] =
pDriverObj->MajorFunction[IRP_MJ_CLOSE] =
pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDispatch;
还有一个重要的任务,就是要创建设备对象并为其建立符号连接。(这里说明一下,在规范的WDM程序中,创建设备对象的任务本来该由AddDev函数来做,而这个函数也是通过驱动程序对象的一个函数指针来定位的。在这种规范的WDM程序中,一旦有新硬件加入,系统就会自动通过驱动程序对象的函数指针找到AddDev函数,并调用它来创建设备对象。但是在这里,我并不是在为实际存在的硬件写驱动,而只是写一个内核模式下的程序,因此就只需要在DriverEntry函数中创建一个设备对象就行了。)
IoCreateDev( pDriverObj,
0,
&devName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
&pDevObj ); //创建设备对象
IoCreateSymbolicLink( &linkName, &devName ); //建立符号连接
从上面调用IoCreateDev函数的参数中还可以看出,设备对象和驱动程序对象是相关联的,这也就可以解释为什么是设备接收到IRP,而相应的处理函数却是由驱动程序对象中的函数指针定位的。至于建立符号连接,那是为了方便在用户模式中调用设备对象。
从前面设定的驱动程序对象中的函数指针可以看到,主要有两个函数:卸载函数DriverUnload和派遣函数DriverDispatch。DriverUnload函数应该很容易理解,它是在驱动程序被卸载出内存的时候调用,主要做一些释放内存之类的工作。而在我的这个程序中,所有的IRP都是在同一个函数里面进行处理的,这就是派遣函数DriverDispatch(实际上很多WDM程序都是这样做的)。下面就分别介绍一下这两个函数。
DriverUnload函数的主要任务是将创建的设备对象和符号连接删除掉,当然如果在程序中还分配了其他内存需要释放,也是在这里完成。
IoDeleteSymbolicLink( &linkName );
IoDeleteDev( pDriverObj->DevObject );
派遣函数DriverDispatch主要负责处理上层的IRP。这里先要提一下,每个IRP都与两个数据结构相关联,就是IRP本身和IRP Stack——IO_STACK_LOCATION结构。在这两个结构里面,包含了所有上层传递给本层设备对象的信息。最重要的一个信息就是:在IO_STACK_LOCATION结构中,包含了IRP的功能码MajorFunction和MinorFunction(IRP的功能码标识了该IRP具体是什么请求,比如读请求的MajorFunction值为IRP_MJ_READ)。
DriverDispatch函数的处理流程一般是这样的:首先通过IRP获得IPR Stack;然后从IRP Stack中得到该IRP的主功能码MajorFunction,判断主功能码并做相应处理;处理完该请求后,根据具体情况选择完成该请求或者向下一层设备对象传递该IRP。获得IRP Stack很简单,只需要调用函数IoGetCurrentIrpStackLocation即可:
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( pIrp );
判断主功能码并做相应的处理这一步一般是由一个switch-case语句实现的:
switch( pIrpStack->MajorFunction )
{case IRP_MJ_CREATE:
DbgPrint( "Info: Create!
case IRP_MJ_CLOSE:
DbgPrint( "Info: Close!
case IRP_MJ_DEVICE_CONTROL:
{switch( pIrpStack->Parameters.DevIoControl.IoControlCode )
{case IOCTL_GET_INFO:
{RtlCopyMemory( pIrp->UserBuffer, "This is a test driver!", 23 );
rmation = 23;
}default:
}}
default:
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = rmation;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
如果需要向下一层设备对象传递该IRP,则要先初始化往下传递的IRP对应IRP Stack(可以直接将当前的IRP Stack给下层IRP Stack),然后调用IoCallDriver函数往下层传递该IRP:
IoCopyCurrentIrpStackLocationToNext(pIrp);
status = IoCallDriver(pLowerDevObj, pIrp);
那么,怎样从用户模式的程序中调用驱动?
用户模式的程序要调用驱动,首先就要打开设备,也就是驱动程序中创建的设备对象。这可以通过调用CreateFile函数来实现。CreateFile函数本来是用于打开文件,它的个参数就是文件名。而这里,我们以设备名作为它的个参数传入,那么该函数打开的就是设备了。这里所说的设备名,实际上是驱动程序里面为设备对象建立的符号连接名。比如用户模式中给出的设备名为” .MyDev”,I/O管理器在执行名称搜索前先自动把”.”转换成”??”,这样就成了” ??MyDev”,这就是驱动程序里面建立的符号连接名了。打开设备后,用户模式的程序就可以调用ReadFile、WriteFile和DevIoControl等函数向驱动程序发出请求了。
,给出我的试验程序的源码。
其他
WDM程序编译出来的并不是我们常见的.exe,而是.sys文件,在未经设置编译环境之前,是不能直接用VC来编译的(这就是为什么会有几百个错误了)。这种类型的文件你可以在WINNTSystem32Drivers里面找到很多。其实驱动程序也是一种PE文件,它同样由DOS MZ header开头,也有完整的DOS stub和PE header,同样拥有Import table和Export table——hoho……那跟普通的PE文件有什么不一样呢?
其实.sys跟.exe文件一样,都是一种PE文件来的。不同的是,.sys文件Import的通常是NTOSKRNL.EXE,而.exe文件Import的通常是KERNEL32.DLL和USER32.DLL。
知道了这些有什么用呢?实际上,由于.sys通常不调用KERNEL32.DLL和USER32.DLL,所以你是不能在设备驱动程序里面调用任何C、C++和Win32函数的,而且也不能用C++关键字new和delete等(可以用malloc和free来代替),而必须使用大量的内核函数。
fopen("xxx.dat","wb")
获得文件长度lenght
fwrite(0,1,lenght,fp);
整个文件用0覆盖
("del xxx.datn");
删除它
你可以建立一个新文件 和 要清空的文件名一样 然后建立的参数选择 没有则创建有则覆盖
C里的函数有待楼下给出
就是string
rrse的意思,返回一个字符串的反序
比如
char
a[]="123";
strrev(a);
a变SetFilePointer为"321"
注意这不是c标准库函数,是微软的扩展。
使用_access函数,函数原型为 int _access( const char path, int mode );
使用CreateFile函数,函数原型为:
HANDLE CreateFile( LPCTSTR lpFileName, // pointer to name of the file
DWORD dwDesiredAccess, // access (read-write) mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
DWORD dwCreationDisition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to file with attributes to // copy
);
使用FindFirstFile函数,函数原型为:的地址空间中。此处用MapViewOfFileEx代替MapViewOfFile使文件映像到进程地址空间
HANDLE FindFirstFile( LPCTSTR lpFileName, // pointer to name of file to search for
LPWIN32_FIND_DATA lpFindFileData // pointer to returned rmation
);
使用GetFileAttributes函数,函数原型如下:
DWORD GetFileAttributes( LPCTSTR lpFileName // pointer to the name of a file or directory ); 5. 使用Shell Lightweight Utility APIs函数 PathFileExists()专门判断文件和目录时否存在的函数文件名可读性比较强还可以判断目录是否存在 Header: Declared in Shlwapi.h Import Library: Shlwapi.lib 以上的各种方法供参考,函数具体用法需参见MSDN。
前半部分的理解是 void CSampleDlg::OnComm(){ switch(m_Serial GetCommEvent()) { case : // 串行口数据接收 处理 }}稍有点错误的
fopen 是包含在standard c library中的 , 它本身是和OS没有任何关系的, 所以说在任何OS中的
表现都是一致的.
在win32环境里, fopen 并不是调用了CreateFile, 而是像你所说的, "调用了它们OS的底层功能实现了创建文件", 只是和 CreateFile 做了不多的工作而已.
后半部分完全正确, CreateFile() 只是Win32的API, 就好像大部分的unix / Linux 系统的API中, CreateFile() 对应的是 open(). 当然,这个也只是对应这些OS的intece, 和 fopen 没有可比性, 只是它们完成了基本一样的事情而已.
你的理解基本没有错,CreateFile 只有在windows下使用,但fopen应该没有调用CreateFile,两个函数实现打开文件的功能,过去有认为fopen调用了CreateFile,但fopen执行速度快于CreateFile这点,说明fopen是的。我建议尽可能用标准的C/C++函数,代码的可移植性较好。
版权声明:本文内容由互联。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发 836084111@qq.com 邮箱删除。