bombless
随笔- 10  文章- 0  评论- 19 
博客园  社区  首页  新随笔  联系  管理  订阅 订阅
2011年12月23日
PHP用闭包思想写的torrent文件解析工具

 

本文原载于我的wp博客 http://mi-wiki.info/wp/2011/12/23/php-torrent-file-parser-again/

转载请注明。

 

PHP对静态词法域的支持有点奇怪,内部匿名函数必须在参数列表后面加上use关键字,显式的说明想要使用哪些外层函数的局部变量。
 
 1 function count_down($count)
2 {
3 return $func = function()
4 use($count,$func)
5 {
6 if(--$count > 0)
7 $func();
8 echo "wow\n";
9 };
10 }
11 $foo = count_down(3);
12 $foo();
 
我本来是想这样的。但是不行,会在第7行调用$func的时候报错。
错误是Fatal error: Function name must be a string in - on line 7
反复试验后发觉,外部的匿名函数应该通过引用传值传给内部,否则是不行的:

function count_down($count)
{
return $foo = function()
use(&$count,&$foo)
{
echo $count."\n";
if(--$count > 0)
$foo();
};
}
$foo = count_down(4);
$foo();


像上面这样写就对了。
下面是另一种方法:

 
function count_down_again($count)
{
return function()use($count)
{
printf("wow %d\n",$count);
return --$count;
};
}
$foo = count_down_again(5);
while($foo() >0);
 
不过,这段代码有点小错误。编译虽然没错,但是$foo函数每次返回的都是4.
也就是use关键字看上去像是支持静态词法域的,在这个例子上,它只是对外层函数使用的变量作了一个简单拷贝。
让我们稍微修改一下,把第3行的use($count)改为use(&$count):
 
function count_down_again($count)
{
return function()use(&$count)
{
printf("wow %d\n",$count);
return --$count;
};
}
$foo = count_down_again(5);
while($foo() >0);
这样才正确。

我个人使用的方式是基于类的,做成了类似下面的形式:
 
class Foo
{
public function __invoke($count)
{
if($count > 0)
$this($count - 1);
echo "wow\n";
}
}
$foo = new Foo();
$foo(4);
 
这样做的行为也是正确的。
这样不会像前一个例子那样失去了递归调用的能力。
虽然这是一个类,但是只不过是在手动实现那些支持闭包和静态词法域的语言中,编译器自动实现的动作。
 

其实今天早上,我本来准备用类scheme的风格写一个解析器的。可能稍微晚点吧。scheme风格的函数式编程是这样的:
 
function yet_another_count_down($func,$count)
{
$func($count);
if($count > 0)
yet_another_count_down($func,$count - 1);
}
yet_another_count_down(function($var){echo $var."\n";},6);
它不是很依赖静态词法域,虽然scheme对静态词法域的支持还是很不错的。它主要还是利用了first-class-function。当然,这也是一种典型的闭包。
 
