星期四, 三月 29, 2007

BCB中各类string简介级转换方式

char a1[10]; 是一个字符数组,有10个字节的空间。对于a1来说,你可以把它当成一个char*指针来用。但是这个指针是const的,不能将它指向其他地方。经常用这样的数组来保存一个字符串。
char *a2; 是一个char*指针。你可以将它指向其他的char型变量,也可以指向一个char数组,比如a2 = a1;但是注意a2自己没有内存空间。如果需要修改a2所指向的变量的内容时,就要留意。
string a; string是一个类,是属于c++标准的。也就是说,它是c++带的类,所以不管到vc,还是bcb,还是linux下什么编译器,只要符合c++标准,就可以使用这个类。它有自己的空间存储字符串。
AnsiString a; 也是一个类,是vcl,或者说bcb,提供的一个类。由于bcb里的其他类和一些其他函数大量使用了这种类型,所以在bcb下编程,我们经常使用这种字符串。它有自己的空间存储字符串。
String是AnsiString的别名(看BCB开发手册写的)
-----------------------------------------------------------------------------------------------------------------------
AnsiString -> char AnsiString.c_str();
char -> AnsiString AnsiString = AnsiString(char);
BSTR -> char* char* char1=AnsiString(bstr).c_str();
wchar_t -> AnsiString AnsiString(wchar_t)
AnsiString -> wchart AnsiString.WideChar();
char -> WideString char *s="adsfdsf";
wchar *p=Widetring(s).BSTR();
WideString -> char wchar_t *p=L"sdsfdfsf";
char *s=AnsiString(p).c_str();
char * c -> wchar_t * cc AnsiString con = c;
cc = con.WideChar( cc , con.WideCharBufSize );
wchar_t * c -> char * cc AnsiString con = c;
cc = con.c_str();
WideString -> wchar_t WideString a = L"abc";
wchar_t *b = new wchar_t[4];
wcscpy(b, a); AnsiString=AnsiString(b);
WideString ws;
// ws = ***;
AnsiString s = ws;

星期三, 三月 28, 2007

BCB 编写 DLL 终极手册 [转帖]

由于现在比较多的网友老是在 CSDN 上询问关于 BCB 编写 DLL 的问题,我编写了这篇文章抛砖引玉
一. 编写 DLL
File/New/Dll 生成 Dll 的向导,然后可以添加导出函数和导出类
导出函数:extern "C" __declspec(dllexport) ExportType FunctionName(Parameter
)
导出类:class __declspec(dllexport) ExportType ClassName{...}
例子:(说明:只是生成了一个 DLL.dll )
#include "DllForm.h" // TDllFrm 定义

USERES("Dll.res");
USEFORM("DllForm.cpp", DllFrm);

class __declspec(dllexport) __stdcall MyDllClass { //导出类
public:
MyDllClass();
void CreateAForm();
TDllFrm* DllMyForm;
};

TDllFrm* DllMyForm2;
extern "C" __declspec(dllexport) __stdcall void CreateFromFunct();//导出函


//-----------------------------------------------------------------------
----
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
return 1;
}
//-----------------------------------------------------------------------
----

MyDllClass::MyDllClass()
{
}

void MyDllClass::CreateAForm()
{
DllMyForm = new TDllFrm(Application);
DllMyForm->Show();
}
//-----------------------------------------------------------------------
----
void __stdcall CreateFromFunct()
{
DllMyForm2 = new TDllFrm(Application);
DllMyForm2->Show();
}


二. 静态调用 DLL
使用 $BCB path\Bin\implib.exe 生成 Lib 文件,加入到工程文件中
将该文件拷贝到当前目录,使用 implib MyDll.lib MyDll.dll 生成
// Unit1.h // TForm1 定义
#include "DllForm.h" // TDllFrm 定义
//-----------------------------------------------------------------------
----

__declspec(dllimport) class __stdcall MyDllClass {
public:
MyDllClass();
void CreateAForm();
TDllFrm* DllMyForm;
};
extern "C" __declspec(dllimport) __stdcall void CreateFromFunct();

class TForm1 : public TForm{...}


// Unit1.cpp // TForm1 实现
void __fastcall TForm1::Button1Click(TObject *Sender)
{ // 导出类实现,导出类只能使用静态方式调用
DllClass = new MyDllClass();
DllClass->CreateAForm();
}
//-----------------------------------------------------------------------
----
void __fastcall TForm1::Button2Click(TObject *Sender)
{ // 导出函数实现
CreateFromFunct();
}
//-----------------------------------------------------------------------
----

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
delete DllClass;
}

三. 动态调用 DLL
// Unit1.h
class TForm1 : public TForm
{
...
private: // User declarations
void (__stdcall *CreateFromFunct)();
...
}

// Unit1.cpp // TForm1
HINSTANCE DLLInst = NULL;
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if( NULL == DLLInst ) DLLInst = LoadLibrary("DLL.dll"); //上面的 Dll
if (DLLInst) {
CreateFromFunct = (void (__stdcall*)()) GetProcAddress(DLLInst,
"CreateFromFunct");
if (CreateFromFunct) CreateFromFunct();
else ShowMessage("Could not obtain function pointer");
}
else ShowMessage("Could not load DLL.dll");
}

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
if ( DLLInst ) FreeLibrary (DLLInst);
}

四. DLL 作为 MDIChild (子窗体) 【只编写动态调用的例子】
实际上,调用子窗体的 DLL 时,系统只是检查应用程序的 MainForm 是否为 fsMDIForm
的窗体,这样只

要把调用程序的 Application 的 Handle 传递给 DLL 的 Application 即可;同时
退出 DLL 时也要恢复

Application

// MDIChildPro.cpp // Dll 实现 CPP
#include "unit1.h" // TForm1 定义
TApplication *SaveApp = NULL;
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
if ( (reason==DLL_PROCESS_DETACH) && SaveApp )
Application = SaveApp ; // 恢复 Application
return 1;
}

extern "C" __declspec(dllexport) __stdcall void TestMDIChild( //1024X768
TApplication* mainApp,
LPSTR lpCaption)
{
if ( NULL == SaveApp ) // 保存 Application,传递 Application
{
SaveApp = Application;
Application = mainApp;
}
// lpCaption 为子窗体的 Caption
TForm1 *Form1 = new TForm1 ( Application, lpCaption );
Form1->Show();
}
注:上面的程序使用 BCB 3.0 编译成功

五. BCB 调用 VC 编写的 DLL
1. 名字分解:
没有名字分解的函数
TestFunction1 // __cdecl calling convention
@TestFunction2 // __fastcall calling convention
TESTFUNCTION3 // __pascal calling convention
TestFunction4 // __stdcall calling convention
有名字分解的函数
@TestFunction1$QV // __cdecl calling convention
@TestFunction2$qv // __fastcall calling convention
TESTFUNCTION3$qqrv // __apscal calling convention
@TestFunction4$qqrv // __stdcall calling convention
使用 extern "C" 不会分解函数名

使用 Impdef MyLib.def MyLib.DLL 生成 def 文件查看是否使用了名字分解

2. 调用约定:
__cdecl 缺省
是 Borland C++ 的缺省的 C 格式命名约定,它在标识符前加一下划线,以保留
它原来所有的全程标识符。参数按最右边参数优先的原则传递给栈,然后清栈。
extaern "C" bool __cdecl TestFunction();
在 def 文件中显示为
TestFunction @1
注释: @1 表示函数的顺序数,将在“使用别名”时使用。

__pascal Pascal格式
这时函数名全部变成大写,第一个参数先压栈,然后清栈。
TESTFUNCTION @1 //def file

__stdcall 标准调用
最后一个参数先压栈,然后清栈。
TestFunction @1 //def file

__fastcall 把参数传递给寄存器
第一个参数先压栈,然后清栈。
@TestFunction @1 //def file

3. 解决调用约定:
Microsoft 与 Borland 的 __stdcall 之间的区别是命名方式。 Borland 采用
__stdcall 的方式去掉了名字起前的下划线。 Microsoft 则是在前加上下划线,在

后加上 @ ,再后跟为栈保留的字节数。字节数取决于参数在栈所占的空间。每一个

参数都舍入为 4 的倍数加起来。这种 Miocrosoft 的 DLL 与系统的 DLL 不一样。


4. 使用别名:
使用别名的目的是使调用文件 .OBJ 与 DLL 的 .DEF 文件相匹配。如果还没有
.DEF 文件,就应该先建一个。然后把 DEF 文件加入 Project。使用别名应不断
修改外部错误,如果没有,还需要将 IMPORTS 部分加入 DEF 文件。
IMPORTS
TESTFUNCTIOM4 = DLLprj.TestFunction4
TESTFUNCTIOM5 = DLLprj.WEP @500
TESTFUNCTIOM6 = DLLprj.GETHOSTBYADDR @51
这里需要说明的是,调用应用程序的 .OBJ 名与 DLL 的 .DEF 文件名是等价的,
而且总是这样。甚至不用考虑调用约定,它会自动匹配。在前面的例子中,函数被
说明为 __pascal,因此产生了大写函数名。这样链接程序不会出错。

5. 动态调用例子
VC DLL 的代码如下:
extern "C" __declspec(dllexport) LPSTR __stdcall BCBLoadVCWin32Stdcall()
{
static char strRetStdcall[256] = "BCB Load VC_Win32 Dll by __stdcall mode
is OK!";

return strRetStdcall;
}

extern "C" __declspec(dllexport) LPSTR __cdecl BCBLoadVCWin32Cdecl()
{
static char strRetCdecl[256] = "BCB Load VC_Win32 Dll by __cdecl mode is
OK!";

return strRetCdecl;
}

extern "C" __declspec(dllexport) LPSTR __fastcall BCBLoadVCWin32Fastcall(
)
{
static char strRetFastcall[256] = "BCB Load VC_Win32 Dll by __fastcall mode
is OK!";

return strRetFastcall;
}

其实动态调用与调用 BCB 编写的 DLL 没有区别,关键是查看 DLL 的导出函数名字

可以使用 tdump.exe(BCB工具) 或者 dumpbin.exe(VC工具) 查看
tdump -ee MyDll.dll >1.txt (查看 1.txt 文件即可)
由于 VC6 不支持 __pascall 方式,下面给出一个三种方式的例子
void __fastcall TForm1::btnBLVCWin32DynClick(TObject *Sender)
{
/*cmd: tdbump VCWin32.dll >1.txt
Turbo Dump Version 5.0.16.4 Copyright (c) 1988, 1998 Borland International

Display of File VCWIN32.DLL

EXPORT ord:0000='BCBLoadVCWin32Fastcall::'
EXPORT ord:0001='BCBLoadVCWin32Cdecl'
EXPORT ord:0002='_BCBLoadVCWin32Stdcall@0'
*/
if ( !DllInst )
DllInst = LoadLibrary ( "VCWin32.dll" );
if ( DllInst )
{
BCBLoadVCWin32Stdcall = (LPSTR (__stdcall *) () )
GetProcAddress ( DllInst, "_BCBLoadVCWin32Stdcall@0" ); //VC Dll
// GetProcAddress ( DllInst, "BCBLoadVCWin32Stdcall" ); //BCB Dll
if ( BCBLoadVCWin32Stdcall )
{
ShowMessage( BCBLoadVCWin32Stdcall() );
}
else ShowMessage ( "Can't find the __stdcall Function!" );

BCBLoadVCWin32Cdecl = (LPSTR (__cdecl *) () )
GetProcAddress ( DllInst, "BCBLoadVCWin32Cdecl" );
if ( BCBLoadVCWin32Cdecl )
{
ShowMessage( BCBLoadVCWin32Cdecl() );
}
else ShowMessage ( "Can't find the __cdecl Function!" );

//Why?不是 'BCBLoadVCWin32Fastcall::',而是 '@BCBLoadVCWin32Fastcall@0'?
BCBLoadVCWin32Fastcall = (LPSTR (__fastcall *) () )
//GetProcAddress ( DllInst, "BCBLoadVCWin32Fastcall::" );
GetProcAddress ( DllInst, "@BCBLoadVCWin32Fastcall@0" );
if ( BCBLoadVCWin32Fastcall )
{
ShowMessage( BCBLoadVCWin32Fastcall() );
}
else ShowMessage ( "Can't find the __fastcall Function!" );
}
else ShowMessage ( "Can't find the Dll!" );
}

6. 静态调用例子
静态调用有点麻烦,从动态调用中可以知道导出函数的名字,但是直接时(加入 lib
文件到工程文件)

Linker 提示不能找到函数的实现
从 4 看出,可以加入 def 文件连接
(可以通过 impdef MyDll.def MyDll.dll 获得导出表)
建立与 DLL 文件名一样的 def 文件与 lib 文件一起加入到工程文件
上面的 DLL(VCWIN32.dll) 的 def 文件为(VCWIN32.def):
LIBRARY VCWIN32.DLL

IMPORTS
@BCBLoadVCWin32Fastcall = VCWIN32.@BCBLoadVCWin32Fastcall@0
_BCBLoadVCWin32Cdecl = VCWIN32.BCBLoadVCWin32Cdecl
BCBLoadVCWin32Stdcall = VCWIN32._BCBLoadVCWin32Stdcall@0

对应的函数声明和实现如下:
extern "C" __declspec(dllimport) LPSTR __fastcall BCBLoadVCWin32Fastcall(
);
extern "C" __declspec(dllimport) LPSTR __cdecl BCBLoadVCWin32Cdecl();
extern "C" __declspec(dllimport) LPSTR __stdcall BCBLoadVCWin32Stdcall();


void __fastcall TfrmStatic::btnLoadDllClick(TObject *Sender)
{
ShowMessage ( BCBLoadVCWin32Fastcall() );
ShowMessage ( BCBLoadVCWin32Cdecl() );
ShowMessage ( BCBLoadVCWin32Stdcall() );
}
注意:在 BCB 5.0 中,可能直接按下 F9 是不能通过 Linker 的,请先 Build 一次

注:上面的程序使用 BCB 5.0 与 VC6.0 编译成功

星期日, 三月 25, 2007

40种网站设计常用技巧

1. oncontextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键
<table border oncontextmenu=return(false)><td>no</table> 可用于Table

2. <body onselectstart="return false"> 取消选取、防止复制

3. onpaste="return false" 不准粘贴

4. oncopy="return false;" oncut="return false;" 防止复制

5. <link rel="Shortcut Icon" href="favicon.ico"> IE地址栏前换成自己的图标

6. <link rel="Bookmark" href="favicon.ico"> 可以在收藏夹中显示出你的图标

7. <input style="ime-mode:disabled"> 关闭输入法

