js 设置鼠标光标形状

auto(default) 默认值。浏览器根据当前情况自动确定鼠标光标类型。
col-resize 有左右两个箭头,中间由竖线分隔开的光标。用于标示项目或标题栏可以被水平改变尺寸。
crosshair 简单的十字线光标。
all-scroll 有上下左右四个箭头,中间有一个圆点的光标。用于标示页面可以向上下左右任何方向滚动。
move 十字箭头光标。用于标示对象可被移动。
help 带有问号标记的箭头。用于标示有帮助信息存在。
no-drop 带有一个被斜线贯穿的圆圈的手形光标。用于标示被拖起的对象不允许在光标的当前位置被放下。
not-allowed 禁止标记(一个被斜线贯穿的圆圈)光标。用于标示请求的操作不允许被执行。
pointer(hand) 竖起一只手指的手形光标。就像通常用户将光标移到超链接上时那样。
progress 带有沙漏标记的箭头光标。用于标示一个进程正在后台运行。
row-resize 有上下两个箭头,中间由横线分隔开的光标。用于标示项目或标题栏可以被垂直改变尺寸。
text 用于标示可编辑的水平文本的光标。通常是大写字母 I 的形状。
vertical-text 用于标示可编辑的垂直文本的光标。通常是大写字母 I 旋转90度的形状。
wait 用于标示程序忙用户需要等待的光标。通常是沙漏或手表的形状。
*-resize 用于标示对象可被改变尺寸方向的箭头光标。
w-resize | s-resize | n-resize | e-resize | ne-resize | sw-resize | se-resize | nw-resize
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js 光标设置</title>
</head>

<body>
<input type="button" value="auto" onclick=SetCursor("auto") /> 
<input type="button" value="col-resize" onclick=SetCursor("col-resize") /> 
<input type="button" value="row-resize" onclick=SetCursor("row-resize") /> 
<input type="button" value="all-scroll" onclick=SetCursor("all-scroll") /> 
<input type="button" value="crosshair" onclick=SetCursor("crosshair") /> 
<input type="button" value="move" onclick=SetCursor("move") /> 
<input type="button" value="help" onclick=SetCursor("help") /> 
<input type="button" value="no-drop" onclick=SetCursor("no-drop") /> 
<input type="button" value="not-allowed" onclick=SetCursor("not-allowed") />
<input type="button" value="pointer" onclick=SetCursor("pointer") /> 
<input type="button" value="progress" onclick=SetCursor("progress") /> 
<input type="button" value="text" onclick=SetCursor("text") /> 
<input type="button" value="vertical-text" onclick=SetCursor("vertical-text") /> 
<input type="button" value="wait" onclick=SetCursor("wait") /> 
<input type="button" value="w-resize" onclick=SetCursor("w-resize") /> 

<div id="test" style="width:600px; height:200px; margin: 0 auto; background-color: blue; color:#FFF;">移动鼠标到此查看效果</div>   


</body>

<script   language="Javascript">   
function SetCursor(str){   
  document.getElementById('test').style.cursor=str;
}   
</script>

</html>

效果演示

function SetCursor(str){
document.getElementById(‘test’).style.cursor=str;
}

移动鼠标到此查看效果
Advertisements

js 按键获取

原生 js 按键获取

 document.onkeydown = function() {

    if (event.ctrlKey == true && event.keyCode == 90) {//Ctrl+Z
        event.returnvalue = false;
        // $("btnSave1").click(); //ID
        alert("Ctrl+Z 被按下");
    }

    if (event.ctrlKey == true && event.keyCode == 89) {//Ctrl+Y
        event.returnvalue = false;
        // window.location.href="SetInfo.aspx?ChId=1&ColId=1"; //ID
        alert("Ctrl+Y 被按下");
    }
};

这样清掉了别的所有的事件。而且比如在火狐上的话,firebug会出一大堆的错误信息,一按下ctrl键其实就能看到浏览器收到了很多的ctrl键值。定义一个按ctrl的flag,然后抬起就至false。其他的按键统统return false的话,原生js也可以做。不过感觉有点粗暴。还是用jQ吧。

使用 jQuery 按键获取

$(document).keydown(function (e) {
    if (e.which === 17){
        isCtrl = true;
        $("#status").text("Ctrl 被按下!");
    }
    $("#status2").text(e.which);
    if (e.which === 90 && isCtrl === true) {
        alert("Ctrl+Z!");
    	$("#status2").text("就不来。。气死你");
    }
    if (e.which === 89 && isCtrl === true) {
        alert("Ctrl+Y!");
    }
}).keyup(function (e) {
    if (e.which === 17){
        isCtrl = false;
        $("#status").text("Ctrl 已抬起!");
	}
});

