网络编程用的基本函数

本文所谈到的Socket函数如果没有特别说明,都是指的Windows Socket API。
一、WSAStartup函数
int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
);
使用Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0。
例:假如一个程序要使用2.1版本的Socket,那么程序代码如下
wVersionRequested = MAKEWORD( 2, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
二、WSACleanup函数
int WSACleanup (void);
  应用程序在完成对请求的Socket库的使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。
三、socket函数
SOCKET socket(
int af,
int type,
int protocol
);
  应用程序调用socket函数来创建一个能够进行网络通信的套接字。第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置PF_INET;第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM;第三个参数指定应用程序所使用的通信协议。
该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET。套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。下面是一个创建流套接字的例子:
struct protoent *ppe;
ppe=getprotobyname("tcp");
SOCKET ListenSocket=socket(PF_INET,SOCK_STREAM,ppe->p_proto);
四、closesocket函数
int closesocket(
SOCKET s
);
closesocket函数用来关闭一个描述符为s套接字。由于每个进程中都有一个套接字描述符表,表中的每个套接字描述符都对应了一个位于操作系统缓冲区中的套接字数据结构,因此有可能有几个套接字描述符指向同一个套接字数据结构。套接字数据结构中专门有一个字段存放该结构的被引用次数,即有多少个套接字描述符指向该结构。当调用closesocket函数时,操作系统先检查套接字数据结构中的该字段的值,如果为1,就表明只有一个套接字描述符指向它,因此操作系统就先把s在套接字描述符表中对应的那条表项清除,并且释放s对应的套接字数据结构;如果该字段大于1,那么操作系统仅仅清除s在套接字描述符表中的对应表项,并且把s对应的套接字数据结构的引用次数减1。
closesocket函数如果执行成功就返回0,否则返回SOCKET_ERROR。
五、send函数
int send(
SOCKET s,
const char FAR *buf,
int len,
int flags
);
不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。该函数的第一个参数指定发送端套接字描述符;第二个参数指明一个存放应用程序要发送数据的缓冲区;第三个参数指明实际要发送的数据的字节数;第四个参数一般置0。这里只描述同步Socket的send函数的执行流程。当调用该函数时,send先比较待发送数据的长度len和套接字s的发送缓冲区的长度,如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完,如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回SOCKET_ERROR)
注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
六、recv函数
int recv(
SOCKET s,
char FAR *buf,
int len,
int flags
);
不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。该函数的第一个参数指定接收端套接字描述符;第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;第三个参数指明buf的长度;第四个参数一般置0。这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,只到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
七、bind函数
int bind(
SOCKET s,
const struct sockaddr FAR *name,
int namelen
);
当创建了一个Socket以后,套接字数据结构中有一个默认的IP地址和默认的端口号。一个服务程序必须调用bind函数来给其绑定一个IP地址和一个特定的端口号。客户程序一般不必调用bind函数来为其Socket绑定IP地址和断口号。该函数的第一个参数指定待绑定的Socket描述符;第二个参数指定一个sockaddr结构,该结构是这样定义的:
struct sockaddr {
u_short sa_family;
char sa_data[14];
};
sa_family指定地址族,对于TCP/IP协议族的套接字,给其置AF_INET。当对TCP/IP协议族的套接字进行绑定时,我们通常使用另一个地址结构:
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
其中sin_family置AF_INET;sin_port指明端口号;sin_addr结构体中只有一个唯一的字段s_addr,表示IP地址,该字段是一个整数,一般用函数inet_addr()把字符串形式的IP地址转换成unsigned long型的整数值后再置给s_addr。有的服务器是多宿主机,至少有两个网卡,那么运行在这样的服务器上的服务程序在为其Socket绑定IP地址时可以把htonl(INADDR_ANY)置给s_addr,这样做的好处是不论哪个网段上的客户程序都能与该服务程序通信;如果只给运行在多宿主机上的服务程序的Socket绑定一个固定的IP地址,那么就只有与该IP地址处于同一个网段上的客户程序才能与该服务程序通信。我们用0来填充sin_zero数组,目的是让sockaddr_in结构的大小与sockaddr结构的大小一致。下面是一个bind函数调用的例子:
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(ListenSocket,(struct sockaddr *)&saddr,sizeof(saddr));
八、listen函数
int listen( SOCKET s, int backlog );
服务程序可以调用listen函数使其流套接字s处于监听状态。处于监听状态的流套接字s将维护一个客户连接请求队列,该队列最多容纳backlog个客户连接请求。假如该函数执行成功,则返回0;如果执行失败,则返回SOCKET_ERROR。
九、accept函数
SOCKET accept(
SOCKET s,
struct sockaddr FAR *addr,
int FAR *addrlen
);
服务程序调用accept函数从处于监听状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道,如果连接成功,就返回新创建的套接字的描述符,以后与客户套接字交换数据的是新创建的套接字;如果失败就返回INVALID_SOCKET。该函数的第一个参数指定处于监听状态的流套接字;操作系统利用第二个参数来返回新创建的套接字的地址结构;操作系统利用第三个参数来返回新创建的套接字的地址结构的长度。下面是一个调用accept的例子:
struct sockaddr_in ServerSocketAddr;
int addrlen;
addrlen=sizeof(ServerSocketAddr);
ServerSocket=accept(ListenSocket,(struct sockaddr *)&ServerSocketAddr,&addrlen);
十、connect函数
int connect(
SOCKET s,
const struct sockaddr FAR *name,
int namelen
);
客户程序调用connect函数来使客户Socket s与监听于name所指定的计算机的特定端口上的服务Socket进行连接。如果连接成功,connect返回0;如果失败则返回SOCKET_ERROR。下面是一个例子:
struct sockaddr_in daddr;
memset((void *)&daddr,0,sizeof(daddr));
daddr.sin_family=AF_INET;
daddr.sin_port=htons(8888);
daddr.sin_addr.s_addr=inet_addr("133.197.22.4");
connect(ClientSocket,(struct sockaddr *)&daddr,sizeof(daddr));