8. 永远都会带着框架
<script language="JavaScript"><!--
if (window == top)top.location.href = "frames.htm"; //frames.htm为框架网页
// --></script>

9. 防止被人frame
<SCRIPT LANGUAGE=JAVASCRIPT><!--
if (top.location != self.location)top.location=self.location;
// --></SCRIPT>

10. 网页将不能被另存为
<noscript><iframe src="/blog/*.html>";</iframe></noscript>

11. <input type=button value=查看网页源代码
onclick="window.location = "view-source:"+ "http://www.williamlong.info"">

12.删除时确认
<a href="javascript:if(confirm("确实要删除吗?"))location="boos.asp?&areyou=删除&page=1"">删除</a>

13. 取得控件的绝对位置
//Javascript
<script language="Javascript">
function getIE(e){
var t=e.offsetTop;
var l=e.offsetLeft;
while(e=e.offsetParent)
alert("top="+t+"/nleft="+l);
}
</script>

//VBScript
<script language="VBScript"><!--
function getIE()
dim t,l,a,b
set a=document.all.img1
t=document.all.img1.offsetTop
l=document.all.img1.offsetLeft
while a.tagName<>"BODY"
set a = a.offsetParent
t=t+a.offsetTop
l=l+a.offsetLeft
wend
msgbox "top="&t&chr(13)&"left="&l,64,"得到控件的位置"
end function
--></script>

14. 光标是停在文本框文字的最后
<script language="javascript">
function cc()
{
var e = event.srcElement;
var r =e.createTextRange();
r.moveStart("character",e.value.length);
r.collapse(true);
r.select();
}
</script>
<input type=text name=text1 value="123" onfocus="cc()">

15. 判断上一页的来源
javascript:
document.referrer

16. 最小化、最大化、关闭窗口
<object id=hh1 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">
<param name="Command" value="Minimize"></object>
<object id=hh2 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">
<param name="Command" value="Maximize"></object>
<OBJECT id=hh3 classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
<PARAM NAME="Command" VALUE="Close"></OBJECT>
<input type=button value=最小化 onclick=hh1.Click()>
<input type=button value=最大化 onclick=hh2.Click()>
<input type=button value=关闭 onclick=hh3.Click()>
本例适用于IE

17.屏蔽功能键Shift,Alt,Ctrl
<script>
function look(){
if(event.shiftKey)
alert("禁止按Shift键!"); //可以换成ALT CTRL
}
document.onkeydown=look;
</script>

18. 网页不会被缓存
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate">
<META HTTP-EQUIV="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GMT">
或者<META HTTP-EQUIV="expires" CONTENT="0">

19.怎样让表单没有凹凸感?
<input type=text style="border:1 solid #000000">

<input type=text style="border-left:none; border-right:none; border-top:none; border-bottom:

1 solid #000000"></textarea>

20.<div><span>&<layer>的区别?
<div>(division)用来定义大段的页面元素,会产生转行
<span>用来定义同一行内的元素,跟<div>的唯一区别是不产生转行
<layer>是ns的标记,ie不支持,相当于<div>

21.让弹出窗口总是在最上面:
<body onblur="this.focus();">

22.不要滚动条?
让竖条没有:
<body style="overflow:scroll;overflow-y:hidden">
</body>
让横条没有:
<body style="overflow:scroll;overflow-x:hidden">
</body>
两个都去掉?更简单了
<body scroll="no">
</body>

23.怎样去掉图片链接点击后,图片周围的虚线?
<a href="#" onFocus="this.blur()"><img src="/blog/logo.jpg" border=0></a>

24.电子邮件处理提交表单
<form name="form1" method="post" action="mailto:****@***.com" enctype="text/plain">
<input type=submit>
</form>

25.在打开的子窗口刷新父窗口的代码里如何写?
window.opener.location.reload()

26.如何设定打开页面的大小
<body onload="top.resizeTo(300,200);">
打开页面的位置<body onload="top.moveBy(300,200);">

27.在页面中如何加入不是满铺的背景图片,拉动页面时背景图不动
<STYLE>
body
{background-image:url(/blog/logo.gif); background-repeat:no-repeat;
background-position:center;background-attachment: fixed}
</STYLE>