上述代码,在IE6上都能跑,但是不知道为什么不能在IE8上alert消息,博主试了好久,结果发现事件已经触发了,就是没alert,莫名其妙的IE系列。

js/jQuery 获取图片宽高

js

<script type="text/javascript">
	var myImg = new Image();
	myImg.src = "images/pingtan1.jpg";	
	myImg.onload = function(){
		// 自身宽高
		var map_w = this.width;
		var map_h = this.height;
		alert("图片 宽: "+map_w+" 高: "+map_h)
	}
</script>

jQuery

<script type="text/javascript">
$(document).ready(function(){

	$("#map").load(function () {
		
		// 窗口宽高
		var height = $(window).height();
		var width = $(window).width();
		// 自身宽高
		var map_w = this.width;
		var map_h = this.height;
		// 居中坐标
		var t_pos = (height - map_h)/2 +"px";
		var l_pos = (width - map_w)/2 +"px";
		// 设置居中
		$("#map_div").css({
			position: "absolute", 
			left: l_pos, 
			top: t_pos });
	});
});
</script>

一些闲话

chrome浏览器中如果要在jquery的 $(document).ready中拿不到到图片宽高,常规解决方案是在 $(window).load 的时候拿
貌似jQuery不准备做低IE的兼容了博主用的jq 1.9 在 IE居然不行,这个问题原生 js 兼容性更高吧

Windows API教程(四) 进程编程

茵蒂克丝

博主很想详细的介绍一下进程神马的、内存神马的,但是真的整理起来发现要做到让有C基础的学生能很好理解,一看就懂还是感觉很有难度。也许是博主水平还不够吧。

作为90后程序员,博主还是不走寻常路吧,到了进程这里开始,我们反过来学一下,顺便补一补一些基础知识。

上一节文件编程的时候,有说过文件流的状态,当我们开始操作一个文件,这个文件的打开关闭都由程序控制,而其中的内容是可以动态增减的,这样的情况类似水流一般,我们把这样的文件状态称作文件流。

那么实际上,用户的输入、输出这一类的“缓存”也就是这样的状态,通常是叫做输入流和输出流,除了这两个还有一个错误流。C语言中的标准库(stdio.h)中有定义这三个常用的流,也即stdin(标准输入流)、stdout(标准输出流)和stderr(标准错误流)。如果大家仔细深究 stdio.h 的头文件会发现,这些流的定义跟文件的定义实际上是同样的。

好了,回到终点,那么既然这些东西都已经是 “流” 的状态了,那么也就意味着我们不需要去打开什么文件,来生成什么文件流,直接可以用文件操作的函数来操作这些输入输出流。

例如:

scanf("%d", &i);
实际上就是 fscanf(stdin, "%d", &i); 的简写而已。

PS:这实际上应该算是C语言的基础知识,不能理解为什么大学都不教(博主观点)

CMD 工具集

那么,开始有讲过的,windows编程其实就是去查API,然后用API。这一节讲进程,我们就通过编写一个学习用的cmd工具集来一步步走向进程编程。

首先我们要从输入流来读取用户的输入,这一操作在标准库中可以使用 fgets 函数来实现,调用 windows API 的话我们就用 ReadFile 就可以了。

BOOL WINAPI ReadFile(
  _In_         HANDLE hFile,				// 读取的文件句柄
  _Out_        LPVOID lpBuffer,				// 保存读取缓冲字符数组
  _In_         DWORD nNumberOfBytesToRead,	// 缓冲数组的大小
  _Out_opt_    LPDWORD lpNumberOfBytesRead,	// 实际读出的大小
  _Inout_opt_  LPOVERLAPPED lpOverlapped 	// 异步IO文件结构体
);

#include <Windows.h>;
#include <stdio.h>;

void welcome();

int main()
{
	char Command_str[MAX_PATH];
	DWORD Command_len;
	HANDLE hConsoleInput;  

    // 获取输出流的句柄
    hConsoleInput = GetStdHandle(STD_INPUT_HANDLE);  

	// 输出欢迎信息
	welcome();

	while(1)
	{
		// 清空命令字符串
		memset(&Command_str, 0, MAX_PATH);
		// 输出提示符
		printf("nLscmd>;");
		// 读取输入流
		ReadFile(
			hConsoleInput,	// 文件句柄
			Command_str,	// 获取内容的缓冲字符数组
			MAX_PATH,		// 缓冲数组大小
			&Command_len,	// 实际读出的大小
			NULL);

		printf("接收到命令:[%s]", Command_str);
	}
}

void welcome()
{
	printf("Lellansin's CMD Tool [版本 0.0.1]n");
	printf("学习自制 (c) www.lellansin.com 欢迎交流n");
}