我实现的torrent解析工具的代码如下:
 
 
<?php
$file_name = '1.torrent';
$file = fopen($file_name,'r');
$nil = new Parser($file);//构造解析器
$nil = $nil();//进行解析
$pos = ftell($file);
echo '读取到文件位置'.sprintf('0x%08X',$pos)."\r\n";
fseek($file,0,SEEK_END);
echo '还剩下'.(ftell($file) - $pos).'字节未读取'."\r\n";
if(!feof($file))
{
echo '文件还未结束,再读一个字符:';
$ch = fgetc($file);
if(is_string($ch) && ereg('\w',$ch))
{
echo $ch."\r\n";
}
else
{
printf('0x%02X',$ch);
echo "\r\n";
}
echo '现在的文件位置是'.sprintf('0x%08X',ftell($file))."\r\n";
echo '文件'.(feof($file)?'已结束':'还未结束')."\r\n";
}
fclose($file);//解析器后面不再工作了,此时可以释放文件指针了。
$info = @$nil['value'][0]['info'];
if(!$info)
{
echo '这是一个有效的B-Encoding文件,但它不是一个有效的种子文件';
exit();
}
$name = $info['name.utf-8'] ?$info['name.utf-8']:$info['name'];
if(!$name)
{
echo '这是一个有效的B-Encoding文件,但它不是一个有效的种子文件';
exit();
}
echo $name."\r\n";
if($info['files'])
{
$index = 0;
foreach($info['files'] as $f)
{
$index += 1;
$path = $f['path.utf8'] ?$f['path.utf8'] :$f['path'];
if(!$path)
{
echo '文件列表中的第'.$index."个文件不含目录\r\n";
continue;
}
if(0 === strpos($path[0],"_____padding_file_"))continue;
$under_folder = false;
foreach($path as $item)
{
if($under_folder)
{
echo '/';
}else{
$under_folder = true;
}
echo $item;
}
echo "\r\n";
}
}
else
{
echo "仅有一个文件\r\n";
}
class Parser
{
private $_file;
public function __construct($file)
{
$this ->_file = $file;
}
public function __invoke($parent = array())
{
$ch = $this ->read();
switch($ch)
{
case 'i':
{
$n = $ch;
while(($ch = $this ->read()) != 'e')
{
if(!is_numeric($ch))
{
echo '在';
echo sprintf(
'0x%08X',ftell($this ->_file));
echo '解析数字时遇到错误',"\r\n";
echo '在i和e之间不应该出现非数字字符'."\r\n";
echo '意外的字符'.sprintf('0x%02X',$ch);
exit();
}
else
{
$n .= $ch;
}
}
$n += 0;

$offset = count($parent['value']);
$parent['value'][$offset] = $n;
return $parent;
}
break;
case 'd':
{
$node = array();
//这个$node变量作为字典对象准备加入到$parent的孩子节点中去
//$node['type'] = 'd';

while('e' != ($tmp = $this($node)))
{//每次给$node带来一个新孩子
$node = $tmp;
}
$child_count = count($node['value']);
if($child_count % 2 != 0)
{
echo '解析结尾于';
echo sprintf('0x%08X',ftell($this ->_file));
echo '的字典时遇到错误:'."\r\n";
echo '字典的对象映射不匹配';
exit();
}
$product = array();
for($i = 0; $i < $child_count; $i += 2)
{
$key = $node['value'][$i];
$value = $node['value'][$i + 1];
if(!is_string($key))
{
echo '无效的字典结尾于';
echo sprintf('0x%08X',ftell($this ->_file));
echo ":\r\n";
echo '解析[k => v]配对时遇到错误,k应为字符串';
exit();
}
$product[$key] = $value;
}
/*
* 思想是这样的:子节点想要加入父节点时,
* 往父节点的value数组添加。
* 当父节点收集好所需的信息后,
* 父节点自身再从它的value节点整合内容
* 对于字典和列表统一这样处理会大大降低代码量
*/
$offset = count($parent['value']);
$parent['value'][$offset] = $product;
return $parent;
}
break;
case 'l';
{
$node = array();
while('e' != ($tmp = $this($node)))
{
$node = $tmp;
}
$offset = count($parent['value']);
$parent['value'][$offset] = $node['value'];
return $parent;
}
break;
case 'e':
return 'e';
break;
default:
{
if(!is_numeric($ch))
{
$this ->unexpected_character(
ftell($this ->_file) - 1,$ch);
}
$n = $ch;
while(($ch = $this ->read()) != ':')
{
$n .= $ch;
if(!is_numeric($n))
{
unexpected_character(
ftell($this ->_file) - 1,$ch);
}
}
$n += 0;
$str = '';
for(; $n > 0; --$n)
{
$str .= $this ->read();
}
$offset = count($parent['value']);
$parent['value'][$offset] = $str;
return $parent;
}
break;
}
}
/*
* read函数包裹了$this ->_file变量
*/
function read()
{
if(!feof($this ->_file))
{
return fgetc($this ->_file);
}else{
echo '意外的文件结束';
exit();
}
}


/*
* unexpected_character函数接收2个参数
* 它用于指明脚本在何处遇到了哪个不合法的字符,
* 并在返回前终止脚本的运行。
*/
function unexpected_character($pos,$val)
{
$hex_pos = sprintf("0x%08X",$pos);
$hex_val = sprintf("0x%02X",$val);
echo 'Unexpected Character At Position ';
echo $hex_pos.' , Value '.$hex_val."\r\n";

echo "Analysing Process Teminated.";
exit();
}
}
?>
这里很有趣的是,明明我对文件调用了fseek($file,0,SEEK_END);移动到文件末尾了,但是feof还是报告说文件没有结束,并且fgetc返回一个0,而没有报错。但是此时文件实际上已经到末尾了。
posted @ 2011-12-23 18:20 bombless 阅读(112) 评论(3) 编辑
2011年11月10日
我只是来刷屏的
摘要: #include <stdio.h>#include <windows.h>void ShowKey(char *key,int level){ HKEY hKey; if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT,key,0,KEY_ENUMERATE_SUB_KEYS,&hKey)){ LONG ret = ERROR_SUCCESS; int index = 0; char buffer[8192]; while(ret != ERROR_NO_MORE_ITEMS){ DWORD buffer_siz阅读全文
posted @ 2011-11-10 22:48 bombless 阅读(160) 评论(0) 编辑
2011年10月6日
windows小程序-用文字堆垒单色BMP位图
好久没更新了……贴一个最近做的图片转文字的大小东西吧=w=
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
//#define _UNICODE
#endif
//#include <stdio.h>
#include <windows.h>
#include <assert.h>
#include <malloc.h>
LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
	static HWND hwndText = NULL;
	static HWND hwndButton = NULL;
	static WCHAR *pcnChar = NULL;
	switch(msg)
	{
	case WM_CREATE:
		pcnChar = (WCHAR*)((LPCREATESTRUCT)lParam)->lpCreateParams;
		hwndText = CreateWindow(L"EDIT", L"",
            WS_CHILD | WS_VISIBLE | WS_BORDER,
            1, 1, 60, 60,
            hwnd, (HMENU)0, ((LPCREATESTRUCT)(lParam))->hInstance, NULL);
		hwndButton = CreateWindow(L"BUTTON", L"OK",
            WS_CHILD | WS_VISIBLE | WS_BORDER,
            65, 1, 60, 60,
            hwnd, (HMENU)1, ((LPCREATESTRUCT)(lParam))->hInstance, NULL);
	break;
	case WM_COMMAND:
		if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == 1){
			if(!GetWindowText(hwndText, pcnChar, 2) || *pcnChar < 0xFF){
				MessageBox(0,L"请输入一个汉字!",0,0);
				break;
			}
			DestroyWindow(hwnd);
		}
	break;
	case WM_DESTROY:
		if(!*pcnChar && *pcnChar < 0xFF)
		{
			MessageBox(0,L"获取汉字失败,现在退出",0,0);
			exit(0);
		}
		PostQuitMessage(0);
		//GetWindowText(hwndText, pcnChar, 1);
	break;
	}
	return DefWindowProc(hwnd,msg,wParam,lParam);
		
}
typedef struct{
	BYTE *byte_ptr;
	int bit_pointer;
}BitPointer;
void BitPointerInc(BitPointer *o)
{
	assert(0 <= o->bit_pointer && o->bit_pointer < 8);
	if(o->bit_pointer == 7){
		o->bit_pointer = 0;
		o->byte_ptr += 1;
	}else{
		o->bit_pointer += 1;
	}
}
void SetBit(BitPointer *o)
{
	assert(0 <= o->bit_pointer && o->bit_pointer < 8);
	*(o->byte_ptr) |= (1 << (7 - (o->bit_pointer))); //这里要注意比特的顺序……
}
int GetBit(BitPointer *o)
{
	assert(0 <= o->bit_pointer && o->bit_pointer < 8);
	return (*(o->byte_ptr)) & (1 << (7 - (o->bit_pointer))); //这里要注意比特的顺序……
}
void JmpByte(BitPointer *o)
{
	o->byte_ptr += 1;
	o->bit_pointer = 0;
}
int CountBits(BitPointer *o,BYTE *start)
{
	return ((o->byte_ptr) - start) * 8 + (o->bit_pointer);
}
static WCHAR g_szClassName[] = L"MyWindowClass";
static HINSTANCE g_hInstance;
int ReadBitmapData(BYTE *buffer,DWORD size,int nWidth,int nHeight,WCHAR **rslt)
{
	DWORD bitLineLength = (nWidth % 32 == 0)? nWidth: 32 - nWidth % 32 + nWidth;
	WCHAR **bitLine = NULL;
	int i,j;
	bitLine = (WCHAR**)malloc(nHeight * sizeof(WCHAR*));
	BitPointer ptr = { buffer, 0 };  
	for(i = nHeight - 1; i >= 0; --i)
	{
		bitLine[i] = (WCHAR*)malloc(nWidth * sizeof(WCHAR));
		ZeroMemory(bitLine[i],nWidth * sizeof(WCHAR));
		for(j = 0; j < nWidth; ++j,BitPointerInc(&ptr))
		{
			bitLine[i][j] = !GetBit(&ptr);
		}
		while(CountBits(&ptr, buffer) % bitLineLength != 0)
		{
			JmpByte(&ptr);
		}
		
	}

	
	static WCHAR cnChar[2] = L"\0";
	HWND hwnd = CreateWindow(
		g_szClassName,
		L"输入一个汉字",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, 240, 100,
		NULL, NULL, g_hInstance, cnChar);
	ShowWindow(hwnd,SW_SHOW);
	UpdateWindow(hwnd);
	MSG msg;
	while(GetMessage(&msg,NULL,0,0)){
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	if(*cnChar == L'\0')
	{
		MessageBox(0,L"程序出错,请向程序作者报告这个错误",0,0);
	}
	for(i = 0; i < nHeight; ++i)
	{
		bitLine[i] = (WCHAR*)realloc(bitLine[i],(nWidth+2)*sizeof(WCHAR));
		bitLine[i][nWidth] = L'\r';
		bitLine[i][nWidth+1] = L'\n';
		for(j = 0; j < nWidth; ++j)
		{
			if(bitLine[i][j])bitLine[i][j] = *cnChar;
			else bitLine[i][j] = L' ';
		}
		if(i > 0){
			bitLine[0] = (WCHAR*)realloc(bitLine[0],(i + 1)*(2 + nWidth) * sizeof(WCHAR));
			memcpy(bitLine[0] + ((i) * (2 + nWidth)),bitLine[i],(2 + nWidth) * sizeof(WCHAR));
			free(bitLine[i]);
		}
	}
	int nRslt = 1 + (i)*(2 + nWidth);
	bitLine[0] = (WCHAR*)realloc(bitLine[0],nRslt * sizeof(WCHAR));
	*(bitLine[0] + (i)*(2 + nWidth)) = 0;//terminate the unicode string
#ifdef _DEBUG
	MessageBox(0,bitLine[0],0,0);
#endif
	*rslt = bitLine[0];
	free(bitLine);
	return nRslt;
}

void Process(PWSTR filename)
{
	//MessageBox(0,filename,0,0);
	HANDLE hFile = CreateFile(filename, GENERIC_READ,
		FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0);
	assert(hFile != INVALID_HANDLE_VALUE);
	static BYTE buffer[8192];
	ZeroMemory(buffer,sizeof buffer);
	DWORD dwRead;
	assert(ReadFile(hFile, buffer, 2, &dwRead, NULL));
	assert(2 == dwRead);
	if(!!strcmp("BM",(const char*)buffer)){
		MessageBox(0,L"不是有效的BMP文件",0,0);
		exit(0);
	}
	
	assert(SetFilePointer(hFile,0x0E,0,FILE_BEGIN)); //跳过图像文件头
	//读图像信息头的第一个项,Bitmap Header Size,占2个双字
	assert(ReadFile(hFile, buffer, 4, &dwRead, NULL)); 
	assert(4 == dwRead && 4 == sizeof(DWORD));
	//*************************************************
	//测试计算机的字节序,暂时只支持小端对齐
	
	{
		BYTE edian_test[4] = { 0xAa, 0xBb, 0xCc, 0xDd };
		if((DWORD)0xDdCcBbAa != *(DWORD*)edian_test)
		{
			MessageBox(0,L"程序暂不支持大端的处理器\n"
				L"您可能在某些处理器上运行WinCE\n"
				L"或在ARM处理器上运行桌面版的Windows 8\n"
				L"本程序暂不处理这些情况,程序现在退出",
				L"程序的版本需要为此计算机更新",0);
			exit(0);
		}
	}
	//**************************************************/
	//
	//

	if(*(DWORD*)buffer != 0x28) //图像信息头的大小应为0x28
	{
		MessageBox(0,L"不认识这个类型的BMP文件,"
			L"如无特别原因请用Windows自带画图程序生成单色BMP文件",0,0);
		exit(0);
	}
	assert(SetFilePointer(hFile,0x02,0,FILE_BEGIN));
	assert(ReadFile(hFile, buffer, 4, &dwRead, NULL));
	assert(4 == dwRead);
	if(GetFileSize(hFile,NULL) != *(DWORD*)buffer)
	{
		wsprintf((PWSTR)buffer,L"BMP文件具有错误的文件大小,应为%d字节,实际为%d字节",
			*(DWORD*)buffer,GetFileSize(hFile,NULL));
		MessageBox(0,(PWSTR)buffer,L"图像文件破损",0);
		exit(0);
	}
	DWORD dwFileSize = *(DWORD*)buffer;
	assert(SetFilePointer(hFile,0x1C,0,FILE_BEGIN));
	assert(ReadFile(hFile, buffer, 2, &dwRead, NULL));
	assert(2 == dwRead);
	if(*(WORD*)buffer != 0x01) //mono BMP
	{
		MessageBox(0,L"这是一个正确的BMP文件,但本程序用于将单色BMP转为文字"
			L"如无特别原因请用Windows自带画图程序生成单色BMP文件\n"
			L"或使用Windows自带画图程序打开此图片后重新另存为单色BMP",0,0);
		exit(0);
	}
	assert(ReadFile(hFile, buffer, 2, &dwRead, NULL));
	assert(2 == dwRead);
	if(*(WORD*)buffer != 0){
		MessageBox(0,L"这是一个正确的单色BMP文件\n"
			L"但本程序暂时不处理此图片使用的RLE压缩方式\n"
			L"如无特别原因请用Windows自带画图程序生成单色BMP文件\n"
			L"或使用Windows自带画图程序打开此图片后重新另存为单色BMP",
			L"需要更新程序版本来支持此图片",0);
		exit(0);
	}

	assert(SetFilePointer(hFile,0x12,0,FILE_BEGIN));
	DWORD nWidth,nHeight;
	assert(ReadFile(hFile, buffer, 4, &dwRead, NULL));
	assert(4 == dwRead);
	nWidth = *(DWORD*)buffer;
	assert(ReadFile(hFile, buffer, 4, &dwRead, NULL));
	assert(4 == dwRead);
	nHeight = *(DWORD*)buffer;
	assert(ReadFile(hFile, buffer, 4, &dwRead, NULL));
	assert(4 == dwRead);
	if(0x00010001 != *(DWORD*)buffer) //小端字节序
	{
		MessageBox(0,L"位面数和像素占用位数应为1",
			L"破损的单色BMP图片",0);
		exit(0);
	}
	DWORD dwStartOffset = 0;
	assert(SetFilePointer(hFile,0x0A,0,FILE_BEGIN));
	assert(ReadFile(hFile, buffer, 4, &dwRead, NULL));
	assert(4 == dwRead);
	dwStartOffset = *(DWORD*)buffer;
	assert(SetFilePointer(hFile,0x22,0,FILE_BEGIN));
	assert(ReadFile(hFile, buffer, 4, &dwRead, NULL));
	assert(4 == dwRead);
	DWORD dwBitmapDataSize = *(DWORD*)buffer;
	if(dwBitmapDataSize != dwFileSize - dwStartOffset)
	{
		MessageBox(0,L"BMP文件破损,数据不一致",0,0);
		exit(0);
	}
	assert(SetFilePointer(hFile,dwStartOffset,0,FILE_BEGIN));
	BYTE *dataSpace = (BYTE*)malloc(dwBitmapDataSize);
	assert(dataSpace);
	assert(ReadFile(hFile, dataSpace, dwBitmapDataSize, &dwRead, NULL));
	assert(dwBitmapDataSize == dwRead);
	WCHAR *rslt = NULL;
	int nRslt = ReadBitmapData(dataSpace,dwBitmapDataSize,nWidth,nHeight,&rslt);
	free(dataSpace);
	dataSpace = NULL;
	HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE,nRslt * sizeof(WCHAR));
	void *p = GlobalLock(gData);
	assert(p);
	memcpy(p,rslt,nRslt * sizeof(WCHAR));
	OpenClipboard(NULL);
	
	EmptyClipboard();
	SetClipboardData(CF_UNICODETEXT,gData);
	CloseClipboard();
	GlobalUnlock(gData);
	GlobalFree(gData);
	MessageBox(0,L"结果已保存到剪切板,现在可以把结果黏贴到任意位置了",L"",0);
	

}