- 阅读全文 -

程序的自动运行

内容提要
在工作中经常遇到一些程序,当计算机启动时会自动将该程序加载,以实现对计算机的监控等特殊的目的。本文就针对这个问题,阐述了系统加载特定程序的原理和方法,同时利用VC++ 6.0编程实现这种特定的功能的,并对其中的关键代码进行了分析。
文章正文
工作中经常遇到一些程序,它们在系统启动的过程中,自动打开并运行,以便实现对系统的监控或者病毒的检测等特定的目的,典型的例子就是常用的一些杀毒软件如:KV300及瑞星杀毒软件等。笔者在此,以自己的编程实践为基础,说明这些这些程序自动启动的原理和方法,同时对一些典型程序代码进行分析,以便读者在今后的编程过程中使用。
一、 程序自动启动的原理及方法:
1. 利用WIN.INI文件实现相关程序的自动启动
  WIN.INI是系统保存在C:WINDOWS目录下的一个系统初始化文件。系统在起动时会检索该文件中的相关项,以便对系统环境的初始设置。
在该文件中的"[windows]"数据段中,有两个数据项"load="和"run=",它们的作用就是在系统起动之后自动得装入和运行相关的程序。如果我们需要在系统起动之后装入并运行一个程序,只将需要运行文件的全文件名添加在该数据项的后面系统起动后就会自动运行该程序,系统也会进入特定的操作环境中去。
2. 利用注册表实现相关程序的自动启动
系统注册表保存着系统的软件、硬件及其他与系统配置有关的重要信息,一个计算机系统的系统注册表一旦遭到破坏,整个系统将无法运行。
在计算机的系统注册表中的子目录中有一个目录的名称为HKEY_LOCAL_MACHINESoftware MicrosoftWindowsCurrent_VersionRun,如果你想让程序在系统起动的过程中启动该程序,就可以向该目录添加一个子项,具体的过程是在注册表中右击该项,选中其中的"新建"项目,然后选中其中的"串值",建立新的串值后将它的名称改成相应的名称,双击新建的串值,输入新的数值,自动启动程序的过程就设置完成。
二、 利用VC++编程实现程序自动启动的编程实例。
微软公司提供的VC++ 6.0程序开发工具功能非常强大。在VC++ 6.0中同时具有对注册表和*.INI文件操作的函数。笔者经过一段时间的摸索,成功的利用VC++ 6.0开发成功了一个小软件,该软件利用系统自动启动程序的原理,将原来需要的繁琐的手动工作转变成成计算机的自动设置工作,使系统自动启动相关程序的设置工作变的非常简单可靠。
1.程序功能概述:
程序的主界面是对话框,在主界面对话框中有编辑框(EDIT BOX),圆形按钮(RADIO BUTTON)和普通按钮(COMMON BUTTON)组成。操作者通过向编辑框中添加需要自动加载的程序的全文件名(也可以通过浏览按钮来完成),然后通过对两个RADIO BUTTON的选择,进而完成对加载方式的选择(是选用注册表方式还是选者修改WIN.INI文件的方式),最后操作者通过点击"应用"按钮实现程序的自动加载功能,同时系统会提示操作者是否将操作计算机重新启动,以便观察程序的具体功能完成情况。程序在WIN98中调试运行正常。
2.编码说明:
① 浏览按钮的功能代码:
void CAutoloadDlg::OnLiulan()
{
// TODO: Add your control notification handler code here
CFileDialog fileDlg(TRUE,_T("EXE"),_T("*.exe"),OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,(_T("Executable Files (*.exe) |*.exe ||")));//显示打开文件的对话框
if(fileDlg.DoModal()==IDOK)//当操作者选择OK时,程序,取得选择文//件的全路径名(包括文件的路径及文件名称),并将相应的数值传输给相//关的控件变量
{
m_filename=fileDlg.GetPathName();//m_filename是EDIT BOX控件的相应的变量。
UpdateData(FALSE);//向将变量中的数值传输给控件显示出来。
}
② 应用按钮的功能代码:
void CAutoloadDlg::OnOK()
{
// TODO: Add extra validation here
LPCTSTR title;
UpdateData(TRUE);
if(m_title.IsEmpty())//如果操作者没有填写要设置项的标题,程序显示对话框,提示操作者进行相关的填写。
{
MessageBox(_T("Please input the title name"));
return;
}
title=m_title;
if(m_filename.IsEmpty())//如果操作者没有选择要设置的程序的全路径文//件名,程序显示对话框,提示操作者进行相关的选择。
{
MessageBox(_T("Please input the programe file name"));
return;
}
if(IsDlgButtonChecked(IDC_RADIO1))//如果操作者选择注册表方式,程序修改系统的注册表。
{
HKEY hKey;
LPCTSTR data_Set="SoftwareMicrosoftWindowsCurrentVersionRun";//设置注册表中相关的路径
Longret0=(::RegOpenKeyEx(HKEY_LOCAL_MACHINE,data_Set,0,KEY_WRITE,&hKey));//打开注册表中的相应项
if(ret0!=ERROR_SUCCESS)
{
MessageBox("错误0");
}
int length=m_filename.GetLength()+1;//将控件中的内容进行转换,以达到注册表修改函数的参数调用需求。
for(int i=0;i){
if(m_filename[i]==92)
length=length+1;
}
DWORD cbData=length;
LPBYTE lpb=new BYTE[length];
int j=0;
for(i=0;i{
if(m_filename[i]==92)
{
lpb[j]=92;
j++;
lpb[j]=92;
j++;
}
else
{
lpb[j]=m_filename[i];
j++;
}
}
lpb[j]=0;
long ret1=(::RegSetValueEx(hKey,title,NULL,REG_SZ,lpb,cbData));//将相关的信息写入注册表。
if(ret1!=ERROR_SUCCESS)//判断系统的相关注册是否成功?
{
MessageBox("错误1");
}
delete lpb;
::RegCloseKey(hKey);//关闭注册表中的相应的项
}
if(IsDlgButtonChecked(IDC_RADIO2))//如果操作者选择用修改WIN.INI文件的方式
{
LPCTSTR filename;
filename=m_filename;
WritePrivateProfileString(_T("windows"),_T("load"),filename,_T("c:windowswin.ini"));
WritePrivateProfileString(_T("windows"),_T("run"),filename,_T("c:windowswin.ini"));
}
yzdlg.DoModal();//显示对话框,提示操作者是否需要重新启动计算机,以便验证程序的功能。
CDialog::OnOK();
}
③ 重新启动按钮的功能代码:
void yanzheng::OnOK()
{
OSVERSIONINFO OsVerInfo;//保存系统版本信息的数据结构
OsVerInfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
GetVersionEx(&OsVerInfo);//取得系统的版本信息
if(OsVerInfo.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)
{
ExitWindowsEx(EWX_REBOOT,0);//重新启动计算机
}
CDialog::OnOK();
}

- 阅读全文 -

Delphi的TWebBrowser编程综述

转自:安全中国        

Delphi3开始有了TWebBrowser构件,不过那时是以ActiveX控件的形式出现的,而且需要自己引入,在其后的4.0和5.0中,它就在封装好shdocvw.dll之后作为Internet构件组之一出现在构件面板上了。常常听到有人骂Delphi的帮助做得极差,这次的TWebBrowser又是Microsoft的东东,自然不会好到哪里去,虽说MSDN上什么都有,可是内容太过庞杂,如果没有入口点更是件烦人的事,查找起来给人的感觉大概可以用一句话来形容:非常复杂、复杂非常。 
   这里有平时我自己用TWebBrowser做程序的一些心得和上网收集到的部分例子和资料,整理了一下,希望能给有兴趣用TWebBrowser编程的朋友带来些帮助。 
   1、初始化和终止化(Initialization & Finalization) 
     大家在执行TWebBrowser的某个方法以进行期望的操作,如ExecWB等的时候可能都碰到过“试图激活未注册的丢失目标”或“OLE对象未注册”等错误,或者并没有出错但是得不到希望的结果,比如不能将选中的网页内容复制到剪贴板等。以前用它编程的时候,我发现ExecWB有时侯起作用但有时侯又不行,在Delphi生成的缺省工程主窗口上加入TWebBrowser,运行时并不会出现“OLE对象未注册”的错误。同样是一个偶然的机会,我才知道OLE对象需要初始化和终止化(懂得的东东实在太少了)。 
     我用我的前一篇文章《Delphi程序窗口动画&正常排列平铺的解决》所说的方法编程,运行时出了上面所说的错误,我便猜想应该有OleInitialize之类的语句,于是,找到并加上了下面几句话,终于搞定!究其原因,我想大概是由于TWebBrowser是一个嵌入的OLE对象而不算是用Delphi编写的VCL吧。 
     initialization 
      OleInitialize(nil); 
     finalization 
      try 
       OleUninitialize; 
      except 
      end; 
     这几句话放在主窗口所有语句之后,“end.”之前。 
   -----------------------------------------------------------------------------------
   2、EmptyParam 
     在Delphi 5中TWebBrowser的Navigate方法被多次重载: 
     procedure Navigate(const URL: WideString); overload; 
     procedure Navigate(const URL: WideString; var Flags: 
   OleVariant); overload; 
     procedure Navigate(const URL: WideString; var Flags: 
   OleVariant; var TargetFrameName:     OleVariant); overload; 
     procedure Navigate(const URL: WideString; var Flags: 
   OleVariant; var TargetFrameName:     OleVariant; var PostData: 
   OleVariant); overload; 
     procedure Navigate(const URL: WideString; var Flags: 
   OleVariant; var TargetFrameName:     OleVariant; var PostData: 
   OleVariant; var Headers: OleVariant); overload; 
     而在实际应用中,使用后几种方法调用时,由于我们很少用到后面几个参数,但函数声明又要求是变量参数,一般的做法如下: 
     var 
      t:OleVariant; 
     begin 
      webbrowser1.Navigate(edit1.text,t,t,t,t); 
     end; 
     需要定义变量t(还有很多地方要用到它),很麻烦。其实我们可以用EmptyParam来代替(EmptyParam是一个公用的Variant空变量,不要对它赋值),只需一句话就可以了: 
     webbrowser1.Navigate(edit1.text,EmptyParam,EmptyParam,EmptyParam,EmptyParam); 
     虽然长一点,但比每次都定义变量方便得多。当然,也可以使用第一种方式。 
     webbrowser1.Navigate(edit1.text) 
   -----------------------------------------------------------------------------------
   3、命令操作   常用的命令操作用ExecWB方法即可完成,ExecWB同样多次被重载: 
     procedure ExecWB(cmdID: OLECMDID; cmdexecopt:   OLECMDEXECOPT); overload; 
     procedure ExecWB(cmdID: OLECMDID; cmdexecopt: OLECMDEXECOPT; 
                                        var pvaIn:  OleVariant); overload; 
     procedure ExecWB(cmdID: rOLECMDID; cmdexecopt: OLECMDEXECOPT; 
                                             var pvaIn:   OleVariant; var pvaOut: OleVariant); overload; 


     打开: 弹出“打开Internet地址”对话框,CommandID为OLECMDID_OPEN(若浏览器版本为IE5.0, 
         则此命令不可用)。 
     另存为:调用“另存为”对话框。 
         ExecWB(OLECMDID_SAVEAS,OLECMDEXECOPT_DODEFAULT,   EmptyParam,  EmptyParam); 


     打印、打印预览和页面设置: 调用“打印”、“打印预览”和“页面设置”对话框(IE5.5及以上版本才支持打 
                   印预览,故实现应该检查此命令是否可用)。  

         ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DODEFAULT, 
                                      EmptyParam,  EmptyParam); 
         if QueryStatusWB(OLECMDID_PRINTPREVIEW)=3 then 
          ExecWB(OLECMDID_PRINTPREVIEW, OLECMDEXECOPT_DODEFAULT, 
              EmptyParam,EmptyParam); 
         ExecWB(OLECMDID_PAGESETUP, OLECMDEXECOPT_DODEFAULT,   EmptyParam,  EmptyParam); 


     剪切、复制、粘贴、全选: 功能无须多说,需要注意的是:剪切和粘贴不仅对编辑框文字,而且对网页上的非编 
                  辑框文字同样有效,用得好的话,也许可以做出功能特殊的东东。获得其命令使能状 
                  态和执行命令的方法有两种(以复制为例,剪切、粘贴和全选分别将各自的关键字替 
                  换即可,分别为CUT,PASTE和SELECTALL): 
      A、用TWebBrowser的QueryStatusWB方法。 
        if(QueryStatusWB(OLECMDID_COPY)=OLECMDF_ENABLED) or 
         OLECMDF_SUPPORTED) then 
         ExecWB(OLECMDID_COPY, OLECMDEXECOPT_DODEFAULT, 
   EmptyParam, 
              EmptyParam); 
      B、用IHTMLDocument2的QueryCommandEnabled方法。 
        var 
         Doc: IHTMLDocument2; 
        begin 
         Doc :=WebBrowser1.Document as IHTMLDocument2; 
         if Doc.QueryCommandEnabled(’Copy’) then 
          Doc.ExecCommand(’Copy’,false,EmptyParam); 
        end; 
     查找: 参考第九条“查找”功能。 
   -----------------------------------------------------------------------------------
   4、字体大小 
     类似“字体”菜单上的从“最大”到“最小”五项(对应整数0~4,Largest等假设为五个菜单项的名字,Tag 
   属性分别设为0~4)。 
      A、读取当前页面字体大小。 
        var 
         t: OleVariant; 
        Begin 
         WebBrowser1.ExecWB(OLECMDID_ZOOM,  OLECMDEXECOPT_DONTPROMPTUSER,  EmptyParam,t); 
         case t of 
         4: Largest.Checked :=true; 
         3: Larger.Checked :=true; 
         2: Middle.Checked :=true; 
         1: Small.Checked :=true; 
         0: Smallest.Checked :=true; 
         end; 
        end; 
      B、设置页面字体大小。 
        Largest.Checked :=false; 
        Larger.Checked :=false; 
        Middle.Checked :=false; 
        Small.Checked :=false; 
        Smallest.Checked :=false; 
        TMenuItem(Sender).Checked :=true; 
        t :=TMenuItem(Sender).Tag; 
        WebBrowser1.ExecWB(OLECMDID_ZOOM,  OLECMDEXECOPT_DONTPROMPTUSER,  t,t); 
   -----------------------------------------------------------------------------------


   5、添加到收藏夹和整理收藏夹 
   

 const 
        CLSID_ShellUIHelper: TGUID =  ’{64AB4BB7-111E-11D1-8F79-00C04FC2FBE1}’; 
       var 
        p:procedure(Handle: THandle; Path: PChar); stdcall; 
       procedure TForm1.OrganizeFavorite(Sender: Tobject); 
       var 
        H: HWnd; 
       begin 
        H := LoadLibrary(PChar(’shdocvw.dll’)); 
        if H <> 0 then 
        begin 
              p := GetProcAddress(H, PChar(’DoOrganizeFavDlg’)); 
         if Assigned(p) then p(Application.Handle,   PChar(FavFolder)); 
        end; 
         FreeLibrary(h); 
       end; 
       procedure TForm1.AddFavorite(Sender: TObject); 
       var 
        ShellUIHelper: ISHellUIHelper; 
        url, title: Olevariant; 
       begin 
        Title := Webbrowser1.LocationName; 
        Url := Webbrowser1.LocationUrl; 
        if Url <> ’’ then 
        begin 
         ShellUIHelper := CreateComObject(CLSID_SHELLUIHELPER) as 
   IShellUIHelper; 
         ShellUIHelper.AddFavorite(url, title); 
        end; 
       end;

 