28. 检查一段字符串是否全由数字组成
<script language="Javascript"><!--
function checkNum(str){return str.match(//D/)==null}
alert(checkNum("1232142141"))
alert(checkNum("123214214a1"))
// --></script>

29. 获得一个窗口的大小
document.body.clientWidth; document.body.clientHeight

30. 怎么判断是否是字符
if (/[^/x00-/xff]/g.test(s)) alert("含有汉字");
else alert("全是字符");

31.TEXTAREA自适应文字行数的多少
<textarea rows=1 name=s1 cols=27 onpropertychange="this.style.posHeight=this.scrollHeight">
</textarea>

32. 日期减去天数等于第二个日期
<script language=Javascript>
function cc(dd,dadd)
{
//可以加上错误处理
var a = new Date(dd)
a = a.valueOf()
a = a - dadd * 24 * 60 * 60 * 1000
a = new Date(a)
alert(a.getFullYear() + "年" + (a.getMonth() + 1) + "月" + a.getDate() + "日")
}
cc("12/23/2002",2)
</script>

33. 选择了哪一个Radio
<HTML><script language="vbscript">
function checkme()
for each ob in radio1
if ob.checked then window.alert ob.value
next
end function
</script><BODY>
<INPUT name="radio1" type="radio" value="style" checked>Style
<INPUT name="radio1" type="radio" value="barcode">Barcode
<INPUT type="button" value="check" onclick="checkme()">
</BODY></HTML>

34.脚本永不出错
<SCRIPT LANGUAGE="JavaScript">
<!-- Hide
function killErrors() {
return true;
}
window.onerror = killErrors;
// -->
</SCRIPT>

35.ENTER键可以让光标移到下一个输入框
<input onkeydown="if(event.keyCode==13)event.keyCode=9">

36. 检测某个网站的链接速度:
把如下代码加入<body>区域中:
<script language=Javascript>
tim=1
setInterval("tim++",100)
b=1
var autourl=new Array()
autourl[1]="www.njcatv.net"
autourl[2]="javacool.3322.net"
autourl[3]="www.sina.com.cn"
autourl[4]="www.nuaa.edu.cn"
autourl[5]="www.cctv.com"
function butt(){
document.write("<form name=autof>")
for(var i=1;i<autourl.length;i++)
document.write("<input type=text name=txt"+i+" size=10 value="/blog/测试中......>" =》<input type=text
name=url"+i+" size=40> =》<input type=button value=GO

onclick=window.open(this.form.url"+i+".value)><br/>")
document.write("<input type=submit value=刷新></form>")
}
butt()
function auto(url)
else

b++
}
function run(){for(var i=1;i<autourl.length;i++)document.write("<img src=http://"+autourl+"/"+Math.random()+" width=1 height=1

onerror=auto("http://"+autourl+"")>")}
run()</script>

37. 各种样式的光标
auto :标准光标
default :标准箭头
hand :手形光标
wait :等待光标
text :I形光标
vertical-text :水平I形光标
no-drop :不可拖动光标
not-allowed :无效光标
help :?帮助光标
all-scroll :三角方向标
move :移动标
crosshair :十字标
e-resize
n-resize
nw-resize
w-resize
s-resize
se-resize
sw-resize

38.页面进入和退出的特效
进入页面<meta http-equiv="Page-Enter" content="revealTrans(duration=x, transition=y)">
推出页面<meta http-equiv="Page-Exit" content="revealTrans(duration=x, transition=y)">
这个是页面被载入和调出时的一些特效。duration表示特效的持续时间,以秒为单位。transition表示使用哪种特效,取值为1-23:
  0 矩形缩小
  1 矩形扩大
  2 圆形缩小
  3 圆形扩大
  4 下到上刷新
  5 上到下刷新
  6 左到右刷新
  7 右到左刷新
  8 竖百叶窗
  9 横百叶窗
  10 错位横百叶窗
  11 错位竖百叶窗
  12 点扩散
  13 左右到中间刷新
  14 中间到左右刷新
  15 中间到上下
  16 上下到中间
  17 右下到左上
  18 右上到左下
  19 左上到右下
  20 左下到右上
  21 横条
  22 竖条
  23 以上22种随机选择一种

39.在规定时间内跳转
<META http-equiv=V="REFRESH" content="5;URL=http://www.williamlong.info">

40.网页是否被检索
<meta name="ROBOTS" content="属性值">
  其中属性值有以下一些:
  属性值为"all": 文件将被检索,且页上链接可被查询;
  属性值为"none": 文件不被检索,而且不查询页上的链接;
  属性值为"index": 文件将被检索;
  属性值为"follow": 查询页上的链接;
  属性值为"noindex": 文件不检索,但可被查询链接;
  属性值为"nofollow": 文件不被检索,但可查询页上的链接。

最大化窗口?
<script language="JavaScript">
<!--
self.moveTo(0,0)
self.resizeTo(screen.availWidth,screen.availHeight)
//-->
</script>




解决问题:由于层与下拉框之间的优先级是:下拉框 > 层,因此在显示的时候,会因为优先级的次序而会出现如上问题。(如果几个元素都是层的话,我们可以通过层的 z-index 属性来设置)解决办法就是:给层中放一个优先级比下拉框更高的元素(iframe),从而解决此问题!具体解决代码如下:

<div id="menu" style="position:absolute; visibility:hidden; top:20px; left:20px; width:100px; height:200px; background-color:#6699cc;">
<table>
<tr><td>item 1</td></tr>
<tr><td>item 2</td></tr>
<tr><td>item 3</td></tr>
<tr><td>item 4</td></tr>
<tr><td>item 5</td></tr>
</table>
<iframe src="/blog/javascript:false" style="position:absolute; visibility:inherit; top:0px; left:0px; width:100px; height:200px; z-index:-1; filter='progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';"></iframe>
</div>

<a href="#" onclick="document.getElementById('menu').style.visibility='visible'">menu</a>

<form>
<select><option>A form selection list</option></select>
</form>

输入框也可以做的很漂亮了
<div align="center"><input type="hidden" name="hao" value="yes">
外向数:<input
name=answer
style="color: rgb(255,0,0); border-left: medium none; border-right: medium none; border-top: medium none; border-bottom: 1px solid rgb(192,192,192)">
 没回答的题数:<input
name=unanswer id="unanswer"
style="color: rgb(255,0,0); border-left: medium none; border-right: medium none; border-top: medium none; border-bottom: 1px solid rgb(192,192,192)">
<br/>
总得分:
<input
name=score id="score"
style="color: rgb(255,0,0); border-left: medium none; border-right: medium none; border-top: medium none; border-bottom: 1px solid rgb(192,192,192)">
 结    论:
<input
name=xgjg id="xgjg"
style="color: rgb(255,0,0); border-left: medium none; border-right: medium none; border-top: medium none; border-bottom: 1px solid rgb(192,192,192)">
<br/>
<br/>

<input onClick=processForm(this.form) style="FONT-FAMILY: 宋体; FONT-SIZE: 9pt" type=button value=查看结果 name="button">
<input type="reset" name="Submit" value="重做">
</div>
注意:修改<body>为<body onload="max.Click()">即为打开最大

化窗口,而如果改为<body onload="min.Click()">就变为窗口一打开就最小化

<object id="min" type="application/x-oleobject" classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
<param name="Command" value="Minimize">
</object> <object id="max" type="application/x-oleobject" classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
<param name="Command" value="Maximize">
</object>
</body>

页面自动刷新(说明)

当你做网页时,是不是有的时候想让你的网页自动不停刷新,或者过一段时间自动跳转到另外一个你自己设定的页面?其实实现这个效果非常地简单,而且这个效果甚至不能称之为特效。你只要把如下代码加入你的网页中就可以了。

1,页面自动刷新:把如下代码加入<head>区域中<meta http-equiv="refresh" content="20">,其中20指每隔20秒刷新一次页面.

2,页面自动跳转:把如下代码加入<head>区域中<meta http-equiv="refresh" content="20;url=http://www.williamlong.info">,其中20指隔20秒后跳转到http://www.williamlong.info页面。

页面自动关闭

5000是指时间<body onLoad="setTimeout(window.close, 5000)">

弹出窗口自动关闭

10秒后弹出窗口自动关闭

注意:在新的tan.htm的body中要加 <onLoad="closeit()">
head

<script language="JavaScript">

<!--

var gt = unescape('%3e');

var popup = null;

var over = "Launch Pop-up Navigator";

popup = window.open('', 'popupnav', 'width=225,height=235,resizable=1,scrollbars=auto');

if (popup != null) {

if (popup.opener == null) {

popup.opener = self;

}

popup.location.href = 'tan.htm';

}

// -->

</script>
<body>注意:这段代码是在新建文件中的
<script language="JavaScript">

function closeit()

</script>

这个可不是<iframe>(引用)呀。是直接调用的。以下代码加入<body>区域

<object type="text/x-scriptlet" width="800" height="1000" data="../index.htm">
</object>

40种网站设计常用技巧

1. oncontextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键
<table border oncontextmenu=return(false)><td>no</table> 可用于Table

2. <body onselectstart="return false"> 取消选取、防止复制

3. onpaste="return false" 不准粘贴

4. oncopy="return false;" oncut="return false;" 防止复制

5. <link rel="Shortcut Icon" href="favicon.ico"> IE地址栏前换成自己的图标

6. <link rel="Bookmark" href="favicon.ico"> 可以在收藏夹中显示出你的图标

7. <input style="ime-mode:disabled"> 关闭输入法

8. 永远都会带着框架
<script language="JavaScript"><!--
if (window == top)top.location.href = "frames.htm"; //frames.htm为框架网页
// --></script>

9. 防止被人frame
<SCRIPT LANGUAGE=JAVASCRIPT><!--
if (top.location != self.location)top.location=self.location;
// --></SCRIPT>

10. 网页将不能被另存为
<noscript><iframe src="/blog/*.html>";</iframe></noscript>

11. <input type=button value=查看网页源代码
onclick="window.location = "view-source:"+ "http://www.williamlong.info"">

12.删除时确认
<a href="javascript:if(confirm("确实要删除吗?"))location="boos.asp?&areyou=删除&page=1"">删除</a>

13. 取得控件的绝对位置
//Javascript
<script language="Javascript">
function getIE(e){
var t=e.offsetTop;
var l=e.offsetLeft;
while(e=e.offsetParent)
alert("top="+t+"/nleft="+l);
}
</script>

//VBScript
<script language="VBScript"><!--
function getIE()
dim t,l,a,b
set a=document.all.img1
t=document.all.img1.offsetTop
l=document.all.img1.offsetLeft
while a.tagName<>"BODY"
set a = a.offsetParent
t=t+a.offsetTop
l=l+a.offsetLeft
wend
msgbox "top="&t&chr(13)&"left="&l,64,"得到控件的位置"
end function
--></script>

14. 光标是停在文本框文字的最后
<script language="javascript">
function cc()
{
var e = event.srcElement;
var r =e.createTextRange();
r.moveStart("character",e.value.length);
r.collapse(true);
r.select();
}
</script>
<input type=text name=text1 value="123" onfocus="cc()">

15. 判断上一页的来源
javascript:
document.referrer

16. 最小化、最大化、关闭窗口
<object id=hh1 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">
<param name="Command" value="Minimize"></object>
<object id=hh2 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">
<param name="Command" value="Maximize"></object>
<OBJECT id=hh3 classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
<PARAM NAME="Command" VALUE="Close"></OBJECT>
<input type=button value=最小化 onclick=hh1.Click()>
<input type=button value=最大化 onclick=hh2.Click()>
<input type=button value=关闭 onclick=hh3.Click()>
本例适用于IE

17.屏蔽功能键Shift,Alt,Ctrl
<script>
function look(){
if(event.shiftKey)
alert("禁止按Shift键!"); //可以换成ALT CTRL
}
document.onkeydown=look;
</script>

18. 网页不会被缓存
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate">
<META HTTP-EQUIV="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GMT">
或者<META HTTP-EQUIV="expires" CONTENT="0">

19.怎样让表单没有凹凸感?
<input type=text style="border:1 solid #000000">

<input type=text style="border-left:none; border-right:none; border-top:none; border-bottom:

1 solid #000000"></textarea>

20.<div><span>&<layer>的区别?
<div>(division)用来定义大段的页面元素,会产生转行
<span>用来定义同一行内的元素,跟<div>的唯一区别是不产生转行
<layer>是ns的标记,ie不支持,相当于<div>

21.让弹出窗口总是在最上面:
<body onblur="this.focus();">

22.不要滚动条?
让竖条没有:
<body style="overflow:scroll;overflow-y:hidden">
</body>
让横条没有:
<body style="overflow:scroll;overflow-x:hidden">
</body>
两个都去掉?更简单了
<body scroll="no">
</body>

23.怎样去掉图片链接点击后,图片周围的虚线?
<a href="#" onFocus="this.blur()"><img src="/blog/logo.jpg" border=0></a>

24.电子邮件处理提交表单
<form name="form1" method="post" action="mailto:****@***.com" enctype="text/plain">
<input type=submit>
</form>

25.在打开的子窗口刷新父窗口的代码里如何写?
window.opener.location.reload()

26.如何设定打开页面的大小
<body onload="top.resizeTo(300,200);">
打开页面的位置<body onload="top.moveBy(300,200);">

27.在页面中如何加入不是满铺的背景图片,拉动页面时背景图不动
<STYLE>
body
{background-image:url(/blog/logo.gif); background-repeat:no-repeat;
background-position:center;background-attachment: fixed}
</STYLE>

28. 检查一段字符串是否全由数字组成
<script language="Javascript"><!--
function checkNum(str){return str.match(//D/)==null}
alert(checkNum("1232142141"))
alert(checkNum("123214214a1"))
// --></script>

29. 获得一个窗口的大小
document.body.clientWidth; document.body.clientHeight

30. 怎么判断是否是字符
if (/[^/x00-/xff]/g.test(s)) alert("含有汉字");
else alert("全是字符");

31.TEXTAREA自适应文字行数的多少
<textarea rows=1 name=s1 cols=27 onpropertychange="this.style.posHeight=this.scrollHeight">
</textarea>

32. 日期减去天数等于第二个日期
<script language=Javascript>
function cc(dd,dadd)
{
//可以加上错误处理
var a = new Date(dd)
a = a.valueOf()
a = a - dadd * 24 * 60 * 60 * 1000
a = new Date(a)
alert(a.getFullYear() + "年" + (a.getMonth() + 1) + "月" + a.getDate() + "日")
}
cc("12/23/2002",2)
</script>

33. 选择了哪一个Radio
<HTML><script language="vbscript">
function checkme()
for each ob in radio1
if ob.checked then window.alert ob.value
next
end function
</script><BODY>
<INPUT name="radio1" type="radio" value="style" checked>Style
<INPUT name="radio1" type="radio" value="barcode">Barcode
<INPUT type="button" value="check" onclick="checkme()">
</BODY></HTML>

34.脚本永不出错
<SCRIPT LANGUAGE="JavaScript">
<!-- Hide
function killErrors() {
return true;
}
window.onerror = killErrors;
// -->
</SCRIPT>

35.ENTER键可以让光标移到下一个输入框
<input onkeydown="if(event.keyCode==13)event.keyCode=9">

36. 检测某个网站的链接速度:
把如下代码加入<body>区域中:
<script language=Javascript>
tim=1
setInterval("tim++",100)
b=1
var autourl=new Array()
autourl[1]="www.njcatv.net"
autourl[2]="javacool.3322.net"
autourl[3]="www.sina.com.cn"
autourl[4]="www.nuaa.edu.cn"
autourl[5]="www.cctv.com"
function butt(){
document.write("<form name=autof>")
for(var i=1;i<autourl.length;i++)
document.write("<input type=text name=txt"+i+" size=10 value="/blog/测试中......>" =》<input type=text
name=url"+i+" size=40> =》<input type=button value=GO

onclick=window.open(this.form.url"+i+".value)><br/>")
document.write("<input type=submit value=刷新></form>")
}
butt()
function auto(url)
else

b++
}
function run(){for(var i=1;i<autourl.length;i++)document.write("<img src=http://"+autourl+"/"+Math.random()+" width=1 height=1

onerror=auto("http://"+autourl+"")>")}
run()</script>

37. 各种样式的光标
auto :标准光标
default :标准箭头
hand :手形光标
wait :等待光标
text :I形光标
vertical-text :水平I形光标
no-drop :不可拖动光标
not-allowed :无效光标
help :?帮助光标
all-scroll :三角方向标
move :移动标
crosshair :十字标
e-resize
n-resize
nw-resize
w-resize
s-resize
se-resize
sw-resize

38.页面进入和退出的特效
进入页面<meta http-equiv="Page-Enter" content="revealTrans(duration=x, transition=y)">
推出页面<meta http-equiv="Page-Exit" content="revealTrans(duration=x, transition=y)">
这个是页面被载入和调出时的一些特效。duration表示特效的持续时间,以秒为单位。transition表示使用哪种特效,取值为1-23:
  0 矩形缩小
  1 矩形扩大
  2 圆形缩小
  3 圆形扩大
  4 下到上刷新
  5 上到下刷新
  6 左到右刷新
  7 右到左刷新
  8 竖百叶窗
  9 横百叶窗
  10 错位横百叶窗
  11 错位竖百叶窗
  12 点扩散
  13 左右到中间刷新
  14 中间到左右刷新
  15 中间到上下
  16 上下到中间
  17 右下到左上
  18 右上到左下
  19 左上到右下
  20 左下到右上
  21 横条
  22 竖条
  23 以上22种随机选择一种

39.在规定时间内跳转
<META http-equiv=V="REFRESH" content="5;URL=http://www.williamlong.info">

40.网页是否被检索
<meta name="ROBOTS" content="属性值">
  其中属性值有以下一些:
  属性值为"all": 文件将被检索,且页上链接可被查询;
  属性值为"none": 文件不被检索,而且不查询页上的链接;
  属性值为"index": 文件将被检索;
  属性值为"follow": 查询页上的链接;
  属性值为"noindex": 文件不检索,但可被查询链接;
  属性值为"nofollow": 文件不被检索,但可查询页上的链接。

最大化窗口?
<script language="JavaScript">
<!--
self.moveTo(0,0)
self.resizeTo(screen.availWidth,screen.availHeight)
//-->
</script>




解决问题:由于层与下拉框之间的优先级是:下拉框 > 层,因此在显示的时候,会因为优先级的次序而会出现如上问题。(如果几个元素都是层的话,我们可以通过层的 z-index 属性来设置)解决办法就是:给层中放一个优先级比下拉框更高的元素(iframe),从而解决此问题!具体解决代码如下:

<div id="menu" style="position:absolute; visibility:hidden; top:20px; left:20px; width:100px; height:200px; background-color:#6699cc;">
<table>
<tr><td>item 1</td></tr>
<tr><td>item 2</td></tr>
<tr><td>item 3</td></tr>
<tr><td>item 4</td></tr>
<tr><td>item 5</td></tr>
</table>
<iframe src="/blog/javascript:false" style="position:absolute; visibility:inherit; top:0px; left:0px; width:100px; height:200px; z-index:-1; filter='progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)';"></iframe>
</div>

<a href="#" onclick="document.getElementById('menu').style.visibility='visible'">menu</a>

<form>
<select><option>A form selection list</option></select>
</form>

输入框也可以做的很漂亮了
<div align="center"><input type="hidden" name="hao" value="yes">
外向数:<input
name=answer
style="color: rgb(255,0,0); border-left: medium none; border-right: medium none; border-top: medium none; border-bottom: 1px solid rgb(192,192,192)">
 没回答的题数:<input
name=unanswer id="unanswer"
style="color: rgb(255,0,0); border-left: medium none; border-right: medium none; border-top: medium none; border-bottom: 1px solid rgb(192,192,192)">
<br/>
总得分:
<input
name=score id="score"
style="color: rgb(255,0,0); border-left: medium none; border-right: medium none; border-top: medium none; border-bottom: 1px solid rgb(192,192,192)">
 结    论:
<input
name=xgjg id="xgjg"
style="color: rgb(255,0,0); border-left: medium none; border-right: medium none; border-top: medium none; border-bottom: 1px solid rgb(192,192,192)">
<br/>
<br/>

<input onClick=processForm(this.form) style="FONT-FAMILY: 宋体; FONT-SIZE: 9pt" type=button value=查看结果 name="button">
<input type="reset" name="Submit" value="重做">
</div>
注意:修改<body>为<body onload="max.Click()">即为打开最大

化窗口,而如果改为<body onload="min.Click()">就变为窗口一打开就最小化

<object id="min" type="application/x-oleobject" classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
<param name="Command" value="Minimize">
</object> <object id="max" type="application/x-oleobject" classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11">
<param name="Command" value="Maximize">
</object>
</body>

页面自动刷新(说明)

当你做网页时,是不是有的时候想让你的网页自动不停刷新,或者过一段时间自动跳转到另外一个你自己设定的页面?其实实现这个效果非常地简单,而且这个效果甚至不能称之为特效。你只要把如下代码加入你的网页中就可以了。

1,页面自动刷新:把如下代码加入<head>区域中<meta http-equiv="refresh" content="20">,其中20指每隔20秒刷新一次页面.

2,页面自动跳转:把如下代码加入<head>区域中<meta http-equiv="refresh" content="20;url=http://www.williamlong.info">,其中20指隔20秒后跳转到http://www.williamlong.info页面。

页面自动关闭

5000是指时间<body onLoad="setTimeout(window.close, 5000)">

弹出窗口自动关闭

10秒后弹出窗口自动关闭

注意:在新的tan.htm的body中要加 <onLoad="closeit()">
head

<script language="JavaScript">

<!--

var gt = unescape('%3e');

var popup = null;

var over = "Launch Pop-up Navigator";

popup = window.open('', 'popupnav', 'width=225,height=235,resizable=1,scrollbars=auto');

if (popup != null) {

if (popup.opener == null) {

popup.opener = self;

}

popup.location.href = 'tan.htm';

}

// -->

</script>
<body>注意:这段代码是在新建文件中的
<script language="JavaScript">

function closeit()

</script>

这个可不是<iframe>(引用)呀。是直接调用的。以下代码加入<body>区域

<object type="text/x-scriptlet" width="800" height="1000" data="../index.htm">
</object>

C++开发中数据结构和算法的分离

相信每一个在windows下编过程序的人都或多或少地用过位图,大多数人是从网上下载一些成熟完善的DIB类库来使用(例如CxImage、CDIB),少数人有一套自己封装好的DIB类库,方便以后的扩充和使用。(近几年GDI+异军突起,在某些处理方面,如:缩放、旋转、渐变填充等它提供无与伦比的速度和质量,但,如果你想做一个完善的图像处理程序,直接使用它会给架构设计带来困难,你可以用adapter模式封装它后再使用)。
  这时候,如果你需要一些图像处理操作你会怎么办呢?很多没有OO经验的C++程序员(例如一年前的我)可能会这样做:在类中直接添加方法。

//================================================================
int FClamp0255 (int nValue) {return max (0, min (0xFF, nValue));} // 饱和到0--255

class FCObjImage
{
 public :
  Invert () ;
  AdjustRGB (int R, int G, int B) ;
} ;
//================================================================
void FCObjImage::Invert ()
{
 if ((GetHandle() == NULL) || (ColorBits() < 24))
  return ;

 int nSpan = ColorBits() / 8 ; // 每象素字节数3, 4
 for (int y=0 ; y < Height() ; y++)
 {
  BYTE * pPixel = GetBits (y) ;
  for (int x=0 ; x < Width() ; x++, pPixel += nSpan)
  {
   pPixel[0] = ~pPixel[0] ;
   pPixel[1] = ~pPixel[1] ;
   pPixel[2] = ~pPixel[2] ;
  }
 }
}
//================================================================
void FCObjImage::AdjustRGB (int R, int G, int B)
{
 if ((GetHandle() == NULL) || (ColorBits() < 24))
  return ;

 int nSpan = ColorBits() / 8 ; // 每象素字节数3, 4
 for (int y=0 ; y < Height() ; y++)
 {
  BYTE * pPixel = GetBits (y) ;
  for (int x=0 ; x < Width() ; x++, pPixel += nSpan)
  {
   pPixel[0] = FClamp0255 (pPixel[0] + B) ;
   pPixel[1] = FClamp0255 (pPixel[1] + G) ;
   pPixel[2] = FClamp0255 (pPixel[2] + R) ;
  }
 }
}
//================================================================

  这里举了两个例子(分别实现反色,调节RGB值功能),现实中会有大量的此类操作:亮度、对比度、饱和度......现在回想一下,你添加这些方法的步骤是什么,Ooooooooo,RCP(我同事的发明,全称:rapid copy paste^-^),第一步一定是从上面复制一块代码下来,然后改掉其中的接口和处理部分。虽然这里的示范代码很短小,不会连同bug一起复制,但,定时炸弹却又多了一个。有天,你的boss告诉你:我不能忍受长时间的等待,请给我加个进度条.....。你也许会加个全局变量,也许会给每个函数加个参数,但不变的是:你必须修改所有这些处理函数的代码,内心的咒骂并不会使你少改其中的任何一个。而此时,bug已经在旁边伺机而动了...然而苦日子远没熬到头,一个月后,你心血来潮的老板会让你在其中加上区域处理的功能,再一个月后......

  回头重新看看代码?没错,除了红色的代码外,其他地方一摸一样,那能不能把这些算法分离抽出来呢?可能我们马上会想到标准库中qsort和windows中常用的回调方法。好,让我们实作一下:

//================================================================void Pixel_Invert (BYTE * pPixel)
{
 pPixel[0] = ~pPixel[0] ;
 pPixel[1] = ~pPixel[1] ;
 pPixel[2] = ~pPixel[2] ;
}
//================================================================
void FCObjImage::PixelProcess (void(__cdecl*PixelProc)(BYTE * pPixel))
{
 if ((GetHandle() == NULL) || (ColorBits() < 24))
  return ;

 int nSpan = ColorBits() / 8 ; // 每象素字节数3, 4
 for (int y=0 ; y < Height() ; y++)
 {
  BYTE * pPixel = GetBits (y) ;
  for (int x=0 ; x < Width() ; x++, pPixel += nSpan)
  {
   PixelProc (pPixel) ;
  }
 }
}
//================================================================
void FCObjImage::Invert ()
{
 PixelProcess (Pixel_Invert) ;
}
//================================================================

  嗯,看样子不错,算法被剥离到一个单一函数中,我们似乎已经解决问题了。处理Invert它完成的非常好,但处理AdjustRGB时遇到了麻烦,RGB那三个调节参数怎么传进去呢?我们的接口参数只有一个,通过添加全局变量/成员变量?这是一个办法,但随着类方法的增加,程序的可读性和维护性会急剧的下降,反而倒不如改之前的效果好。

那么如何实现高度的抽象和良好的接口呢?我们现场请来OO(object orient),请它来讲一下它的实现。设计如下派生关系:
关系图链接:http://www.uml.org.cn/images/upfile/200442451.gif

  //================================================================
class FCSinglePixelProcessBase
{
 public :
  virtual void ProcessPixel (int x, int y, BYTE * pPixel) PURE ;
} ;
//================================================================
class FCPixelInvert : public FCSinglePixelProcessBase
{
 public :
  virtual void ProcessPixel (int x, int y, BYTE * pPixel) ;
} ;
void FCPixelInvert::ProcessPixel (int x, int y, BYTE * pPixel)
{
 pPixel[0] = ~pPixel[0] ; pPixel[1] = ~pPixel[1] ; pPixel[2] = ~pPixel[2] ;
}
//================================================================
class FCPixelAdjustRGB : public FCSinglePixelProcessBase
{
 public :
  FCPixelAdjustRGB (int DeltaR, int DeltaG, int DeltaB) ;
  virtual void ProcessPixel (int x, int y, BYTE * pPixel) ;
 protected :
  int m_iDeltaR, m_iDeltaG, m_iDeltaB ;
} ;
void FCPixelAdjustRGB::ProcessPixel (int x, int y, BYTE * pPixel)
{
 pPixel[0] = FClamp0255 (pPixel[0] + m_iDeltaB) ;
 pPixel[1] = FClamp0255 (pPixel[1] + m_iDeltaG) ;
 pPixel[2] = FClamp0255 (pPixel[2] + m_iDeltaR) ;
}
//================================================================

  然后我们修改image类如下:

//================================================================
#include PixelProcessor.h
class FCObjImage
{
 public :
  void PixelHandler (FCSinglePixelProcessBase & PixelProcessor, FCObjProgress * progress = NULL) ;
} ;
//================================================================
void FCObjImage::PixelHandler (FCSinglePixelProcessBase & PixelProcessor, FCObjProgress * progress)
{
 if (GetHandle() == NULL)
  return ;

 int nSpan = ColorBits() / 8 ; // 每象素字节数3, 4
 for (int y=0 ; y < Height() ; y++)
 {
  BYTE * pPixel = GetBits (y) ;
  for (int x=0 ; x < Width() ; x++, pPixel += nSpan)
  {
   PixelProcessor.ProcessPixel (x, y, pPixel) ;
  }
  if (progress != NULL)
   progress->SetProgress (y * 100 / Height()) ;
 }
}
//================================================================
void FCObjImage::Invert (FCObjProgress * progress)
{
 PixelHandler (FCPixelInvert(), progress) ;
}
void FCObjImage::AdjustRGB (int R, int G, int B, FCObjProgress * progress)
{
 PixelHandler (FCPixelAdjustRGB (R,G,B), progress) ;
}
//================================================================

  (以上只是一个基本框架,你可以很轻易的把区域处理的参数添加进去-通过构造时传递一个RECT参数。)

  对象真的是一个很奇妙的东西,它可以对外提供一个简单的接口,而自身又可以封装上很多附加信息。

  好,现在让我们来检验一下刚才的成果:添加一个给图像奇数行置黑,给偶数行置白的操作。

//================================================================class FCPixelTest : public FCSinglePixelProcessBase
{
 public :
  virtual void ProcessPixel (int x, int y, BYTE * pPixel) ;
} ;
void FCPixelTest::ProcessPixel (int x, int y, BYTE * pPixel)
{
 if (y % 2) pPixel[0]=pPixel[1]=pPixel[2] = 0 ;
 // 奇数行
 else
  pPixel[0]=pPixel[1]=pPixel[2] = 0xFF ;
// 偶数行
}


  然后进行如下调用:

PixelHandler (FCPixelTest(), progress) ;
//================================================================

  多么的和谐美妙,设计算法的人员只需写出自己的算法,而不用去考虑怎么让它支持进度条和区域这些问题。感觉这就象一把设计优良的AK,你可以不断的往里添加子弹(对象)^-^

  至此,我们应该已经大功告成了。还有问题吗?

  等等,别忙,有些地方不太对,我添加这个算法后,怎么编译这么久啊。

  问题就出在那个不起眼的:

  #include PixelProcessor.h

  image是图像处理的最底层对象,工程中的所有文件都直接或间接地包含它,因此,任何对image.h本身及它所包含的.h的修改都会引起几乎整个工程的build,这当然是无法忍受的,解决的办法是使用“前置声明”,因为在PixelHandler接口中我们只需要它的引用(也即是说:我(接口)并不需要知道传给我的类的内部结构,给我一个32(64)的内存地址就OK了)。

  因此我们把

#include PixelProcessor.h

  替换成:

class FCSinglePixelProcessBase ; // external class 前置声明

  然后在.cpp文件中再包含PixelProcessor.h,这样,对PixelProcessor.h的改变仅仅会导致.cpp文件的重新编译,大大节约了编译时间。

  总结:

  1)可能的话,在编程中永远也别去想“拷贝代码”这个字眼。毕竟,OO就是为了抽象和代码重用才诞生的。

  2)除非必要,否则类的成员变量和函数的参数尽量用指针或引用代替,这样做可以在.h中尽可能地少包含其他.h文件,而用前置声明来替代,以此来减少编译时间和以后可能会产生的交叉包含。

  3)最后说一下效率问题:有些朋友可能会说每个像素都调用虚函数会影响性能,这的确,但实际的损失远没有想象的大。我实测了一下:对1024*768的图片进行反片处理,速度只有5%左右的损失,进行复杂处理(亮度/对比度/gamma)时损失可完全忽略,毕竟多出来的那部分代码只是进出栈和查表,而不是浮点除这样耗时的指令。

星期六, 三月 17, 2007

尽量使用C++风格的类型转换

仔细想想地位卑贱的类型转换功能(cast),其在程序设计中的地位就象goto语句一样令人鄙视。但是它还不是无法令人忍受,因为当在某些紧要的关头,类型转换还是必需的,这时它是一个必需品。
不过C风格的类型转换并不代表所有的类型转换功能。
一来它们过于粗鲁,能允许你在任何类型之间进行转换。不过如果要进行更精确的类型转换,这会是一个优点。在这些类型转换中存在着巨大的不同,例如把一个指向const对象的指针(pointer-to-const-object)转换成指向非const对象的指针(pointer-to-non-const-object)(即一个仅仅去除const的类型转换),把一个指向基类的指针转换成指向子类的指针(即完全改变对象类型)。传统的C风格的类型转换不对上述两种转换进行区分。(这一点也不令人惊讶,因为C风格的类型转换是为C语言设计的,而不是为C++语言设计的)。
二来C风格的类型转换在程序语句中难以识别。在语法上,类型转换由圆括号和标识符组成,而这些可以用在C++中的任何地方。这使得回答象这样一个最基本的有关类型转换的问题变得很困难:“在这个程序中是否使用了类型转换?”。这是因为人工阅读很可能忽略了类型转换的语句,而利用象grep的工具程序也不能从语句构成上区分出它们来。
C++通过引进四个新的类型转换操作符克服了C风格类型转换的缺点,这四个操作符是, static_cast, const_cast, dynamic_cast, 和reinterpret_cast。在大多数情况下,对于这些操作符你只需要知道原来你习惯于这样写,
(type) expression
而现在你总应该这样写:
static_cast(expression)
例如,假设你想把一个int转换成double,以便让包含int类型变量的表达式产生出浮点数值的结果。如果用C风格的类型转换,你能这样写:
int firstNumber, secondNumber;
...
double result = ((double)firstNumber)/secondNumber;
如果用上述新的类型转换方法,你应该这样写:
double result = static_cast(firstNumber)/secondNumber;
这样的类型转换不论是对人工还是对程序都很容易识别。
static_cast在功能上基本上与C风格的类型转换一样强大,含义也一样。它也有功能上限制。例如,你不能用static_cast象用C风格的类型转换一样把struct转换成int类型或者把double类型转换成指针类型,另外,static_cast不能从表达式中去除const属性,因为另一个新的类型转换操作符const_cast有这样的功能。
其它新的C++类型转换操作符被用在需要更多限制的地方。const_cast用于类型转换掉表达式的const或volatileness属性。通过使用const_cast,你向人们和编译器强调你通过类型转换想做的只是改变一些东西的constness或者 volatileness属性。这个含义被编译器所约束。如果你试图使用const_cast来完成修改constness 或者volatileness属性之外的事情,你的类型转换将被拒绝。下面是一些例子:
class Widget { ... };
class SpecialWidget: public Widget { ... };
void update(SpecialWidget *psw);
SpecialWidget sw; // sw 是一个非const 对象。
const SpecialWidget& csw = sw; // csw 是sw的一个引用
// 它是一个const 对象
update(&csw); // 错误!不能传递一个const SpecialWidget* 变量
// 给一个处理SpecialWidget*类型变量的函数
update(const_cast(&csw));
// 正确,csw的const被显示地转换掉(
// csw和sw两个变量值在update
//函数中能被更新)
update((SpecialWidget*)&csw);
// 同上,但用了一个更难识别
//的C风格的类型转换
Widget *pw = new SpecialWidget;
update(pw); // 错误!pw的类型是Widget*,但是
// update函数处理的是SpecialWidget*类型
update(const_cast(pw));
// 错误!const_cast仅能被用在影响
// constness or volatileness的地方上。,
// 不能用在向继承子类进行类型转换。
到目前为止,const_cast最普通的用途就是转换掉对象的const属性。
第二种特殊的类型转换符是dynamic_cast,它被用于安全地沿着类的继承关系向下进行类型转换。这就是说,你能用dynamic_cast把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用,而且你能知道转换是否成功。失败的转换将返回空指针(当对指针进行类型转换时)或者抛出异常(当对引用进行类型转换时):
Widget *pw;
...
update(dynamic_cast(pw));
// 正确,传递给update函数一个指针
// 是指向变量类型为SpecialWidget的pw的指针
// 如果pw确实指向一个对象,
// 否则传递过去的将使空指针。
void updateViaRef(SpecialWidget& rsw);
updateViaRef(dynamic_cast(*pw));
//正确。 传递给updateViaRef函数
// SpecialWidget pw 指针,如果pw
// 确实指向了某个对象
// 否则将抛出异常
dynamic_casts在帮助你浏览继承层次上是有限制的。它不能被用于缺乏虚函数的类型上(参见条款M24),也不能用它来转换掉constness:
int firstNumber, secondNumber;
...
double result = dynamic_cast(firstNumber)/secondNumber;
// 错误!没有继承关系
const SpecialWidget sw;
...
update(dynamic_cast(&sw));
// 错误! dynamic_cast不能转换
// 掉const。
如你想在没有继承关系的类型中进行转换,你可能想到static_cast。如果是为了去除const,你总得用const_cast。
这四个类型转换符中的最后一个是reinterpret_cast。使用这个操作符的类型转换,其的转换结果几乎都是执行期定义(implementation-defined)。因此,使用reinterpret_casts的代码很难移植。
reinterpret_casts的最普通的用途就是在函数指针类型之间进行转换。例如,假设你有一个函数指针数组:
typedef void (*FuncPtr)(); // FuncPtr is 一个指向函数
// 的指针,该函数没有参数
// 返回值类型为void
FuncPtr funcPtrArray[10]; // funcPtrArray 是一个能容纳
// 10个FuncPtrs指针的数组
让我们假设你希望(因为某些莫名其妙的原因)把一个指向下面函数的指针存入funcPtrArray数组:
int doSomething();
你不能不经过类型转换而直接去做,因为doSomething函数对于funcPtrArray数组来说有一个错误的类型。在FuncPtrArray数组里的函数返回值是void类型,而doSomething函数返回值是int类型。
funcPtrArray[0] = &doSomething; // 错误!类型不匹配
reinterpret_cast可以让你迫使编译器以你的方法去看待它们:
funcPtrArray[0] = // this compiles
reinterpret_cast(&doSomething);
转换函数指针的代码是不可移植的(C++不保证所有的函数指针都被用一样的方法表示),在一些情况下这样的转换会产生不正确的结果(参见条款M31),所以你应该避免转换函数指针类型,除非你处于着背水一战和尖刀架喉的危急时刻。一把锋利的刀。一把非常锋利的刀。
如果你使用的编译器缺乏对新的类型转换方式的支持,你可以用传统的类型转换方法代替static_cast, const_cast, 以及reinterpret_cast。也可以用下面的宏替换来模拟新的类型转换语法:
#define static_cast(TYPE,EXPR) ((TYPE)(EXPR))
#define const_cast(TYPE,EXPR) ((TYPE)(EXPR))
#define reinterpret_cast(TYPE,EXPR) ((TYPE)(EXPR))
你可以象这样使用使用:
double result = static_cast(double, firstNumber)/secondNumber;
update(const_cast(SpecialWidget*, &sw));
funcPtrArray[0] = reinterpret_cast(FuncPtr, &doSomething);
这些模拟不会象真实的操作符一样安全,但是当你的编译器可以支持新的的类型转换时,它们可以简化你把代码升级的过程。
没有一个容易的方法来模拟dynamic_cast的操作,但是很多函数库提供了函数,安全地在派生类与基类之间进行类型转换。如果你没有这些函数而你有必须进行这样的类型转换,你也可以回到C风格的类型转换方法上,但是这样的话你将不能获知类型转换是否失败。当然,你也可以定义一个宏来模拟dynamic_cast的功能,就象模拟其它的类型转换一样:
#define dynamic_cast(TYPE,EXPR) (TYPE)(EXPR)
请记住,这个模拟并不能完全实现dynamic_cast的功能,它没有办法知道转换是否失败。
我知道,是的,我知道,新的类型转换操作符不是很美观而且用键盘键入也很麻烦。如果你发现它们看上去实在令人讨厌,C风格的类型转换还可以继续使用并且合法。然而,正是因为新的类型转换符缺乏美感才能使它弥补了在含义精确性和可辨认性上的缺点。并且,使用新类型转换符的程序更容易被解析(不论是对人工还是对于工具程序),它们允许编译器检测出原来不能发现的错误。这些都是放弃C风格类型转换方法的强有力的理由。还有第三个理由:也许让类型转换符不美观和键入麻烦是一件好事。

星期四, 三月 15, 2007

在C++Builder里创建可以被Visual C++使用的DLL

在前两篇文章里,我们讨论了如何在C++Builder工程里调用用MS Visual C++创建的DLL。这篇文章讨论相反的一种情形,举例说明如何用C++Builder创建一个DLL,使它可以在Visual C++工程里调用。

简介: 为什么这个这么难
指导方针摘要
例1: 隐式连接
例2: 显式连接
例3: 用#define组装隐式连接
例4: 用stdcall函数隐式连接
结论
简介:为什么这个这么难
  如果你用BCB创建了一个DLL,它可以被BCB的可执行文件调用,你知道这种使用DLL的方式没什么难度。当你构造一个DLL,BCB生成一个带“.LIB”扩展名的引入库。把这个LIB文件添加到你的工程里。连接器按引入库决定DLL内部调用。当你运行你的程序时,DLL隐式的被载入,你不必去考虑DLL内部调用工作是。

  当EXE文件是由Microsoft Visual C++编译的时候,情况会变得比较复杂。有3个主要的问题。首先,BCB和MSVC对DLL中的函数命名方式是不一致的。BCB使用一种习惯,MSVC使用另一种不同的习惯。当然,两种习惯是不兼容的。命名问题在如何在C++Builder工程里使用VC++编译的DLL那篇文章里已经讨论过了。表1总结了各个编译器在各自的调用习惯下,导出的MyFunction函数。注意Borland给__cdecl函数前加了一个下划线,而MSVC没有。另一方面,MSVC认为导出的__stdcall函数前面带有下划线,后面还有一些垃圾。

表1: Visual C++ and C++Builder 命名习惯

调用习惯      VC++命名 VC++(使用了DEF) C++Builder命名
-----------------------------------------------------------------------
__stdcall _MyFunction@4 MyFunction MyFunction
__cdecl MyFunction MyFunction _MyFunction
  第2个问题是Borland引入库与MSVC不是二进制兼容的。当你编译DLL时,由BCB创建的引入库不能被MSVC用来连接。如果你想使用隐式连接,那么你需要创建一个MSVC格式的引入库。另一种可选择的办法就是采用显式连接(LoadLibrary和GetProcAddress)。

第3个问题是不能从DLL里导出C++类和成员函数,如果你想让MSVC的用户也可以调用它。好吧,那不完全属实。你的DLL能导出C++类,但是MSVC不能使用它们。原因就是C++成员函数名被编译器改编(mangled)。这个改编的名字结果了DLL。为了调用在DLL里被改编的函数,你必需知道被改编的是哪个函数。Borland和Microsoft使用了不同的名字改编方案。结果是,MSVC不能恰好看到Borland编译的DLL里的C++类和成员函数。

注意:
--------------------------------------------------------------------------------

Borland和Microsoft没有采用相同的方式改编函数,因为依照ANSI C++标准,C++编译器不被假定追随相同的指导方针。名字改编只是实现的细节。

--------------------------------------------------------------------------------


  这三个问题使得Borland创建的DLL可以在MSVC里被调用变得非常困难,但并非不可能的。这篇文章描述了一套指导方针,你可以跟着制作与Microsoft兼容的BCB DLL。我们讨论四种不同的技术。三种采用引入库隐式连接调用,一种在运行时利用显式连接。

指导方针摘要
  你可以跟着下面的指导方针摘要列表建造你的DLL。第1个列表讨论隐式连接;第2个列表描述显式连接;第3种技术采用#define组装隐式连接;最后一个例子利用假的MSVC DLL工程为__stdcall函数创建引入库。

技术1: 隐式连接
------------------------------------------------------------------------------
1- 使用__cdecl调用习惯代替__stdcall。
2- 导出简单的"C"风格函数,没有C++类或成员函数。
3- 确定你有一个 extern "C" {} 包围你的函数原型。
4- 创建DEF文件,包含与Microsoft兼容的导出函数别名。别名也就是不包含前面的下划线。
DEF文件内容如下:

EXPORTS
; MSVC name = Borland name
Foo = _Foo
Bar = _Bar

5- 把DEF文件加入到你的工程里重新编译它。
6- 把DLL和DLL头文件拷贝到你的MSVC工程目录里。
7- 运行impdef为DLL创建第2个DEF文件。这个DEF文件用来创建引入库。
> impdef mydll.def mydll.dll
8- 运行Microsoft的LIB工具,用上一步创建的DEF文件创建COFF引入库。调用格式为:
> lib /DEF mydll.def
9- 把用LIB.EXE创建的LIB文件添加到你的MSVC工程里。
技术2: 显式连接
------------------------------------------------------------------------------
1- 使用__cdecl或__stdcall,如果你使用__stdcall可以跳过第4,5步。
2- 导出简单的"C"风格函数,没有C++类或成员函数。
3- 确定你有一个extern "C" {}包围你的函数原型。
4- 如果你使用__cdecl,那么你可能想去掉导出函数前面的下划线,但你不必这么做。你可以用例1的第4,5步去掉下划线。如果你没有去掉下载线,在调用GetProcAddress函数时函数名必须前面的下划线。
5- 把DLL拷贝到MSVC工程目录里。
6- 在MSVC应用程序中,使用LoadLibrary API函数载入DLL。
7- 调用GetProcAddress API在DLL里查找你想要的调用函数,保存GetProcAddress函数返回的函数指针。当你想调用函数的时候,提取函数指针。
8- 当你用完DLL时调用FreeLibrary。


技术3: 用#define组装隐式连接
------------------------------------------------------------------------------
1- 用__cdecl调用习惯代替__stdcall。
2- 导出简单的"C"风格函数,没有C++类或成员函数。
3- 确定你有一个extern "C" {}包围你的函数原型。
4- 在你的DLL头文件里,为每一个导出函数名创建一个#define。
#define会调用预编译器在每一个函数名前加上下划线。因为我们只想为MSVC创建别名,所以代码检查_MSC_VER。

#ifdef _MSC_VER
#define Foo _Foo
#define Bar _Bar
#endif

5- 把DLL和DLL头文件拷贝到MSVC工程目录里。
6- 运行impdef为DLL函数DEF文件。
> impdef mydll.def mydll.dll
7- 使用Microsoft的LIB工具为DEF文件创建COFF格式的引入库。
>lib /def mydll.def
8- 把LIB.EXE创建的LIB文件添加到MSVC工程里。


技术4: 用__stdcall函数隐式连接
------------------------------------------------------------------------------
1- 当建造你的DLL时使用__stdcall调用习惯。
2- 导出简单的"C"风格函数,没有C++类或成员函数。
3- 确定你有一个extern "C" {}包围你的函数原型。
4- 为MSVC创建一个引入库。这一部分比较困难。你不能用LIB.EXE为__stdcall函数创建引入库。你必须创建一个由MSVC编译的的假的DLL。这样做,按这些步骤:
4a- 用MSVC创建一个不使用MFC的DLL
4b- 从BCB里拷贝覆盖DLL头文件和DLL源代码
4c- 编辑你的DLL源代码,抛开每一个例程的函数体部分,使用一个假的返回值返回
4d- 配置MSVC工程生成的DLL,采用和BCB DLL同的的名字
4e- 把DEF文件添加到MSVC工程,禁止它对__stdcall命名进行修饰(_Foo@4)
5- 编译第4步得到的虚假DLL工程。这将会生成一个DLL(你可以把它丢到垃圾筒里)和一个LIB文件(这是你需要的)。
6- 把从第5步得到的LIB文件添加到你需要调用这个BCB DLL的MSVC工程里。LIB文件会确保连接。为MSVC可执行文件配置BCB DLL(不是虚假DLL)。

注意:
--------------------------------------------------------------------------------

  一般情况下,隐式连接比显式连接要优先考虑,因为对程序员来说隐式连接更简单,而且它是类型安全的(错误发生在连接时而不是运行时)。不管用哪种方法,当你在编译器间共享DLL时,如果你选择坚持使用隐式连接,就必须为每一个编译器创建兼容的引入库。创建兼容的引入库比用显式连增加的负担就是要注意更多的要求。

--------------------------------------------------------------------------------




注意:
--------------------------------------------------------------------------------

  如果你想使你的DLL可以被Visual Basic的开发者使用,显式连接的指导方针同样适用。如果你想把你的DLL给VC开发者,按显式连接的指导方针,采用__stdcall调用习惯。

--------------------------------------------------------------------------------




下面4个部分详细描述每一种技术。

例1: 显式连接
  这个例子详细描述了上一部分技术1的指导方针。技术1的指针方针可以分为两组。1-5项处理在BCB这边编译DLL;6-9项处理在MSVC这边使用DLL。我们将沿这条主线分别进行讨论。

  在这个例子里,我们将用BCB建造一个DLL,它导出两个函数: Foo和Bar。两个函数都返回一个整型值。函数原型为:

int Foo (int Value);
int Bar (void);
然后我们在MSVC里建造一个测试EXE,用来调用Borland DLL。

用BCB编译DLL
  下面两个程序清单包含我们的DLL源代码。清单1要在BCB和MSVC之间共享的头文件;清单2包含我们的DLL函数实现部分。创建一个BCB DLL工程,从清单1和2中拷贝代码粘贴到工程里。或者你可以下载这篇文章的源代码以节省时间。BCB DLL工程已经为你设置好了。(参见最下面的下载部分)

// ----------------------------------------------
// Listing 1- DLL header file
#ifndef BCBDLL_H
#define BCBDLL_H

#ifdef __cplusplus
extern "C" {
#endif

#ifdef BUILD_DLL
#define IMPORT_EXPORT __declspec(dllexport)
#else
#define IMPORT_EXPORT __declspec(dllimport)
#endif

IMPORT_EXPORT int __cdecl Foo (int Value);
IMPORT_EXPORT int __cdecl Bar (void);

#ifdef __cplusplus
}
#endif

#endif
// ----------------------------------------------
// ----------------------------------------------
// Listing 2- DLL source code
#include
#pragma hdrstop

#define BUILD_DLL
#include "bcbdll.h"

int __cdecl Foo (int Value)
{
return Value + 1;
}

int __cdecl Bar (void)
{
static int ret = 0;
return ret++;
}
// ----------------------------------------------
  关于头文件有两个要注意的地方。首先,观察我们用 extern "C" 的方法确保函数名不会被C++编译器改编;其次,注意到在我们建造DLL时,导出函数有一个特殊指示的前缀__declspec(dllexport)。当我们从MSVC里使用DLL时,函数前缀变为__declspec(dllimport)。这个指示的改变是通过IMPORT_EXPORT宏定义实现的。

最后,注意我们显式声明了__cdecl为调用习惯。技术上,我们可以省略__cdecl关键字,因为__cdecl已经是默认的。但是,我想不管怎样把它列出来是一个好习惯。通过列出调用习惯,你显式的告诉人们你选择了__cdecl作为一个前提。同样,默认的调用习惯在两个编译器里可以通过编译开关改变。你肯定不想这些编译器开关影响到你DLL的可用性。

头文件本身满足了指导方针中的1-3项 。我们需要做的下一件事情是处理第4项: 给导出函数建立别名。

首先,按现在的情况建造DLL代码。其次,运行TDUMP工具检查函数的函数名确实包含前面的下划线。

c:> tdump -m -ee bcbdll.dll
Turbo Dump Version 5.0.16.12 Copyright (c) 1988, 2000 Inprise Corporation
Display of File BCBDLL.DLL

EXPORT ord:0001='_Bar'
EXPORT ord:0002='_Foo'
EXPORT ord:0003='___CPPdebugHook'


注意:
--------------------------------------------------------------------------------

使用TDUMP时别忘了用 -m 开关。TDUMP尝试反改编(unmangle)被修饰的名字,使他们更容易阅读。但是,当你查看一个DLL的时候,明智的选择是查看函数的原始格式。-m 开关告诉TDUMP显示原始函数名。

--------------------------------------------------------------------------------


像你看到的那样,Foo和Bar都包含前端下划线。至于__CPPdebugHook,你可以不理它,它是幕后操纵的,当它不存在好了。它对你没什么意义,你也不能让它走开,因此就不要把它放在心上了。

为了用别名去掉下划线,我们需要做三件事:首先创建DLL的DEF文件;然后调整DEF文件,为Borland名字创建MSVC的别名;最后,把DEF文件添加到你的BCB工程里,重建DLL。

要创建DEF文件,对DLL运行Borland的IMPDEF工具。

C:> impdef bcbdllx.def bcbdll.dll
我选择bcbdllx.def为文件名,因为稍后(在我们创建MSVC引入库之前)我们将使用其它DEF文件。我想避免两者混淆。bcbdllx.def内容如下:

LIBRARY BCBDLL.DLL

EXPORTS
_Bar @1 ; _Bar
_Foo @2 ; _Foo
___CPPdebugHook @3 ; ___CPPdebugHook
注意到在Foo和Boo前端的下划线。如果DLL把Foo和Bar导出为_Foo和_Bar,当MSVC用户设法建造他们的工程的时候,将看到连接错误。我们需要剥去下划线。我们用在DEF文件里给函数别名的方法实现。

DEF文件别名允许我们为真实的函数导出担当代理或占位符的函数名。在DLL里的真实的函数仍然是_Foo和_Bar。代理名将是Foo和Bar(注意没有了下划线)。当我们给两个函数别名的时候,DLL将导出两个将的符号,它们归诸于原来的函数。

完成别名, 编辑DEF文件,改变成下面的样子:

LIBRARY BCBDLL.DLL

EXPORTS
Bar = _Bar
Foo = _Foo
这个DEF文件创建两个新的出口,Foo和Bar,它们分别担当_Foo和_Bar的点位符。把这个DEF文件保存到你的硬盘上。一旦你完成了这些工作,便可以把DEF文件添加到你的BCB工程里,使用Project-Add菜单项。添加后,BCB会在工程管理器(Project Manager)的树状结构里显示出DEF文件。

一旦你把DEF文件加入到工程里,做一次完全的重建。工程连接好之后,再次对DLL运行TDUMP,检查从DLL里导出的带下划线函数。

>tdump -m -ee bcbdll.dll
Turbo Dump Version 5.0.16.12 Copyright (c) 1988, 2000 Inprise Corporation
Display of File BCBDLL.DLL

EXPORT ord:0004='Bar'
EXPORT ord:0005='Foo'
EXPORT ord:0002='_Bar'
EXPORT ord:0001='_Foo'
EXPORT ord:0003='___CPPdebugHook'
对TDUMP的输出有两点要注意的事情要注意。首先,观察Foo和Bar到场了(没有前端下划线)。现在DLL导出函数名与MSVC的一致了。还注意到原来的函数,_Foo和_Bar,还在那儿。被修饰过的函数仍就从DLL里导出。使用DEF文件别名并不隐藏原来的函数。

你可能会想把这原来的两个函数用什么办法隐藏起来。但是,这么做将会危害到从BCB工程里使用DLL的人们。记得BCB的连接器期望在那儿有一个前端下划线。如果你真的用了什么方法从DLL里把_Foo和_Bar隐藏了(以我的知识是不可能实现的),那么你的DLL从BCB里调用将变得非常困难。

如果TDUMP的输出没有列出代理函数(不带下划线的函数),那么返回上一步,检查你的DEF文件。在你可以继续之前,你需要得到别名的出现。如果DLL看起来OK了,那么该是转到MSVC这边的时间了。

从MSVC里调用DLL
一旦你拥有了一个被反改编__cdecl函数出口的DLL模型,下一步就是要为MSVC用户生成一个引入库。为这,你将需要刚刚创建的DLL,使用Borland的IMPDEF实用工具(再一次),和来自MSVC的LIB.EXE工具。第一步是创建DLL的DEF文件。为这,我建议你拷贝DLL和DLL头文件到你的MSVC工程目录里,在那儿工作。

C:> impdef bcbdll.def bcbdll.dll
IMPDEF将创建一个DEF文件,内容如下:

C:> impdef bcbdll.def bcbdll.dll

LIBRARY BCBDLL.DLL

EXPORTS
Bar @4 ; Bar
Foo @5 ; Foo
_Bar @2 ; _Bar
_Foo @1 ; _Foo
___CPPdebugHook @3 ; ___CPPdebugHook
打开DEF文件,改变它的内容为:

LIBRARY BCBDLL.DLL

IMPORTS
Bar @4 ; Bar
Foo @5 ; Foo
注意到我们移除了包含下划线的函数,和调试钩挂(debug hook)函数。我们还把EXPORT改成了IMPORTS,因为我们现在是在引入函数,而不是导出它们(我怀疑它对MSVC LIB.EXE来说会产生不同)。

下一步,我们用Microsoft LIB.EXE,从DEF文件那儿创建一个COFF格式的库。语法为:

lib /DEF:bcbdll.def /out:bcbdll_msvc.lib


注意:
--------------------------------------------------------------------------------

MSVC命令行实用工具在默认情况下不在你的配置的路径里。你可能需要运行一个MSVC带的批处理文件,使得LIB.EXE可以被直接调用。批处理文件叫做VCVARS32.BAT,它位于DevStudio安装路径的\VC\BIN子目录下。

--------------------------------------------------------------------------------


这里,所有艰苦的工作都做完了。现在你需要做就是把你的DLL,MSVC LIB文件,和DLL文件件加入到你的MSVC客户端。要使用DLL,需要添加LIB文件到MSVC工程里,并且在源代码内#include DLL头文件。

我准备了一个MSVC的简单工程来证明上面的概念。清单3给出客户端DLL的源代码。没什么特别的地方,就是一个main函数,一个DLL头文件的#include,和对DLL的几个函数调用。主要是你正确的添加了引入库,由LIB.EXE生成的那个,添加到MSVC工程里。

// ----------------------------------------------
// Listing 3- MSVC DLL client code
#include
#include
using namespace std;

#include "bcbdll.h"

int main()
{
cout << "Foo(10) = " << Foo(10) << endl;
cout << "Bar() = " << Bar() << endl;
cout << "Bar() = " << Bar() << endl;
cout << "Bar() = " << Bar() << endl;
return 0;
}
// ----------------------------------------------
例2:显式连接
这个例子向你展示了如何从MSVC里使用显式连接调用BCB编译的DLL。用显式连接,你不必摆弄创建一个MSVC兼容的引入库。显示连接不利的是它需要在用户端做更多的工作,它不及隐式连接类型安全,错误被延期到运行时而不是连接时。虽然显式连接有许多不利因素,但在某些情况下它还是十分有用的。

在这个例子里,我们将创建一个DLL,它导出两个函数:Foo和Bar。函数的原型同上一个例子一样。

int Foo (int Value);
int Bar (void);
这一显式连接的指导方针与隐式连接的相仿。我们需要导出简单的C函数,需要防止C++名字改编。如果我们用__cdecl调用习惯,那么我们可能想要为BCB导出的函数建立别名,以去掉它们前端的下划线。如果我们选择不用别名去掉下划线的方法,那么当按名字载入函数时,我们必须包含下划线。换句话说,当你对__cdecl函数起作用时,你必须在某几点上处理下划线。你也可以在BCB建造DLL的时候处理下划线,或者在运行时调用DLL时处理它。我们利用__stdcall代替__cdecl以回避整个讨论的下划线问题。这是我们在这个例子里要做的。清单4和5给出的我们DLL的源代码。

注意:
--------------------------------------------------------------------------------

如果你导出__stdcall函数,至关紧要的是要让客户端应用程序知道。一些人容易犯一个错误,认为使用__stdcall只不过是去掉了__cdecl函数前面的下划线。别掉进这个陷井。__stdcall函数处理堆栈方式也__cdecl是不同的。如果客户端应用程序把__stdcall当作__cdecl函数调用(也就是,堆栈将被破坏,客户端程序会死得很难看),将要发生一些错误。

--------------------------------------------------------------------------------


// ----------------------------------------------
// Listing 4- DLL header file
#ifndef BCBDLL_H
#define BCBDLL_H

#ifdef __cplusplus
extern "C" {
#endif

#ifdef BUILD_DLL
#define IMPORT_EXPORT __declspec(dllexport)
#else
#define IMPORT_EXPORT __declspec(dllimport)
#endif

IMPORT_EXPORT int __stdcall Foo (int Value);
IMPORT_EXPORT int __stdcall Bar (void);

#ifdef __cplusplus
}
#endif

#endif
// ----------------------------------------------
// ----------------------------------------------
// Listing 5- DLL source code
#include

#define BUILD_DLL
#include "bcbdll.h"

int __stdcall Foo (int Value)
{
return Value + 1;
}

int __stdcall Bar (void)
{
static int ret = 0;
return ret++;
}
// ----------------------------------------------
注意这段代码几乎与隐式连接的一模一样。唯一不同的地方就是把Foo和Bar的调用习惯改成__stdcall代替__cdecl。

现在让我们看一下调用DLL的MSVC程序代码。代码如清单6所示。

// ----------------------------------------------
// Listing 6- MSVC client code
#include
#include
using namespace std;

HINSTANCE hDll = 0;
typedef int (__stdcall *foo_type) (int Value);
typedef int (__stdcall *bar_type) ();
foo_type Foo=0;
bar_type Bar=0;

void DLLInit()
{
hDll = LoadLibrary("bcbdll.dll");
Foo = (foo_type)GetProcAddress(hDll, "Foo");
Bar = (bar_type)GetProcAddress(hDll, "Bar");
}

void DLLFree()
{
FreeLibrary(hDll);
}

int main()
{
DLLInit();

cout << "Foo() = " << Foo(10) << endl;
cout << "Bar() = " << Bar() << endl;
cout << "Bar() = " << Bar() << endl;
cout << "Bar() = " << Bar() << endl;
DLLFree();
return 0;
}
// ----------------------------------------------
这段代码片段里有许多需要消化的地方。首先也是最重要的,观察代码本身是编译器中立的。你可以在BCB或MSVC里编译它。我首先在BCB里编译它,确信它可以按我所想的工作。

第二,注意到代码没有为#include bcbdll.h操心。有一个重要的原因。bcbdll.h为Foo和Bar函数定义的原型。但是,我们不把我们的代码同任何预先定义的那些原型连接。通常,这些原型的存根来自引入库。但是这个例子示范的是显式连接,当你显示地连接时,是不使用引入库的,在头文件里的Foo和Bar原型对我们来说没多大意义。

第三件要注意的事情是关于这段代码里出现的typedef和函数指针,位于源文件的顶部附近。晃式连接需要你在运行时用API GetProcAddrress得到DLL函数的地址。你必须把GetProcAddress返回的结果存储到某个地方。最好的地点是把结果存储到函数指针里。通过把函数地址存储到函数指针里,你可以使用正常的函数调用语法调用函数(如 Foo(10))。

typedef声明创建了两个新的类型: foo_type和bar_type。它们都是函数指针类型。foo_type声明了一个指向__stdcall函数的类型,这个函数打官腔一个整型参数,返回一个整型值。bar_type定义了一个指向__stdcall类型的、没有参数、有一个整型返回值的函数。这些typedef产生了两个效果。第一,它们提供了清晰的方式来声明函数指针变量Foo和Bar。第二,它们使我们可以很方便的转换GetProcAddress返回的结果。从GetProcAddress返回的结果是一个指向__stdcall类型的、没有参数、有一个整型返回值的函数。除非你的函数与这个格式相同,否则你需要转换GetProcAddress的结果(这个转换是显式连接比隐式连接缺管类型安全的原因)。

在typedef的下面有两个变量Foo和Bar。这两个是函数指针变量。它们会保存我们想要调用的两个函数的地址。注意这些变量的名字是任意的。我选择Foo和Bar是为了使代码像隐式连接。不要犯这样的错误,Foo和Bar变量名没有与DLL里的真实函数建立连接。我们可以把变量命名为Guido和Bjarne,如果你想的话。

在函数指针声明下面,你会看到两个叫DllInit和DllFree的函数实体。这两个实体处理载入DLL,查找导出函数,和在我们使用赛后释放程序库。用这种方法,其余的代码不知道DLL是显式连接的。它可以像往常一样调用Foo和Bar(或者Guido和Bjarne,如果你改变了名字)。唯一要协调的是你必须在调用任何DLL程序之前调用DllInit。我们也应当细致的,调用DllFree释放程序库。

注意:
--------------------------------------------------------------------------------

当在命名总题上Borland编译器和Microsoft编译器之间大战之时,GetProcAddress是你的最后一道防线。这包括Borland __cdecl命名带一个前端下划线(如 _Foo)。也包括改编C++名字。如果有人支持你用改编函数名字的DLL,你可以永远传递这些难看的参数,把改编名字给GetProcAddress。不管你实际上你能调用函数而没碰到其它的什么问题,但是至少你将会有一个机会。

--------------------------------------------------------------------------------


这就是全部。在MSVC里编译代码,你就完成了。你不必摆弄DEF文件或是引入库。但是在你这边的代码里有些琐碎的工作要处理。

例3: 用#define组装隐式连接
这个例子展示了可能是从MSVC工程里调用BCB DLL最简单的一种方法,但它也可能是最没有吸引力的一种方法。代码使用一个狡诈的#define,当检查到是Microsoft编译器时给__cdecl函数前加上下划线。也就是说,我们简单的#define了Foo为_Foo。

这种技术的优势在于我们不必实行任何别名。 我们能直接导出包含下划线的__cdecl函数。但是,我们仍就必须用Microsoft的LIB.EXE创建一个MSVC兼容的引入库。

这种技术是关键是MSVC不期望__cdecl函数有任何的修饰(见表1)。它们应当和看起来一样。 如果MSVC应用程序试图执行一个__cdecl函数Foo,它期望在DLL里查找一个没有下划线的函数Foo。如果我们改变MSVC的代码,让它调用_Foo,那么它将试图在DLL里查找一个叫做_Foo的函数。

Borland给__cdecl函数前加上了下划线。我们可以哄骗MSVC,让它在调用函数的时候在函数名的前面加一个下划线。紧记我们只想在MSVC这边添加一个下划线,而不是Borland这边。

#define组装的DLL代码与例1里清单2的代码完全一样。唯一不同的是DLL头文件。当检测到是MSVC时,DLL头文件为每一个函数原型加一个下划线。清单7展示了修改后的头文件。

// ----------------------------------------------
// Listing 7- DLL header file
#ifndef BCBDLL_H
#define BCBDLL_H

#ifdef __cplusplus
extern "C" {
#endif

#ifdef BUILD_DLL
#define IMPORT_EXPORT __declspec(dllexport)
#else
#define IMPORT_EXPORT __declspec(dllimport)
#endif

// #define kludge. If we are being compiled with MSVC, then just tack on a
// leading underscore because Borland C++ will export Foo and Bar as _Foo
// and _Bar respectively
#ifdef _MSC_VER
#define Foo _Foo
#define Bar _Bar
#endif

IMPORT_EXPORT int __cdecl Foo (int Value);
IMPORT_EXPORT int __cdecl Bar (void);

#ifdef __cplusplus
}
#endif
#endif
// ----------------------------------------------
在头文件里,除#define组装之外,你还必须创建一个MSVC兼容的引入库。你可以按前面的步骤完成。对编译好的DLL运行IMPDEF,得到一个DEF文件。然后运行Microsoft LIB.EXE工具创建一个COFF格式的引入库。这时,你不必考虑去编辑DEF文件。最后,拷贝DLL,COFF引入库,和DLL文件件到你的MSVC工程里。把LIB文件添加到你的MSVC工程里,重建。

这是创建MSVC引入库的命令行例子。注意我们不必编辑DEF文件。我们刚好可以把它传递给LIB.EXE。

// Create def file
> impdef bcbdll.def bcbdll.dll

// create COFF import library using MS lib.exe
>lib /def bcbdll.def
例4: 用__stdcall函数隐式连接
在我们进行之前,让我们调查一下为什么我们需要单独论述__stdcall函数。MSVC没有提供与Borland的IMPLIB相当的工具。你不能攫取DLL,生成一个MSVC可用的引入库。最接近的工具是LIB.EXE,它可以通过一个DEF文件创建一个引入库。DEF文件必须是手动创建,或利用Borland的IMPDEF工具生成的。

没什么大不了的啊?你仍能创建MSVC引入库,只是必须通过中间步骤创建一个DEF文件,然后把它传递给LIB.EXE工具。正确的,在你采用__cdecl函数的时候。当你转到__stdcall的时候,问题就发生了。问题是Microsoft的LIB.EXE工具为导出__stdcall函数的DLL生成引入库显得无能为力。

因为这个原因,我把用__stdcall隐式连接分离出来作为它自己的一部分。我们需要跟着一个不同步骤的次序来创建Microsoft兼容的引入库。(同样注意到我把这部分放到最后的好理由,至少这些步骤是冗长乏味的)。

既然我们不能用LIB.EXE为用__stdcall的BCB DLL生成引入库,那我们需要提出一种不同的策略。有一种生成引入库的方法(可能是唯一的方法),依靠只要你建造一个DLL的,MSVC就可以生成一个引入库这一事实。如果你建造一个包含__stdcall函数的MSVC DLL,编译器和连接器会正确的分解导出的__stdcall函数,生成引入库。

那么你会问它会怎么帮助我们呢?毕竟,我们正在用Borland C++编译DLL。在MSVC里创建一个DLL工程有什么好处?我们想让EXE用MSVC编译,但是DLL应当保持在BCB这边。这个问题的答案是我们在MSVC里编译虚假DLL工程,唯一的目的是生成一个__stdcall的引入库。由MSVC创建的DLL可以被丢到垃圾筒里。我们不需要它。

这种技术是建立在虚假DLL工程的基础之上的。我们在MSVC里创建一个虚假DLL工程,就是得到生成Microsoft兼容的引入库的好处。于是我们可以把这个引入库和BCB生成的DLL相结合,再提供给MSVC用户,使得他们可以调用我们的带有__stdcall函数的Borland DLL。

这是这种技术所必须的几步。首先,用BCB编译你的DLL。用__stdcall调用习惯,导出简单的C函数,用extern "C"包装所有的声明。DLL的代码与例2中清单4和5的代码相同,因此我不把它们再列出来了。第二步是在MSVC里创建虚假DLL工程。编译虚假DLL工程,盗取生成的引入库。最后一步是把这个引入库添加到任一想要调用Borland DLL的MSVC工程里。

这一技术最有挑战兴趣的是围绕虚假DLL工程和引入库的基因。建造一个虚假DLL工程,用MSVC创建一个non-MFC DLL工作区。编辑MSVC工程设置,以便使生成DLL的函数与BCB DLL的名字相匹配(在我们的例子里是bcbdll.dll)。这个设置可以在Project-Settings-Link下找到。从Borland工程目录里拷贝你的DLL头文件源代码到虚假DLL工程目录。如果你的工程由多个CPP文件组成,那么只需拷贝包含导出声明的头文件。把CPP源代码文件添加到虚假工作区。

下一步,进入每一个导出函数的定义,删除每个函数实体的代码。以一堆空函数告终。如果函数有返回值,在适当的位置保留返回语句。只是一些虚假的返回值(比如0)。除丢弃函数体之外,移除任何不必要的#include语句(你应当可以移大部分#include,因为所有的函数体都是空的)。

我们的BCB DLL与例2的清单4和5包含同样的代码。 清单8展示了同样的代码被修整下来后的版本。这个修整下来后的版被添加到虚假DLL工作区。

// ----------------------------------------------
// Listing 8- dummy DLL source code
#define BUILD_DLL
#include "bcbdll.h"

int __stdcall Foo (int Value)
{
return 0;
}

int __stdcall Bar (void)
{
return 0;
}
// ----------------------------------------------
这时,我们应当可以在MSVC里编译虚假DLL工作。但是在我们编译之前,我们必须再实行一步,以抗击Microsoft编译器的一些特性。我们的虚假DLL导出__stdcall函数。当Microsoft DLL导出__stdcall函数时,通常都给函数名做了修饰,添加了前端下划线,附加了'@'符号和一个数字的结尾(见文章开始处的表1)。例如,Foo将被导出为_Foo@4。这不是我们想要的行为。虚假DLL的全部的目的就是为我们的BCB DLL生成MSVC引入库。我们的BCB DLL包含简单的、没有下划线的、__stdcall函数(Foo和Bar)。它没有给生成引入库带来任何好处,因为与修饰的名字(_Foo@4和_Bar@0)不匹配 DLL包含简单的、没有下划线的、__stdcall函数(Foo和Bar)。它没有给生成引入库带来任何好处,因为与修饰的名字(_Foo@4和_Bar@0)不匹配。

幸运地,我们可以防止MSVC修饰虚假__stdcall函数,方法是添加一个DEF文件到虚假DLL工程里。DEF文件简单的列出每一个要导出的函数。内容如下:

LIBRARY BCBDLL.DLL

EXPORTS
Bar
Foo
注意:
--------------------------------------------------------------------------------

在DEF文件里的程序库名应当与由MSVC生成的虚假DLL名字相匹配,而它转而应当与用BCB创建的DLL名字相匹配。如果这三项不匹配,那么你会运行出各种不同的错误(通常是未解决的连接器错误)。

--------------------------------------------------------------------------------


把DEF文件添加到虚假DLL工程里,建造虚假DLL。当MSVC建造DLL工程的时候,它会创建一个引入库。这个引入库是让MSVC用户可以用隐式连接的方式调用导出__stdcall函数的BCB DLL的关键因素,把它连同你的DLL一起提供给MSVC用户。你的用户应当把这个引入库添加到任何调用你的BCB DLL的MSVC工程里。

结论
这篇文章提供了四种技术,让Microsoft Visual C++可以调用由BCB编译的DLL。我希望这篇文章把每一种技术描述的很充分(这些内容有的不太容易理解)。为了帮助你理解每一种技术,我为每一种可代利用的技术制作了例子代码,可以从这儿下载。zip文件解压出四个子目录,一个对应一种技术。每一个子目录包含一个用BCB5 DLL工程的DLL目录,和一个用来调用BCB DLL的MSVC工程的EXE目录。MSVC工程是VC++ 5工程,但它们应当可以在MSVC 6下正常的工作。

关于asp学习的好东东手册

基本运算
+ 数字加法及字符串连接
- 数字减法
* 数字乘法
/ 数字除法
Mod 求余数
\ 求商数
& 字符串连接
^ 次方
= 相等
<> 不相等
>= 大于或等于
> 大于
<= 小于或等于
< 小于
Not 非
And 且
Or 或
Xor 异或

循环及决策
if ....then 若...则...
if ...then...else 若...则...非
else if... 非若
select case... 群组选择条件
end select
for ... next 计数循环
while...wend 条件循环(一)
do while...loop 条件循环(二)
do...loop while 条件循环(三)
do until...loop 条件循环(四)
do...loop until 条件循环(五)

数学函数
Abs 绝对值
Sgn 正负号
Hex 转换成十六进制
Oct 转换成八进制 [Page]
Sqr 平方根
Int 取整数
Fix 取整数
Round 取整数
Log 以e为底的对数
Sin 正弦函数
Cos 余弦函数
Tan 正切函数

字符串处理函数
Len 字符串长度
Mid 取部分字符串
Left 从字符串开头取部分字符串
Right 从字符串结尾取部分字符串
Lcase 转换成小写
Ucase 转换成大写
Trim 清除字符串开头及结尾的空格符
Ltrim 清除字符串开头空格符
Rtrim 清除字符串结尾空格符
Replace 替换字符串部分字符
Instr 判断是否包含于另一个字符串(从起始搜寻)
InstrRev 判断是否包含于另一个字符串(从结尾搜寻)
Space 任意字符数的空格符
String 任意字符数的任一字符
StrReverse 反转字符串
Split 以某字符分割字符串

数据类型转换函数
Cint 转换成整形
Cstr 转换成字符串
Clng 转换成常整数
Cbool 转换成布尔函数
Cdate 转换成日期函数
CSng 转换成单精度
CDbl 转换成双精度

日期时间函数
Date 现在日期
Time 现在时间
NOw 现在日期时间
DateAdd 增加日期 [Page]
DateDiff 两日期差
DateSerial 日期设定
DateValue 日期设定
Year 现在年份
Month 现在月份
Day 现在天
Hour 现在时刻
Minute 现在分钟
Second 现在秒钟
Timer 午夜距现在秒数
TimeSerial 时间设定
TimeValue 时间所属部分
WeekDay 星期名称
MonthName 月份名称

其它函数
Array 产生数组
Asc 字符ASCII码
Chr ASCII码字符
Filter 过滤数组
InputBox 输入窗口
Join 合并数组中的元素
MsgBox 信息窗口
Lbound 数组下界
Ubound 数组上界

指令
Const 设定常数
Dim 定义变量或者数组
Erase 清除数组
ReDim 重新声明数组
Randomize 起始随机数
Rnd 取得随机数

ASP对象

Session对象
IsEmpty 测试Session变量是否存在
TimeOut 设定Session变量生存周期
Abandon 强制清除Session变量

Application对象
IsEmpty 测试Application变量是否存在
Lock 锁定Application变量
Unlock 解除Lock指令的锁定

Cookies对象
Expires 设定Cookies变量的生存周期

Connection对象
Open 打开与数据库的连接
Execute 打开Recordset对象 [Page]
Close 关闭Connection对象

Recordset对象
movefirst 将记录指针移至第一条
movelast 将记录指针移至最后一条
movenext 将记录指针移至下一条
moveprevious 将记录指针移至上一条
bof 测试是否为recordset的起始
eof 测试是否为recordset的结束
open 打开Recoreset对象
close 关闭recordset对象
fields 读取数据的子对象
fileds.count 字段个数
pagesize 每页记录条数
absolutepage 设定为某页
pagecount 总页数
Absoluteposition 直接跳至某条记录

asp常用函数

Array()
FUNCTION:返回一个数组
SYNTAX:Array(list)
ARGUMENTS:字符,数字均可
EXAMPLE:
RESULT:建立了一个包含7个元素的数组myArray
myArray("Sunday","Monday",......"Saturday")

CInt()
FUNCTION:将一个表达式转化为数字类型
SYNTAX:CInt(expression)
ARGUMENTS:任何有效的字符均可
EXAMPLE:
RESULT:236
转化字符"234"为数字"234",如果字符串为空,则返回0值

CreateObject()
FUNCTION:建立和返回一个已注册的ACTIVEX组件的实例。
SYNTAX:CreateObject(objName)
ARGUMENTS:objName是任何一个有效、已注册的ACTIVEX组件的名字.
EXAMPLE:
RESULT:

CStr()
FUNCTION:转化一个表达式为字符串.
SYNTAX:CStr(expression)
ARGUMENTS:expression是任何有效的表达式。
EXAMPLE:
RESULT:转化数字“5”为字符“5”。

Date()
FUNCTION:返回当前系统日期.
SYNTAX:Date()
ARGUMENTS:None.
EXAMPLE:
RESULT:8/4/99

DateAdd()
FUNCTION:返回一个被改变了的日期。
SYNTAX:DateAdd(timeinterval,number,date)
ARGUMENTS:timeintervalisthetimeintervaltoadd;numberisamountof
timeintervalstoadd;anddateisthestartingdate.
EXAMPLE:


RESULT:11/4/99
3:34:45PM

"m"="month";
"d"="day";

IfcurrentDateisintimeformatthen,
"h"="hour";
"s"="second";

DateDiff()
FUNCTION:返回两个日期之间的差值。
SYNTAX:DateDiff(timeinterval,date1,date2[,firstdayofweek][,
firstweekofyear]])
ARGUMENTS:timeinterval表示相隔时间的类型,如“M“表示“月”。
EXAMPLE:
RESULT:从8/4/99到2000年还有150天. [Page]

Day()
FUNCTION:返回一个月的第几日.
SYNTAX:Day(date)
ARGUMENTS:date是任何有效的日期。
EXAMPLE:
RESULT:4

FormatCurrency()
FUNCTION:返回表达式,此表达式已被格式化为货币值
SYNTAX:FormatCurrency(Expression[,Digit][,LeadingDigit][,Paren][,
GroupDigit]]]])
ARGUMENTS:Digit指示小数点右侧显示位数的数值。默认值为-1,指示使用的是
计算机的区域设置;LeadingDigit三态常数,指示是否显示小数值小数点前面的
零。
EXAMPLE:
RESULT:$34.35

FormatDateTime()
FUNCTION:返回表达式,此表达式已被格式化为日期或时间
SYNTAX:FormatDateTime(Date,[,NamedFormat])
ARGUMENTS:NamedFormat指示所使用的日期/时间格式的数值,如果省略,则使用
vbGeneralDate.
EXAMPLE:
RESULT:Wednesday,August04,1999

FormatNumber()
FUNCTION:返回表达式,此表达式已被格式化为数值.
SYNTAX:FormatNumber(Expression[,Digit][,LeadingDigit][,Paren][,
GroupDigit]]]])
ARGUMENTS:Digit指示小数点右侧显示位数的数值。默认值为-1,指示使用的是
计算机的区域设置。;LeadingDigiti指示小数点右侧显示位数的数值。默认值为-
1,指示使用的是计算机的区域设置。;Paren指示小数点右侧显示位数的数值。默认
值为-1,指示使用的是计算机的区域设置。;GroupDigiti指示小数点右侧显示位数
的数值。默认值为-1,指示使用的是计算机的区域设置。.
EXAMPLE:
RESULT:45.325