可以简单的看到,我们的输入都有获取到,并且输出的时候还连带我们输的回车(换行符)

定制两个简单的命令

#include <Windows.h>;
#include <stdio.h>;
#include <string.h>;	// for strcmp
#include <stdlib.h>;	// for exit

void welcome();
void command_switch(char *cmd_str);

int main()
{
	char Command_str[MAX_PATH];
	DWORD Command_len;
	HANDLE hConsoleInput;  

    // 获取输出流的句柄
    hConsoleInput = GetStdHandle(STD_INPUT_HANDLE);  

	// 输出欢迎信息
	welcome();

	while(1)
	{
		// 清空命令字符串
		memset(&Command_str, 0, MAX_PATH);
		// 输出提示符
		printf("nLscmd>;");
		// 读取输入流
		ReadFile(
			hConsoleInput,	// 文件句柄
			Command_str,	// 获取内容的缓冲字符数组
			MAX_PATH,		// 缓冲数组大小
			&Command_len,	// 实际读出的大小
			NULL);

		command_switch(Command_str);
	}
}

void command_switch(char *cmd_str)
{
	char cmd_tmp[MAX_PATH]={0};
	char *pstr = cmd_str, *ptmp = cmd_tmp;

	// 一直赋值到换行之前
	while(*pstr != '\r' && *pstr != '\n')
	{
		*ptmp++ = *pstr++;
	}
	// printf("收到命令:[%s]n", cmd_tmp);

	// 判断命令
	if( strcmp(cmd_tmp, "hi") == 0 )
	{
		printf("你好~");
	} else if ( strcmp( cmd_tmp, "exit" ) == 0 )
	{
		exit(0);
	}else
	{
		printf("Error: 命令未找到n");
	}
}

void welcome()
{
	printf("Lellansin's CMD Tool [版本 0.0.1]n");
	printf("学习自制 (c) www.lellansin.com 欢迎交流n");
}

创建进程

那么到现在为止,我们的cmd工具已经有了一个基本的雏形,接下来要做的就是调用我们原来写的命令。

有的同学可能会想到直接用 stdlib.h 里的 system 函数来解析命令,这个是可以的。但是这个函数是C标准库的,并不是windows系统的API,它的效率是非常的低的。而且,这里是在在讲的是 windows 编程所以读者请不要偷懒哦。

首先,我们来了解一下进程的概念。当一个程序运行起来的时候,操作系统一定要为这个程序(Program)创建一个进程(Process),以方便管理。有些同学也知道每个程序跑起来之后都被分配了一个进程ID,实际上在进程调度的时候,操作系统还会为我们的程序分配进程的一些列事务:资源、虚拟内存地址空间、系统调用接口、优先级、环境变量等等。

所以进程实际上可以理解成一个运行起来的程序,就如进程的英文原本的意思(Process)一样这是个运行过程。每个运行起来的程序都要被操作系统安排进程(过程)的来管理。

如果你能想象这一过程,那么就不难理解,想要运行一个程序必须要创建一个进程。所以,这里我们就需要为我们原本所写的程序创建一个进程,就可以调用了。

BOOL WINAPI CreateProcess(
  _In_opt_     LPCTSTR lpApplicationName,	// 启动程序路径
  _Inout_opt_  LPTSTR lpCommandLine,		// 启动程序的命令行代码
  _In_opt_     LPSECURITY_ATTRIBUTES lpProcessAttributes,	// 进程属性
  _In_opt_     LPSECURITY_ATTRIBUTES lpThreadAttributes,	// 线程属性
  _In_         BOOL bInheritHandles,		// 是否继承句柄
  _In_         DWORD dwCreationFlags,		// 标识和优先级
  _In_opt_     LPVOID lpEnvironment,		// 环境变量设置
  _In_opt_     LPCTSTR lpCurrentDirectory,	// 当前目录设置
  _In_         LPSTARTUPINFO lpStartupInfo,	// 启动信息设置
  _Out_        LPPROCESS_INFORMATION lpProcessInformation 	// 新进程的信息
);

MSDN 文档: CreateProcess function

返回值

如果函数执行成功,返回非零值。如果函数执行失败,返回零,可以使用GetLastError函数获得错误的附加信息。


#include <Windows.h>;
#include <stdio.h>;
#include <string.h>;	// for strcmp
#include <stdlib.h>;	// for exit

void welcome();
void command_switch(char *cmd_str);
BOOL CreateChildProcess(char *cmd_str);