用上面的通过ISHellUIHelper接口来打开“添加到收藏夹”对话框的方法比较简单,但是有个缺陷,就是打开的窗口不是模式窗口,而是独立于应用程序的。可以想象,如果使用与OrganizeFavorite过程同样的方法来打开对话框,由于可以指定父窗口的句柄,自然可以实现模式窗口(效果与在资源管理器和IE中打开“添加到收藏夹”对话框相同)。问题显然是这样的,上面两个过程的作者当时只知道shdocvw.dll中DoOrganizeFavDlg的原型而不知道DoAddToFavDlg的原型,所以只好用ISHellUIHelper接口来实现(或许是他不够严谨,认为是否是模式窗口无所谓?)。 
     下面的过程就告诉你DoAddToFavDlg的函数原型。需要注意的是,这样打开的对话框并不执行“添加到收藏夹”的操作,它只是告诉应用程序用户是否选择了“确定”,同时在DoAddToFavDlg的第二个参数中返回用户希望放置Internet快捷方式的路径,建立.Url文件的工作由应用程序自己来完成。 
       procedure TForm1.AddFavorite(IE: TEmbeddedWB); 
        procedure CreateUrl(AUrlPath, AUrl: PChar); 
        var 
         URLfile: TIniFile; 
        begin 
         URLfile := TIniFile.Create(String(AUrlPath)); 
        RLfile.WriteString(’InternetShortcut’, ’URL’, 
   String(AUrl)); 
        RLfile.Free; 
        end; 
       var 
        AddFav: function(Handle: THandle; 
         UrlPath: PChar; UrlPathSize: Cardinal; 
         Title: PChar; TitleSize: Cardinal; 
         FavIDLIST: pItemIDList): Bool; stdcall; 
        FDoc: IHTMLDocument2; 
        UrlPath, url, title: array[0..MAX_PATH] of char; 
        H: HWnd; 
        pidl: pItemIDList; 
        FRetOK: Bool; 
       begin 
        FDoc := IHTMLDocument2(IE.Document); 
        if FDoc = nil then exit; 
        StrPCopy(Title, FDoc.Get_title); 
        StrPCopy(url, FDoc.Get_url); 
        if Url <> ’’ then 
        begin 
         H := LoadLibrary(PChar(’shdocvw.dll’)); 
         if H <> 0 then 
         begin 
          SHGetSpecialFolderLocation(0, CSIDL_FAVORITES, pidl); 
          AddFav := GetProcAddress(H, PChar(’DoAddToFavDlg’)); 
          if Assigned(AddFav) then 
           FRetOK :=AddFav(Handle, UrlPath, Sizeof(UrlPath), 
   Title, Sizeof(Title), pidl) 
         end; 
         FreeLibrary(h); 
         if FRetOK then 
          CreateUrl(UrlPath, Url); 
        end 
       end; 
   -----------------------------------------------------------------------------------
   6、使WebBrowser获得焦点 
     TWebBrowser非常特殊,它从TWinControl继承来的SetFocus方法并不能使得它所包含的文档获得焦点,从而不能立即使用Internet 
   Explorer本身具有得快捷键,解决方法如下:< 
     procedure TForm1.SetFocusToDoc; 
     begin 
      if WebBrowser1.Document <> nil then 
       with WebBrowser1.Application as IOleobject do 
        DoVerb(OLEIVERB_UIACTIVATE, nil, WebBrowser1, 0, Handle, 
   GetClientRect); 
     end; 
     除此之外,我还找到一种更简单的方法,这里一并列出: 
     if WebBrowser1.Document <> nil then 
      IHTMLWindow2(IHTMLDocument2(WebBrowser1.Document).ParentWindow).focus 
     刚找到了更简单的方法,也许是最简单的: 
     if WebBrowser1.Document <> nil then 
      IHTMLWindow4(WebBrowser1.Document).focus 
     还有,需要判断文档是否获得焦点这样来做: 
     if IHTMLWindow4(WebBrowser1.Document).hasfocus then 
   -----------------------------------------------------------------------------------


   7、点击“提交”按钮 
     如同程序里每个窗体上有一个“缺省”按钮一样,Web页面上的每个Form也有一个“缺省”按钮——即属性为“Submit”的按钮,当用户按下回车键时就相当于鼠标单击了“Submit”。但是TWebBrowser似乎并不响应回车键,并且,即使把包含TWebBrowser的窗体的KeyPreview设为True,在窗体的KeyPress事件里还是不能截获用户向TWebBrowser发出的按键。

