星期五, 二月 02, 2007

使用Visual C++ 2005 Express Edition搭建Windows下免费的C/C++跨平台程序开发环境

Visual C++ 2005 Express Edition是微软的免费C/C++开发工具,用它可以进行基于.Net平台的C++程序开发,配合Platform SDK还可以开发基于Windows API的应用程序,但是,但是,微软对它的功能也作了很多限制,其中对个人开发来说最主要的可能是他本身不带资源编辑器,只能手动编写资源文件代码添加资源,而且他也没带MFC,只能编写API程序。其实,借助第三方的免费库和工具,加上VC优秀的代码编辑和调试能力,我们完全可以搭建一个高效的带有GUI界面编辑器的开发环境。
对于Windows的RC格式的资源文件,可以使用ResEdit编辑,C++库可以使用wxWidgets,他也是一个跨平台库,而wxWidgets下XRC资源,可以使用wxFormBuilder生成。下面是他们的下载地址:
VC2005:http://msdn.microsoft.com/vstudio/express/visualc/download/
Platform SDK:http://www.microsoft.com/downloads/details.aspx?FamilyId=0BAF2B35-C656-4969-ACE8-E4C0C0716ADB
ResEdit:http://www.resedit.net/
wxWidgets:http://www.wxwidgets.org/
wxFormBuilder:http://wxformbuilder.org/

1.VC和Platform SDK的安装
基本按照微软的说明来,但是有一个地方需要修改,就是修改默认链接库那一步,微软给的是
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib
由于wxWidgets还需要其他的一些库,改成
kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib
这样编译wxWidgets程序就不需要额外的设置了

2.ResEdit
这个工具只对Windows开发有用,ResEdit能够编辑RC格式的文件,而且界面和VC2005很相近,经过配置很容易集成到vc中使用。ResEdit不需要安装,下载后直接解压缩,放到该放的地方就能用了,但是要和vc配合使用,需要一点配置。在一个win32的项目中(配置第1步应该创建一个以验证其正确),右键点击一个rc文件(如果没有可以添加一个文本文件,把后缀改成rc),点Open with,在弹出对话框中点Add,添加ResEdit,并且把它设置成默认的编辑器,以后在vc中双击rc文件,就可以打开ResEdit进行编辑。ResEdit界面友好,使用方法类似于vc收费版的资源编辑器。