int main()
{
	char Command_str[MAX_PATH];
	DWORD Command_len;
	HANDLE hConsoleInput;  

    // 获取输出流的句柄
    hConsoleInput = GetStdHandle(STD_INPUT_HANDLE);  

	// 输出欢迎信息
	welcome();

	while(1)
	{
		// 清空命令字符串
		ZeroMemory(&Command_str, MAX_PATH);
		// 输出提示符
		printf("nLscmd>;");
		// 读取输入流
		ReadFile(
			hConsoleInput,	// 文件句柄
			Command_str,	// 获取内容的缓冲字符数组
			MAX_PATH,		// 缓冲数组大小
			&Command_len,	// 实际读出的大小
			NULL);

		command_switch(Command_str);
	}
}

void command_switch(char *cmd_str)
{
	char *pstr = cmd_str;

	// 遍历到换行之前
	while(*pstr != '\r' && *pstr != '\n')
	{
		*pstr++;
	}
	// 覆盖换行
	*pstr = '\0';
	// printf("收到命令:[%s]n", cmd_str);

	// 判断命令
	if( strcmp(cmd_str, "hi") == 0 )
	{
		printf("你好~ 欢迎使用 Lellansin 的cmd工具n");
	} else if ( strcmp( cmd_str, "exit" ) == 0 )
	{
		exit(0);
	}else
	{
		// 创建子进程
		CreateChildProcess(cmd_str);
	}
}

BOOL CreateChildProcess(char *cmd_str)
{
	STARTUPINFO start_info;
	PROCESS_INFORMATION process_info;
	BOOL flag;

	// 将启动信息结构清零 ( 相当于 memset 0, 不过效率更高 )
	ZeroMemory( &start_info, sizeof(start_info) );
	// 设置结构大小,cb属性应为结构的大小
	start_info.cb = sizeof(start_info);
	// 将进程信息结构清零
	ZeroMemory( &process_info, sizeof(process_info) );

	flag = CreateProcess(
		NULL,			// 不传程序路径, 使用命令行
		cmd_str,		// 命令行命令
		NULL,			// 不继承进程句柄(默认)
		NULL,			// 不继承线程句柄(默认)
		FALSE,			// 不继承句柄(默认)
		0,				// 没有创建标志(默认)
		NULL,			// 使用默认环境变量
		NULL,			// 使用父进程的目录
		&start_info,    // STARTUPINFO 结构
		&process_info );// PROCESS_INFORMATION 保存相关信息

	if ( !flag )
	{
		// 创建失败
		printf( "Error: 命令未找到 (%d).n", GetLastError() );
		return 0;
	}

	// 等待子进程结束
	// 使用到了通过 PROCESS_INFORMATION 结构体获取子进程的句柄 hProcess
	WaitForSingleObject( process_info.hProcess, INFINITE );
	// 关闭进程句柄和线程句柄
	CloseHandle( process_info.hProcess );
	CloseHandle( process_info.hThread );

	return 1;
}

void welcome()
{
	printf("Lellansin's CMD Tool [版本 0.0.1]n");
	printf("学习自制 (c) www.lellansin.com 欢迎交流n");
}

查看进程

CreateToolhelp32Snapshot

获取当前的系统快照

HANDLE WINAPI CreateToolhelp32Snapshot(
  _In_  DWORD dwFlags,
  _In_  DWORD th32ProcessID
);

dwFlags [输入参数]
指明所需的系统快照。该参数可以是如下列表中的一个或多个值。

Value Meaning
TH32CS_INHERIT (0x80000000) 表明快照 (snapshot) 句柄是可以被继承 (inheritable) 。
TH32CS_SNAPALL 包含所有系统中的所有进程和线程,加上指定进程中堆和模块的信息( 通过th32ProcessID特别指明的进程id,如果为0则无)。相当于指定 TH32CS_SNAPHEAPLIST, TH32CS_SNAPMODULE, TH32CS_SNAPPROCESS, 和 TH32CS_SNAPTHREAD 通过或运算(‘|’) 联合使用
TH32CS_SNAPHEAPLIST (0x00000001) 快照中包含通过 th32ProcessID 指定进程的所有堆 (heaps) 信息。 想要列举堆的信息,请查询 Heap32ListFirst
TH32CS_SNAPMODULE (0x00000008) 快照中包含通过 th32ProcessID 指定进程的所有modules信息。 想要列举modules信息,请查询 Module32First 。 如果函数失败报错ERROR_BAD_LENGTH,请重试此函数直到成功。
TH32CS_SNAPMODULE32 (0x00000010) 快照中包含所有通过 th32ProcessID 指定进程的32位模块(modules)信息(通过64位调用也是返回32位)。 该flag可以与 TH32CS_SNAPMODULE 或者 TH32CS_SNAPALL 联合使用。如果函数失败报错 ERROR_BAD_LENGTH ,请重试此函数直到成功。
TH32CS_SNAPPROCESS (0x00000002) 快照中包含系统中的所有进程 (processes) 信息。 想要列举 processes 信息,请查询 Process32First
TH32CS_SNAPTHREAD (0x00000004) 快照中包含系统中的所有线程 (threads) 信息。想要列举 threads 信息, 请查询 Thread32First

