Apr 25, 2009

改写为C/S模式

上次贴的C代码有不少不规范的地方
随着学习的深入我把代码改写了下,写成C/S模式,但是运行时客户端只能连上一次服务器然后就断开。正在DEBUG

----------------------------makefile-------------------------------------------
# This makefile will generate "filetool" with fileoperator.c and fileoperator.h
# Author: Youbin Wang
# Date: 2009.04.17

OBJECTS=menu.o puberr.o simplecli.o
INCLUDE=-I/home/user01/NO05/include
MYPATH=/home/user01/NO05/bin
SERVEROBJ=simplesrv.o

all:filetool server

filetool: $(OBJECTS)
gcc -o filetool $(OBJECTS)
mv filetool $(MYPATH)

menu.o:menu.c
gcc $(INCLUDE) -c menu.c
`
puberr.o:puberr.c
gcc $(INCLUDE) -c puberr.c

simplecli.o:simplecli.c
gcc $(INCLUDE) -c simplecli.c

server : $(SERVEROBJ) puberr.o
gcc -o server $(SERVEROBJ) puberr.o
mv server $(MYPATH)

simplesrv.o:simplesrv.c
gcc $(INCLUDE) -c simplesrv.c

clean:
rm $(OBJECTS) $(SERVEROBJ)


------------------------------------menu.c-----------------------------------
/* -------------------------------------------------------
* $Source : menu.c 16-apr-2009.17:00:00 Youbin Wang $
* Function: Provide main menu and invoke client socket function
* Author : Youbin Wang
* Record :
2009.04.24 Change programme to c/s structure.
* 2009.04.22 Add errlog.
* 2009.04.16 Add the Version commentary.
* 2009.04.15 Create the main programme.
* ------------------------------------------------------- */
#include
#include
#include
#include
#include


/* -------------------------------------------------------
* global values
* ------------------------------------------------------- */
char * g_syslog="mytest";
char * g_print_str="testfile";

/* -------------------------------------------------------
* Show main menu if user haven't decided to exit the system
* or they just finish one operation
* Input parameter: none
* Output Parameter: return 0 while execute successfully
* ------------------------------------------------------- */
int show_menu()
{
printf(" --------------------------------------------\n");
printf(" Welcome TO This Programme \n");
printf(" This is the Main Menu \n");
printf(" --------------------------------------------\n");
printf("\n");
printf("Please Make your selection\n");
printf(" 1: Create a new file and write;\n");
printf(" 2: Open a file and write;\n");
printf(" 3: Open a file with read-only mode;\n");
printf(" 4: Exit;\n");
printf("\n");
return 0;
}


/* -------------------------------------------------------
* if the user choose to exit the system then invoke this function
* InputParameter:None
* OutputParameter: retrun 0 while execute successfully
* ------------------------------------------------------- */
int exit_system()
{
printf("\n");
printf("Exiting.........\n");
printf("\n");
printf(" Thank you for using this system.\n");
printf("\n");
printf(" If you have any problems or advices,contract the author at qisi1986@gmail.com\n");
printf("\n");
printf(" Wish you a good day. Bye Bye.\n");
return 0;
}

/* -------------------------------------------------------
* invoke if the user input the selection
* InputParamete r: the selection of user
* OutputParameter: return 0 while successfully
* Hint : if the user put the wrong key then it will provide hints
* ------------------------------------------------------- */
int selection(int Index)
{
int i_return=1;
printf("Your Selection is %d \n", Index);
printf("Now will invoke corresponding function,please wait.");
printf("\n");
sleep(1);
switch(Index)
{
case 1:
i_return = use_socket("127.0.0.1",1);
break;
case 2:
i_return = use_socket("127.0.0.1",2);
break;
case 3:
i_return = use_socket("127.0.0.1",3);
break;
case 4:
exit_system();
break;
default:
printf("Error!Please Enter a correct number!\n");
pub_err(2,__FILE__,__LINE__,g_syslog,"Enter an error number %s\n",g_print_str);
sleep(1);
}
if(0 == i_return)
printf("Operation successfully!!\n");
else
{
printf("Opeartion failed, please retry!\n");
i_return = 1;
return 1;
}
i_return = 1;
return 0;
}

int main()
{
int inputInt;
system("clear");
while(inputInt != 4)
{
system("clear");
show_menu();
printf("Enter your selection here: ");
scanf("%d", &inputInt);
selection(inputInt);
}
return 0;
}

-------------------------------puberr.c----------------------------------
#include
#include
#include
#include
#include
#include

#define FNAME_LEN 128
void pub_time(long *plHostDate, long *plHostTime)
{
struct tm *p;
time_t tmp;

time(&(tmp));
p = localtime(&tmp);
p->tm_year += 1899;

if (plHostDate != NULL)
*plHostDate =
(p->tm_year + 1) * 10000 + (p->tm_mon + 1) * 100 + p->tm_mday;
if (plHostTime != NULL)
*plHostTime = p->tm_hour * 10000 + p->tm_min * 100 + p->tm_sec;
return;
}


int _p_log(char *tmp_buf)
{
char logname[FNAME_LEN];
FILE *fp_log;
long l_date, l_time;

/*错误日志 */
pub_time(&l_date, &l_time);
sprintf(logname, "%s/log/pub_err%ld.log", getenv("HOME"),l_date);
// sprintf(logname, "../log/pub_err%ld.log", l_date);

if ((fp_log = fopen(logname, "a")) != NULL) {
fprintf(fp_log, "[%06ld/%06ld] %s\n", l_date % 1000000, l_time, tmp_buf);

fclose(fp_log);
}
else
{
fprintf(stderr, "[%06ld/%06ld] %s\n", l_date % 1000000, l_time, tmp_buf);
}

return 0;
}

/*
int pub_err(ret_code, file, line, recode, ...)
int ret_code;
char *file;
long line;
char *recode
*/
int pub_err(int ret_code, char *file, long line, char *recode, ...)
{
short i;
char *fmt, tmp_buf[2048], log[2048];
va_list args;

memset(tmp_buf, 0, 2048);
va_start(args,recode);
fmt = va_arg(args, char *);
vsprintf(tmp_buf, fmt, args);
va_end(args);
if (ret_code >= 0)
{
sprintf(log, "run message [%s][%ld][%s]", file, line,tmp_buf);
_p_log(log);
}
return ret_code;
}


------------------------------simplecli.c--------------------------------
/* ----------------------------------------------------------------
* simplecli.c
* Function:provide use_socket to set up a client socket
* and send the selection of user to server
* Record of Modification:
* 2009.04.24:Create function user_socket and test main
* ---------------------------------------------------------------- */

#include
#include
#include
#include
#include
#include


// define the defualt connect port id
#define SERVER_PORT 20130
// define the defualt client port as a random port
#define CLIENT_PORT ((20001+rand())%65536)
#define BUFFER_SIZE 255
#define REUQEST_MESSAGE "welcome to connect the server.\n"


int use_socket(char* ip, int i_select)
{
int servfd,clifd,length = 0;
int recvbyte;
struct sockaddr_in servaddr,cliaddr;
socklen_t socklen = sizeof(servaddr);
char buf[BUFFER_SIZE];

if ((clifd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
printf("create socket error!\n");
exit(1);
}
srand(time(NULL));//initialize random generator

bzero(&cliaddr,sizeof(cliaddr));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(CLIENT_PORT);
cliaddr.sin_addr.s_addr = htons(INADDR_ANY);

bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
//inet_aton(argv[1],&servaddr.sin_addr);
//servaddr.sin_addr.s_addr=inet_addr(ip);
inet_aton(ip,&servaddr.sin_addr);
servaddr.sin_port = htons(SERVER_PORT);
//servaddr.sin_addr.s_addr = htons(INADDR_ANY);

if (bind(clifd,(struct sockaddr*)&cliaddr,sizeof(cliaddr))<0)
{
printf("bind to port %d failure!\n",CLIENT_PORT);
exit(1);
}
if (connect(clifd,(struct sockaddr*)&servaddr, socklen) < 0)
{
printf("can't connect to %s!\n",ip);
exit(1);
}

send(servfd,(char *)&i_select,sizeof(i_select),0);
length = recv(clifd,(char *)&recvbyte,sizeof(recvbyte),0);
if(length < 0)
{
printf("error comes when recieve data from server %s!", ip);
exit(1);
}
if(recvbyte == 0)
{
close(clifd);
return 0;
}
else
{
close(clifd);
exit(1);
}
}

/*-----------this is the test main function of use_socket-----------
int main()
{
int i=3;
if(use_socket("127.0.0.1", i))
printf("successful");
else
printf("failed");
return 0;

}-------------------------------------------------------------------*/

--------------------------simplesrv.c-------------------------------------
/* -------------------------------------------------------
* simplesrv.c
* Function:Start the server and listen a port
* When client send data, it recieve data
* deal with the data and send feedback info
* Record of Modification:
* 2009.04.23: Create it.
* -------------------------------------------------------*/
#include
#include
#include
#include
#include
#include
#include
#include "puberr.h"

#define SERVER_PORT 20130 // define the defualt connect port id
#define LENGTH_OF_LISTEN_QUEUE 10 //length of listen queue in server
#define BUFFER_SIZE 255
#define WELCOME_MESSAGE "welcome to connect the server. "


/* -------------------------------------------------------
* global values
* ------------------------------------------------------- */
char * g_syslog="mytest";
char * g_print_str="testfile";

/* -------------------------------------------------------
* invoke when the user choose to open a file with readonly mode(3)
* InputParameter:None
* OutputParameter: return 0 if execute successfully, 1 if error occured
* ------------------------------------------------------- */
int open_file_readonly()
{
char c_output;
FILE *fl_p_readonly;
fl_p_readonly = fopen("mytext.txt","rt");

printf("Reading mytext.txt ...\n");
if(NULL==fl_p_readonly)
{
pub_err(2, __FILE__, __LINE__, g_syslog, "Cannot open file mytext.txt in read-only mode %s\n", g_print_str);
getchar();
return 1;
}

printf("Open file successfully! \n");
sleep(1);
c_output=fgetc(fl_p_readonly);
while(c_output!=EOF)
{
printf("Reading the text...\n");
sleep(1);
putchar(c_output);
c_output=fgetc(fl_p_readonly);
}
fclose(fl_p_readonly);
pub_err(2, __FILE__, __LINE__, g_syslog,"Close the file %s\n", g_print_str);
return 0;
}

/* -------------------------------------------------------
* invoke when the user choose to create a new file(1)
* InputParameter: None
* OutputParameter: return 0 if execute successfully, 1 if error accured
* -------------------------------------------------------*/
int create_file()
{
printf("Creating a file ..\n");
FILE *fl_createfile;
fl_createfile = fopen("new.txt","wt+");
if(NULL==fl_createfile)
{
pub_err(2, __FILE__, __LINE__, g_syslog, "Cannot create a file %s\n",g_print_str);
getchar();
return 1;
}
char c_inputchar;
printf("Successfully create a file...\n");
sleep(1);
printf("Please input your text: \n");
while (c_inputchar!='\n')
{
printf("I'm reading your input\n");
fputc(c_inputchar,fl_createfile);
c_inputchar=getchar();
}
/* rewind the position of file pointer to the top of file */
rewind(fl_createfile);
printf("Your inputment is: ");
c_inputchar=fgetc(fl_createfile);
while(c_inputchar!=EOF)
{
printf("I'm writing your input\n");
putchar(c_inputchar);
c_inputchar=fgetc(fl_createfile);
}
printf("\n");
fclose(fl_createfile);
printf("Done!!\n");
sleep(1);
return 0;
}

/* -------------------------------------------------------
* if the user choose to open a exist file(2) and write then invoke this function
* InputParameter:None
* OutputParameter: return 0 if execute successfully, 1 if an error occured
* ------------------------------------------------------- */
int open_file()
{
printf("Opening a file...\n");
FILE *fl_openfile;
fl_openfile = fopen("mytext.txt","wt+");
if(NULL==fl_openfile)
{
pub_err(2,__FILE__,__LINE__,g_syslog,"Cannot open a file %s\n",g_print_str);
getchar();
return 1;
}

char c_inputchar;
printf("Successfully open a file");
printf("Please input your text: \n");
while (c_inputchar!='\n')
{
fputc(c_inputchar,fl_openfile);
c_inputchar=getchar();
}
/* rewind the position of file pointer to the top of file */
rewind(fl_openfile);
printf("Your inputment is: ");
c_inputchar=fgetc(fl_openfile);
while(c_inputchar!=EOF)
{
putchar(c_inputchar);
c_inputchar=fgetc(fl_openfile);
}
printf("\n");
fclose(fl_openfile);
printf("Done!!\n");
sleep(1);
return 0;
}

/* -------------------------------------------------------
* invoke if the user input the selection
* InputParamete r: the selection of user
* OutputParameter: return 0 while successfully
* Hint : if the user put the wrong key then it will provide hints
* ------------------------------------------------------- */
int selection(int i_input)
{
switch(i_input)
{
case 1:
create_file();
break;
case 2:
open_file();
break;
case 3:
open_file_readonly();
break;
default:
pub_err(2,__FILE__,__LINE__,g_syslog,"Enter an error number %s\n",g_print_str);
return 1;
}
return 0;
}


int main(int argc, char **argv)
{
int servfd,clifd,recvbyte;
int length=0;
int sel_feedback=1;
struct sockaddr_in servaddr,cliaddr;
if ((servfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
printf("create socket error!\n");
exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERVER_PORT);
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
if (bind(servfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
{
printf("bind to port %d failure!\n",SERVER_PORT);
exit(1);
}
if (listen(servfd,LENGTH_OF_LISTEN_QUEUE) < 0)
{
printf("call listen failure!\n");
exit(1);
}

while (1)
{//server loop will nerver exit unless any body kill the process
char buf[BUFFER_SIZE];
long timestamp;
socklen_t length = sizeof(cliaddr);
clifd = accept(servfd,(struct sockaddr*)&cliaddr,&length);
if (clifd < 0)
{
printf("error comes when call accept!\n");
break;
}
strcpy(buf,WELCOME_MESSAGE);
//inet_ntop(INET_ADDRSTRLEN,cliaddr.sin_addr,buf,BUFFER_SIZE);
printf("from client,IP:%s,Port:%d\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
timestamp = time(NULL);
strcat(buf,"timestamp in server:");
strcat(buf,ctime(×tamp));
//send(clifd,buf,BUFFER_SIZE,0);
length=recv(servfd,(char *)&recvbyte,sizeof(recvbyte),0);
if(length<0)
{
printf("error comes when recieve data from client");
close(clifd);
exit(1);
}
sel_feedback = selection(recvbyte);
send(clifd,(char *)sel_feedback,sizeof(sel_feedback),0);
close(clifd);
sel_feedback = 1;
}//exit
close(servfd);
return 0;
}

Apr 22, 2009

vi常用命令

进入vi的命令
  vi filename :打开或新建文件,并将光标置于第一行首
  vi +n filename :打开文件,并将光标置于第n行首
  vi + filename :打开文件,并将光标置于最后一行首
  vi +/pattern filename:打开文件,并将光标置于第一个与pattern匹配的串处
  vi -r filename :在上次正用vi编辑时发生系统崩溃,恢复filename
  vi filename....filename :打开多个文件,依次进行编辑
  
  移动光标类命令[/b]
  h :光标左移一个字符
  l :光标右移一个字符
  space:光标右移一个字符
  Backspace:光标左移一个字符
  k或Ctrl+p:光标上移一行
  j或Ctrl+n :光标下移一行
  Enter :光标下移一行
  w或W :光标右移一个字至字首
  b或B :光标左移一个字至字首
  e或E :光标右移一个字至字尾
  ) :光标移至句尾
  ( :光标移至句首
  }:光标移至段落开头
  {:光标移至段落结尾
  nG:光标移至第n行首
  n+:光标下移n行
  n-:光标上移n行
  n$:光标移至第n行尾
  H :光标移至屏幕顶行
  M :光标移至屏幕中间行
  L :光标移至屏幕最后行
  0:(注意是数字零)光标移至当前行首
  $:光标移至当前行尾
  
  屏幕翻滚类命令
  Ctrl+u:向文件首翻半屏
  Ctrl+d:向文件尾翻半屏
  Ctrl+f:向文件尾翻一屏
  Ctrl+b;向文件首翻一屏
  nz:将第n行滚至屏幕顶部,不指定n时将当前行滚至屏幕顶部。
  
  插入文本类命令
  i :在光标前
  I :在当前行首
  a:光标后
  A:在当前行尾
  o:在当前行之下新开一行
  O:在当前行之上新开一行
  r:替换当前字符
  R:替换当前字符及其后的字符,直至按ESC键
  s:从当前光标位置处开始,以输入的文本替代指定数目的字符
  S:删除指定数目的行,并以所输入文本代替之
  ncw或nCW:修改指定数目的字
  nCC:修改指定数目的行
  
  删除命令
  ndw或ndW:删除光标处开始及其后的n-1个字
  do:删至行首
  d$:删至行尾
  ndd:删除当前行及其后n-1行
  x或X:删除一个字符,x删除光标后的,而X删除光标前的
  Ctrl+u:删除输入方式下所输入的文本
  
  搜索及替换命令
  /pattern:从光标开始处向文件尾搜索pattern
  ?pattern:从光标开始处向文件首搜索pattern
  n:在同一方向重复上一次搜索命令
  N:在反方向上重复上一次搜索命令
  :s/p1/p2/g:将当前行中所有p1均用p2替代
  :n1,n2s/p1/p2/g:将第n1至n2行中所有p1均用p2替代
  :g/p1/s//p2/g:将文件中所有p1均用p2替换
  
  选项设置
  all:列出所有选项设置情况
  term:设置终端类型
  ignorance:在搜索中忽略大小写
  list:显示制表位(Ctrl+I)和行尾标志($)
  number:显示行号
  report:显示由面向行的命令修改过的数目
  terse:显示简短的警告信息
  warn:在转到别的文件时若没保存当前文件则显示NO write信息
  nomagic:允许在搜索模式中,使用前面不带“\”的特殊字符
  nowrapscan:禁止vi在搜索到达文件两端时,又从另一端开始
  mesg:允许vi显示其他用户用write写到自己终端上的信息
  
  最后行方式命令
  :n1,n2 co n3:将n1行到n2行之间的内容拷贝到第n3行下
  :n1,n2 m n3:将n1行到n2行之间的内容移至到第n3行下
  :n1,n2 d :将n1行到n2行之间的内容删除
  :w :保存当前文件
  :e filename:打开文件filename进行编辑
  :x:保存当前文件并退出
  :q:退出vi
  :q!:不保存文件并退出vi
  :!command:执行shell命令command
  :n1,n2 w!command:将文件中n1行至n2行的内容作为command的输入并执行之,若不指定n1,n2,则表示将整个文件内容作为command的输入
  :r!command:将命令command的输出结果放到当前行
  
  寄存器操作
  "?nyy:将当前行及其下n行的内容保存到寄存器?中,其中?为一个字母,n为一个数字
  "?nyw:将当前行及其下n个字保存到寄存器?中,其中?为一个字母,n为一个数字
  "?nyl:将当前行及其下n个字符保存到寄存器?中,其中?为一个字母,n为一个数字
  "?p:取出寄存器?中的内容并将其放到光标位置处。这里?可以是一个字母,也可以是一个数字
  ndd:将当前行及其下共n行文本删除,并将所删内容放到1号删除寄存器中。

Apr 21, 2009

写软件的需求分析全方位攻略

原文:http://blog.csdn.net/broadview2006/archive/2009/04/21/4096395.aspx

Apr 20, 2009

Oracle Test05

其它数据库对象
1. 创建序列empno_seq,开始值为8000,每次增长2,最大值为9000,其他参数自选
2. 使用数据字典视图user_sequences查询序列的信息
3. 使用empno_seq向emp表中插入数据,要求empno的值为empno_seq的递增值,其他字段的值任意.
4. 为emp表中的员工姓名(ename)定义索引
5. 使用数据字典user_indexs查看索引的信息
6. 使用sys/sys身份登录,为scott.emp建立同义词emp_synm

Oracle Test04

视 图
1. 使用表emp创建视图emp_view,其中包括姓名(ename),员工号(empno,工资(sal),部门号(deptno).
2. 显示视图的结构
3. 查询数据字典视图user_views,检查视图的定义
4. 查询emp_view视图中的全部数据
5. 从emp_view中查询工资最高的三位员工信息
6. 修改视图emp_view,将视图中的数据限定在部门号是30的范围内

Oracle Test03

约 束
1. 向表emp的id列中添加PRIMARY KEY约束(my_emp_id_pk)
2. 向表dept的id列中添加PRIMARY KEY约束(my_dept_id_pk)
3. 查询数据字典,获取约束的信息
4. 向表emp中添加列dept_id,并在其中定义FOREIGN KEY约束,与之相关联的列是dept表中的id列。
5. 查询数据字典,获取约束的信息

Oracle Test02

创建和管理表
1. 创建表departments
name type
id Number(4)
name Varchar2(25)
Hiredate Date
2. 将表dept中的数据插入表departments中
3. 创建表employees
name type
id Number(4)
name Varchar2(25)
deptno Number(2)
4. 将列name的长度增加到50
5. 查询数据字典视图user_tables检查刚才的操作
6. 根据表employees创建his_employees
7. 删除表employees
8. 将表his_employees重命名为employees
9. 在表departments和employees中添加新列test_column,并在数据字典中检查所作的操作

Oracle Test01

1. 使用PL/SQL Developer创建表my_employee,字段见第三题表头显示,字段统一定义为Varchar2(20).

2. 显示表my_employee的结构

3. 向表中插入下列数据,并提交

ID

FIRST_NAME

LAST_NAME

USERID

SALARY

1

patel

Ralph

Rpatel

895

2

Dancs

Betty

Bdancs

860

3

Biri

Ben

Bbiri

1100

4

Newman

Chad

Cnewman

750

5

Ropeburn

Audrey

Aropebur

1550

4. 3号员工的last_name修改为“drelxer,并提交

5. 将所有工资少于1000的员工的工资修改为1000,(不提交),并设置回滚点

6. 删除my_employee表中所有数据(不提交)

7. 回滚到第五题中的设置的回滚点

8. 删除表my_employee中所有数据,并提交

Apr 16, 2009

c写的小玩意

最近在上ProC的培训课程,感觉这门课不是一般的杂,刚开始是unix,接下来是unix下c编程,接下来是proc,接下来是tux,unix下c编程还推荐用vi,理由是某些公司里是禁止用ue的。呵呵,今天还是用ue登陆到老师开的虚拟机服务器ftp上面将c文件载下来写。最近写到这里了
用C写果然最有成就感。。。不像用delphi,大部分是开发环境生成的,成就感少了一点。
c语言博大精深,千万不要觉得c++/Java更洋气一些。James Gosling现在就后悔当初Java引入了类,现在有多少人正在将Java当成OOP语言真正的在用呢?
c是除了汇编外最让人尊重的语言,老前辈,一定要牢牢掌握
用c写的是控制台下应用的一个小软件,提供主菜单,根据选择调用相应函数。命名规则采用unix环境下推荐的命名规则。只不过缩进我还是用空格敲,不喜欢用tab,虽然可能每次要敲4个或8个空格我还是敲,呵呵

------------------------------------------.c文件------------------------------------------------------
/*
* fileoperator.c
* Programme Name:Exercise
* Copyright@ Hundun Version: V 1.0
* Function:Provide main menu under console, invoke certain function according to user's choice
* Author:Youbin Wang
* Record of Modification:
* 2009.04.16 Add the Version commentary. Separate the .c file and .h file, add a function
* open_file_readonly() by Youbin Wang
* 2009.04.15 Create the main programme,
* declare and define Selection,ShowMenu,CreateFile,OpenFile,ExitSystem by Youbin Wang
*/
#include
#include
#include"user01_20090416.h"


/*
* Show main menu if user haven't decided to exit the system
* or they just finish one operation
* Input parameter: none
* Output Parameter: return 1 while execute successfully
*/
int show_menu()
{
printf("/*--------------------------------------------\n");
printf(" * Welcome TO This Programme \n");
printf(" * This is the Main Menu \n");
printf(" *--------------------------------------------*/\n");
printf("\n");
printf("Please Make your selection\n");
printf(" 1: Create a new file and write;\n");
printf(" 2: Open a file and write;\n");
printf(" 3: Open a file with read-only mode;\n");
printf(" 4: Exit;\n");
printf("\n");
return 1;
}

/*
* if the user choose to open a file with readonly mode(3) then invoke this function
* InputParameter:None
* OutputParameter: return 1 if execute successfully, return 0 if a error occured while open a file
*/
int open_file_readonly()
{
char c_output;
FILE *fl_p_readonly;
fl_p_readonly = fopen("mytext.txt","rt");
if(NULL==fl_p_readonly)
{
printf("\nCannot open file readonly, press any key to continue!\n");
getchar();
return 0;
}
printf("Successfully read a file\n");
sleep(1);
c_output=fgetc(fl_p_readonly);
while(c_output!=EOF)
{
printf("I'm reading the text\n");
sleep(1);
putchar(c_output);
c_output=fgetc(fl_p_readonly);
}
fclose(fl_p_readonly);
return 1;
}
/*
* if the user choose to create a new file(1) then invoke this function
* InputParameter: None
* OutputParameter: return 1 while execute successfully, o while error accured in creating file
*/
int create_file()
{
printf("Creating a file...\n");
FILE *fl_createfile;
fl_createfile = fopen("new.txt","wt+");
if(NULL==fl_createfile)
{
printf("\nCannot create file, press any key to continue!\n");
getchar();
return 0;
}
char c_inputchar;
printf("Successfully create a file!\n");
sleep(1);
printf("Please input your text: \n");
while (c_inputchar!='\n')
{
printf("I'm reading your input\n");
fputc(c_inputchar,fl_createfile);
c_inputchar=getchar();
}
/* rewind the position of file pointer to the top of file */
rewind(fl_createfile);
printf("Your inputment is: ");
c_inputchar=fgetc(fl_createfile);
while(c_inputchar!=EOF)
{
printf("I'm writing your input\n");
putchar(c_inputchar);
c_inputchar=fgetc(fl_createfile);
}
printf("\n");
fclose(fl_createfile);
printf("Done!!\n");
return 1;
}

/*
* if the user choose to open a exist file(2) and write then invoke this function
* InputParameter:None
* OutputParameter: return 1 while execute successfully, 0 when a error occured in creating or read file
*/
int open_file()
{
printf("Opening a file...\n");
FILE *fl_openfile;
fl_openfile = fopen("mytext.txt","wt+");
if(NULL==fl_openfile)
{
printf("\nCannot open file, press any key to continue!\n");
getchar();
return 0;
}

char c_inputchar;
printf("Successfully open a file");
printf("Please input your text: \n");
while (c_inputchar!='\n')
{
fputc(c_inputchar,fl_openfile);
c_inputchar=getchar();
}
/* rewind the position of file pointer to the top of file */
rewind(fl_openfile);
printf("Your inputment is: ");
c_inputchar=fgetc(fl_openfile);
while(c_inputchar!=EOF)
{
putchar(c_inputchar);
c_inputchar=fgetc(fl_openfile);
}
printf("\n");
fclose(fl_openfile);
printf("Done!!\n");
return 1;
}

/*
* if the user choose to exit the system then invoke this function
* InputParameter:None
* OutputParameter: retrun 1 while execute successfully
*/
int exit_system()
{
printf("\n");
printf("Exiting.........\n");
printf("\n");
printf(" Thank you for using this system.\n");
printf("\n");
printf(" If you have any problems or advices,contract the author at qisi1986@gmail.com\n");
printf("\n");
printf(" Wish you a good day. Bye Bye.\n");
return 1;
}

/*
* if the user input the selection, then the function 'main' will invoke this function
* InputParameter: the selection of user
* OutputParameter: return 1 while successfully
* if the user put the wrong key then it will provide hints
*/
int Selection(int Index)
{
printf("Your Selection is %d \n", Index);
printf("Now will invoke corresponding function,please wait.");
printf("\n");
sleep(1);
switch(Index)
{
case 1:
create_file();
break;
case 2:
open_file();
break;
case 3:
open_file_readonly();
break;
case 4:
exit_system();
break;
default:
printf("Error!Please Enter a correct number!\n");
sleep(1);
}
return 1;
}

int main()
{
char inputInt;
system("clear");
while(inputInt != 4)
{
system("clear");
show_menu();
printf("Enter your selection here: ");
scanf("%d", &inputInt);
Selection(inputInt);
}
return 1;
}

------------------------------------------------.h文件------------------------------------------
/*
* fileoperator.h
* Copyright@ Hundun Version: V 1.0
* Function:contains all the declaration of functions in fileoperator.c
* Author: Youbin Wang
* Record of Modification:
* 2009.04.16 Create the .h File, add open_file_readonly by Youbin Wang
*/
#include

/*
* The declaration of functions
* CreateFile,OpenFile, ExitSystem are invoked by Selection
*/
int show_menu();
int create_file();
int open_file();
int exit_system();
int selection(int index);
int open_file_readonly();

Apr 15, 2009

毕业设计(新增加密解密工具)




1:配置文件里新增了加密的相关配置信息。主要就是两个,一个是加密的关键字符串key,另外一个是一个0~1之间的小数(最好只到小数点后两位)。它们共同构成了加密的算法。算法是我从网上搜刮来的,我改了一下。原先我将算法直接用来加密memo里的所有内容,结果发现会发生一些小概率的加密未完成便中止的现象。后来我把它改成逐行读取逐行加密就没有问题了。这个BUG真的很奇怪,我已经发给老大了,希望他有时间帮我看一下。我数学不行啊。哎~~~~~



2:该图是输入明文后的效果。这个加密工具可以独立使用也可以嵌入到任何一个工具里。只要包含了加密工具的单元,调用它的一个hasIniOriginalText或者hasIniDecodedText将其设置为true,就可以讲你要加密的内容从你的工具里导入到加密工具的明文或密文输入栏了。


3:上图为加密后的脚本内容,看不懂吧,呵呵。这个密文可以放到明文显示栏里再加密,每次加密可以更换不同的key和percent,只要记住顺序,一次次的用正确的key和percent解密就行了,很方便很强大吧。。。真是要感谢贴了这个加密算法的蝈蝈。之前上网搜了,论坛问了,有人让我用MD5加密,这个加密虽然破解还是有一定难度,可是解密也很麻烦的。当然,我对MD5甚至对密码学根本是门外汉,上述完全是凭直觉哈。

下边贴出我稍微改动后的加密算法代码:

var
frmCryptograph: TfrmCryptograph;

key: string;
percent1: Double;

implementation

{$R *.dfm}

function TfrmCryptograph.DeCode(aCryptograph, aKey: string): string;
var
i,keylen,codelen:integer;
begin
keylen :=Length(akey);
codelen:=Length(aCryptograph);
SetLength(Result, Length(aCryptograph));
for i:=1 to codelen do
begin
Result[i]:=Chr(Ord(aCryptograph[i])-Ord(aKey[(i mod KeyLen)+1]));
end;
end;


function TfrmCryptograph.EnCode(aCryptograph, aKey: string): string;
var
i,keylen,codelen:integer;
begin
keylen:=Length(akey);
codelen:=Length(aCryptograph);
SetLength(Result, Length(aCryptograph));
for i:=1 to codelen do
begin
Result[i]:=Chr(Ord(aCryptograph[i])+Ord(aKey[(i mod KeyLen)+1]));
end;
end;

function TfrmCryptograph.GetKey(aKey: string; aPercent: Double): string;
var
i:integer;
begin
SetLength(Result,Length(aKey));
for i:=1 to Length(aKey) do
begin
Result[i]:=Chr(Round(Ord(aKey[i])*aPercent));
end;
end;

procedure TfrmCryptograph.btnEncodeClick(Sender: TObject);
var
i: Integer;
str: string;
begin
mmoOutput.Clear;
pgcCryptograph.ActivePageIndex := 1;
for i := 0 to mmoInput.Lines.Count do
begin
str := EnCode(mmoInput.Lines.Strings[i],GetKey(Key,Percent1));
mmoOutput.Lines.Append(str);
end;
end;

procedure TfrmCryptograph.btnDecodeClick(Sender: TObject);
var
i: Integer;
str: string;
begin
mmoInput.Clear;
pgcCryptograph.ActivePageIndex := 0;
for i := 0 to mmoOutput.Lines.Count do
begin
str := DeCode(mmoOutput.Lines.Strings[i],GetKey(Key,Percent1)) ;
mmoInput.Lines.Append(str)
end;
end;

procedure TfrmCryptograph.FormCreate(Sender: TObject);
begin
initEnDeCodeForm(Sender);
pgcCryptograph.ActivePageIndex := 0;
end;

procedure TfrmCryptograph.initEnDeCodeForm(Sender: TObject);
var
iniFileName: string;
begin
{如果没有初始化的明文输入,则清空}
if not withIniOriginalText then
mmoInput.Clear;
if not withIniDecodedText then
mmoOutput.Clear;
iniFileName := 'config\config.ini';
with TInifile.Create(iniFileName) do
begin
percent1 := ReadFloat('CRYPTOGRAPHY','PERCENT',0);
key := ReadString('CRYPTOGRAPHY','KEY','');
Free;
end;
end;

procedure TfrmCryptograph.FormShow(Sender: TObject);
begin
initEnDeCodeForm(Sender);
end;

procedure TfrmCryptograph.btnImportEncodeClick(Sender: TObject);
begin
dlgOpenCryptograph.Execute;
mmoInput.Clear;
try
mmoInput.Lines.LoadFromFile(dlgOpenCryptograph.FileName);
except
Exit;
// MessageBox(Handle, '读取文件出错,请重试', '提示', MB_OK);
end;
end;

procedure TfrmCryptograph.btnExportEncodeClick(Sender: TObject);
begin
dlgSaveCryptograph.Execute;
try
mmoInput.Lines.SaveToFile(dlgSaveCryptograph.FileName + '.sql');
except
Exit;
// MessageBox(Handle, '保存文件出错,请重试', '提示', MB_OK);
end;
end;

procedure TfrmCryptograph.btnImportDecodeClick(Sender: TObject);
begin
dlgOpenCryptograph.Execute;
mmoInput.Clear;
try
mmoInput.Lines.LoadFromFile(dlgOpenCryptograph.FileName);
except
Exit;
// MessageBox(Handle, '读取文件出错,请重试', '提示', MB_OK);
end;
end;

procedure TfrmCryptograph.btnExportDecodeClick(Sender: TObject);
begin
dlgSaveCryptograph.Execute;
try
mmoOutput.Lines.SaveToFile(dlgSaveCryptograph.FileName + '.sql');
except
Exit;
// MessageBox(Handle, '保存文件出错,请重试', '提示', MB_OK);
end;
end;

procedure TfrmCryptograph.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
mmoInput.Clear;
mmoOutput.Clear;
withIniOriginalText := False;
withIniDecodedText := False;
end;

总结:这个小工具花了我一天多的时间。其中大部分用来找加密未完成却中断的原因了,结果还是没有找到,可见要成为IT中的牛人,数学是多么重要。虽然实际开发中未必用得了算法,未必要那么考虑效率,可是,对于一个程序员来说,追求完美是一种天性,应该坚持下去。

Apr 14, 2009

查看SQL SERVER 加密存储过程,函数,触发器,视图

原作出处:http://dev.csdn.net/develop/article/23/23218.shtm
作者:CSDN某大虾
读后感:目前难以理解透

create PROCEDURE sp_decrypt(@objectname varchar(50))
AS
begin
set nocount on
--CSDN:j9988 copyright:2004.07.15
--V3.2
--破解字节不受限制,适用于SQLSERVER2000存储过程,函数,视图,触发器
--修正上一版"视图触发器"不能正确解密错误
--发现有错,请E_MAIL:CSDNj9988@tom.com
begin tran
declare @objectname1 varchar(100),@orgvarbin varbinary(8000)
declare @sql1 nvarchar(4000),@sql2 varchar(8000),@sql3 nvarchar(4000),@sql4 nvarchar(4000)
DECLARE @OrigSpText1 nvarchar(4000), @OrigSpText2 nvarchar(4000) , @OrigSpText3 nvarchar(4000), @resultsp nvarchar(4000)
declare @i int,@status int,@type varchar(10),@parentid int
declare @colid int,@n int,@q int,@j int,@k int,@encrypted int,@number int
select @type=xtype,@parentid=parent_obj from sysobjects where id=object_id(@objectname)

create table #temp(number int,colid int,ctext varbinary(8000),encrypted int,status int)
insert #temp SELECT number,colid,ctext,encrypted,status FROM syscomments WHERE id = object_id(@objectname)
select @number=max(number) from #temp
set @k=0

while @k<=@number begin if exists(select 1 from syscomments where id=object_id(@objectname) and number=@k) begin if @type='P' set @sql1=(case when @number>1 then 'ALTER PROCEDURE '+ @objectname +';'+rtrim(@k)+' WITH ENCRYPTION AS '
else 'ALTER PROCEDURE '+ @objectname+' WITH ENCRYPTION AS '
end)

if @type='TR'
begin
declare @parent_obj varchar(255),@tr_parent_xtype varchar(10)
select @parent_obj=parent_obj from sysobjects where id=object_id(@objectname)
select @tr_parent_xtype=xtype from sysobjects where id=@parent_obj
if @tr_parent_xtype='V'
begin
set @sql1='ALTER TRIGGER '+@objectname+' ON '+OBJECT_NAME(@parentid)+' WITH ENCRYPTION INSTERD OF INSERT AS PRINT 1 '
end
else
begin
set @sql1='ALTER TRIGGER '+@objectname+' ON '+OBJECT_NAME(@parentid)+' WITH ENCRYPTION FOR INSERT AS PRINT 1 '
end

end
if @type='FN' or @type='TF' or @type='IF'
set @sql1=(case @type when 'TF' then
'ALTER FUNCTION '+ @objectname+'(@a char(1)) returns @b table(a varchar(10)) with encryption as begin insert @b select @a return end '
when 'FN' then
'ALTER FUNCTION '+ @objectname+'(@a char(1)) returns char(1) with encryption as begin return @a end'
when 'IF' then
'ALTER FUNCTION '+ @objectname+'(@a char(1)) returns table with encryption as return select @a as a'
end)

if @type='V'
set @sql1='ALTER VIEW '+@objectname+' WITH ENCRYPTION AS SELECT 1 as f'

set @q=len(@sql1)
set @sql1=@sql1+REPLICATE('-',4000-@q)
select @sql2=REPLICATE('-',8000)
set @sql3='exec(@sql1'
select @colid=max(colid) from #temp where number=@k
set @n=1
while @n<=CEILING(1.0*(@colid-1)/2) and len(@sql3)<=3996 begin set @sql3=@sql3+'+@' set @n=@n+1 end set @sql3=@sql3+')' exec sp_executesql @sql3,N'@sql1 nvarchar(4000),@ varchar(8000)',@sql1=@sql1,@=@sql2 end set @k=@k+1 end set @k=0 while @k<=@number begin if exists(select 1 from syscomments where id=object_id(@objectname) and number=@k) begin select @colid=max(colid) from #temp where number=@k set @n=1 while @n<=@colid begin select @OrigSpText1=ctext,@encrypted=encrypted,@status=status FROM #temp WHERE colid=@n and number=@k SET @OrigSpText3=(SELECT ctext FROM syscomments WHERE id=object_id(@objectname) and colid=@n and number=@k) if @n=1 begin if @type='P' SET @OrigSpText2=(case when @number>1 then 'CREATE PROCEDURE '+ @objectname +';'+rtrim(@k)+' WITH ENCRYPTION AS '
else 'CREATE PROCEDURE '+ @objectname +' WITH ENCRYPTION AS '
end)


if @type='FN' or @type='TF' or @type='IF'
SET @OrigSpText2=(case @type when 'TF' then
'CREATE FUNCTION '+ @objectname+'(@a char(1)) returns @b table(a varchar(10)) with encryption as begin insert @b select @a return end '
when 'FN' then
'CREATE FUNCTION '+ @objectname+'(@a char(1)) returns char(1) with encryption as begin return @a end'
when 'IF' then
'CREATE FUNCTION '+ @objectname+'(@a char(1)) returns table with encryption as return select @a as a'
end)

if @type='TR'
begin

if @tr_parent_xtype='V'
begin
set @OrigSpText2='CREATE TRIGGER '+@objectname+' ON '+OBJECT_NAME(@parentid)+' WITH ENCRYPTION INSTEAD OF INSERT AS PRINT 1 '
end
else
begin
set @OrigSpText2='CREATE TRIGGER '+@objectname+' ON '+OBJECT_NAME(@parentid)+' WITH ENCRYPTION FOR INSERT AS PRINT 1 '
end

end

if @type='V'
set @OrigSpText2='CREATE VIEW '+@objectname+' WITH ENCRYPTION AS SELECT 1 as f'

set @q=4000-len(@OrigSpText2)
set @OrigSpText2=@OrigSpText2+REPLICATE('-',@q)
end
else
begin
SET @OrigSpText2=REPLICATE('-', 4000)
end
SET @i=1

SET @resultsp = replicate(N'A', (datalength(@OrigSpText1) / 2))

WHILE @i<=datalength(@OrigSpText1)/2
BEGIN

SET @resultsp = stuff(@resultsp, @i, 1, NCHAR(UNICODE(substring(@OrigSpText1, @i, 1)) ^
(UNICODE(substring(@OrigSpText2, @i, 1)) ^
UNICODE(substring(@OrigSpText3, @i, 1)))))
SET @i=@i+1
END
set @orgvarbin=cast(@OrigSpText1 as varbinary(8000))
set @resultsp=(case when @encrypted=1
then @resultsp
else convert(nvarchar(4000),case when @status&2=2 then uncompress(@orgvarbin) else @orgvarbin end)
end)
print @resultsp

set @n=@n+1

end

end
set @k=@k+1
end

drop table #temp
rollback tran
end

Apr 13, 2009

毕业设计(未完工)

前言: 上周的时间几乎都花在搞毕设上了。感觉做的这个毕业设计很奇怪,没有参考代码就算了,连需求文档,软件架构分析,模块划分等都没有,直接就是开了几个小会,说了些功能要求就让我们自己写。没法,TA的老大们最近忙着推出4.0版本的TA系统,没空理我们。我们10F这些实习生闲的心慌慌,以为毕设没法完成了。谁知道Delphi这么强大。。开发进度很快,已经差不多要完工了。贴几张图上来记录一下成果
格式: 图在上,解说在下.




1 :这是框架的配置文件,主要用于配置登录数据库的方案名,用户名(暂时没把密码考虑进去,不然还要考虑加密,况且数据库目前只支持Oracle)。登录窗口直接从这里读取这两个配置文件从而进行初始化这里使用了Delphi中的TIniFile类进行配置文件的读取和写入。确实是非常好用的一个类。
可以在配置文件上直接更改。若检测到改动会提示保存与否。




2:这是登录成功后从系统工具菜单里打开的新报表生成工具的第一页。主要用于查询现有可用报表,从一个专门的数据库表单中读取数据并显示在DBGrid中。这一页使用的是DBGrid,确实没有DBGridEh方便,为了写一个随鼠标滚轮而移动TDataSource的游标,并且实现在移动到表格边界时自动翻页,我得另外写个窗口过程,将DBGrid原先的窗口过程保存为一个TWndMethod的变量,将新的窗口过程赋给它。在新的窗口过程中要捕获WM_MOUSEWHEEL消息并做如下处理
if Message.WParam >0 then
ADBGrid.DataSource.DataSet.MoveBy(-1)
else
ADBGrid.DataSource.DataSet.MoveBy(1);
对于非鼠标滚轮消息则调用保存的原DBGrid窗口过程处理。同时,如果要在不同页里其他DBGrid里实现相同效果,我得做类似的处理。还要小心非鼠标滚轮的消息不会被忘记处理,不然马上会报错。
而如果使用DBGridEh,它是自带的属性 -_-##
上边蓝色部分两个Edit组件是用来跟踪当前选中报表信息,可以随顺表滚轮滚动动态更新为当前所在记录行



3:这是第2个页面。主要功能是绿色部分的SQL语句查询功能并在下边的表格里显示。在SQL语句输入框上有几个功能键:可以从外部带入一个SQL脚本,可以将输入的SQL脚本导出到硬盘,清空SQL输入框内容,执行SQL语句,另外带了一个拼写帮助。如右上角对话框所示。
可以在显示结果的DBGridEh中直接修改记录数据,并且修改将会被自动保存(暂时不支持回滚)。令人感动的DBGridEh自带了even和odd行颜色不同的功能,如果用普通的DBGrid又得自己写属性。



3:这是第三个字段设置页。主要用来勾选新报表要采用的字段,grid里的checkbox还没有加上去,因为要修改原表结构,添加至少三个字段进去,所以要问下导师要不要加。可以查询所有可用字段,可以添加和删除字段。
这一页可以从第一页双击GRID中某行数据跳转过来,如果是这样的话左边的新报表信息栏会已从第一页选中的报表配置为模版导入。图中左边显示的就是导入报表模版的结果



4:最后一个是SQL脚本生成。将生成新报表的操作全部翻译为SQL语句,导出成为脚本,如果某台计算机上装了相应的数据库导入了相应的表,那只要运行这个脚本,就可以在任意计算机上生成根据配置结果生成的新报表。

总结:一切都只草草带过。具体技术细节有时间我会陆续发上来,不过其实也没什么难的,是Delphi这把刀太好使了。如果是用MFC,这框架就得累死我。
这个框架写的还是不错的,可扩展性很强。有新的功能可以独立编写一个功能模块然后把相应的FORM做一些格式上更改就可以直接加入现有的框架了。但我觉得还不够,如果所有的功能模块都可以写成DLL文件形式的那就跟方便了,主程序也不会越写越大。

同时我有个想法,因为DLL文件调试很不方便(一旦生成了就没法跟进文件调了),而调试DLL的方法除了专门建个测试工程,还剩一个使用系统日志调试的。我想写个这样的DLL调试工具,liangpei2008在CSDN里回复说建议我用COM+写. GOSH,我还不清楚COM+是什么呢!慢慢来。总是会懂的,我要加油

咖啡全集

拿铁咖啡

拿铁咖啡是意大利浓缩咖啡与牛 奶的经典混合,意大利人也很喜欢把拿铁作为早餐的饮料。意大利人早晨的厨房里,照得到阳光的炉子上通常会同时煮着咖啡和牛奶。喝拿铁的意大利人,与其说他 们喜欢意大利浓缩咖啡,不如说他们喜欢牛奶,也只有espresso才能给普普通通的牛奶带来让人难以忘怀的味道。

意大利式拿铁咖啡 (Caffe Latte)需要一小杯Espresso和一杯牛奶(150~200毫升),拿铁咖啡中牛奶多而咖啡少,这与Cappuccino有很大不同。拿铁咖啡做 法极其简单,就是在刚刚做好的意大利浓缩咖啡中倒入接近沸腾的牛奶。事实上,加入多少牛奶没有一定之规,可依个人口味自由调配。

如果在热牛奶上再加上一些打成泡沫的冷牛奶,就成了一杯美式拿铁咖啡。

星巴克的美式拿铁就是用这种方法制成的,底部是意大利浓缩咖啡,中间是加热到65~75℃的牛奶,最后是一层不超过半厘米的冷的牛奶泡沫。

如果不放热牛奶,而直接在意大利浓缩咖啡上装饰两大勺牛奶泡沫,就成了被意大利人叫做Espresso Macchiato的玛奇哈朵咖啡。

欧蕾咖啡(Café Au Lait)

欧 蕾咖啡可以被看成是欧式的拿铁咖啡,与美式拿铁和F拿铁都不太相同。 欧蕾咖啡的做法也很简单,就是把一杯意大利浓缩咖啡和一大杯热热的牛奶同时倒入一个大杯子,最后在液体表面放两勺打成泡沫的奶油。 欧蕾咖啡区别于美式拿铁和意式拿铁最大的特点就是它要求牛奶和浓缩咖啡一同注入杯中,牛奶和咖啡在第一时间相遇,碰撞出的是一种闲适自由的心情。 法国人是欧蕾咖啡最热情的拥护者,你在法国入的早餐桌上会看到肚子圆圆的欧蕾杯,里面盛的是他们一天好心情的源泉。 有趣的是,比较所有的咖啡杯,可能法国人用来盛欧蕾咖啡的杯子是最大号的。

拿铁是最为国人熟悉的意式咖啡品项.它是在沉厚浓郁的 ESPRESSO中,加进等比例,甚至更多牛奶的花式咖啡.有了牛奶的温润调味,让原本甘苦的咖啡变得柔滑香甜\甘美浓郁,就连不习惯喝咖啡的人,也难敌 拿铁芳美的滋味.和卡布奇诺一样,拿铁因为含有多量的牛奶而适合在早晨饮用.意大利人也喜欢拿它来暖胃,搭配早餐用.很多人搞不清楚拿铁,欧蕾之间的关 系,其实拿铁是意大利式的年奶咖啡,以机器蒸汽的方式来蒸热牛奶,而欧蕾则是法式咖啡,他们用火将牛奶煮热,口感都是一泒的温润滑美. 作法:使用器具与材料. 深烘焙的咖啡豆适量. 牛奶适量(咖啡\鲜奶的比例为:1:1) 1.以热水浸泡杯子(温杯),使其温度上升后,再倒掉多余的水分使用. 2.将深烘焙的咖啡豆研磨后,将咖啡粉倒进填压器内,用压棒将咖啡粉压平,再将填压器扣住意式咖啡机萃取口,萃取出ESPRESSO(咖啡\鲜奶的比例为 1:1). 3.取适量牛奶,将其置于意式浓缩咖啡机的蒸汽喷嘴下,使其蒸成热牛奶. 4.将蒸热的牛奶倒进杯中. 5.将杯子上下摇晃,使奶泡上升 6.最后将ESPRESSO缓缓地倒进杯中即可.

卡布奇诺(Gappuccino/Cappuccino Coffee) 20世纪初期,意大利人阿奇布夏发明蒸汽压力咖啡机的同时,也发展出了卡布奇诺咖啡。卡布奇诺是一种加入以同量的意大利特浓咖啡和蒸汽泡沫牛奶相混合的意 大利咖啡。此时咖啡的颜色,就象卡布奇诺教会的修士在深褐色的外衣上覆上一条头巾一样,咖啡因此得名。传统的卡布奇诺咖啡是三分之一浓缩咖啡,三分之一蒸 汽牛奶和三分之一泡沫牛奶。 特浓咖啡的浓郁口味,配以润滑的奶泡;颇有一些汲精敛露的意味。撒上了肉桂粉的起沫牛奶,混以自下而上的意大利咖啡的香气,新一代咖啡族为此而心动不已。 它有一种让人无法抗拒的独特魅力,起初闻起来时味道很香,第一口喝下去时,可以感觉到大量奶泡的香甜和酥软,第二口可以真正品尝到咖啡豆原有的苦涩和浓 郁,最后当味道停留在口中,你又会觉得多了一份香醇和隽永……一种咖啡可以喝出多种不同的独特味道,不觉得很神奇吗?第一口总让人觉得苦涩中带着酸味,大 量的泡沫就像年轻人轻挑的生活,而泡沫的破灭和那一点点的苦涩又像是梦想与现实的冲突。最后品尝过生活的悲喜后,生命的香醇回甘却又让人陶醉…… 这就好像正值青春期的青少年一般,在享受过童稚、美好的时光后,便要开始面对踏入成人世界的冲击,真正尝到人生的原味——除了甘甜之外,还有一份苦涩。

卡 布奇诺的由来 维也纳人柯奇斯基(Fanz George Kolschitsky)是牛奶加咖啡的Cafe Latte创始人。 这两种饮料均是咖啡和牛奶洐生出来,但卡布奇诺的来历却更有学问,一直是欧美研究文字变迁的最佳体材。 Cappuccino此字的历史: 创设于1525五年以后的圣芳济教会(Capuchin)的修士都穿著褐色道袍,头戴一顶尖尖帽子,圣芳济教会传到意大利时,当地人觉得修士服饰很特殊, 就给他们取个Cappuccino的名字,此字的意大利文是指僧侣所穿宽松长袍和小尖帽,源自意大利文“头巾”即Cappuccio。 然而,老意爱喝咖啡,发觉浓缩咖啡、牛奶和奶泡混合后,颜色就像是修士所穿的深褐色道袍,于是灵机一动,就给牛奶加咖啡又有尖尖奶泡的饮料,取名为卡布奇 诺(Cappuccino)。英文最早使用此字的时间在1948年,当时旧金山一篇报导率先介绍卡布奇诺饮料,一直到一九九0年以后,才成为世人耳熟能详 的咖啡饮料。应该可以这么说Cappuccino咖啡这个字,源自圣芳济教会(Capuchin)和意大利文头巾(Cappucio),相信 Cappuccino的原始造字者,做梦也没料到僧侣的道袍最后会变成一种咖啡饮料名称。 卡布奇诺也和一种猴名有关。非洲有一种小猴子,头顶上有一撮黑色的锥状毛发,很像圣芳济教会道袍上的小尖帽,这种小猴子也因此被取名为Capuchin, 此一猴名最早被英国人使用的时间在1785年。Capuchin此字数百年后洐生成咖啡饮料名和猴子名称,一直是文字学者津津乐道的趣闻。 干卡布奇诺与湿卡布奇诺 你知道卡布奇诺可以干喝也可以湿喝吗?所谓干卡布奇诺(Dry Cappuccino)是指奶泡较多,牛奶较少的调理法,喝起来咖啡味浓过奶香,适合重口味者饮用。到于湿卡布奇诺(Wet Cappuccino)则指奶泡较少,牛奶量较多的做法,奶香盖过浓呛的咖啡味,适合口味清淡者。 湿卡布奇诺的风味和时下流行的拿铁差不多。一般而言,卡布奇诺的口味比拿铁来得重,如果您是重口味不妨点卡布奇诺或干卡布奇诺,您如果不习惯浓呛的咖啡 味,可以点拿铁或湿卡布奇诺。 卡布奇诺咖啡的制作 在意大利特浓咖啡的基础上,加一层厚厚的起沫的牛奶,就成了卡布奇诺。特浓咖啡的质量在牛奶和泡沫下会看不太出来,但它仍然是决定卡布奇诺口味的重要因 素。把经过部分脱脂的牛奶倒入一只壶中,然后用起沫器让牛奶起沫、冲气,并且让牛奶不经过燃烧就可以象掼奶油一样均匀。盛卡布奇诺的咖啡杯应该是温热的不 然倒入的牛奶泡沫会散开。平时可以将这些杯子放在咖啡机的顶部保温。将牛奶和泡沫倒在特浓咖啡上面,自然形成了一层,就好像把下面的咖啡包了起来。注意倒 入冲泡好的意大利咖啡约五分满,打过奶泡的热牛奶倒至八分满。最后可随个人喜好,洒上少许再切成细丁的肉桂粉或巧克力粉,剩余的牛奶也可以一起倒进去,这 样,一杯美味的卡布奇诺就制成了。

蓝山咖啡(BLUEMOUNTAIN)   是生产于牙买加蓝山海拔2500尺以上的咖啡豆,是一种微酸、柔顺、带甘、风味细腻的咖啡;纯蓝山咖啡口感、香味较淡,但喝起来却非常香醇精致;具有贵族的品味,乃咖啡中之极品。   

摩卡咖啡(MOCHA)   目前以也门所生产的咖啡为最佳,其次为依索比亚的摩卡;摩卡咖啡带润滑中之中酸至强酸、甘性特佳、风味独特,含有巧克力的味道;具有贵妇人的气质,是极具特色的一种纯品咖啡。   

哥伦比亚咖啡(COLOMBIA)  哥伦比亚咖啡中以SUPREMO最具特色,其咖啡柔软香醇;带微酸至中酸,其品质及香味稳定,属中度咖啡,是用以调配综合咖啡的上品。   

曼特宁咖啡(MANDELING)   是生产于印度尼西亚,苏门答腊中最具代表性的咖啡;风味香、浓、苦,口味相当强,但柔顺不带酸,是印度尼西亚生产的咖啡中品质最好的一种咖啡。    碳烧咖啡(CHARCALFIRE)   是一种重度烘焙的咖啡,味道焦、苦不带酸,咖啡豆有出油的现象,极适合用于蒸气加压咖啡。   

巴西咖啡(SANTOS)   巴西乃世界第一的咖啡生产国,所产之咖啡,香味温和、微酸、微苦,为中性咖啡之代表,是调配温和咖啡不可或缺的品种。    肯亚咖啡(KENYAAA)   是非洲高地栽培的代表性咖啡。AA代表其级数也就是最高级品,其咖啡豆肉质厚呈圆形,味浓质佳,通常采用浅焙。清晨起来喝一杯肯亚,具有醒脑的效用。   

夏威夷咖啡(KONAFANCY)   属于夏威夷西部火山所栽培的咖啡,也是美国唯一生产的咖啡品种,口感较强,香味浓,带强酸,风味特殊。品质相当稳定,是前往夏威夷的观光客必购土产之一。

維也納咖啡 (Viennese)乃奥地利最著名的咖啡, 是一个名叫爱因·舒伯纳的马车夫发明的,也许是由于这个原因,今天,人们偶尔也会称维也纳咖啡为“单头马车”。以浓浓的鲜奶油和巧克力的甜美风味迷倒全球 人士。雪白的鲜奶油上,洒落五色缤纷七彩米,扮相非常漂亮;隔着甜甜的巧克力糖浆、冰凉的鲜奶油啜饮滚烫的热咖啡,更是别有风味!

  维也纳咖啡是慵懒的周末或是闲适的午后最好的伴侣,喝上一杯维也纳咖啡就是为自己创造了一个绝好的放松身心的机会。但是,由于含有太多糖分和脂肪,维也纳咖啡并不适合于减肥者。

hazelnut咖啡 Hazelnut就是指榛果咖啡
French Vanilla就是法式香草.你没必要把中文一起打上来的.懂的人一看就懂.不懂的就会根据这些去找地方复制答案了.这两种都是拿铁.只是里面放了不同的糖浆.底子都是
Espresso Ristretto 1杯 (15-20ml) +蒸汽奶泡 约200ml

白咖啡

Malaysia是唯一出产正统白咖啡的地方哦~
现在市面上存在的咖啡都属于黑咖啡,黑咖啡是咖啡豆加焦糖经过高温炭烤而成,这一做工使得做出的咖啡有焦苦、酸、焦糖和炭化的味道。在健康上它会伤胃,上火,造成黑色素的沉淀等不利之处。
  白咖啡是咖啡豆不加焦糖直接低温烘焙,去除了一般高温热炒及炭烤的焦枯、酸涩味,而且保留了原始咖啡的自然风味及浓郁的香气,香浓顺口而心动。不伤肠胃,不上火,低咖啡因,口感滑顺,甘醇芬芳。

几种咖啡的调制      综合热咖啡:巴西3.5、曼特宁1、爪哇2、哥伦比亚2、摩卡1.5(调配式)香、甘、苦、酸。      综合热咖啡:巴西2、曼特宁1、爪哇3.5、哥伦比亚2、摩卡1.5(调配式)香、甘、苦、酸。      综合冰咖啡:曼特宁2、哥伦比亚2、爪哇2.5、荷兰一号2、摩卡1.5(调配式)香、醇、苦      碳烧咖 啡:独特的烘培方式,产于苏门答腊,哥伦比亚2、巴西2、曼特宁1.5、爪哇4.5。苦、醇。   意大利咖啡 : 一般在家中冲泡意大利咖啡,是利用意大利发明的摩卡壶冲泡成的,这种咖啡壶也是利用蒸气压力的原理来淬取咖啡(又一个瓦特的徒弟)。摩卡壶可以使受压的蒸 气直接通过咖啡粉,让蒸气瞬间穿过咖啡粉的细胞壁(还是虎克的徒弟),将咖啡的内在精华淬取出来,故而冲泡出来的咖啡具有浓郁的香味及强烈的苦味,咖啡的 表面并浮现一层薄薄的咖啡油,这层油正是意大利咖啡诱人香味的来源。 康宝蓝 马琪雅朵咖啡: 意大利咖啡真是“百花齐放”,又开出康宝蓝与马琪雅朵两朵花来。只要在意大利浓缩咖啡中加入适量的鲜奶油,即轻松地完成一杯康宝蓝。嫩白的鲜奶油轻轻漂浮 在深沉的咖啡上,宛若一朵出淤泥而不染的白莲花,令人不忍一口喝下。 在意大利浓缩咖啡中,不加鲜奶油、牛奶,只加上两大勺绵密细软的奶泡就是一杯马琪雅朵。不象康宝蓝,要想享受马琪雅朵的美味,就要一口喝下。 法国牛奶咖啡: 咖啡和牛奶的比例为1:1,正统的法国牛奶咖啡冲泡时,要牛奶壶和咖啡壶从两旁同时注入咖啡杯,这种冲配方法延续了几百年。今天,它仍是法国人早餐桌上不 可或缺的饮品。法国baby,奶味十足! 土耳其咖啡: 至今仍采用原始煮法,复杂的工艺带着几许异国情调的神秘色彩。从中,我们可以窥视到奥斯曼帝国盛极一时的风采。 爱尔兰咖啡: 名字里就带着一阵威士忌浓烈的熏香,爱尔兰人视威士忌如生命,也少不了在咖啡中做些手脚!以威士忌调成的爱尔兰咖啡,更能将咖啡的酸甜味道衬托出来。一丝 成熟的忧郁…… 小心,咖啡喝多了也会醉! 皇家咖啡: 这一道极品可是由一位能征贯战的皇帝发明的,对了,他就是法兰西帝国的皇帝拿破仑!他可不喜欢奶味,他喜欢的是法国的骄傲——白兰地!(又一个在咖啡中掺 烈酒的家伙!)蓝色的火焰舞起白兰地的芳醇与方糖的焦香,再合上浓浓的咖啡香,苦涩中略带甘甜……法国的高傲,法国的浪漫。 绿茶咖啡: 绿茶的清香将我们的视线从遥远的国度拉了回来,日本是一个善于吸收与融合的民族,这一次,他们又在西方的咖啡与东方的绿茶之间找到了平衡点,也为爱喝茶的 朋友们提供了新宠。这是一道纯东洋风味的咖啡,绿茶的幽雅清香、咖啡的浓郁厚重交流激荡。 冰拿铁咖啡: 我们不得不再一次提到拿铁咖啡。(是它太诱人,还是它风花雪月太过头?)利用果糖与牛奶混合增加牛奶的比重,使它与比重较轻的咖啡不会混合,成为黑白分明 的两层,形成如鸡尾酒般曼妙的视觉效果,再加上冰块,给人一种高雅而浪漫的温馨感觉。 魔力冰淇淋咖啡: 这一道充满创意与富有变化的神奇口味只属于年轻的你!在冰凉的香草冰淇淋上倒入意大利浓缩咖啡,再用巧克力酱在鲜奶油和冰淇淋上自由构图,魔力般水乳交融 的冰品咖啡,只留芳香与清爽在你口中。 摩卡霜冻咖啡: 喜爱巧克力的伙计们,还有没有胃口试试“霜冻”了的巧克力摩卡咖啡?用果汁机将冰块与冰淇淋打碎调和,创造出一种绵密的视觉效果,再加入摩卡冰咖啡,就大 功告成了!入口溜滑,沁爽香醇,夏日炎炎中给你一个清凉的下午。

  这种维也纳咖啡有着独特的喝法。品尝维也纳咖啡最大的技巧在于不去搅拌咖啡,而是享受杯中三段式的快乐:首先是冰凉的奶油,柔和爽口;然后是浓香的咖啡,润滑却微苦;最后是甜蜜的糖浆,即溶未溶的关键时刻,带给人们发现宝藏般的惊喜。

  维也纳咖啡的制作有点像美式摩卡咖啡。首先在湿热的咖啡杯底部撒上薄薄一层砂糖或细冰糖,接着向杯中倒入滚烫而且偏浓的黑咖啡,最后在咖啡表面装饰两勺冷的新鲜奶油,一杯经典的维也纳咖啡就做好了。

【又是一个偷懒的转载】Returning语句的几个小问题

ORACLE的DML语句中可以指定RETURNING语句。RETURNING语句的使用在很多情况下可以简化PL/SQL编程。

这里不打算说明RETURNING语句的使用(其实使用起来也很简单,和SELECT INTO语句没有多大区别。),主要打算说明RETURNING语句的几个特点。


其实这篇文章源于同事问我的一个问题:

使用UPDATE语句的时候,RETURNING得到的结果是UPDATE之前的结果还是UPDATE之后的结果?

这个问题把我问住了。考虑DELETE的情况,RETURNING返回的肯定是DELETE之前的结果,而考虑INSERT的情况,RETURNING返回的一定是INSERT之后的结果。但是UPDATE到底返回那种情况,就无法推断出来了。而且,由于一般在使用UPDATE的RETURNING语句时,都会返回主键列,而主键列一般都是不会修改的,因此确实不清楚Oracle返回的是UPDATE之前的结果还是之后的结果。

当然,一个简单的例子就可以测试出来:

SQL> CREATE TABLE T (ID NUMBER, NAME VARCHAR2(30));

表已创建。

SQL> SET SERVEROUT ON
SQL> DECLARE
2 V_NAME VARCHAR2(30);
3 BEGIN
4 INSERT INTO T VALUES (1, 'YANGTK') RETURNING NAME INTO V_NAME;
5 DBMS_OUTPUT.PUT_LINE('INSERT: ' || V_NAME);
6 V_NAME := NULL;
7 UPDATE T SET NAME = 'YTK' RETURNING NAME INTO V_NAME;
8 DBMS_OUTPUT.PUT_LINE('UPDATE: ' || V_NAME);
9 V_NAME := NULL;
10 DELETE T RETURNING NAME INTO V_NAME;
11 DBMS_OUTPUT.PUT_LINE('DELETE: ' || V_NAME);
12 END;
13 /
INSERT: YANGTK
UPDATE: YTK
DELETE: YTK

PL/SQL 过程已成功完成。

显然,UPDATE操作的RETURNING语句是返回UPDATE操作之后的结果。

顺便总结几个RETURNING操作相关的问题:

1.RETURNING语句似乎和RETURN通用。

SQL> SET SERVEROUT ON
SQL> DECLARE
2 V_NAME VARCHAR2(30);
3 BEGIN
4 INSERT INTO T VALUES (1, 'YANGTK') RETURN NAME INTO V_NAME;
5 DBMS_OUTPUT.PUT_LINE('INSERT: ' || V_NAME);
6 V_NAME := NULL;
7 UPDATE T SET NAME = 'YTK' RETURN NAME INTO V_NAME;
8 DBMS_OUTPUT.PUT_LINE('UPDATE: ' || V_NAME);
9 V_NAME := NULL;
10 DELETE T RETURN NAME INTO V_NAME;
11 DBMS_OUTPUT.PUT_LINE('DELETE: ' || V_NAME);
IXDBA.NET技术社区
12 END;
13 /
INSERT: YANGTK
UPDATE: YTK
DELETE: YTK

PL/SQL 过程已成功完成。

2.RETURNING语句也可以使用SQLPLUS的变量,这样,RETURNING语句不一定非要用在PL/SQL语句中。

SQL> VAR V_NAME VARCHAR2(30)
SQL> INSERT INTO T VALUES (1, 'YANGTK') RETURNING NAME INTO :V_NAME;

已创建 1 行。

SQL> PRINT V_NAME

V_NAME
--------------------------------
YANGTK

SQL> UPDATE T SET NAME = 'YTK' RETURNING NAME INTO :V_NAME;

已更新 1 行。

SQL> PRINT V_NAME

V_NAME
--------------------------------
YTK

SQL> DELETE T RETURNING NAME INTO :V_NAME;

已删除 1 行。

SQL> PRINT V_NAME

V_NAME
--------------------------------
YTK

3.INSERT INTO VALUES语句支持RETURNING语句,而INSERT INTO SELECT语句不支持。MERGE语句不支持RETURNING语句。

SQL> MERGE INTO T USING (SELECT * FROM T) T1
2 ON (T.ID = T1.ID)
3 WHEN MATCHED THEN UPDATE SET NAME = T1.NAME
4 WHEN NOT MATCHED THEN INSERT VALUES (T1.ID, T1.NAME)
5 RETURNING NAME INTO :V_NAME;
RETURNING NAME INTO :V_NAME
*第 5 行出现错误:
ORA-00933: SQL 命令未正确结束


SQL> INSERT INTO T SELECT * FROM T RETURNING NAME INTO :V_NAME;
INSERT INTO T SELECT * FROM T RETURNING NAME INTO :V_NAME
*第 1 行出现错误:
ORA-00933: SQL 命令未正确结束

这两个限制确实不大方便。不知道Oracle在以后版本中是否会放开。
个人感觉RETURNING语句和BULK COLLECT INTO语句配合使用的机会更多一些。

Apr 9, 2009

Delphi中调试DLL文件(感谢liangpei2008的回答)

可以调试!

完整的调试 DLL方法如下:

1)新建一个 DLL 工程,名字就叫 MyDll 吧,编译后生成 MyDll.dll,我们要调试的就是它了。

2)新建一个用来调试 MyDll 的 Application 工程,名字就叫 MyDllTest 吧,编译后生成的可执行性文件为 MyDllTest.exe,这就是我们用来调试 MyDll.dll 的宿主程序

3)MyDllTest.exe 所在目录中不能有 MyDll.dll(重要!)

4)MyDllTest 采用静态调用的方法调用 MyDll.dll 的导出函数(重要!)

5)进入 MyDll 工程,执行菜单“Run”->“Parameters”,将弹出的对话框的 Local 页中的“Host Application”设置为上面的 MyDllTest.exe(含路径)


还要注意的是,调试 dll 的时候,被调试的 dll 和宿主程序不能在同一个 Project Group中,也就是说,你不要把 dll 和宿主程序放在同一个 Project Group中再进行调试,一定要单独打开 dll 工程进行调试。否则,调试也会不成功。


不过能调试DLL是省了一些时间,但不太规范!
创建一个日志输出模块(最好写成COM+),声明几个接口,这样整个项目的日志记录均输出于此(同时在WIndows下的各种开发环境均可调用)!而且还可以复用!

Apr 6, 2009

Delphi做的百叶窗小程序

做了一个百叶窗小程序
碰到了几个问题,比如说两个用到LoadFromFile函数的地方第一次可以识别相对路径
而如果我更改了其中一个的路径使其变为硬盘上某个绝对路径
那另外一个LoadFromFile就不能识别原先的相对路径了,很奇怪
我采取了加判断绕过这个问题的方法
先上代码,主要的是shutter单元,是作为一个组件来编写的
另外还写了个测试单元。主要针对Shutter的某些属性进行设置从而测试Shutter

unit Shutter;
{ CopyRight@ Swetter }
interface

uses
SysUtils, Windows, Classes, Forms, Controls, Graphics,
Messages, ExtCtrls, Dialogs;

const
{位图344*500,沿Y轴等分成10块
yInc存储了每块的高度
BitMapWid存储了位图的宽度
BitMapHei存储了位图的高度
ShowPixels为随Timer每次递增的行宽度
TimerInterval为Timer的时间间隔}
YInc = 50;
// ShowPixels = 1;
BitMapWid = 344;
BitMapHei = 500;
// TimerInterval = 50;

type
EShutterError = class(Exception);

TShutter = class(TCustomPanel)
private
FMemBitMap: TBitmap;
FMemMskBtMp: TBitmap;
FTimer: TTimer;
FActive: Boolean;
FCurrHei: Integer;
FVRect: TRect;
{FOpen指示百叶窗打开与关闭效果,true则百叶窗打开,false则百叶窗关闭
FDone指示位图是否显示完毕
FViewPic指示是否要查看原图
true为查看原图,此时FCurrHei值直接为yInc而不置0
false为不查看原图,此时在SetActive为false后FCurrHei置0
FNFrstm指示在百叶窗打开模式下(即清空图像)是否已贴上原图
false为未贴上原图,需重新贴一次;true为已贴上原图,可以开始贴屏蔽图}
FOpen: Boolean;
FDone: Boolean;
FViewPic: Boolean;
FOnDone: TNotifyEvent;
FNFrstm: Boolean;
FBkFileName: string;
FFgFileName: string;
ShowPixels: Integer;
TimerInterval: Integer;
procedure IncWid;
procedure SetActive(AValue: Boolean);
procedure ActOnTimer(Sender: TObject);
procedure DoTimer;
protected
procedure Paint; override;
procedure FillBitmap; virtual;
public
procedure CoordinateTimer;
procedure ImageChanged;
property Pixel: Integer read ShowPixels write ShowPixels;
property Interval: Integer read TimerInterval write TimerInterval;
property BkFileName: string read FBkFileName write FBkFileName;
property FgFileName: string read FFgFileName write FFgFileName;
property NFrstm: Boolean read FNFrstm write FNFrstm;
property Open: Boolean read FOpen write FOpen;
property ViewPic: Boolean read FViewPic write FViewPic;
property Active: Boolean read FActive write SetActive;
property Done: Boolean read FDone write FDone;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
{ Publish inherited properties: }
property OnDone: TNotifyEvent read FOnDone write FOnDone;
property Align;
property Alignment;
property BevelInner;
property BevelOuter;
property BevelWidth;
property BorderWidth;
property BorderStyle;
property Color;
property Ctl3D;
property Font;
property Locked;
property ParentColor;
property ParentCtl3D;
property ParentFont;
property Visible;
property OnClick;
property OnDblClick;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnResize;
end;

implementation

{ TShutter }

procedure TShutter.DoTimer;
begin
with FTimer do
begin
Enabled := False;
Interval := TimerInterval;
OnTimer := ActOnTimer;
end;
end;

constructor TShutter.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
TimerInterval := 50;
ShowPixels := 1;
FTimer := TTimer.Create(Self);
DoTimer;
{ 实例初始化的值 }
Width := 344;
Height := 500;
FActive := False;
BevelWidth := 3;
FFgFileName := '.\pics\pic.bmp';
FBkFileName := '.\pics\view.bmp';
end;

destructor TShutter.Destroy;
begin
SetActive(False);
FTimer.Free;
inherited Destroy;
end;

procedure TShutter.ActOnTimer(Sender: TObject);
begin
IncWid;
InvalidateRect(Handle, @FVRect, false);
end;

procedure TShutter.FillBitmap;
begin
//FVRect存储了整张内存位图的区域大小
FVRect := Rect(0, 0, BitMapWid, BitMapHei);
//初始化FMemBitMap的宽度和高度
FMemMskBtMp:= TBitmap.Create;
FMemBitMap := TBitmap.Create;
FMemMskBtMp.Width := BitMapWid;
FMemMskBtMp.Height:= BitMapHei;
FMemBitMap.Width := BitMapWid;
FMemBitMap.Height := BitMapHei;
try
if FBkFileName <> '.\pics\view.bmp' then
FMemMskBtMp.LoadFromFile(FBkFileName);
if FFgFileName <> '.\pics\pic.bmp' then
FMemBitMap.LoadFromFile(FFgFileName);
except
on EFOpenError DO
MessageBox(Handle,'打开文件出错,请重试','提示',MB_OK);
end;
end;

procedure TShutter.IncWid;
begin
if not FOpen and (not FDone) then
begin
if FCurrHei + ShowPixels <= yInc then Inc(FCurrHei, ShowPixels) //为保存位图,不要SetActive(False) else FDone := True; end else if FOpen and(not FDone) then begin if FCurrHei - ShowPixels >= 0 then
Dec(FCurrHei, ShowPixels)
else
FDone := True;
end;
end;

procedure TShutter.Paint;
var
i: Integer;
begin
//如果要查看背景图,直接贴上去
if FViewPic then
BitBlt(Canvas.Handle, 0, 0, BitMapWid, BitMapHei,
FMemBitMap.Canvas.Handle, 0, 0, srcCopy);

if FActive and not FOpen then
for i := 0 to 9 do
BitBlt(Canvas.Handle, 0, i*YInc, BitMapWid, FCurrHei,
FMemBitMap.Canvas.Handle, 0, i*YInc, srcCopy)
else if FActive and FOpen then
begin
if not NFrstm then
begin
for i := 0 to 9 do
BitBlt(Canvas.Handle, 0, i*YInc, BitMapWid, YInc,
FMemBitMap.Canvas.Handle, 0, i*YInc, SRCCOPY);
NFrstm := True;
end
else
for i := 0 to 9 do
BitBlt(Canvas.Handle, 0, i*YInc , BitMapWid, YInc - FCurrHei,
FMemMskBtMp.Canvas.Handle, 0, i*YInc, SRCCOPY);
end
else inherited Paint;
end;

procedure TShutter.SetActive(AValue: Boolean);
begin
if AValue and (not FActive) then
begin
FActive := True;
FillBitmap;
{FViewPic,true则查看位图
FOpen,True则为百叶窗打开模式}
if not FOpen then
FCurrHei := 0
else
FCurrHei := YInc;

try
FTimer.Enabled := True;
except
EShutterError.Create('Timer初始化出错');
end;
end
else if (not AValue) and FActive then
begin
FTimer.Enabled := False; //停止计时
if Assigned(FOnDone) // fire OnDone event,
then FOnDone(Self);
FActive := False; //停止组件
FMemBitMap.Free; //释放内存位图
FMemMskBtMp.Free; //释放屏蔽位图
Invalidate; //清空组件窗口内容
end;
end;

procedure TShutter.ImageChanged;
begin
if (FMemMskBtMp <> nil) and (FMemBitMap <> nil) then
begin
FMemMskBtMp.FreeImage; //释放所占用的位图资源免得重新加载错误
FMemBitMap.FreeImage;
end;
FillBitmap;
end;

procedure TShutter.CoordinateTimer;
begin
DoTimer;
end;

end.

基本上运行效果还是可以的
但是从代码中可以看到很多的boolean值的参数
它们是用来判断功能从而有选择的调用BitBlt函数
设计还是不够好,其实可以用case语句来区分不同功能
我用了太多的boolean来判断导致扩展起来很不方便,容易出BUG
先放这里,慢慢再改

Apr 5, 2009

仿Windows的画图程序

仅贴出关键的代码部分

unit MainFrm;

interface

uses
SysUtils, Windows, Messages, Classes, Graphics, Controls,
Forms, Dialogs, Buttons, ExtCtrls, ColorGrd, StdCtrls, Menus, ComCtrls;

const
crMove = 1;
rate1 = 0.38;
rate2 = 0.19;
type

TDrawType = (dtLineDraw, dtRectangle, dtEllipse, dtRoundRect,
dtClipRect, dtCrooked, dtPolyLine, dtPolygon,
dtTriangle, dtFan);
{用于画三角形的record}
TTriangle = record
FStrtDot: TPoint;
FEndDot : TPoint;
FFrstDot: TPoint;
FScndDot: TPoint;
FThrdDot: TPoint;
FScndVal: Boolean;
end;
{用于画扇形的record}
TFan = record
FStrtDot: TPoint;
FOrgDot : TPoint;
FLstDot : TPoint;
FNFrst : Boolean;
end;
{用于画五角星的record,记录五个端点}
TStar = record
FTL: TPoint;
FBR: TPoint;
FFrstDot: TPoint;
FScndDot: TPoint;
FThrdDot: TPoint;
FFrthDot: TPoint;
FFfthDot: TPoint;
end;

TMainForm = class(TForm)
sbxMain: TScrollBox;
imgDrawingPad: TImage;
pnlToolBar: TPanel;
sbLine: TSpeedButton;
sbRectangle: TSpeedButton;
sbEllipse: TSpeedButton;
sbRoundRect: TSpeedButton;
pnlColors: TPanel;
cgDrawingColors: TColorGrid;
pnlFgBgBorder: TPanel;
pnlFgBgInner: TPanel;
Bevel1: TBevel;
mmMain: TMainMenu;
mmiFile: TMenuItem;
mmiExit: TMenuItem;
N2: TMenuItem;
mmiSaveAs: TMenuItem;
mmiSaveFile: TMenuItem;
mmiOpenFile: TMenuItem;
mmiNewFile: TMenuItem;
mmiEdit: TMenuItem;
mmiPaste: TMenuItem;
mmiCopy: TMenuItem;
mmiCut: TMenuItem;
sbRectSelect: TSpeedButton;
SaveDialog: TSaveDialog;
OpenDialog: TOpenDialog;
stbMain: TStatusBar;
pbPasteBox: TPaintBox;
sbFreeForm: TSpeedButton;
RgGrpFillOptions: TRadioGroup;
cbxBorder: TCheckBox;
sbPolyline: TSpeedButton;
sbPolygon: TSpeedButton;
sbTriangle: TSpeedButton;
sbFan: TSpeedButton;
dlgColor: TColorDialog;
btnBorder: TButton;
btnFill: TButton;
procedure FormCreate(Sender: TObject);
procedure sbLineClick(Sender: TObject);
procedure imgDrawingPadMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure imgDrawingPadMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure imgDrawingPadMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure cgDrawingColorsChange(Sender: TObject);
procedure mmiExitClick(Sender: TObject);
procedure mmiSaveFileClick(Sender: TObject);
procedure mmiSaveAsClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure mmiNewFileClick(Sender: TObject);
procedure mmiOpenFileClick(Sender: TObject);
procedure mmiEditClick(Sender: TObject);
procedure mmiCutClick(Sender: TObject);
procedure mmiCopyClick(Sender: TObject);
procedure mmiPasteClick(Sender: TObject);
procedure pbPasteBoxMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure pbPasteBoxMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure pbPasteBoxMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure pbPasteBoxPaint(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure RgGrpFillOptionsClick(Sender: TObject);
procedure btnBorderClick(Sender: TObject);
procedure btnFillClick(Sender: TObject);
public
{ Public declarations }
MouseOrg: TPoint; // Stores mouse information
NextPoint: TPoint; // Stores mouse information
Drawing: Boolean; // Drawing is being performed flag
DrawType: TDrawType; // Holds the draw type information: TDrawType
FillSelected, // Fill shapes flag
BorderSelected: Boolean; // Draw Shapes with no border flag
EraseClipRect: Boolean; // Specifies whether or not to erase the
// clipping rectangle
Modified: Boolean; // Image modified flag
FileName: String; // Holds the filename of the image
OldClipViewHwnd: Hwnd; // Holds the old clipboard view window
{ Paste Image variables }
PBoxMoving: Boolean; // PasteBox is moving flag
PBoxMouseOrg: TPoint; // Stores mouse coordinates for moving PasteBox
PasteBitMap: TBitmap; // Stores a bitmap image of the pasted data
Pasted: Boolean; // Data pasted flag
LastDot: TPoint; // Hold the TPoint coordinate for performing
// free line drawing
procedure DrawToImage(TL, BR: TPoint; PenMode: TPenMode);
{ This procedure paints the image specified by the DrawType field
to imgDrawingPad }
procedure SetDrawingStyle;
{ This procedure sets various Pen/Brush styles based on values
specified by the form's controls. The Panels and color grid is
used to set these values }
procedure CopyPasteBoxToImage;
{ This procedure copies the data pasted from the Windows clipboard
onto the main image component imgDrawingPad }
procedure WMDrawClipBoard(var Msg: TWMDrawClipBoard);
message WM_DRAWCLIPBOARD;
{ This message handler captures the WM_DRAWCLIPBOARD messages
which is sent to all windows that have been added to the clipboard
viewer chain. An application can add itself to the clipboard viewer
chain by using the SetClipBoardViewer() Win32 API function as
is done in FormCreate() }
procedure CopyCut(Cut: Boolean);
{ This method copies a portion of the main image, imgDrawingPad, to the
Window's clipboard. }
end;

var
MainForm: TMainForm;

{用于画PolyLine的专用全局变量,Polyline随鼠标滑动而动态绘制
PLStart: TPoint,用于保存Polyline的起点,随着每次鼠标UP而动态更新
PLLast: TPoint,用于保存鼠标每滑动一次的终止点
PLDrawed: Boolean,用于鼠标滑动时检查是否已画了折线
true 则XOR掉起点到Move当前点上一点所绘制的折线并重画
false则直接重画
}
PLStart, PLLast: TPoint;
PLDrawed: Boolean;

{用于画Triangle的量,为TTriangle类型}
Trngl: TTriangle;
{用于画Fan的量,为TFan类型}
Fn: TFan;

implementation
uses ClipBrd, Math;

{$R *.DFM}

procedure TMainForm.FormCreate(Sender: TObject);
{ This method sets the form's field to their default values. It then
creates a bitmap for the imgDrawingPad. This is the image on which
drawing is done. Finally, it adds this application as part of the
Windows clipboard viewer chain by using the SetClipBoardViewer()
function. This makes enables the form to get WM_DRAWCLIPBOARD messages
which are sent to all windows in the clipboard viewer chain whenever
the clipboard data is modified. }
begin
Screen.Cursors[crMove] := LoadCursor(hInstance, 'MOVE');

FillSelected := False;
BorderSelected := True;

Modified := False;
FileName := '';
Pasted := False;
pbPasteBox.Enabled := False;

// Create a bitmap for imgDrawingPad and set its boundaries
with imgDrawingPad do
begin
SetBounds(0, 0, 600, 400);
Picture.Graphic := TBitMap.Create;
Picture.Graphic.Width := 600;
Picture.Graphic.Height := 400;
end;
// Now create a bitmap image to hold pasted data
PasteBitmap := TBitmap.Create;
pbPasteBox.BringToFront;
{ Add the form to the Windows clipboard viewer chain. Save the handle
of the next window in the chain so that it may be restored by the
ChangeClipboardChange() Win32 API function in this form's
FormDestroy() method. }
OldClipViewHwnd := SetClipBoardViewer(Handle);
end;

procedure TMainForm.WMDrawClipBoard(var Msg: TWMDrawClipBoard);
begin
{ This method will be called whenever the clipboard data
has changed. Because the main form was added to the clipboard
viewer chain, it will receive the WM_DRAWCLIPBOARD message
indicating that the clipboard's data was changed. }
inherited;
{ Make sure that the data contained on the clipboard is actually
bitmap data. }
if ClipBoard.HasFormat(CF_BITMAP) then
mmiPaste.Enabled := True
else
mmiPaste.Enabled := False;
Msg.Result := 0;
end;


procedure TMainForm.DrawToImage(TL, BR: TPoint; PenMode: TPenMode);
{ This method performs the specified drawing operation. The
drawing operation is specified by the DrawType field }
var
//用于画五角星的点
x1,y1,x2,x3: Integer;
begin
with imgDrawingPad.Canvas do
begin
Pen.Mode := PenMode;

case DrawType of
dtLineDraw, dtPolyLine, dtTriangle, dtFan:
begin
MoveTo(TL.X, TL.Y);
LineTo(BR.X, BR.Y);
end;
dtPolygon:
begin
//x1这一点前边别忘记加上TL.X,否则画出奇怪的图形啊!!!
x1 := TL.X + (BR.X - TL.X) div 2;
y1 := trunc(TL.Y+(BR.Y-TL.Y)*rate1);
x2 := trunc(TL.X+(BR.X-TL.X)*rate2);
x3 := trunc(BR.X-(BR.X-TL.X)*rate2);
MoveTo(x1, TL.Y);
LineTo(x2, BR.y);
LineTo(BR.X, y1);
LineTo(TL.X, y1);
LineTo(x3, BR.Y);
LineTo(x1, TL.Y);
x1 := 0;
y1 := 0;
x2 := 0;
x3 := 0;
end;
dtRectangle:
Rectangle(TL.X, TL.Y, BR.X, BR.Y);
dtEllipse:
Ellipse(TL.X, TL.Y, BR.X, BR.Y);
dtRoundRect:
RoundRect(TL.X, TL.Y, BR.X, BR.Y,
(TL.X - BR.X) div 2, (TL.Y - BR.Y) div 2);
dtClipRect:
Rectangle(TL.X, TL.Y, BR.X, BR.Y);

end;
end;
end;

procedure TMainForm.CopyPasteBoxToImage;
{ This method copies the image pasted from the Windows clipboard onto
imgDrawingPad. It first erases any bounding rectangle drawn by PaintBox
component, pbPasteBox. It then copies the data from pbPasteBox onto
imgDrawingPad at the location where pbPasteBox has been dragged
over imgDrawingPad. The reason we don't copy the contents of
pbPasteBox's canvas and use PasteBitmap's canvas instead, is because
when a portion of pbPasteBox is dragged out of the viewable area,
Windows does not paint the portion pbPasteBox not visible. Therefore,
it is necessary to the pasted bitmap from the off-screen bitmap }
var
SrcRect, DestRect: TRect;
begin
// First, erase the rectangle drawn by pbPasteBox
with pbPasteBox do
begin
Canvas.Pen.Mode := pmNotXOR;
Canvas.Pen.Style := psDot;
Canvas.Brush.Style := bsClear;
Canvas.Rectangle(0, 0, Width, Height);
DestRect := Rect(Left, Top, Left+Width, Top+Height);
SrcRect := Rect(0, 0, Width, Height);
end;
{ Here we must use the PasteBitmap instead of the pbPasteBox because
pbPasteBox will clip anything outside if the viewable area. }
imgDrawingPad.Canvas.CopyRect(DestRect, PasteBitmap.Canvas, SrcRect);
pbPasteBox.Visible := false;
pbPasteBox.Enabled := false;
Pasted := False; // Pasting operation is complete
end;

procedure TMainForm.imgDrawingPadMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Modified := True;
// Erase the clipping rectangle if one has been drawn
if (DrawType = dtClipRect) and EraseClipRect then
DrawToImage(MouseOrg, NextPoint, pmNotXOR)
else if (DrawType = dtClipRect) then
EraseClipRect := True; // Re-enable cliprect erasing
if Pasted then
CopyPasteBoxToImage;

Drawing := True;
// Save the mouse information
if (DrawType = dtPolyline) and (not PLDrawed) then
begin
PLStart := Point(X,Y);
PLLast := PLStart;
end ;
if (DrawType = dtTriangle) and (not Trngl.FScndVal) then
begin
Trngl.FFrstDot := Point(X,Y);
Trngl.FStrtDot := Point(X,Y);
Trngl.FEndDot := Trngl.FStrtDot;
end;
if (DrawType = dtFan) and (not Fn.FNFrst) then
begin
Fn.FStrtDot := Point(X,Y);
Fn.FOrgDot := Point(X,Y);
Fn.FLstDot := Point(X,Y);
end;
MouseOrg := Point(X, Y);
NextPoint := MouseOrg;
LastDot := NextPoint; // Lastdot is updated as the mouse moves
//将线的起点移动到鼠标按下的点
imgDrawingPad.Canvas.MoveTo(X, Y);
end;

procedure TMainForm.imgDrawingPadMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
{ This method determines the drawing operation to be performed and
either performs free form line drawing, or calls the
DrawToImage method which draws the specified shape }
begin
if Drawing then
begin
case DrawType of
dtCrooked:
begin
imgDrawingPad.Canvas.MoveTo(LastDot.X, LastDot.Y);
imgDrawingPad.Canvas.LineTo(X, Y);
LastDot := Point(X,Y);
end;
dtPolyLine:
begin
DrawToImage(PLStart, PLLast, pmNotXor);
PLLast := Point(X,Y);
DrawToImage(PLStart, PLLast, pmNotXor);
PLDrawed := True;
end;
dtTriangle:
begin
DrawToImage(Trngl.FStrtDot, Trngl.FEndDot, pmNotXor);
Trngl.FEndDot := Point(X,Y);
DrawToImage(Trngl.FStrtDot, Trngl.FEndDot, pmNotXor);
end;
dtFan:
if not Fn.FNFrst then
begin
DrawToImage(Fn.FStrtDot, Fn.FOrgDot, pmNotXor);
Fn.FOrgDot := Point(X, Y);
DrawToImage(Fn.FStrtDot, Fn.FOrgDot, pmNotXor);
end
else begin
///:~@@@@@@@使用了pmCopy后扇形的前一条边就不会消失,为什么??
DrawToImage(Fn.FStrtDot, Fn.FOrgDot, pmCopy);
DrawToImage(Fn.FOrgDot, Fn.FLstDot, pmNotXor);
DrawToImage(Fn.FStrtDot, Fn.FLstDot, pmNotXor);
Fn.FLstDot := Point(X,Y);
DrawToImage(Fn.FOrgDot, Fn.FLstDot, pmNotXor);
DrawToImage(Fn.FStrtDot, Fn.FLstDot, pmNotXor);
end
else begin
DrawToImage(MouseOrg, NextPoint, pmNotXor);
NextPoint := Point(X, Y);
DrawToImage(MouseOrg, NextPoint, pmNotXor)
end;
end;
end;
// Update the status bar with the current mouse location
stbMain.Panels[1].Text := Format('X: %d, Y: %D', [X, Y]);
end;

procedure TMainForm.imgDrawingPadMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Drawing then
{ Prevent the clipping rectangle from destroying the images already
on the image }
case DrawType of
dtPolyLine:
begin
DrawToImage(PLStart, Point(X,Y), pmCopy);
PLStart := Point(X,Y);
end;
dtTriangle:
begin
if not Trngl.FScndVal then
begin
Trngl.FScndDot := Point(X,Y);
DrawToImage(Trngl.FStrtDot, Trngl.FEndDot, pmCopy);
Trngl.FStrtDot := Trngl.FScndDot;
Trngl.FScndVal := True;
end
else begin
Trngl.FThrdDot := Point(X,Y);
DrawToImage(Trngl.FStrtDot, Trngl.FEndDot, pmCopy);
DrawToImage(Trngl.FThrdDot, Trngl.FFrstDot, pmNotXor);
Trngl.FScndVal := False;
end;
end;
dtFan:
if not Fn.FNFrst then
begin
DrawToImage(Fn.FStrtDot, Fn.FLstDot, pmCopy);
Fn.FNFrst := True;
end
else begin
DrawToImage(Fn.FStrtDot, Fn.FOrgDot, pmCopy);
DrawToImage(Fn.FStrtDot, Fn.FLstDot, pmCopy);
DrawToImage(Fn.FOrgDot, Fn.FLstDot, pmCopy);
Fn.FOrgDot := Point(X,Y);
end;
dtLineDraw, dtRectangle, dtEllipse, dtRoundRect, dtCrooked:
DrawToImage(MouseOrg, Point(X, Y), pmCopy);
end;
Drawing := False;
end;

procedure TMainForm.sbLineClick(Sender: TObject);
begin
// First erase the cliprect if current drawing type
if DrawType = dtClipRect then
DrawToImage(MouseOrg, NextPoint, pmNotXOR);

{ Now set the DrawType field to that specified by the TSpeedButton
invoking this method. The TSpeedButton's Tag values match a
specific TDrawType value which is why the typecasting below
successfully assigns a valid TDrawType value to the DrawType field. }
if Sender is TSpeedButton then
DrawType := TDrawType(TSpeedButton(Sender).Tag);

// Now make sure the dtClipRect style doesn't erase previous drawings
if DrawType = dtClipRect then begin
EraseClipRect := False;
end;
// Set the drawing style
SetDrawingStyle;
end;

procedure TMainForm.cgDrawingColorsChange(Sender: TObject);
{ This method draws the rectangle representing fill and border colors
to indicate the users selection of both colors. pnlFgBgInner and
pnlFgBgBorder are TPanels arranged one on to of the other for the
desired effect }
begin
pnlFgBgBorder.Color := cgDrawingColors.ForeGroundColor;
pnlFgBgInner.Color := cgDrawingColors.BackGroundColor;
SetDrawingStyle;
end;


procedure TMainForm.SetDrawingStyle;
{ This method sets the various drawing styles based on the selections
on the pnlFillStyle TPanel for Fill and Border styles }
begin
with imgDrawingPad do
begin
if DrawType = dtClipRect then
begin
Canvas.Pen.Style := psDot;
Canvas.Brush.Style := bsClear;
Canvas.Pen.Color := clBlack;
end

else if FillSelected then
Canvas.Brush.Style := bsSolid
else
Canvas.Brush.Style := bsClear;

if BorderSelected then
Canvas.Pen.Style := psSolid
else
Canvas.Pen.Style := psClear;


if FillSelected and (DrawType <> dtClipRect) then
Canvas.Brush.Color := pnlFgBgInner.Color;

if DrawType <> dtClipRect then
Canvas.Pen.Color := pnlFgBgBorder.Color;
end;
end;

procedure TMainForm.mmiExitClick(Sender: TObject);
begin
Close; // Terminate application
end;

procedure TMainForm.mmiSaveFileClick(Sender: TObject);
{ This method saves the image to the file specified by FileName. If
FileName is blank, however, SaveAs1Click is called to get a filename.}
begin
if FileName = '' then
mmiSaveAsClick(nil)
else begin
imgDrawingPad.Picture.SaveToFile(FileName);
stbMain.Panels[0].Text := FileName;
Modified := False;
end;
end;

procedure TMainForm.mmiSaveAsClick(Sender: TObject);
{ This method launches SaveDialog to get a file name to which
the image's contents will be saved. }
begin
if SaveDialog.Execute then
begin
FileName := SaveDialog.FileName; // Store the filename
mmiSaveFileClick(nil)
end;
end;

procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
{ If the user attempts to close the form before saving the image, they
are prompted to do so in this method. }
var
Rslt: Word;
begin
CanClose := False; // Assume fail.
if Modified then begin
Rslt := MessageDlg('File has changed, save?', mtConfirmation, mbYesNOCancel, 0);
case Rslt of
mrYes: mmiSaveFileClick(nil);
mrNo: ; // no need to do anything.
mrCancel: Exit;
end
end;
CanClose := True; // Allow use to close application
end;

procedure TMainForm.mmiNewFileClick(Sender: TObject);
{ This method erases any drawing on the main image after prompting the
user to save it to a file in which case the mmiSaveFileClick event handler
is called. }
var
Rslt: Word;
begin
if Modified then begin
Rslt := MessageDlg('File has changed, save?', mtConfirmation, mbYesNOCancel, 0);
case Rslt of
mrYes: mmiSaveFileClick(nil);
mrNo: ; // no need to do anything.
mrCancel: Exit;
end
end;

with imgDrawingPad.Canvas do begin
Brush.Style := bsSolid;
Brush.Color := clWhite; // clWhite erases the image
FillRect(ClipRect); // Erase the image
FileName := '';
stbMain.Panels[0].Text := FileName;
end;
SetDrawingStyle; // Restore the previous drawing style
Modified := False;
end;

procedure TMainForm.mmiOpenFileClick(Sender: TObject);
{ This method opens a bitmap file specified by OpenDialog.FileName. If
a file was already created, the user is prompted to save
the file in which case the mmiSaveFileClick event is called. }
var
Rslt: Word;
begin

if OpenDialog.Execute then
begin

if Modified then begin
Rslt := MessageDlg('File has changed, save?', mtConfirmation, mbYesNOCancel, 0);
case Rslt of
mrYes: mmiSaveFileClick(nil);
mrNo: ; // no need to do anything.
mrCancel: Exit;
end
end;

imgDrawingPad.Picture.LoadFromFile(OpenDialog.FileName);
FileName := OpenDialog.FileName;
stbMain.Panels[0].Text := FileName;
Modified := false;
end;

end;

procedure TMainForm.mmiEditClick(Sender: TObject);
{ The timer is used to determine if an area on the main image is
surrounded by a bounding rectangle. If so, then the Copy and Cut
menu items are enabled. Otherwise, they are disabled. }
var
IsRect: Boolean;
begin
IsRect := (MouseOrg.X <> NextPoint.X) and (MouseOrg.Y <> NextPoint.Y);
if (DrawType = dtClipRect) and IsRect then
begin
mmiCut.Enabled := True;
mmiCopy.Enabled := True;
end
else begin
mmiCut.Enabled := False;
mmiCopy.Enabled := False;
end;
end;

procedure TMainForm.CopyCut(Cut: Boolean);
{ This method copies a portion of the main image to the clipboard.
The portion copied is specified by a bounding rectangle
on the main image. If Cut is true, the area in the bounding rectandle
is erased. }
var
CopyBitMap: TBitmap;
DestRect, SrcRect: TRect;
OldBrushColor: TColor;
begin
CopyBitMap := TBitMap.Create;
try
{ Set CopyBitmap's size based on the coordinates of the
bounding rectangle }
CopyBitMap.Width := Abs(NextPoint.X - MouseOrg.X);
CopyBitMap.Height := Abs(NextPoint.Y - MouseOrg.Y);
DestRect := Rect(0, 0, CopyBitMap.Width, CopyBitmap.Height);
SrcRect := Rect(Min(MouseOrg.X, NextPoint.X)+1,
Min(MouseOrg.Y, NextPoint.Y)+1,
Max(MouseOrg.X, NextPoint.X)-1,
Max(MouseOrg.Y, NextPoint.Y)-1);
{ Copy the portion of the main image surrounded by the bounding
rectangle to the Windows clipboard }
CopyBitMap.Canvas.CopyRect(DestRect, imgDrawingPad.Canvas, SrcRect);
{ Previous versions of Delphi required the bitmap's Handle property
to be touched for the bitmap to be made available. This was due to
Delphi's caching of bitmapped images. The step below may not be
required. }
CopyBitMap.Handle;
// Assign the image to the clipboard.
ClipBoard.Assign(CopyBitMap);
{ If cut was specified the erase the portion of the main image
surrounded by the bounding Rectangle }
if Cut then
with imgDrawingPad.Canvas do
begin
OldBrushColor := Brush.Color;
Brush.Color := clWhite;
try
FillRect(SrcRect);
finally
Brush.Color := OldBrushColor;
end;
end;
finally
CopyBitMap.Free;
end;
end;

procedure TMainForm.mmiCutClick(Sender: TObject);
begin
CopyCut(True);
end;

procedure TMainForm.mmiCopyClick(Sender: TObject);
begin
CopyCut(False);
end;

procedure TMainForm.mmiPasteClick(Sender: TObject);
{ This method pastes the data contained in the clipboard to the
paste bitmap. The reason it is pasted to the PasteBitmap, an off-
screen bitmap, is so that the user can relocate the pasted image
elsewhere on to the main image. This is done by having the pbPasteBox,
a TPaintBox component, draw the contents of PasteImage. When the
user if done positioning the pbPasteBox, the contents of TPasteBitmap
is drawn to imgDrawingPad at the location specified by pbPasteBox's location.}
begin
{ Clear the bounding rectangle }

pbPasteBox.Enabled := True;
if DrawType = dtClipRect then
begin
DrawToImage(MouseOrg, NextPoint, pmNotXOR);
EraseClipRect := False;
end;

PasteBitmap.Assign(ClipBoard); // Grab the data from the clipboard
Pasted := True;
// Set position of pasted image to top left
pbPasteBox.Left := 0;
pbPasteBox.Top := 0;
// Set the size of pbPasteBox to match the size of PasteBitmap
pbPasteBox.Width := PasteBitmap.Width;
pbPasteBox.Height := PasteBitmap.Height;

pbPasteBox.Visible := True;
pbPasteBox.Invalidate;
end;

procedure TMainForm.pbPasteBoxMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
{ This method set's up pbPasteBox, a TPaintBox for being moved by the
user when the left mouse button is held down }
begin
if Button = mbLeft then
begin
PBoxMoving := True;
Screen.Cursor := crMove;
PBoxMouseOrg := Point(X, Y);
end
else
PBoxMoving := False;
end;

procedure TMainForm.pbPasteBoxMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
{ This method moves pbPasteBox if the PBoxMoving flag is true indicating
that the user is holding down the left mouse button and is dragging
PaintBox }
begin
if PBoxMoving then
begin
pbPasteBox.Left := pbPasteBox.Left + (X - PBoxMouseOrg.X);
pbPasteBox.Top := pbPasteBox.Top + (Y - PBoxMouseOrg.Y);
end;
end;

procedure TMainForm.pbPasteBoxMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
{ This method disables moving of pbPasteBox when the user lifts the left
mouse button }
if PBoxMoving then
begin
PBoxMoving := False;
Screen.Cursor := crDefault;
end;
pbPasteBox.Refresh; // Redraw the pbPasteBox.
end;

procedure TMainForm.pbPasteBoxPaint(Sender: TObject);
{ The paintbox is drawn whenever the user selects the Paste option
form the menu. pbPasteBox draws the contents of PasteBitmap which
holds the image gotten from the clipboard. The reason for drawing
PasteBitmap's contents in pbPasteBox, a TPaintBox class, is so that
the user can also move the object around on top of the main image.
In other words, pbPasteBox can be moved, and hidden when necessary. }
var
DestRect, SrcRect: TRect;
begin
// Display the paintbox only if a pasting operation occurred.
if Pasted then
begin
{ First paint the contents of PasteBitmap using canvas's CopyRect
but only if the paintbox is not being moved. This reduces
flicker }
if not PBoxMoving then
begin
DestRect := Rect(0, 0, pbPasteBox.Width, pbPasteBox.Height);
SrcRect := Rect(0, 0, PasteBitmap.Width, PasteBitmap.Height);
pbPasteBox.Canvas.CopyRect(DestRect, PasteBitmap.Canvas, SrcRect);
end;
{ Now copy a bounding rectangle to indicate that pbPasteBox is
a moveable object. We use a pen mode of pmNotXOR because we
must erase this rectangle when the user copies PaintBox's
contents to the main image and we must preserve the original
contents. }
pbPasteBox.Canvas.Pen.Mode := pmNotXOR;
pbPasteBox.Canvas.Pen.Style := psDot;
pbPasteBox.Canvas.Brush.Style := bsClear;
pbPasteBox.Canvas.Rectangle(0, 0, pbPasteBox.Width, pbPasteBox.Height);
end;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
// Remove the form from the clipboard chain
ChangeClipBoardChain(Handle, OldClipViewHwnd);
PasteBitmap.Free; // Free the PasteBitmap instance
end;

procedure TMainForm.RgGrpFillOptionsClick(Sender: TObject);
begin
FillSelected := RgGrpFillOptions.ItemIndex = 0;
BorderSelected := cbxBorder.Checked;
SetDrawingStyle;
end;

procedure TMainForm.btnBorderClick(Sender: TObject);
begin
dlgColor.Execute;
pnlFgBgBorder.Color := dlgColor.Color;
SetDrawingStyle;
end;

procedure TMainForm.btnFillClick(Sender: TObject);
begin
dlgColor.Execute;
pnlFgBgInner.Color := dlgColor.Color;
SetDrawingStyle;
end;

end.


关键的几个函数是DrawToImage, 还有
procedure imgDrawingPadMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure imgDrawingPadMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure imgDrawingPadMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
这三个函数用来跟踪画图面板上的鼠标消息
在其中加入处理函数可以实现与画图程序一样的绘图效果
注意,procedure TMainForm.DrawToImage(TL, BR: TPoint; PenMode: TPenMode);
最后一个PenMode如果要实现动态绘制(即鼠标移到哪线就画到哪)
就必须用pmNotXor用来动态擦除再动态画上新的
而如果鼠标左键点下(代表确定要画在那),则要使用pmCopy把最终一条线画上去
Powered By Blogger