FormatPercent()
FUNCTION:返回表达式,此表达式已被格式化为尾随有%符号的百分比(乘以
100)。(%)
SYNTAX:FormatPercent(Expression[,Digit][,LeadingDigit][,Paren][,
GroupDigit]]]])
ARGUMENTS:同上.
EXAMPLE:
RESULT:45.267%

Hour()
FUNCTION:以24时返回小时数.
SYNTAX:Hour(time)
ARGUMENTS:
EXAMPLE:
RESULT:16
(Hourhasbeenconvertedto24-hoursystem)

Instr()
FUNCTION:返回字符或字符串在另一个字符串中第一次出现的位置.
SYNTAX:Instr([start,]strToBeSearched,strSearchFor[,compare])
ARGUMENTS:Start为搜索的起始值,strToBeSearched接受搜索的字符串
strSearchFor要搜索的字符.compare比较方式(详细见ASP常数)
EXAMPLE:
RESULT:9

InstrRev()
FUNCTION:同上,只是从字符串的最后一个搜索起
SYNTAX:InstrRev([start,]strToBeSearched,strSearchFor[,compare])
ARGUMENTS:同上.
EXAMPLE:
RESULT:13


Int()
FUNCTION:返回数值类型,不四舍五入,注意取值是不大于它的整数。
SYNTAX:Int(number)
ARGUMENTS:
EXAMPLE:
RESULT:32-4