3.wxWidgets
wxWidgets是一个开放源代码的跨平台GUI程序开发库,可以开发Mac、Linux、Windows应用程序,本身结构设计与MFC相似(个人认为他的面相对象设计比MFC好),使用称作XRC的XML文件描述资源(基本上所有的GUI元素),资源文件本身可以单独存在,动态加载,也可以集成到程序中,有很大的灵活性,更重要的是现在存在许多XRC编辑器,都能可视化的生成或者编辑XRC文件,进行wxWidgets界面设计。
下载wxWidgets的Windows安装文件,安装。打开vc,菜单中的tools->options左边选择projects and solutions->vc++ directorys,后面那些(wxWidgets_installdir)应该用你的实际安装路径代替,添加(wxWidgets_installdir)\include\msvc、(wxWidgets_installdir)\include、(wxWidgets_installdir)\contrib\include三个目录到vc的include目录,把(wxWidgets_installdir\lib\vc_lib、(wxWidgets_installdir)\contrib\lib两个目录加到vc的library目录里,环境配置好了。然后进入wxWidgets安装目录下的build\msw,打开wx.dsw,在vc中使用build->batch build,选择所有项目中你需要的配置(一般来讲windows xp和2000选择所有的unicode release和unicode debug,强烈不建议选择太多,据说所有的全部编译需要4G以上的空间),build,编译完成后,就可以使用了。

4.wxFormBuilder
安装wxFormBuilder后,就可以可视化的构建wxWidgets的界面,生成XRC文件了。由于wxFormBuilder不支持直接编辑,所以不用在vc中创建文件关联,但是,可以在vc菜单的tools->extern tools里把它加入到vc的外部程序列表中,方便调用。

5.测试
上面几步环境配置好了,下面写一个简单的wxWidgets程序,测试刚才配置的环境。
在vc中创建一个新的win32工程,在向导中选择项目类型为win32 application,并选择其为empty(向导生成的代码是基于windows api的)。向其中添加一个头文件,内容为
wxHello.h
class MyFrame;
class MyApp: public wxApp{
public:
bool OnInit();
protected:
MyFrame * mainWnd;};

DECLARE_APP(MyApp);

class MyFrame: public wxFrame{
public:
void OnExit(wxCommandEvent & evt);
DECLARE_EVENT_TABLE()};

添加一个源代码文件,wxHello.cpp:
#include
#include
#include "wxHello.h"
IMPLEMENT_APP(MyApp);

bool MyApp::OnInit(){
wxXmlResource::Get()->InitAllHandlers();
wxXmlResource::Get()->Load(wxT("wxHello_res.xrc")); //这里用你自己的资源文件名代替
mainWnd = new MyFrame();
wxXmlResource::Get()->LoadFrame(mainWnd, NULL, wxT("MyFrame1")); //这里的资源名字也应该根据你自己的资源文件作相应的修改
mainWnd->Show(true);
SetTopWindow(mainWnd);
return true;}

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(XRCID("menuItem1"), MyFrame::OnExit) //这里的menuItem1应该用你自己编辑的菜单项的实际名字代替
END_EVENT_TABLE()

void MyFrame::OnExit(wxCommandEvent &/*evt*/){ Close(true);}

build项目生成可执行文件,下面创建xrc资源文件,打开wxFormBuilder,首先在修改工程属性,代码生成类型选择为xrc,文件名wxHello_res,然后依次加入一个Frame、wxMenuBar、wxMenu、wxMenuItem,根据需要改变上面显示的文字,并改变控件名(控件的name属性),需要同时在源代码中修改加载资源的名字。添加好后保存,然后在菜单的file中选择generate code或者点f8键,就能导出xrc文件,导出的xrc文件在刚才保存的项目文件同一个目录下。

复制生成的xrc文件到刚才vc生成的可执行文件同一个目录下,运行,就能看到新创建的程序了。这个程序创建过程中,c++源代码没有作任何界面创建的工作,它只是简单的从xrc文件中加载源代码,显示,然后就是处理界面生成的消息,wxWidgets的消息机制很像MFC,消息用ID标识,但是当使用xrc资源时,我们一般没有消息id,所以使用宏XRCID(ctrlname)来从一个控件名转换消息id。而界面的生成全部由可视化的工具wxFormBuilder完成,这大大减小了代码编辑量。程序和界面这样分离带来的其它好处还有比如国际化,只需要提供一个新的xrc文件就可以完成,非常方便。

6.说明
使用这种方法我们搭建了一个windows下工作免费的gui程序开发环境,而且所有软件是经过授权的,这个环境开发出来的代码事实上是可以移植的,将源代码在其它平台上重新编译(当然需要安装相应的wxWidgets),加上那个xrc文件(不用修改)就可以在这个平台上运行了。
但是,需要说明的是,wxWidgets本身是比较新的,还存在许多bug,其稳定性应当不如MFC,而且不支持某些Windows平台专有特性,比如开发ActiveX控件或DirectX,所以,使用上有一定的局限。而且xrc资源编辑器一般也都有这样那样的bug,比如这里介绍的wxFormBuilder,就不能编写独立的工具栏或者菜单栏以供程序动态调用。
据说还有一个开发wxWidgets程序的好软件,叫做codeblocks(http://www.codeblocks.org/),也是免费的,但是这个软件太新,而且现在1.0正式版还没出来呢,想用的只好继续等了。其它还有很多xrc的编辑器,可以在wxWidgets的wiki上找找

下面是一些参考资料:
有关wxWidgets编程的详细信息可以去www.wxwidgets.org 上的document或者wiki里找,有一些很不错的资料
wxFormBuilder的用法可以去他的论坛上看看,www.wxformbuilder.org
codeblock的主页在www.codeblocks.org ,在他的论坛上有最新的工具的源代码,有兴趣的可以自己下载下载编译一个试试。

星期四, 十一月 02, 2006

获得网卡ipv6地址列表

MS在IP Helper库中提供的函数使应用程序在运行过程中可以方便的设置和获取系统网络配置信息,IPv4的网卡配置信息获取可以使用GetAdapterInfo函数调用实现,而IPv6的信息获取,则调用GetAdaptersAddresses函数,这个函数在最新的MSDN中有比较完整的示例,但是没有演示如何处理获得的IPv6地址,这里写一下。因为MS在WindowsXP之后才正式支持IPv6,所以此函数仅在WindowsXP以后的操作系统上有效,而且好像VC6也没有这个函数的支持。
GetAdaptersAddresses函数原型为:
ULONG WINAPI GetAdaptersAddresses(
ULONG Family,
ULONG Flags,
PVOID Reserved,
PIP_ADAPTER_ADDRESSES AdapterAddresses,
PULONG SizePointer
);
详细的说明可以参见MSDN,其中一个重要的数据结构是PIP_ADAPTER_ADDRESSES,它包含网卡的配置信息,定义如下:
typedef struct _IP_ADAPTER_ADDRESSES {
//...
struct _IP_ADAPTER_ADDRESSES* Next;
PIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress;
//...
} IP_ADAPTER_ADDRESSES, *PIP_ADAPTER_ADDRESSES;
上面定义中省略的部分可以参见MSDN,其中Next指向下一个网卡的IP_ADAPTER_ADDRESSES指针,而FirstUnicastAddress就是网卡的ip地址字段,它是一个链接结构的PIP_ADAPTER_UNICAST_ADDRESS,定义如下:
typedef struct _IP_ADAPTER_UNICAST_ADDRESS {
//...
struct _IP_ADAPTER_UNICAST_ADDRESS* Next;
SOCKET_ADDRESS Address;
//...
} IP_ADAPTER_UNICAST_ADDRESS, *PIP_ADAPTER_UNICAST_ADDRESS;
其中Address就是我们需要的当前项目的ip地址的内容,而Next包含了当前的网卡(程序里就是当前使用的那个PIP_ADAPTER_ADDRESSES指针)下一个ip地址配置的相关信息。
我们在调用GetAdaptersAddresses成功后(调用需要指定标志AF_INET6或者AF_UNSPEC),获取一个sockaddr_in6(socket函数使用)的变量,可以使用如下强制类型转换语句:
((struct sockaddr_in6 *)AdapterAddresses->FirstUnicastAddress->Address.lpSockaddr);
遍历以AdapterAddresses和FirstUnicastAddress为首的链表,我们就能够得到所有网卡上配置的所有IPv6地址的列表了。

在linux下获取网卡IPv6地址列表好像读取/proc/net/if_inet6

当然了,以上讲的方法都需要首先在系统里添加IPv6支持,Windows在网络配置中添加ipv6协议,而linux运行modprobe ipv6加载ipv6模块(如果没有自动加载的话)。

星期六, 四月 15, 2006

汇编语言的sha1算法实现

手动做了一些优化,比linux上那个sha1sum快一些,但是比rfc上c的代码还是慢,很受打击

用cygwin下的gcc可以编译成dll文件,命令
gcc -shared -mno-cygwin -osha1.dll sha1.s

在cygwin下连同下面的测试程序编译成windows可执行文件用这个命令
gcc -mno-cygwin -osha1.exe main.c sha1.s

导出三个函数给c用

/*sha1.h*/
/*重新开始新的计算*/
int init();
/*输入新数据到缓冲区*/
int input(void* data, int length);
/*取得计算结果*/
int final(void* result);

#下面是汇编的代码,比c还慢的那个
#sha1.s
.equ K0TO19, 0x5A827999
.equ K20TO39, 0x6ED9EBA1
.equ K40TO59, 0x8F1BBCDC
.equ K60TO79, 0xCA62C1D6

.section .bss
.lcomm buffer, 64 #已经输入数据缓冲区
.lcomm length, 4 #已经输入数据长度
.lcomm totalenlow, 4 #数据总长度
.lcomm totalenhi, 4 #数据长度,高32bit
.lcomm stat, 4 #-1=error, 0=ok
.lcomm H0, 4
.lcomm H1, 4
.lcomm H2, 4
.lcomm H3, 4
.lcomm H4, 4
.lcomm W, 320

.section .text
.globl _init, _input, _final
# 填充数据缓冲至512bit
# 函数使用edi作缓冲区地址,ecx为块中已有数据长度,edx为数据总长度(填充末尾需要)
# %eax, %ebx用作临时变量
# 最后64位用来记录数据长度
_pad:
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
pushl %edi
movl $buffer, %edi
movl length, %ecx #肯定小于64
movb $0x80, %al #第一个填充字节1000 0000B
addl %ecx, %edi #寻址填充起点
cld #增长方向
stosb
movl $64, %eax #块大小,总数
subl %ecx, %eax #应填充数
subl $1, %eax #一个已经填充
cmp $8, %eax #查看预留空间是否够用
jl padbufferfull
jmp padbufferok
padbufferfull:
movl %eax, %ecx
xorl %eax, %eax
rep stosb
call _compute
movl $buffer, %edi
movl $64, %eax
padbufferok:
subl $8, %eax #留出数据长度存放空间
movl %eax, %ecx #准备填充
xorl %eax, %eax #eax清零
rep stosb
#由于计算机内存储方式与rfc中相反,故应作相应
#存放长度
movl totalenhi, %edx
movl %edx, %eax
shr $24, %eax
stosb
movl %edx, %eax
shr $16, %eax
stosb
movl %edx, %eax
shr $8, %eax
stosb
movb %dl, (%edi)
incl %edi
movl totalenlow, %edx
movl %edx, %eax
shr $24, %eax
stosb
movl %edx, %eax
shr $16, %eax
stosb
movl %edx, %eax
shr $8, %eax
stosb
movb %dl, (%edi)
incl %edi
call _compute
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax
ret

# 重置各种参数,准备新的计算
# H0 = 67452301
# H1 = EFCDAB89
# H2 = 98BADCFE
# H3 = 10325476
# H4 = C3D2E1F0
# 已经输入数据长度清零
# int init()
_init:
movl $0x67452301, H0
movl $0xefcdab89, H1
movl $0x98badcfe, H2
movl $0x10325476, H3
movl $0xc3d2e1f0, H4
movl $0, length
movl $0, totalenlow
movl $0, totalenhi
movl $0, stat
xorl %eax, %eax
ret

# 散列计算函数
# rfc3174中method1
_compute:
pushl %esi
pushl %edi
pushl %ecx
pushl %eax
pushl %ebx
pushl %edx
pushl %ebp
# a. Divide M(i) into 16 words W(0), W(1), ... , W(15), where W(0)
# is the left-most word.
stepa:
movl $buffer, %esi
movl $W, %edi
movl $16, %ecx
movoneword:
lodsl #load a word to eax
#高位放低位
movl %eax, %ebx
shr $24, %eax
stosb
mov %ebx, %eax
shr $16, %eax
stosb
movl %ebx, %eax
shr $8, %eax
stosb
movb %bl, (%edi)
incl %edi
loop movoneword
# b. For t = 16 to 79 let
# W(t) = S^1(W(t-3) XOR W(t-8) XOR W(t-14) XOR W(t-16)).
# 使用ecx作为计数器
# edx作为索引
movl $16, %ecx
stepb:
movl %ecx, %edx
subl $3, %edx
movl W(, %edx, 4), %eax
movl %ecx, %edx
subl $8, %edx
xorl W(, %edx, 4), %eax
movl %ecx, %edx
subl $14, %edx
xorl W(, %edx, 4), %eax
movl %ecx, %edx
subl $16, %edx
xorl W(, %edx, 4), %eax
rol $1, %eax
movl %eax, W(, %ecx, 4)
inc %ecx
cmp $79, %ecx
jle stepb
# c. Let A = H0, B = H1, C = H2, D = H3, E = H4.
# use registers for a, b, c, d, e
# eax, ebx, ecx, edx, ebp for faster speed
stepc:
movl H0, %eax
movl H1, %ebx
movl H2, %ecx
movl H3, %edx
movl H4, %ebp

# d. For t = 0 to 79 do
# TEMP = S^5(A) + f(t;B,C,D) + E + W(t) + K(t);
# E = D; D = C; C = S^30(B); B = A; A = TEMP;
# then esi and edi are used for counter
# f(t;B,C,D) = (B AND C) OR ((NOT B) AND D) ( 0 <= t <= 19)
# f(t;B,C,D) = B XOR C XOR D (20 <= t <= 39)
# f(t;B,C,D) = (B AND C) OR (B AND D) OR (C AND D) (40 <= t <= 59)
# f(t;B,C,D) = B XOR C XOR D (60 <= t <= 79)
# K(t) = 5A827999 ( 0 <= t <= 19)
# K(t) = 6ED9EBA1 (20 <= t <= 39)
# K(t) = 8F1BBCDC (40 <= t <= 59)
# K(t) = CA62C1D6 (60 <= t <= 79)
xorl %esi, %esi
stepd:
stepd1:
movl %eax, %edi
rol $5, %edi #S^5(A)
addl %ebp, %edi #edi=S^5(A)+E
addl W(,%esi,4), %edi #+W(t)
addl $K0TO19, %edi #edi=S^5(A)+E+W(t)+K(t)
#E=ebp可用
#f(f;B,C,D)
#(B AND C) OR ((NOT B) AND D)
movl %ebx, %ebp
andl %ecx, %ebp
pushl %ebx
notl %ebx
andl %edx, %ebx
orl %ebx, %ebp
popl %ebx
addl %ebp, %edi #edi=TEMP, +f(t;B,C,D)
#E = D; D = C; C = S^30(B); B = A; A = TEMP
movl %edx, %ebp
movl %ecx, %edx
rol $30, %ebx
movl %ebx, %ecx
movl %eax, %ebx
movl %edi, %eax
incl %esi
cmpl $19, %esi
jle stepd1
stepd2:
movl %eax, %edi
rol $5, %edi
addl %ebp, %edi
addl W(,%esi,4), %edi
addl $K20TO39, %edi
movl %ebx, %ebp
xorl %ecx, %ebp
xorl %edx, %ebp
addl %ebp, %edi
movl %edx, %ebp
movl %ecx, %edx
rol $30, %ebx
movl %ebx, %ecx
movl %eax, %ebx
movl %edi, %eax
incl %esi
cmpl $39, %esi
jle stepd2
stepd3:
movl %eax, %edi
rol $5, %edi
addl %ebp, %edi
addl W(,%esi,4), %edi
addl $K40TO59, %edi
#f(t;B,C,D) = (B AND C) OR (B AND D) OR (C AND D)
movl %ebx, %ebp
andl %ecx, %ebp
pushl %ebx
andl %edx, %ebx
orl %ebx, %ebp
popl %ebx
pushl %ecx
andl %edx, %ecx
orl %ecx, %ebp
popl %ecx
addl %ebp, %edi
movl %edx, %ebp
movl %ecx, %edx
rol $30, %ebx
movl %ebx, %ecx
movl %eax, %ebx
movl %edi, %eax
incl %esi
cmpl $59, %esi
jle stepd3
stepd4:
movl %eax, %edi
rol $5, %edi
addl %ebp, %edi
addl W(,%esi,4), %edi
addl $K60TO79, %edi
movl %ebx, %ebp
xorl %ecx, %ebp
xorl %edx, %ebp
addl %ebp, %edi
movl %edx, %ebp
movl %ecx, %edx
rol $30, %ebx
movl %ebx, %ecx
movl %eax, %ebx
movl %edi, %eax
incl %esi
cmpl $79, %esi
jle stepd4

# e. Let H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4
# + E.
stepe:
addl %eax, H0
addl %ebx, H1
addl %ecx, H2
addl %edx, H3
addl %ebp, H4
popl %ebp
popl %edx
popl %ebx
popl %eax
popl %ecx
popl %edi
popl %esi
ret

# 添加数据,数据到512bit时进行一次计算,并重新开始填充
# int input(void* data, int len);
_input:
movl stat, %eax
cmp $-1, %eax
je inputstaterr
jmp inputstatok
inputstaterr:
ret
inputstatok:
pushl %ebp
movl %esp, %ebp
pushl %esi
pushl %edi
pushl %ecx
pushl %edx
pushl %eax
pushl %ebx
movl length, %edx #缓冲区已有数据长度(字节)
movl totalenlow, %eax #总长
movl 12(%ebp), %ebx #新送进来数据长度
movl 8(%ebp), %esi #用户缓冲区地址
movl totalenhi, %ebp
movl $buffer, %edi
addl %edx, %edi #定位填充起点
#%esi为源起点,%edi目的
movl $64, %ecx
subl %edx, %ecx #缓冲区剩余空间
#计算过程中ecx用作缓冲区剩余空间计数
#ebx未复制数据计数
#eax为总长度计数
inputnextbyte:
movsb
#inc %eax #总长度加一
addl $8, %eax
jo inctotalenhi
jmp countbuf
inctotalenhi:
inc %ebp
jo toolong
jmp countbuf
toolong:
movl $-1, %eax
movl %eax, stat
jmp finish
countbuf:
dec %ecx
jz bufferfull
jmp bufferok
bufferfull:
movl $64, %ecx #缓冲区空
movl $buffer, %edi
call _compute
bufferok:
dec %ebx
jz finish
jmp inputnextbyte
finish:
movl %eax, totalenlow
movl %ebp, totalenhi
movl $64, %edx
subl %ecx, %edx
movl %edx, length
popl %ebx
popl %eax
popl %edx
popl %ecx
popl %edi
popl %esi
pop %ebp
movl stat, %eax
ret

# 完成计算,取得最后散列结果
# int final(void* result);
_final:
movl stat, %eax
cmp $-1, %eax
je finalstaterr
jmp finalstatok
finalstaterr:
ret
finalstatok:
pushl %ebp
movl %esp, %ebp
pushl %esi
pushl %edi
pushl %ecx
pushl %eax
pushl %ebx
call _pad
movl 8(%ebp), %edi
movl $H0, %esi
movl $5, %ecx
movoners:
pushl %ecx
lodsl
movl %eax, %ebx
shr $24, %eax
stosb
movl %ebx, %eax
shr $16, %eax
stosb
movl %ebx, %eax
shr $8, %eax
stosb
movb %bl, (%edi)
incl %edi
popl %ecx
loop movoners
popl %ebx
popl %eax
popl %ecx
popl %edi
popl %esi
popl %ebp
xorl %eax, %eax
ret

/*再给个测试使用的例子*/
/*main.c*/
#include
#include
#include "sha1.h"
int main()
{
char *buffer = "this is a test";
int len = 0;
char result[20];
int i;
init();
len = strlen(buffer);
input(buffer, len);
final(result);
for(i=0; i<20; i++)
{
printf("%02X ", 0x0ff & result[i]);
}
printf("\n");
return 0;
}

星期一, 一月 09, 2006

使用at&t汇编写x86启动扇区

At&t的汇编语言语法在类unix操作系统中使用很多,linux内核新的源代码全部都是使用at&t汇编完成的,at&t汇编使用编译器是gas,但是,使用at&t汇编与使用nasm或masm编写启动扇区略有不同。新版本的gas不支持直接生成可执行的二进制代码文件,所以还需使用ld进行连接或使用objcopy将其生成的目标文件进行转换才可以使用,下面给出一个例子:
Boot sector的源代码:

START_SEG = 0x07c0
.code16
.text
.global _start
_start:

ljmp $START_SEG, $_start2

_start2:
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw $0x7c00, %sp
sti
cld

movw $hellomsg, %si

msg_loop:
lodsb
andb %al, %al
jz die
movb $0xe, %ah
int $0x10
jmp msg_loop

die:
jmp die

hellomsg:
.ascii "Hello, World!\r\n"
.byte 0

.org 510
boot_flag: .word 0xaa55

这个代码仿照linux内核的bootsect.s写成。编译连接步骤如下:
1. as –o boot1.o boot1s
这句不用解释了
2. 这里有两种方法可以实现
a. ld –o boot1.bin –Ttext 0x0 --oformat binary boot1.o
其中的-Ttext 0x0指定代码中的text段偏移量从0开始
--oformat binary指定输出文件是纯二进制形式
b. objcopy -O binary boot1.o boot1.bin
objcopy的作用是转换文件格式,上面的命令将as生成的目标文件直接转换成对应的二
进制文件,因为此文件是一个自包含即不依赖于任何外界库的文件,所以可以直接转换。
以上两句的结果是等价的,都生成了一个512字节的boot1.bin,在vmware中直接指定floppy image开机就可以看到效果了。在使用cygwin的系统中a方法似乎会失败。
这里主要介绍使用at&t编写boot sector的编译和连接工作,具体的boot sector的内容可以在网上找到。

星期三, 十一月 16, 2005

eclipse+jst+jboss4=j2ee

发现eclipse的插件jst支持session bean开发还不错,花了几天弄出来,写出与人分享。使用jboss4的j2ee容器,一个字:便宜!
这里准备演示一个j2ee的helloworld的开发,需要的软件如下:
jdk:http://java.sun.com
eclipse+jst:http://www.eclipse.org (下载eclipse后使用update manager在线安装jst)
projst:http://www.projst.com/eclipse/updates/ (这个添加jst对jboss4的支持,用update manager安装)
xdoclet:http://xdoclet.sourceforge.net (这个软件可以简化配置文件的编写,是jst必需的)
jboss:http://www.jboss.org
完成这些软件的下载安装以后,需要做一些配置工作:
在菜单上选择Window->preferences
1. 配置xdoclet
在左边展开j2ee annotations,选中xdoclet,指定xdoclet目录和版本,选中enable xdoclet builder选框,如图

然后展开xdoclet,分别在ejbdoclet和webdoclet下将jboss选成4.0版本的,并将其选中,如图


2. 配置jdk
展开java,选中installed jres,点击add,选择你的jdk安装路径,如图

配置完这个以后,在preferences页中点ok,然后重新打开preference使jdk在后面的设置中可用。
3. 配置jboss4
展开左边的server,选中installed runtimes,点击add,选中jboss->jboss4.0.x,在弹出对话框中填入你的jboss的安装路径,如图

设置至此完成,可以开始程序编写。
1.新建一个项目,选择new->project->j2ee->j2ee application project,点next,在name中填入J2EEHello

next,使用new Module给新建的j2ee项目添加一个ejb子项和一个web子项,如图


点击finish回到eclipse编辑界面,可以发现多了一个ejb工程(默认名字是J2EEHelloEJB)、一个web工程(默认名字是J2EEHelloWeb)和一个j2ee工程(第一次使用上面的某一步会提示你切换到j2ee视图,确定就行了)。
2.右键点击J2EEHelloEJB,new->enterprise java bean

选中session,点next,输入ejb的信息,package是ejbs,名字是HelloBean,选中下面的generate an annotated bean,点finish
回到编辑界面,打开HelloBean.java(顺便提一下,是用这个工具只需要手动编辑HelloBean.java文件,其他的remote和home会由xdoclet自动生成),添加一个成员函数,如下:

/**
*
* @ejb.interface-method view-type="remote"
*/
public String helloMsg()
{
return "Hello, J2EE!";
}

注意那段注释,不能省,这是xdoclet可以自动生成remote和home的关键,如图

至此ejb已经做完了,下面写ejb的客户。
3.打开J2EEHelloWeb项目的属性,选中Java jar Dependences,把ejb项目的jar选中,ok,如图

返回编辑器,展开那个J2EEHelloWeb项目,右键点击WebContent,new->JSP,填入名字hello.jsp,然后finish就可以了。
在jsp文件的body标签之间添加如下代码保存
<%
ejbs.HelloHome home = ejbs.HelloUtil.getHome();
ejbs.Hello hello = home.create();
%>
<%= hello.helloMsg() %>
ejbs.HelloUtil是jst生成的一个辅助类,可以简化ejb的使用
4.客户端写完了,可以开始调试了,右键点击那个hello.jsp,run as->run on server
选jboss4.0.x,选中下面的set server as project default,

在最后一页把J2EEHello加入服务器,点finish。

过一会应该可以在右边的内嵌浏览器中看到效果,如图(如果提示404就等一会再刷新一下):

将其地址复制到ie地址栏中也能看到这张页面:

这里使用eclipse和jboss开发j2ee服务器,虽然可能没有商用的工具成熟好用(毕竟他还不支持CMP的开发)。但是这里使用的工具都是免费的,所以有比较大的实用价值。

星期六, 十一月 05, 2005

tomcat+datasource

这两天在调试程序发现tomcat下的工具使datasource用起来很简单,所以把建立过程写出来与大家分享。班门弄斧,有错欢迎指正。
首先要建立一个给应用程序使用的数据源,假如你的应用程序部署在/test下,那么打开并登陆http://localhost:8080/admin/
tomcat server->service->Host(localhost)->Context(/test)->Resources->Datasource
在右上方的下拉列表里选Create New DataSource
Jndi Name是资源引用的名称,由你起,这里用jdbc/user
Datasource URL就是你平时用DriverManager.getConnetion(url)里的url,但不要用户名和密码信息,比如:jdbc:mysql://localhost/database_name?autoReconnect=true (autoreconnect是连接被断开后tomcat会自动重连)
Driver Class就相当于Class.forName(className)里的className,mysql用org.gjt.mm.mysql.Driver
其他你自己看着办,据tomcat-docs上说是用空的数据库密码会导致连接失败,我没试过,但为了安全性还是给数据库加上密码。
点save就ok了。
然后你部署在/test下的应用程序就能用这个datasource了,以后建立数据库连接的时候就这样:
InitialContext ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup("java:comp/env/jdbc/user");
//也就是java:comp/env + 你起的jndi name
//原理你可以看j2ee tutorial
//由于tomcat的链接池实现是按j2ee标准的,所以你的代码不用修改就可以在
//weblogic等j2ee容器中使用
Connection conn = ds.getConnection();//这句取得了数据库链接
//下面就使你使用Connection的代码了,一切都和以前一样,从这里开始
是不是很简单?以后用class.forname的都不好意思给你打招呼了:-)
最后要注意的就是平时我们使用数据库的时候因为连接很费时,所以要早连接,晚释放,而用datasource(连接池)时,则要晚连接,早释放,因为连接是从链接池中取得的,释放则返回链接池,这样就能保证链接池有足够多的连接数。释放使用conn.close()就可以了,这不是直接关闭到数据库的链接,而是把链接返回链接池。

星期五, 十一月 04, 2005

DOM to XML

// 获得将DOM文档转化为XML文件的转换器,在jdk1.4中,有类TransformerFactory
// 来实现,类Transformer实现转化API。
TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer transformer = tfactory.newTransformer();
// 将DOM对象转化为DOMSource类对象,该对象表现为转化成别的表达形式的信息容器。
DOMSource source = new DOMSource(document);
// 获得一个StreamResult类对象,该对象是DOM文档转化成的其他形式的文档的容器,可以是XML文件,文本文件,HTML文件。这里为一个XML文件。
StreamResult result = new StreamResult(new File(“text.xml”));
// 调用API,将DOM文档转化成XML文件。
transformer.transform(source,result);

apache+tomcat+jk2_jni

今天忙活和一天,终于搞出来apache和tomcat使用jni整合配置,用的jni方式通信,不用让tomcat启动,用的时候apache会自动启动tomcat的。把配置过程共享出来,希望对其他人能有帮助。我是在windowsxp下试成功的,linux下没成功,可能是apache版本太低,如果有人作出来了希望能不吝赐教!
1) (八股?!)下载软件安装
apache2.0.49
tomcat5.19
jk2.0.2
在那下载自己找,我就不提供了吧?
装好后启动服务器,先测试一把
http://localhost           以该能看到apache的默认页
http://localhost:8080      应该能看到tomcat的默认页面
测试成功则关闭apache和tomcat并把tomcat的启动方式改为手动再继续