确认是否属于某个指定的进程,可以在列举线程信息的时候,拿进程标识与 THREADENTRY32 结构体的 th32OwnerProcessID 成员相比较来判断 。

th32ProcessID [输入参数]
如果为0则获取所有进程的快照,如果不为零则获取该进程id的信息

返回值

成功则返回该快照的句柄

遍历进程信息

BOOL WINAPI Process32First(
  _In_     HANDLE hSnapshot,
  _Inout_  LPPROCESSENTRY32 lppe
);
BOOL WINAPI Process32Next(
  _In_   HANDLE hSnapshot,
  _Out_  LPPROCESSENTRY32 lppe
);

与文件目录类似, Process32First 用于获取第一个进程的信息, Process32Next 用于获取下一个进程的信息。参数一 hSnapshot 是指获取的进程快照,参数二 lppe 则是一个指向 PROCESSENTRY32 结构体的指针(LP + PROCESSENTRY32)。

PROCESSENTRY32 结构体

系统进程快照中某一个进程信息的具体结构

typedef struct tagPROCESSENTRY32
{
    DWORD   dwSize;
    DWORD   cntUsage;
    DWORD   th32ProcessID;          // this process
    ULONG_PTR th32DefaultHeapID;
    DWORD   th32ModuleID;           // associated exe
    DWORD   cntThreads;
    DWORD   th32ParentProcessID;    // this process's parent process
    LONG    pcPriClassBase;         // Base priority of process's threads
    DWORD   dwFlags;
    CHAR    szExeFile[MAX_PATH];    // Path
} PROCESSENTRY32;

MSDN 文档 :
CreateToolhelp32Snapshot
Process32First
Process32Next
PROCESSENTRY32 结构体

ps 查看进程列表

再来新建一个项目名字叫做 ps ,记得要是空项目随后添加如下代码


#include <Windows.h>
#include <stdio.h>

#include <TlHelp32.h>
/*
TlHelp32.h for
	PROCESSENTRY32
	CreateToolhelp32Snapshot()
	Process32First()
	Process32Next()
*/

int main(int argc, char const *argv[])
{
	HANDLE hSnapshot;
	HANDLE hProcess;
	PROCESSENTRY32 pe32;
	// 获取进程快照
	hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
	if( hSnapshot == INVALID_HANDLE_VALUE )
	{
		printf( "CreateToolhelp32Snapshot (of processes) 失败" );
		return ;
	}
	// 设置输入参数,结构的大小
	pe32.dwSize = sizeof( PROCESSENTRY32 );

	// 开始列举进程信息
	if( !Process32First( hSnapshot, &pe32 ) )
	{
		printf( "Process32First() 失败" );
		CloseHandle( hSnapshot ); // 关闭句柄
		return ;
	}

	printf("进程IDt父进程t线程数t优先级t进程名"); // 基本优先级
	do {
		// 打印进程相关信息
		printf( "n%u", pe32.th32ProcessID );	// 进程id
		printf( "t%u", pe32.th32ParentProcessID );	// 父进程id
		printf( "t%d", pe32.cntThreads );		// 线程数
		printf( "t%d", pe32.pcPriClassBase );	// 基本优先级
		printf( "t%s", pe32.szExeFile );		// 进程名

	} while( Process32Next( hSnapshot, &pe32 ) );

	CloseHandle( hSnapshot );	//关闭句柄

	return ;
}

因为是在命令行的环境下运行,所以直接F7生成exe即可,不需要直接执行。如果是拿lscmd来做实验的话,程序写好了还要找到exe复制到path目录下,感觉有点麻烦,为了方便大家也可以直接将程序的生成目录,改成博主开始使用的 F:mytools目录 (Visual Studio 如何设置生成目录)

测试数据:

Lellansin's CMD Tool [版本 0.0.1]
学习自制 (c) www.lellansin.com 欢迎交流