IsArray()
FUNCTION:判断一对象是否为数组,返回布尔值.
SYNTAX:IsArray(name)
ARGUMENTS:
EXAMPLE:
RESULT:False

IsDate()
FUNCTION:判断一对象是否为日期,返回布尔值 [Page]
SYNTAX:IsDate(expression)
ARGUMENTS:expressionisanyvalidexpression.
EXAMPLE:
RESULT:True

IsEmpty()
FUNCTION:判断一对象是否初始化,返回布尔值.
SYNTAX:IsEmpty(expression)
ARGUMENTS:
EXAMPLE:
RESULT:True

IsNull()
FUNCTION:判断一对象是否为空,返回布尔值.
SYNTAX:IsNull(expression)
ARGUMENTS:
EXAMPLE:
RESULT:False

IsNumeric()
FUNCTION:判断一对象是否为数字,返回布尔值.
SYNTAX:IsNumeric(expression)
ARGUMENTS:
EXAMPLE:
RESULT:True
就算数字加了引号,ASP还是认为它是数字。

IsObject()
FUNCTION:判断一对象是否为对象,返回布尔值.
SYNTAX:IsObject(expression)
ARGUMENTS:
EXAMPLE:
RESULT:True

LBound()
FUNCTION:返回指定数组维的最小可用下标.
SYNTAX:Lbound(arrayname[,dimension])
ARGUMENTS:;dimension指明要返回哪一维下界的整数。使用1表示第一维,2
表示第二维,以此类推。如果省略dimension参数,默认值为1.
EXAMPLE:
RESULT:0