我的解决办法是用ApplicatinEvents构件或者自己编写TApplication对象的OnMessage事件,在其中判断消息类型,对键盘消息做出响应。至于点击“提交”按钮,可以通过分析网页源代码的方法来实现,不过我找到了更为简单快捷的方法,有两种,第一种是我自己想出来的,另一种是别人写的代码,这里都提供给大家,以做参考。 
     A、用SendKeys函数向WebBrowser发送回车键 
       在Delphi 
   5光盘上的InfoExtrasSendKeys目录下有一个SndKey32.pas文件,其中包含了两个函数SendKeys和AppActivate,我们可以用SendKeys函数来向WebBrowser发送回车键,我现在用的就是这个方法,使用很简单,在WebBrowser获得焦点的情况下(不要求WebBrowser所包含的文档获得焦点),用一条语句即可: 
      Sendkeys(’~’,true);// press RETURN key 
      SendKeys函数的详细参数说明等,均包含在SndKey32.pas文件中。 
     B、在OnMessage事件中将接受到的键盘消息传递给WebBrowser。 
      procedure TForm1.ApplicationEvents1Message(var Msg: TMsg; 
   var Handled: Boolean); 
      {fixes the malfunction of some keys within webbrowser 
   control} 
      const 
       StdKeys = [VK_TAB, VK_RETURN]; { standard keys } 
       ExtKeys = [VK_DELETE, VK_BACK, VK_LEFT, VK_RIGHT]; { 
   extended keys } 
       fExtended = $01000000; { extended key flag } 
      begin 
       Handled := False; 
       with Msg do 
       if ((Message >= WM_KEYFIRST) and (Message <= WM_KEYLAST)) 
   and 
        ((wParam in StdKeys) or 
        {$IFDEF VER120}(GetKeyState(VK_CONTROL) < 0) or {$ENDIF} 
        (wParam in ExtKeys) and 
        ((lParam and fExtended) = fExtended)) then 
       try 
        if IsChild(Handle, hWnd) then { handles all browser 
   related messages } 
        begin 
         with {$IFDEF 
   VER120}Application_{$ELSE}Application{$ENDIF} as 
           IOleInPlaceActiveObject do 
          Handled := TranslateAccelerator(Msg) = S_OK; 
          if not Handled then 
          begin 
           Handled := True; 
           TranslateMessage(Msg); 
           DispatchMessage(Msg); 
          end; 
          end; 
       except 
       end; 
      end; // MessageHandler 
     (此方法来自EmbeddedWB.pas) 
   -----------------------------------------------------------------------------------
   8、直接从TWebBrowser得到网页源码及Html 
     下面先介绍一种极其简单的得到TWebBrowser正在访问的网页源码的方法。一般方法是利用TWebBrowser控件中的Document对象提供的IPersistStreamInit接口来实现,具体就是:先检查WebBrowser.Document对象是否有效,无效则退出;然后取得IPersistStreamInit接口,接着取得HTML源码的大小,分配全局堆内存块,建立流,再将HTML文本写到流中。程序虽然不算复杂,但是有更简单的方法,所以实现代码不再给出。其实基本上所有IE的功能TWebBrowser都应该有较为简单的方法来实现,获取网页源码也是一样。下面的代码将网页源码显示在Memo1中。 
      Memo1.Lines.Add(IHtmlDocument2(WebBrowser1.Document).Body.OuterHtml); 
     同时,在用TWebBrowser浏览HTML文件的时候要将其保存为文本文件就很简单了,不需要任何的语法解析工具,因为TWebBrowser也完成了,如下: 
      Memo1.Lines.Add(IHtmlDocument2(WebBrowser1.Document).Body.OuterText); 
   -----------------------------------------------------------------------------------
   9、“查找”功能 
     查找对话框可以在文档获得焦点的时候通过按键Ctrl-F来调出,程序中则调用IOleCommandTarget对象的成员函数Exec执行OLECMDID_FIND操作来调用,下面给出的方法是如何在程序中用代码来做出文字选择,即你可以自己设计查找对话框。 
      var 
       Doc: IHtmlDocument2; 
       TxtRange: IHtmlTxtRange; 
      begin 
       Doc :=WebBrowser1.Document as IHtmlDocument2; 
       Doc.SelectAll;    //此处为简写,选择全部文档的方法请参见第三条命令操作 
                   //这句话尤为重要,因为IHtmlTxtRange对象的方法能够操作的前提是 
                   //Document已经有一个文字选择区域。由于接着执行下面的语句,所以不会 
                   //看到文档全选的过程。 
       TxtRange :=Doc.Selection.CreateRange as IHtmlTxtRange; 
       TxtRange.FindText(’Text to be searched’,0.0); 
       TxtRange.Select; 
      end; 
     还有,从Txt.Get_text可以得到当前选中的文字内容,某些时候是有用的。 
   -----------------------------------------------------------------------------------
   10、提取网页中所有链接 
     这个方法来自大富翁论坛hopfield朋友的对一个问题的回答,我本想自己试验,但总是没成功。 
     var 
      doc:IHTMLDocument2; 
      all:IHTMLElementCollection; 
      len,i:integer; 
      item:OleVariant; 
     begin 
      doc:=WebBrowser1 .Document as IHTMLDocument2; 
      all:=doc.Get_links;             //doc.Links亦可 
      len:=all.length; 
      for i:=0 to len-1 do begin 
       item:=all.item(i,varempty);        //EmpryParam亦可 
       memo1.lines.add(item.href); 
      end; 
     end; 
   -----------------------------------------------------------------------------------
   11、设置TWebBrowser的编码 
     为什么我总是错过很多机会?其实早就该想到的,但是一念之差,便即天壤之别。当时我要是肯再多考虑一下,多试验一下,这就不会排到第11条了。下面给出一个函数,搞定,难以想象的简单。 
     procedure SetCharSet(AWebBrowser: TWebBrowser; ACharSet: 
   String); 
     var 
      RefreshLevel: OleVariant; 
     Begin 
      IHTMLDocument2(AWebBrowser.Document).Set_CharSet(ACharSet); 
      RefreshLevel :=7;              //这个7应该从注册表来,帮助有Bug。 
      AWebBrowser.Refresh2(RefreshLevel); 
     End;   