Lscmd>ps
进程ID  父进程  线程数  优先级  进程名
0       0       2       0       [System Process]
4       0       106     8       System
292     4       2       11      smss.exe
392     380     49      8       avgrsa.exe
432     392     10      8       avgcsrva.exe
640     632     10      13      csrss.exe
704     632     3       13      wininit.exe
720     696     13      13      csrss.exe
764     696     3       13      winlogon.exe
812     704     10      9       services.exe
824     704     7       9       lsass.exe
832     704     11      8       lsm.exe
940     812     10      8       svchost.exe
1020    812     9       8       svchost.exe
724     812     19      8       svchost.exe
888     812     22      8       svchost.exe
1732    812     33      8       avgwdsvc.exe
1816    812     4       8       sqlwriter.exe
1852    812     6       8       svchost.exe
1924    812     4       8       vmware-usbarbitrator64.exe
1976    812     6       8       vmnat.exe
...... 省略N条 ......
7008    1676    3       8       notepad++.exe
6204    1500    11      6       chrome.exe
6600    1500    11      6       chrome.exe
6628    1500    11      6       chrome.exe
6920    1500    11      6       chrome.exe
7716    3800    10      8       MSBuild.exe
5980    724     5       8       audiodg.exe
4132    1500    11      8       chrome.exe
5048    1500    11      8       chrome.exe
4976    2052    6       8       vcpkgsrv.exe
8072    7196    5       8       mspdbsrv.exe
4724    3800    8       8       vcpkgsrv.exe
3844    720     5       8       conhost.exe
7124    3400    1       8       ps.exe
Lscmd>

终止进程

OpenProcess 获取进程句柄

HANDLE WINAPI OpenProcess(
  _In_  DWORD dwDesiredAccess,
  _In_  BOOL bInheritHandle,
  _In_  DWORD dwProcessId
);

参数

dwDesiredAccess [输入参数]

进程句柄对该进程的访问权限。详见进程访问权限

bInheritHandle [输入参数]

句柄是否继承(填写 TRUE 或者 FALSE),如果继承(TRUE)那么如果该进程创建子进程的时候这个句柄也会被继承到子进程。

dwProcessId [输入参数]

将要打开(open)的进程ID。

如果指定进程是系统进程 (0x00000000),该函数会失败并且最后的错误会是ERROR_INVALID_PARAMETER。如果指定进程是系统空闲进程(原文:Idle process 博主备注:一种内存管理进程,准确的来讲名字叫做 System Idle Process)或者某个子系统进程(原文:CSRSS processes 博主备注:准确的说是 Client Server Runtime Process 任务管理器里可以看到它 csrss.exe),该函数会失败并且最后的错误会是ERROR_ACCESS_DENIED 因为其访问限制会阻止用户级别(user-level)的代码获取其句柄。

返回值

如果函数成功,返回值为指定进程的句柄。
如果函数失败,返回值为NULL。可以使用 GetLastError 函数获得错误的附加信息。

MSDN: OpenProcess

TerminateProcess

终止(Terminate) + 进程(Process) = 终止进程(TerminateProcess)

BOOL WINAPI TerminateProcess(
  _In_  HANDLE hProcess,
  _In_  UINT uExitCode
);

参数一 hProcess [输入参数]

待终止的进程句柄。该句柄必须拥有 PROCESS_TERMINATE (进程终止) 的权限。更多信息,请查看进程的安全与访问权限

参数二 uExitCode [输入参数]

设置通过使用该方法退出的进程与线程的退出码?(exit code 嘛,博主觉得返回值更好理解一些)。 使用GetExitCodeProcess函数可以可以获取到进程退出时返回的值。使用GetExitCodeThread函数可以获取到线程退出时返回的值。

返回值
函数执行成功,返回值为非零。
函数执行失败,返回值为零。更多信息可以通过GetLastError函数获取。

kill 终止进程

新建一个空项目名为 kill ,随后代码如下


#include <windows.h>
#include <stdio.h>

void help();

int main(int argc, char const *argv[])
{
	int ProcessID;
	HANDLE hProcess;

	// 如果只有一个参数
	if(argc == 1)
	{
		help();
		return 0;
	}

	// 如果有两个参数
	if(argc == 2)
	{
		// 获取进程id
		ProcessID = atoi(argv[1]);
		// 获取进程句柄
		hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, (DWORD)ProcessID );
		// 终止进程
		TerminateProcess(hProcess, 0);
	}
}

void help()
{
	printf("终止进程n");
	printf("kill <进程id>n");
}

使用演示:

打开两个lscmd,第一个执行

Lellansin's CMD Tool [版本 0.0.1]
学习自制 (c) www.lellansin.com 欢迎交流

Lscmd>cmd.exe
Microsoft Windows [版本 6.1.7601]
版权所有 (c) 2009 Microsoft Corporation。保留所有权利。

C:UsersLellansin>

执行cmd.exe之后发现程序切换到了cmd之下,这个时候我们在打开第二个 lscmd ,(注:如果你的 lscmd 放在path路径之下的话,博主的也就是F:mytools,可以直接 win+r 调出运行,输入 lscmd 回车即可调用出来)

在我们的第二个CMD工具集中使用ps查看进程