LCase()
FUNCTION:返回字符串的小写形式
SYNTAX:Lcase(string)
ARGUMENTS:stringisanyvalidstringexpression.
EXAMPLE:
RESULT:thisisatest!

Left()
FUNCTION:返回字符串左边第length个字符以前的字符(含第length个字符).
SYNTAX:Left(string,length)
ARGUMENTS:
EXAMPLE:
RESULT:Thi

Len()
FUNCTION:返回字符串的长度.
SYNTAX:Len(string|varName)
ARGUMENTS:
EXAMPLE:
RESULT:15

LTrim()
FUNCTION:去掉字符串左边的空格.
SYNTAX:LTrim(string)
ARGUMENTS:
EXAMPLE:
RESULT:Thisisatest!

Mid()
FUNCTION:返回特定长度的字符串(从start开始,长度为length).
SYNTAX:Mid(string,start[,length])
ARGUMENTS:
EXAMPLE:
RESULT:Today

Minute()
FUNCTION:返回时间的分钏.
SYNTAX:Minute(time)
ARGUMENTS:
EXAMPLE:
RESULT:45

Month()
FUNCTION:返回日期.
SYNTAX:Month(date)
ARGUMENTS:dateisanyvaliddateexpression.
EXAMPLE:
RESULT:8

MonthName()
FUNCTION:Returnsastringidentifyingthespecifiedmonth.
SYNTAX:MonthName(month,[,Abb])
ARGUMENTS:monthisthenumericrepresentationforagivenmonth;Abb
(optional)isabooleanvalueusedtodisplaymonthabbreviation.True
willdisplaytheabbreviatedmonthnameandFalse(default)willnotshow
theabbreviation.
EXAMPLE:
RESULT:August