int WINAPI WinMain(HINSTANCE hinst,HINSTANCE hpreInst,PSTR lpCmdLine,int nShow)
{
	g_hInstance = hinst;
	OPENFILENAME ofn;
	WCHAR szFileName[MAX_PATH] = L"";

	ZeroMemory(&ofn, sizeof(ofn));

	ofn.lStructSize = sizeof(ofn);
	ofn.lpstrFilter = L"Text Files (*.bmp)\0*.bmp\0All Files (*.*)\0*.*\0\0";
	ofn.lpstrFile = szFileName;
	ofn.nMaxFile = MAX_PATH;
	ofn.lpstrDefExt = L"bmp";
	ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	if(!GetOpenFileName(&ofn)){
		MessageBox(0,L"查找文件失败",0,0);
		return 0;
	}
	WNDCLASS WndClass = {0};
	WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
	WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
	WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	WndClass.lpfnWndProc   = WndProc;
	WndClass.lpszClassName = g_szClassName;
	if(!RegisterClass(&WndClass))
	{
		MessageBox(0, L"Window Registration Failed!", L"Error!",
		MB_ICONEXCLAMATION | MB_OK | MB_SYSTEMMODAL);
		return 0;
	}
	Process(szFileName);

	return 0;
}

我把它叫做bmp-to-text.c程序呵呵~