- 阅读全文 -

indy中,IdTcpServer 出现thread terminate timeout 错误的解决办法

引用:

http://delphi.newswhat.com/geoxml/forumhistorythread?groupname=borland.public.delphi.internet.winsock&messageid=3f25c582$1@newsgroups.borland.com

使用indy9.0

在client没有disconnect的时候,设置tcpserver.active:=false 出现"Terminate Thread Timeout."错误。

另外,虽然主窗体已经消失了,但是,还有程序还是在后台运行,没有退出。

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

解决办法:

1.IdTCPServer的事件处理中不要有GUI的操作

2.如果AThread.data赋值的是不规范的结构,即不是TObject类型的,需要在disConnect时,
设置Athread.data:=nil;可以在TcpServer的onDisConnect事件中处理。
如果没有设置为nil,在Active:=false时,就会出现timeout了。
> If you do not have an actual TObject assigned to
> the AThead.Data property, and the property is not nil, then you will get a
> crash during shutdown.

3.在TCPServer.Active:=false时,用try..except包围起来,不然还是会出现timeout的exception,不过已经可以退出程序了。

try
IdTCPServer1.Active := False;
exceptone: Exceptiondo
begin
WritetoLog(0, e.Message);end;end;

4.有时候,还是无法正常退出后台程序,那么遍历client的连接,disconnect掉
大部分都是推荐的connection.disconnect,好像是connection.disconnectSocket比较有效。