Lellansin's CMD Tool [版本 0.0.1]
学习自制 (c) www.lellansin.com 欢迎交流

Lscmd>ps
进程ID  父进程  线程数  优先级  进程名
... 其余省略 ...
1676    1956    36      8       explorer.exe
7904    1676    1       8       lscmd.exe
5844    7904    1       8       cmd.exe
4180    1676    1       8       lscmd.exe
5568    4180    1       8       ps.exe

#通过观察进程列表可以发现我们使用lscmd打开的cmd.exe
#其进程id是5844,那么我们用新鲜的kill.exe来试试它

Lscmd>kill 5844

#运行之后发现第一个lscmd中的cmd.exe退出来了,牛刀小试、程序ok

更多改进

1.首先工具集本身还缺乏一些过多的指令,比如我们常用的cd(切换目录)命令,我们可以继续自定义一些命令

2.实际上关于一个进程我们还可以再获取更多的信息,也即我们的ps命令可以修改一下多加一个参数。形如: ps 1676 这样调用时则列举出进程id为1676的搜有信息,这个信息可以有很多,包括其进程具体的:

  • 线程信息(Thread32FirstThread32Next
  • 模块信息(Module32FirstMoudle32Next
  • 堆信息(Heap32ListFirstHeap32Next
  • 内存使用情况(GetProcessMemoryInfo

3.kernel32.dll是windows的核心DLL,很多内核级别的API都需要从其中导出,其实上述的例子中关于查看进程信息的大部分介绍的都是该DLL导出的函数(如果上一个问题又解决的,仔细查看模块信息会可以找到到程序调用掉用的每一个DLL),不过不是在Windows API中而是在Tool help API中,需要引用的是 Tlhelp32.h (不是Windows.h)

关于进程一些信息操作,除了kernel32.dll中的Tool help API还有一个 PS API (从Psapi.dll中导出头文件为 psapi.h ),大家有兴趣的可以搜搜看,然后尝试用其中的函数来改写。

4.kill 命令需要我们通过一个进程的id来杀死它,有的时候找一个进程的id有些麻烦,不过找名字却很容易所以你可以考虑改写这个程序,让它可以通过名称来(etProcessIdByName函数)终止一个进程。

5.设置与获取环境变量( GetEnvironmentStrings,GetEnvironmentVariableSetEnvironmentVariable等)属于支线部分,大家可以研究使用这个API来跳过设置PATH系统环境变量,直接弄一个程序内部的环境变量就可以了,这样程序的可移植性更高。

小结

任何一个程序,想要运行那么必须为这个进程分配空间并且分配一个唯一的进程标识(进程ID),在上面的例子中我们有看到这样的额一串数据:

进程ID  父进程  线程数  优先级  进程名
1676    1956    36      8       explorer.exe
7904    1676    1       8       lscmd.exe
5844    7904    1       8       cmd.exe
4180    1676    1       8       lscmd.exe
5568    4180    1       8       ps.exe

在其中一个lscmd中打开cmd,我们很直观的就能看到cmd.exe的父进程就是该lscmd.exe。
仔细看我们可以发现,我打开了两个lscmd程序,而这个两个进程的父进程则是explorer.exe(1676)即我们的桌面,不论是通过win+r调用还是双击打开,实际上都需要explorer为其新建一个进程来运行我们打开的程序。

概念上理解之后,剩下的就是熟悉API了,各位可以参照上面的 “更多改进” 来编写一些程序提高API的熟练度

相关函数

大部分与进程或线程有关的函数可以在MSND的 Process and Thread Functions 中被找到。小部分,例如 PS API 中的一些函数就找不到了。

文章索引

上一讲:windows 编程之路(三) 文件系统
下一讲:windows 编程之路(五) 线程编程

C语言 设置控制台字体颜色 SetConsoleTextAttribute

BOOL WINAPI SetConsoleTextAttribute(
  _In_  HANDLE hConsoleOutput,	// 控制台输出流句柄
  _In_  WORD wAttributes		// 设置属性
);

hConsoleOutput [输入参数]

控制台屏幕的输出流的句柄(handle to console screen buffer)。这个文件流的句柄必须有写入(GENERIC_READ)的权限。更多的信息,请查看MSDN上的Console Buffer Security and Access Rights

wAttributes [输入参数]

具体常见设置属性如下:

属性 意义
FOREGROUND_BLUE 前景色包含 蓝色
FOREGROUND_GREEN 前景色包含 绿色
FOREGROUND_RED 前景色包含 红色
FOREGROUND_INTENSITY 前景色加强
BACKGROUND_BLUE 背景色包含 蓝色
BACKGROUND_GREEN 背景色包含 绿色
BACKGROUND_RED 背景色包含 红色
BACKGROUND_INTENSITY 背景色加强
COMMON_LVB_GRID_HORIZONTAL 顶部水平网格
COMMON_LVB_GRID_LVERTICAL 左竖直网格
COMMON_LVB_GRID_RVERTICAL 右竖直网格
COMMON_LVB_UNDERSCORE 下划线

代码测试

#include <windows.h>  
#include <stdio.h>
#include <stdlib.h>
  
int main()  
{  
	HANDLE hOut;  

	//  获取输出流的句柄
	hOut = GetStdHandle(STD_OUTPUT_HANDLE);    

	printf("普通颜色看看n");

	SetConsoleTextAttribute(hOut,  
	                        FOREGROUND_GREEN |		// 前景色_绿色
							FOREGROUND_INTENSITY ); // 前景色_加强
	printf("设置了浅绿色.n");  
	printf("并且完了之后就一直是浅绿色n");

	SetConsoleTextAttribute(hOut,  
	                        FOREGROUND_BLUE |		// 前景色_蓝色
							FOREGROUND_INTENSITY |	// 前景色_加强
							COMMON_LVB_UNDERSCORE);	// 添加下划线
	printf("文字蓝色,再加个下划线.n");    

	SetConsoleTextAttribute(hOut,  
	                        FOREGROUND_RED |		// 前景色_红色
							FOREGROUND_INTENSITY |	// 前景色_加强
							BACKGROUND_BLUE );		// 背景色_蓝色
	printf("设置文字红色,背景蓝色n");  

	SetConsoleTextAttribute(hOut,  
	                        FOREGROUND_RED |			// 前景色_红色
							FOREGROUND_INTENSITY |		// 前景色_加强
							COMMON_LVB_GRID_LVERTICAL );// 网格_左_竖
	printf("                      加 左 网格n");  

	SetConsoleTextAttribute(hOut,  
	                        FOREGROUND_RED |			// 前景色_红色
							FOREGROUND_INTENSITY |		// 前景色_加强
							COMMON_LVB_GRID_RVERTICAL );// 网格_右_竖  
	printf("                      加 右 网格n");    

	SetConsoleTextAttribute(hOut,  
	                        FOREGROUND_RED |	// 前景色_红色
							FOREGROUND_GREEN |	// 前景色_绿色
							FOREGROUND_BLUE );  // 前景色_蓝色
	printf("改回白色n");  

	system("pause");
	return 0;  
}

具体配色,请参见下图:

例如紫色可以用 蓝色+红色 配出来:

#include <windows.h>  
#include <stdio.h>	// for printf
#include <stdlib.h>	// for system
  
int main()  
{  
	HANDLE hOut;  

	//  获取输出流的句柄
	hOut = GetStdHandle(STD_OUTPUT_HANDLE);    

	SetConsoleTextAttribute(hOut,  
	                        FOREGROUND_RED |  // 前景色_红色
							FOREGROUND_BLUE | // 前景色_蓝色
							FOREGROUND_INTENSITY);// 加强
	printf(" 红 + 蓝 = 紫色 ^_^n");  

	system("pause");
	return 0;  
}  

再要配其他颜色的话,就要去查一些配色表了

工作室部分成员 博客列表

魔舟网络

魔舟(福州)网络科技有限公司成立于2013年3月,起源于魔舟工作室(2009年9月),位于东南沿海重要都市-福州。我们专注于企业级移动互联网应用开发及网络营销服务,帮助各个行业利用移动互联网创造价值,并成为用户长期的业务合作伙伴。我们的理想是利用移动互联网的特性,迎合用户使用习惯,为用户创造更多便捷的”无线可能”。

链接地址:魔舟网络

闽江魔舟

福大魔舟

网络魔舟

如果工作室内成员有新开博客或者为链接上可以联系博主补齐列表

cmd 设置PATH环境变量

环境变量

环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,比如临时文件夹位置和系统文件夹位置等。

PATH的作用

PATH 是一个很常见得环境变量。主要用来配置系统程序调用的默认路径。也就是当你在某个目录下执行某个程序,但是本身目录下却没有这个程序的时候,系统会自动到默认路径(也就是PATH中配置的路径)下面也去找一找,有没有你要执行的程序。

设置方式

【我的电脑】->右键【属性】->【高级】->【环境变量】->找到PATH然后在末尾加一个; 分号在添加你想要加入PATH路径的目录即可。

然后就可以在任意目录下,调用你放在该 PATH 路径中程序:

博主的 F:mytools 放了一个 touch.exe 原本是要切换到 F:mytools 下才能执行的,但是现在可以直接在默认目录下调用了,这就是添加 PATH 的好处。