gcc -o b2t bmp-to-text.c -finput-charset=gbk -mwindows

把BMP单色位图转换成文字。。。

例如做一个位图,在位图上画一个云字,可以产生下面的效果:

     云云云云云云云云云云云云云云云云云云云云云云云            
     云云云云云云云云云云云云云云云云云云云云云云云            
                                        
                                        
                                        
                                        
                                        
                                        
                                        
                                        
  云云云云云云云云云云云云云云云云云云云云云云云云云云云云云         
  云云云云云云云云云云云云云云云云云云云云云云云云云云云云云         
             云云云                        
            云云云                         
            云云云                         
           云云云                          
           云云云       云                  
          云云云       云云云                 
          云云云        云云云                
         云云云         云云云                
        云云云           云云云               
        云云云            云云云              
       云云云             云云云云             
      云云云               云云云             
     云云云云                云云云            
    云云云云云云云云云云云云云云云云云云云云云云云云云           
     云云云云云云云云云云云云         云云云           
     云                     云云云          
                            云           

也就是没事随便玩玩~



posted @ 2011-10-06 19:01 bombless 阅读(43) 评论(0) 编辑
2010年12月30日
.NET中利用反射功能遍历系统预定义的画刷和颜色

昨天晚上在做GDI+的一个图形程序,想试试系统中的哪个预定义颜色比较适合作为人脸的颜色,