/////////////
var
List: TList;
I: Integer;
begin
List := IdTCPServer1.Threads.LockList;
try
StatusBar1.Panels[1].Text := 'Disconnecting...';
Application.ProcessMessages;
for I := 0 to List.Count - 1 do
begin
try
TIdPeerThread(List.Items[I]).Connection.DisconnectSocect;
except
on E: Exception do
begin
TIdPeerThread(List.Items[I]).Stop;
end;
end;
end;
finally
IdTCPServer1.Threads.UnlockList;
end;

////////////

 

Del.icio.us :
Flickr :
Technorati :

- 阅读全文 -

AirPlay, 音乐为我而生

是什么让AirPlay与众不同?
  • 绿色免安装,不含任何恶意软件
  • 使用方便,界面酷炫
  • 自动下载和显示歌词、专辑封面
  • 汇编级优化,占用极少系统资源

官方首页

 界面确实很酷:

专辑图片和信息

image

显示歌词:

image

内存占用:

image

foobar15m,airplay 1m

- 阅读全文 -

vim C/C++ folder配置

"下面是设置自动folder的 而且是根据写C代码设置的 如果你不喜欢使用folder那么可以省略掉
au BufReadPost *.c,*.cpp    syn region myFold start="{" end="}" transparent fold
au BufReadPost *.c,*.cpp    syn sync fromstart
au BufReadPost *.c,*.cpp    set foldmethod=syntax
set foldlevel=0