2)安装jk2
把下载到的jk解压,把那个dll文件改名为mod_jk2.dll(你也可以改成其他名字或不改,以下的过程也跟着变就是了)放到[apache安装目录]\modules下
编辑httpd.conf加入
    LoadModule jk2_module modules/mod_jk2.dll
重启apache,成功则关闭apache继续(不成功能不能继续我没试过:-)

3)编辑配置文件
a)workers2.properties
在[apache安装目录]\conf下新建一个文件,命名为workers2.properties
内容如下,注意,根据你服务器的安装路径可能要作修改(下面有提示的地方)!
[config:]
file=${serverRoot}/conf/workers2.properties
debug=0
debugEnv=0

[shm:]
disabled=1

#在tomcat4中从这里开始到下一个标记处不需要
# Example socket channel, override port and host.
[channel.socket:localhost:8009]                    
port=8009
host=127.0.0.1

# define the worker
[ajp13:localhost:8009]
channel=channel.socket:localhost:8009
#在tomcat4中从这里到上一个标记之间的不需要

[channel.jni:jni]
info=The jni channel, used if tomcat is started inprocess

[vm:]
info=Parameters used to load a JVM in the server process
classpath=D:/server/Tomcat5/lib                 #可能需要修改
OPT=-Dtomcat.home=D:/server/Tomcat5             #可能要修改
OPT=-Dcatalina.home=D:/server/Tomcat5           #可能要修改
OPT=-Xmx128M
disabled=0