结果犹豫了半天没选好。如果做个小程序直接把各种预定义的颜色一行一行展示出来那不就方便了!

 

后面参考了一篇关于反射的文章,感觉利用反射可以达到我们的目标。

 

参考的是这篇文章:

http://download.csdn.net/source/1184896 (第02个小程序:遍历画笔(FlipThroughTheBrushes.cs))

 

程序比较短小,就是利用Brushes这个类型做反射,遍历里面的所有的域,然后一行一行画在我们的主窗口上。

 

下面是整个小程序。(做的过程中试了下用VS2005的命令行程序做编译,然后用EditPlus码代码,感觉这样挺能练习对语言的熟练度的,呵呵)

 

代码
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
namespace ShowColor
{
struct BrushWithName
{
public Brush Value;
public String Name;
}
public class ShowColor : Form
{
public static void Main(string[] args)
{
Application.Run(
new ShowColor());
}
public ShowColor()
{
this.Text = "ShowColor程序";
this.ClientSize = new Size(800, 600);
this.Paint += new PaintEventHandler(this.PaintForm);
this.SizeChanged += new EventHandler(this.RefreshForm);
}
private void RefreshForm(object sender, EventArgs e)
{
this.Invalidate();
}

private void PaintForm(object sender, PaintEventArgs e)
{
PropertyInfo[] props;
props
= typeof(Brushes).GetProperties();
const int numGroup = 4;
BrushWithName[][] brushGroups
= new BrushWithName[numGroup][];
int brushCount = 0;
int brushEachGroup = props.Length / numGroup;
for (int i = 0; i < numGroup; ++i)
{
BrushWithName[] brushSubGroup;
brushSubGroup
= new BrushWithName[brushEachGroup];
for (int j = 0; j < brushSubGroup.Length && brushCount < props.Length; ++j, ++brushCount)
{
brushSubGroup[j].Value
= (Brush)props[brushCount].GetValue(null, null);
brushSubGroup[j].Name
= props[brushCount].Name;
}
brushGroups[i]
= brushSubGroup;
}
Size clientSize
= this.Size;
Size blockSize
= new Size(clientSize.Width / numGroup, clientSize.Height / brushEachGroup);
Point location
= new Point();
for (int i = 0; i < brushGroups.Length; ++i)
{
for (int j = 0; j < brushGroups[i].Length; ++j)
{
Graphics g
= e.Graphics;
if (brushGroups[i][j].Value == null) continue;
g.FillRectangle(brushGroups[i][j].Value,
new Rectangle(location, blockSize));
if (blockSize.Height > 10)
{
Font font
= new Font("宋体", 10);
string output = brushGroups[i][j].Name;
g.DrawString(output, font, Brushes.Black, location);
}
location.Offset(
0, blockSize.Height);
}
location
= new Point(location.X + blockSize.Width, 0);
}
}
}
}

 

 