- 阅读全文 -

用ADO连接MYSQL的连接字符串

How to set up the dbGo (ADO) ConnectionString for mySQL database

From Zarko Gajic,

If your database choice is mySQL and you are using dbGO (ADO) components, over mySQL ODBC 3.51 driver or MySQL Connector/ODBC 5.0, this is how your TADOConnection's ConnectionString property should look:

  • When using "ODBC 3.51 LOCAL database":

    'DRIVER={MySQL ODBC 3.51 Driver}; SERVER=localhost; DATABASE=myDatabase; USER=myUsername; PASSWORD=myPassword;OPTION=3;'

  • When using "ODBC 3.51 REMOTE database":

    'DRIVER={MySQL ODBC 3.51 Driver}; SERVER=data.domain.com; PORT=3306; DATABASE=myDatabase; USER=myUsername; PASSWORD=myPassword; OPTION=3;'

  • When using "ODBC 5.0 LOCAL database":

    'DRIVER={MySQL Connector/ODBC v5}; SERVER=localhost; DATABASE=myDatabase; UID=myUsername; PASSWORD=myPassword;OPTION=3;'

  • When using "ODBC 5.0 REMOTE database":

    'DRIVER={MySQL Connector/ODBC v5}; SERVER=data.domain.com; PORT=3306; DATABASE=myDatabase; UID=myUsername; PASSWORD=myPassword;OPTION=3;'