Now()
FUNCTION:Returnsthecurrentsystemdateandtime.
SYNTAX:Now()
ARGUMENTS:None
EXAMPLE:
RESULT:8/4/999:30:16AM [Page]

Replace()
FUNCTION:Returnsastringinwhichaspecifiedsub-stringhasbeen
replacedwithanothersubstringaspecifiednumberoftimes.
SYNTAX:Replace(strToBeSearched,strSearchFor,strReplaceWith[,start
][,count][,compare]]])
ARGUMENTS:strToBeSearchedisastringexpressioncontainingasub-
stringtobereplaced;strSearchForisthestringexpressiontosearchfor
withinstrToBeSearched;strReplaceWithisthestringexpressiontoreplace
sub-stringstrSearchFor;start(optional)isthenumericcharacter
positiontobeginsearch;count(optional)isavalueindicatingthe
comparisionconstant.
EXAMPLE:
RESULT:Thisisanorange!

Right()
FUNCTION:返回字符串右边第length个字符以前的字符(含第length个字符).
SYNTAX:Right(string,length)
ARGUMENTS:.
EXAMPLE:
RESULT:st!

Rnd()
FUNCTION:产生一个随机数.
SYNTAX:Rnd[(number)]
ARGUMENTS:
EXAMPLE:
RESULT:任何一个在0到1之间的数