这里主要都是做GDI+的画图。我特意用了下二级数组,因为以前我做类似的任务都喜欢用List泛型,感觉还是要多熟练下多维数组的用法。

 

不过List泛型里的ForEach方法我还是觉得蛮过瘾,因为用匿名委托感觉就像是在做函数式编程~

 

下面是程序的输出画面~在各种平台里运行这东西应该显示效果都不会差太远吧~

 

posted @ 2010-12-30 17:42 bombless 阅读(149) 评论(0) 编辑
2010年12月29日
在MinGW或VS 2005 SP1环境下创建一个要求UAC权限的程序。

之前在给U盘挂自制操作系统的那篇教程里,我给写了一个pdev程序用来输出windows下,系统各磁盘的主引导记录。

 

不过那个程序用起来每次都要记得用管理员权限启动程序,这多少增加了不便。(特别是对带UAC功能的win7\vista来说)。

 

如果能让程序启动时自动要求提权,那当然更符合windows程序的一般习惯。

 

我查到了一篇这种做法的教程:

http://www.zu14.cn/2010/05/14/delphi-win32-program-on-windows7-vista-uac-administrator-rights/

 

也就是需要用到以下这个xml文件(保存成uac.manifest文件,不过还可以保存成任意的.manifest文件也行。

程序清单文件
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

 

下面具体讲讲在MinGW或者VS2005 SP1的情况下该怎么做。

 

VS2005下简单,只需要在项目的资源里加这个uac.manifest文件即可。

我在VS2005的“添加资源”一项里没有找到“添加程序清单”,索性我选择了添加xml文件,并保存为uac.manifest,把以上内容复制到文件中,

再编译,就生成了启动时会提示UAC提权的提示框。(这种要求UAC提权的程序不能直接用F5调试运行,在VC里如果要直接看效果可以按Ctrl + F5 直接运行)。

 

在MinGW下,这个问题要稍复杂那么一点点。

 

需要在.rc资源文件里添加一个

#include <winuser.h>
1 RT_MANIFEST uac.manifest

这一段。添加#include <winuser.h>文件包含是因为在winuser.h里有对RT_MANIFEST声明,

#define RT_MANIFEST 24

把这段资源脚本文件保存为uac.rc,放到与前面说的uac.manifest相同的目录下,最后需要用MinGW工具中的windres程序将它处理成.res资源文件。

命令如下:

windres --input-format=rc -O coff -i uac.rc -o uac.res

这样就产生了uac.res资源文件,再接下来用gcc将它和程序源文件一起编译就可以了。于是问题解决!

当然如果嫌这样的.rc文件长了,或者不愿意include一个winuser.h文件,可以写成更加简短的版本:

1 24 uac.manifest

 

效果一样。(在VC中也可以做同样处理,如果你想在资源里添加对程序清单的描述的话。)

 

写这篇文章参考了以下几个网址,如果需要深入这个问题可以作为参考:

http://www.cnblogs.com/dflying/archive/2007/03/21/683190.html  

(Windows Vista for Developers——第四部分:用户帐号控制(User Account Control,UAC))

这篇文章需要详细关注“使用应用程序清单”这一部分。

 

http://msdn.microsoft.com/en-us/library/aa381043%28v=VS.85%29.aspx

http://msdn.microsoft.com/fr-fr/library/bb773175%28en-us,VS.85%29.aspx

 

上面2篇MSDN文章中关于RT_MANIFEST的部分值得注意。

 

总之原理就是,PE文件的资源中有一种类型是RT_MANIFEST,它的内容实际上是一个xml文件,被称为程序清单文件。

换句话说,这个xml格式的.manifest文件文件会被打包进可执行程序文件中。

而程序清单文件中描述了运行程序需要的权限,因此借助这个机制,有UAC管理功能的windows系统会在启动程序时向用户要求提权。

 

下面把我的pdev程序贴一下,作为一个要求UAC提权的程序的有趣的例子。它可以判断系统中有哪些磁盘,并打印出磁盘的主引导记录。

(注意,这类程序即使在命令行打开,windows仍然会打开一个新的命令行窗口运行程序,并且程序路径是一个完整路径,

因此这时候我们的命令行程序不得不总是在将要推出前用系统的pause命令做暂停,否则用户看到的会是一个一闪而过的窗口。

 

 

代码
#include <stdio.h>
#include
<stdlib.h>
#include
<windows.h>
void ShowError(){
void *pError;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
FORMAT_MESSAGE_FROM_SYSTEM
|
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,GetLastError(),
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
(LPTSTR)
&pError,0,NULL);
MessageBox(
0,(PTSTR)pError,0,0);
LocalFree(pError);
}
void ReadDevice(HANDLE hDeviceHandle,int DriveNumber,FILE *output_file){
BYTE buffer[
512];
DWORD nBytes;
int iCount;
if(hDeviceHandle != INVALID_HANDLE_VALUE){
printf(
"#正在尝试读取物理驱动器%d ...",DriveNumber);
ReadFile(hDeviceHandle,buffer,
sizeof buffer,&nBytes,NULL);
}
else{
if(GetLastError() == ERROR_FILE_NOT_FOUND && DriveNumber > 0){
printf(
"#以上%d项就是系统中现有的全部物理驱动器。",DriveNumber);
system(
"pause");
exit(
0);
}
perror(
"读取设备时发生错误");
puts(
"请检查您是否正以管理员账户运行此程序,");
printf(
"或许物理驱动器%d不存在.\n",DriveNumber);
ShowError();
system(
"pause");
exit(
-1);
}
if(nBytes > 0){
printf(
"\n#读取了%d字节的数据:\n",nBytes);
for(iCount = 0; iCount < sizeof buffer; ++iCount){
if(iCount % 16 == 0){
printf(
"\n %04X\t|",iCount);
}
printf(
" %02X",buffer[iCount]);
}
printf(
"\n ----\t ");
for(iCount = 0; iCount < 16; ++iCount){
printf(
" --");
}
printf(
"\n\n");
if(output_file != NULL){
fwrite(buffer,
sizeof buffer,1,output_file);
printf(
"已将以上%d字节的数据写入文件。\n",sizeof buffer);
}

}
else{
perror(
"失败。");
ShowError();
system(
"pause");
exit(
-1);
}
}
int main(int argc,char *argv[]){
HANDLE hDeviceHandle
= NULL;
int iCount;
FILE
*output_file = NULL;
TCHAR DriveNameBuffer[MAX_PATH]
= TEXT("");
PTSTR DriveNamePrefix
= TEXT("\\\\.\\PHYSICALDRIVE");
if(argc > 1){//带命令行参数,说明用户在命令行里使用本程序
if(argc == 3 && !strcmp("-o",argv[1])){
printf(
"#以512字节为单位连续输出到文件%s...\n",argv[2]);
output_file
= fopen(argv[2],"wb");
if(output_file == NULL){
perror(
"无法打开指定的文件");
system(
"pause");
exit(
-1);
}
}
else{
printf(
"Usage:\n"
"%s -o [filename ...]"
"\n将系统中所有的物理驱动器的主引导记录依次写入文件。\n",
argv[
0]);
printf(
"\n不合法的命令行参数,将退出程序。");
system(
"pause");
exit(
0);
}
}
else{//不带命令行参数,很可能用户是直接双击程序运行的。
output_file = NULL;
}
for(iCount = 0; iCount < 256; ++iCount){
wsprintf(DriveNameBuffer,TEXT(
"%s%d"),DriveNamePrefix,iCount);
hDeviceHandle
= CreateFile(
DriveNameBuffer,
GENERIC_READ,FILE_SHARE_READ,
NULL,OPEN_EXISTING,
0,0);
ReadDevice(hDeviceHandle,iCount,output_file);
CloseHandle(hDeviceHandle);
}
return 0;
}

编译之前我们先用之前的uac.manifest文件和uac.rc文件准备好我们的uac.res资源文件:

windres --input-format=rc -O coff -i uac.rc -o uac.res

之后将uac.res文件和程序一起打包:

gcc -o pdev pdev.c uac.res

 

这下大功告成!

posted @ 2010-12-29 20:43 bombless 阅读(449) 评论(0) 编辑
用GNU工具集产生一个无格式的二进制指令文件

本来是用gas汇编器的ld -Ttext 0x0命令后,总是得到在一个代码仍然从0x200开头的文件。

这一度使我以为这是gas的设计错误或是怎么的。

还在昨天写的教程里留下错误的理解:

http://www.cnblogs.com/bombless/archive/2010/12/28/writing-X86-os-part1.html

 

今天又参考了一篇文章,

http://dev.csdn.net/htmls/31/31794.html

终于理解应该怎么办了。

以以下汇编代码为例:

 

.global start
.code16
.text
start:
movw $0xb800,%ax
movw %ax,%ds
movb $0x41,(
0)
movb $0x1f,(
1)
hlt

保存为文件gas.s。

 

只需要下面几个步骤,就可以生成一个只有代码的纯二进制指令文件gas.bin:

as -o gas.o gas.s

objcopy -O binary gas.o gas.bin

posted @ 2010-12-29 19:27 bombless 阅读(195) 评论(2) 编辑
2010年12月28日
手写一个X86操作系统实战:从零开始构建一个U盘启动的自制操作系统(一)
摘要: 看完本教程,读者应该可以手写一个在PC上运行的不需要其他软件来协助的自启动的代码,这无疑是一个完整的操作系统的基础。阅读全文
posted @ 2010-12-28 23:36 bombless 阅读(1353) 评论(11) 编辑
2010年11月21日
在SDL工程中让SDL_ttf渲染汉字
摘要: 有时候在关于SDL的博文中看到一些评论,说SDL对中文的支持不佳,因为当程序涉及中文时总是输出乱码。照我个人观点,这里面很多都是误解。下面就根据我在windows下使用SDL的情况,说说我的观点。SDL作为一个跨平台的库,在字符方面有它独特的地方。那就是,它的运行库支持的字符编码为UTF8,而不是windows中常见的各种本地字符编码。比如中文版windows使用的codepage 936,也有称...阅读全文
posted @ 2010-11-21 15:23 bombless 阅读(513) 评论(0) 编辑
2010年11月2日
C#小程序——从百度摘取搜索结果。
摘要: 百度不使用xhtml,这样使得.NET原有的XML功能就不是那么好用了。(而且,谁会真正喜欢DOM呢?用起来多累人啊!)不过百度的页面很不规则,所以迫不得已使用了大量的硬编码。因此,这个程序对百度的页面设计做了相当多的假设,无法很好的适应百度的页面结构在未来的改变。还好这种小程序写起来轻松,所以没事改一改也没事。另外这个程序使用了大量的正则表达式,这可能会使得它在效率上不适合于用来整合各个搜索引擎...阅读全文
posted @ 2010-11-02 18:29 bombless 阅读(367) 评论(3) 编辑
2010年10月29日
小发现,关于windows窗口中的F10快捷键
摘要: 今天本来在学习Direct3D9,运行一个窗口的时候发觉,如果在键盘上按下F10,画面就会停止,再按一下F10以后,画面又继续显示。后来打开其他程序再测试,发觉原来是按F10的时候就相当于按下了选中菜单的快捷键(相当于Alt键)。而众所周知,当一个窗口在拖动之类的情况下是不会更新画面的。于是就有了这种奇怪的表现。可是我的窗口明明没有菜单,却可以被选中?(在win7系统下得出的结果)这可以说完全是一...阅读全文
posted @ 2010-10-29 14:00 bombless 阅读(246) 评论(0) 编辑
仅列出标题  
Copyright ©2012 bombless