Note: "data.domain.com" is the name of the data server, "myDatabase" is the name of the database, "myUsername" is the name of the user, "myPassword" is the password for "myUsername"

- 阅读全文 -

delphi中使用ado连接mysql乱码解决

delphi中,使用ado连接mysql,adoconnection的providor 设为:MSDASQL.1,可以先用系统数据源连接,然后在程序里面用连接字符串。

 

查询后,发现,listbox里面显示中文乱码

image

参考http://bbs.mysql.cn/viewthread.php?tid=364&extra=page%3D1%26amp%3Bfilter%3Ddigest

发现,可以执行以下语句

SET NAMES 'CHARSETNAME'

用来设置客户端的字符集,例如:"SET NAMES 'GB2312'",可设置客户端的字符集为gb2312 。(UCS2暂时不可用)

在连接上数据库后,上面的语句以sql语句执行,即可。

执行后,乱码解决

image

- 阅读全文 -

启动到vertifying dmi pool data停止

清楚了cmos后,设了从硬盘启动,但是启动到vertifying dmi pool data后,一直不动。硬盘灯一直闪。

参考:http://tilling-day.blog.sohu.com/42913621.html,后

设置了:

试着重新配置在BIOS中“PNP/PCI configuration”“Reset Configuration Data”也可以使用“Force Update ESCD”之类的BIOS选项设置为“Enabled”,然后存盘退出。如果能够不能启动,再试着设置为“Disabled”试一试。

也把bios write protection disable了,还是不行。

虽然有一点:

.如果主机能够通过软盘启动,但是不能通过硬盘启动,这种情况有可能是硬盘的主引导区的数据被破坏,这时可以通过分区软件来修复主引导区的程序代码,如用软盘启动电脑后,使用FDISK/MBR命令来修复主引导区。也可以到相应硬盘生产商的网站下载特殊的硬盘分析工具程序,来检查自己的硬盘的具体故障。

我一直认为,如果不能从硬盘启动,会有错误报告的,哪知道竟然没有。

后来,改为从cdrom启动,才发觉竟然是可以的,害我白跑了几趟。

硬盘上的启动信息没了,重新安装系统后,OK。

- 阅读全文 -

bios 乱码、自检乱码

症状如下:

1、XP启动时显示某些系统文件丢失,如PCI.SYS之类的.
2、BIOS及自检画面都是乱码
3、用启动盘进入DOS一样是这种乱码。


    但用启动盘进入GHOST或运行其它启动盘中的软件,这些软件就不会乱码。

A:

估计是病毒破坏,拿下主板电池,恢复。

- 阅读全文 -

热门文章

最新文章

最近回复

  • wfzlxvvgte: 你的文章让我感受到了不一样的风景,谢谢分享。 http://ww...
  • sfiutfecff: 你的文章内容非常用心,让人感动。 http://www.55ba...
  • dyqoaaqpdc: 你的文章内容非常卖力,让人点赞。 http://www.55ba...
  • vrfedcneqp: 你的文章内容非常卖力,让人点赞。 http://www.55ba...
  • wmhoubuazg: 你的文章内容非常用心,让人感动。 http://www.55ba...
  • khhhttrppu: 你的文章内容非常卖力,让人点赞。 http://www.55ba...
  • atjzyxiutr: 你的文章内容非常用心,让人感动。 http://www.55ba...
  • dlamzmkwwi: 你的文章内容非常卖力,让人点赞。 http://www.55ba...
  • predcxvhsz: 《蛋黄人》国产剧高清在线免费观看:https://www.jgz...
  • qmffcradmz: 《逃狱者》剧情片高清在线免费观看:https://www.jgz...

分类

标签

其它