Round()
FUNCTION:返回按指定位数进行四舍五入的数值.
SYNTAX:Round(expression[,numRight])
ARGUMENTS:numRight数字表明小数点右边有多少位进行四舍五入。如果省略,则
Round函数返回整数.
EXAMPLE:
RESULT:32

Rtrim()
FUNCTION:去掉字符串右边的字符串.
SYNTAX:Rtrim(string)
ARGUMENTS:
EXAMPLE:
RESULT:Thisisatest!!

Second()
FUNCTION:返回秒.
SYNTAX:Second(time)
ARGUMENTS:.
EXAMPLE:
RESULT:28

StrReverse()
FUNCTION:反排一字符串
SYNTAX:StrReverse(string)
ARGUMENTS:
EXAMPLE:
RESULT:!!tsetasisihT

Time()
FUNCTION:返回系统时间.
SYNTAX:Time()
ARGUMENTS:.
EXAMPLE:
RESULT:9:58:28AM

Trim()
FUNCTION:去掉字符串左右的空格.
SYNTAX:Trim(string)
ARGUMENTS:stringisanyvalidstringexpression.
EXAMPLE:
RESULT:Thisisatest!!

UBound()
FUNCTION:返回指定数组维数的最大可用下标.
SYNTAX:Ubound(arrayname[,dimension])
ARGUMENTS:;dimension(optional)指定返回哪一维上界的整数。1表示第一
维,2表示第二维,以此类推。如果省略dimension参数,则默认值为1.
EXAMPLE:
RESULT:2

UCase()
FUNCTION:返回字符串的大写形式.
SYNTAX:UCase(string)
ARGUMENTS:
EXAMPLE:
RESULT:THISISATEST!!

VarType()
FUNCTION:返回指示变量子类型的值
SYNTAX:VarType(varName)
ARGUMENTS:
EXAMPLE:
RESULT:2(数字)详见"asp常数"

WeekDay()
FUNCTION:返回在一周的第几天.
SYNTAX:WeekDay(date[,firstdayofweek])
ARGUMENTS:.
EXAMPLE:
RESULT:4(星期三) [Page]

WeekDayName()
FUNCTION:返回一周第几天的名字.
SYNTAX:WeekDayName(weekday[,Abb][,firstdayofweek]])
ARGUMENTS:Abb可选。Boolean值,指明是否缩写表示星期各天的名称。如果省
略,默认值为False,即不缩写星期各天的名称.firstdayofweek指明星期第一天的
数值
EXAMPLE:
RESULT:Wednesday

Year()
FUNCTION:返回当前的年份.
SYNTAX:Year(date)
ARGUMENTS:
EXAMPLE:
RESULT:1999

得到远程机器MAC地址源代码

#include
#include
#include
#include "iphlpapi.h"

#pragma comment ( lib, "ws2_32.lib" )
#pragma comment ( lib, "Iphlpapi.lib" )

void main( int argc, char ** argv )
{
int numberOfHost = 1;
struct hostent *remoteHostent;

//处理命令行参数
if ( argc == 3 )
numberOfHost = atoi( argv[2] );
if ( ( argc >3 ) || ( argc < 2 ) )
{
printf( "RmtHost v0.2 - Get remote HostName /MacAddress\n" );
printf( "by ShotgunLabs ( Shotgun@xici.net )\n\n" );
printf( "Usage :\n\tRmtHost.exe [RemoteIP] \n\n" );
printf( "Example:\n\tRmtHost.exe 192.168.0.3\n" );
// 本文转自 C++Builder 研究 - http://www.ccrun.com/article.asp?i=104&d=ml2x1o
printf( "\tRmtHost.exe 192.168.0.3 255\n\n" );
exit( 0 );
}

//初始化SOCKET
WSADATA wsaData;
int iRet = WSAStartup(MAKEWORD(2,1), &wsaData);
if ( iRet != 0 )
{
printf( "WSAStartup Error:%d\n", GetLastError() );
exit( 0 );
}
int nRemoteAddr = inet_addr( argv[1] );
remoteHostent= (struct hostent*)malloc( sizeof(struct hostent ));
struct in_addr sa;
for ( int i = 0; i < numberOfHost; i ++ )
{
//获取远程机器名
sa.s_addr = nRemoteAddr;
printf( "\nIpAddress : %s\n", inet_ntoa( sa ) );
remoteHostent = gethostbyaddr( (char*)&nRemoteAddr,4, AF_INET );
if ( remoteHostent )
printf( "HostName : %s\n",remoteHostent->h_name );
else
printf( "gethostbyaddr Error:%d\n",GetLastError() );
//发送ARP查询包获得远程MAC地址

unsigned char macAddress[6];
ULONG macAddLen = 6;
iRet=SendARP(nRemoteAddr, (unsigned long)NULL,(PULONG)&macAddress, &macAddLen);
if ( iRet == NO_ERROR )
{
printf( "MacAddress: " );
for( int i =0; i<6; i++ )
{
printf( "%.2x", macAddress[i] );
if ( i<5 ) printf( "-" );
}
printf( "\n" );
}
else
printf( "SendARP Error:%d\n", GetLastError());
nRemoteAddr = htonl( ntohl( nRemoteAddr ) + 1 );
}
}

怎样在C++ Builder中创建使用DLL

动态链接库(DLL)是Windows编程常遇到的编程方法,下面我就介绍一下在BCB (C++ Builder下简称BCB) 中如何创建使用DLL和一些技巧。

  一、创建:
  使用BCB File|NEW建立一个新的DLL工程,并保存好文件BCB,生成一个DLL的程序框架。
  1.DllEntryPoint函数为一个入口方法,如果使用者在DLL被系统初始化或者注销时被调用,用来写入对DLL的初始化程序和卸载程序;参数:hinst用来指示DLL的基地址;reason用来指示DLL的调用方式,用于区别多线程单线程对DLL的调用、创建、卸载DLL;
  2.在程序中加入自己所要创建的DLL过程、函数;
  3.用dllimport描述出口;
  例程序如下:
Enter in the Conditionals defines field: __BUILDING_THE_DLL.
file.h

#ifdef __BUILDING_THE_DLL
#define _EXPORT_DLL __declspec(dllexport)
#else
#define _EXPORT_DLL __declspec(dllimport)
#endif

extern "C" _EXPORT_DLL bool __stdcall test(int n);


  unit.cpp
//---------------------------------------------------------------------------

#include
#include
#pragma hdrstop
//---------------------------------------------------------------------------
// Important note about DLL memory management when your DLL uses the
// static version of the RunTime Library:
//
// If your DLL exports any functions that pass String objects (or structs/
// classes containing nested Strings) as parameter or function results,
// you will need to add the library MEMMGR.LIB to both the DLL project and
// any other projects that use the DLL. You will also need to use MEMMGR.LIB
// if any other projects which use the DLL will be performing new or delete
// operations on any non-TObject-derived classes which are exported from the
// DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling
// EXE's to use the BORLNDMM.DLL as their memory manager. In these cases,
// the file BORLNDMM.DLL should be deployed along with your DLL.
//
// To avoid using BORLNDMM.DLL, pass string information using "char *" or
// ShortString parameters.
//
// If your DLL uses the dynamic version of the RTL, you do not need to
// explicitly add MEMMGR.LIB as this will be done implicitly for you
//---------------------------------------------------------------------------

#pragma argsused
#include "File1.h"
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
return 1;
}
//---------------------------------------------------------------------------
bool __stdcall test(int n)
{
if(n==1)
{
ShowMessage("ok");
return true;
}
else
ShowMessage("not ok");
return false;
}
  注意:动态链接库中调用过程、函数时有不同的CALL方式 __cdecl、__pascal, __fastcall、__stdcall,BCB中默认的方式为__cdecl(可不写),如果考虑兼容性可用时__stdcall声明方法为:
// 本文转自 C++Builder 研究 - http://www.ccrun.com/article.asp?i=106&d=r3n13t
  extern "C" __declspec(dllexport) int __stdcall test();
  对于其中过程、函数也改为:
  int __stdcall test()

  二、使用DLL
  在BCB中使用DLL有两种方法:

  1.用静态调用法
  首先需要在BCB的项目中加入输入接口库(import library),打开工程项目,使用BCB View|Project Manager打开项目列表,向项目中加入接口库(*.lib)。
  其次在头文件中加入接口声明。
  例程序如下:
   //define in include file
   extern "C" __declspec(dllimport) int __cdecl test();
  //use function in main program
  int I;
  I=test();
  注意:
  (1)动态链接库调用过程、函数时CALL方式 与创建时方式一样不写为__cdecl,其它需要声明。
  (2)BCB创建的DLL有对应的输入接口库(import library),如只有DLL而无库时,可用BCB的implib工具产生:implib xxx.lib xxx.dll;另外可用:tlibxxx.lib,xxx.lst 产生DLL的内部函数列表,许多Windows的未公开技术就是用这种方法发现的。

  2.动态调用法
  动态调用法要用Windows API 中的LoadLibrary()和GetProcAddress()来调入DLL库,指出库中函数位置,这种方法较常见。
  例程序如下:
.h
typedef bool __stdcall (*test)(int n);

.cpp
test tDll;
HINSTANCE hins;
hins = LoadLibrary("Project2.dll");
if(hins != Null)
{
tDll = (test)GetProcAddress(hins,(LPCSTR)"test");
if(tDll)
{
if(tDll(1))
{
ShowMessage("work");
}
else
ShowMessage("work too");
}
else
ShowMessage("not work");
}
FreeLibrary(hins);


  三、注意: (not necessary)
  创建DLL时编译链接时注意设置Project Options。
  Packages标签:去除Builder with runtime packages检查框。
  Linker标签:去除Use dynamic RTL检查框。
  否则创建的DLL需要Runtime packages or Runtime library。