[worker.jni:onStartup]
info=Command to be executed by the VM on startup. This one will start tomcat.
class=org/apache/jk/apr/TomcatStarter
ARG=start
disabled=0
stdout=${serverRoot}/logs/stdout.log
stderr=${serverRoot}/logs/stderr.log

[worker.jni:onShutdown]
info=Command to be executed by the VM on shutdown. This one will stop tomcat.
class=org/apache/jk/apr/TomcatStarter
ARG=stop
disabled=0

[status:]
info=Status worker, displays runtime informations

[uri:/jkstatus/*]
info=Display status information and checks the config file for changes.
group=status:

[uri:/jsp-examples/*]
info=Map the whole webapp (for tomcat5)

[uri:j.e16.dhs.org/*.jsp]                  #虚拟主机用,根据你的域名修改
info=e16 test site

[uri:j.e16.dhs.org/servlet/*]              #也是虚拟主机映射
info=servlets

然后在[tomcat安装目录]下新建个目录,命名为lib,把tomcat带的所有jar文件解压到这个目录下(有点劲大,但这样最省事,好像)。

b)jk2.properties
好像[tomcat安装目录]\conf下本来就有,编辑它,内容如下:
## THIS FILE MAY BE OVERRIDEN AT RUNTIME. MAKE SURE TOMCAT IS STOPED
## WHEN YOU EDIT THE FILE.

## COMMENTS WILL BE _LOST_

## DOCUMENTATION OF THE FORMAT IN JkMain javadoc.

# Set the desired handler list
handler.list=apr,request,channelJni        #这行原来是被注释掉的
#
# Override the default port for the socketChannel
# channelSocket.port=8019
# Default:
# channelUnix.file=${jkHome}/work/jk2.socket
# Just to check if the the config  is working
# shm.file=${jkHome}/work/jk2.shm

# In order to enable jni use any channelJni directive
# channelJni.disabled = 0
# And one of the following directives:

# apr.jniModeSo=/opt/apache2/modules/mod_jk2.so

# If set to inprocess the mod_jk2 will Register natives itself
# This will enable the starting of the Tomcat from mod_jk2
apr.jniModeSo=inprocess                   #这行也是
下面,激动人心……
启动apache,打开浏览器,输入
http://localhost/jsp-examples/
看到JSP Samples页面就成功了(刚启动后马上可能看不到,稍微等一会,要十分钟后还看不到则肯定是挂了。

4)虚拟主机
上面配置的是让tomcat替apache解析jsp-examples目录下的所有文件,也可以把整个虚拟主机交给tomcat(假设域名是j.e16.dhs.org)
a)编辑httpd.conf,在最后加入
NameVirtualHost *:80
<VirtualHost *:80>
     DocumentRoot f:/jwebapp      #目录根据你的需要改
     serverName j.e16.dhs.org
</VirtualHost>

<Directory f:/jwebapp>               #和上面保持一致
     AllowOverride All            #这里为了一会设定web-inf权限
     Options +Indexes             #这里可以不要,为了调试程序方便
</Directory>
b)编辑workers2.properties,加入
[uri:j.e16.dhs.org/*.jsp]
info=e16 test site

[uri:j.e16.dhs.org/servlet/*]
info=servlets
#这样你就把所有虚拟主机下的jsp和servlet交给tomcat了,但还要在tomcat下作相应的配置
c)编辑server.xml,加入
     <Host name="j.e16.dhs.org" debug="0" appBase="f:/jwebapp" unpackWARs="true" autoDeploy="true">

          <Context path="" docBase="" debug="1"/>

          <Valve className="org.apache.catalina.valves.AccessLogValve"
                 directory="logs"  prefix="e16j." suffix=".txt"
                 pattern="common" resolveHosts="false"/>
     </Host>
<!--地方自己找,原来应该有个host标记,和它放在一起-->
d)对web用户隐藏web-inf目录
这一步是为安全考虑
新建一个文件.htaccess,内容如下
Options None
Order deny,allow
Deny from all
在每个web-inf下放一个
启动apache,试试http://j.e16.dhs.org/        把域名改成你的域名,应该会有个惊喜

补充:
上面的虚拟主机(j.e16.dhs.org)不会影响php和perl的执行。用tomcat4(我试了4.1.30)以上配置不用改变,而且可以按照提示去掉一些东西(我不知道为什么)。这里我在winxp下虽然配置成功了,但原理仍然不是太懂,如果谁很清楚的话望能赐教。附带的附件是我的配置文件(正在运行的)。欢迎指正以上可能存在的错误,我的BMYid是neilzheng。