2004年11月11日








套接口选项
本文出自:http://sunsland.top263.net 作者: (2001-10-23 09:00:00)





获取和设置影响套接口选项的函数:


getsockopt :获取套接口选项 setsockopt: 获取套接口选项 fcntl: 设置套接口为非阻塞I/O型信号驱动I/O型等 oictl 套接口选项 SO-KEEPALIVE SO-LINGER SE-RCVBUF 和 SO-SNDBUF SO-RCVWAT和 SO-SNDLOWAT SO-RCVTIMEO和 SO-SNDTIMEO SO-REUSEADDR和 SO-REUSPORT IP-TTL TCP-KEEPALIVE



getsockopt 和 setsockopt


获得套接口选项: int getsockopt ( int sockfd, int level, int optname, void * optval, socklen_t *opteln ) 设置套接口选项: int setsockopt ( int sockfd, int level, int optname, const void * optval, socklen_t *opteln ) sockfd(套接字): 指向一个打开的套接口描述字 level:(级别): 指定选项代码的类型。 SOL_SOCKET: 基本套接口 IPPROTO_IP: IPv4套接口 IPPROTO_IPV6: IPv6套接口 IPPROTO_TCP: TCP套接口 optname(选项名): 选项名称 optval(选项值): 是一个指向变量的指针 类型:整形,套接口结构, 其他结构类型:linger{}, timeval{ } optlen(选项长度) :optval 的大小 返回值:标志打开或关闭某个特征的二进制选项



检查套接口选项的程序


输出套接口的选项: 定义感兴趣的套接口选项 调用getsockopt 输出套接口选项 定义联合:不同的套接口选项有不同类型


union val { //套接口选项可能有的5个类型分别作为一个成员: int i_val; long l_val; char c_val[10]; struct linger linger_val; struct timeval timeval_val; //struct {int S; int uS} } val;


//函数原型(prototype),这些函数用于输出套接口选项的值 static char *sock_str_flag(union val *, int); //静态函数,只可在本文件中被调用 static char *sock_str_int(union val *, int); static char *sock_str_linger(union val *, int); static char *sock_str_timeval(union val *, int); //定义结构sock_opts, 其中包含了获得或输出套接口选项的所有信息 struct sock_opts { char *opt_str; //字符名称 int opt_level; //级别 int opt_name; //名称 char *(*opt_val_str)(union val *, int); //函数指针,用于输出, }


//定义结构数组并初始化 struct sock_opts sock_opts[ ] = { //全局变量数组才可以初始化 “SO_BROADCAST”, SOL_SOCKET, SO_BROADCAST, sock_str_flag, “SO_DEBUG”, SOL_SOCKET, SO_DEBUG, sock_str_flag, #ifdef SO_REUSEPORT //编译时用的宏定义 “SO_REUSEPORT”, SOL_SOCKET, SO_REUSEPORT, sock_str_flag, #else //没有这个选项 “SO_REUSEPORT”, 0, 0, NULL, //NULL表示没有定义 #endif “SO_TYPE”, SOL_SOCKET, SO_TYPE, sock_str_int, “IP_TTL”, IPPROTO_IP, IP_TTL, sock_str_int, “TCP_MAXSEG”, IPPROTO_TCP,TCP_MAXSEG, sock_str_int, NULL, { /*结束标志 */} 0, 0, NULL }; 源程序: int main(int argc, char **argv) { int fd, len; struct sock_opts *ptr; //结构类型 fd = Socket(AF_INET, SOCK_STREAM, 0); //获得套接字 for (ptr = sock_opts; ptr->opt_str != NULL; ptr++) { //从第一个选项到最后一个 printf(“%s: ”, ptr->opt_str); //输出字符名 if (ptr->opt_val_str == NULL) //没有定义的情况 printf(“(undefined)\n”); else { len = sizeof(val); //获得套接口选项 if (getsockopt(fd, ptr->opt_level, ptr->opt_name, val, len) == -1) { //返回值为1,函数调用失败 err_ret(“getsockopt error”);} //输出选项的缺省值 else printf(“default = %s\n”, (*ptr->opt_val_str)(&val, len)); } } //End of for loop exit(0); }


static char strres[128]; //静态变量,在函数调用后保留原值 static char * sock_str_flag(union val *ptr, int len) { if (len != sizeof(int)) //长度不相符 snprintf(strres, sizeof(strres), “size (%d) not sizeof(int)”, len); else //转换字符串 snprintf(strres, sizeof(strres),”%s”, (ptr->i_val == 0) ? “off” : “on”); return(strres); }




基本套接口选项


SO_KEEPALIVE


检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入。 设置该选项后,如果2小时内在此套接口的任一方向都没有数据交换,TCP就自动给对方 发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的TCP分节.它会导


致以下三种情况: 对方接收一切正常:以期望的ACK响应。2小时后,TCP将发出另一个探测分节。 对方已崩溃且已重新启动:以RST响应。套接口的待处理错误被置为ECONNRESET,套接 口本身则被关闭。 对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图得到 一个响应。在发出第一个探测分节11分钟15秒后若仍无响应就放弃。套接口的待处理错 误被置为ETIMEOUT,套接口本身则被关闭。如ICMP错误是“host unreachable(主机不 可达)”,说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置为 EHOSTUNREACH。


SO_RCVBUF和SO_SNDBUF


每个套接口都有一个发送缓冲区和一个接收缓冲区。 接收缓冲区被TCP和UDP用来将接收到的数据一直保存到由应用进程来读。 TCP:TCP通告另一端的窗口大小。 TCP套接口接收缓冲区不可能溢出,因为对方不允许发出超过所通告窗口大小的数据。 这就是TCP的流量控制,如果对方无视窗口大小而发出了超过宙口大小的数据,则接 收方TCP将丢弃它。 UDP:当接收到的数据报装不进套接口接收缓冲区时,此数据报就被丢弃。UDP是没有 流量控制的;快的发送者可以很容易地就淹没慢的接收者,导致接收方的UDP丢弃数据报。


SO_LINGER 指定函数CLOSE对面相连接的协议如何操作——当由数据残留在套接口发送缓冲区时的处理 LINGER结构 struct linger { int l_onoff; // 0=off, nonzero=on int l_linger; //linger time in seconds };


SO_RCVLOWAT 和SO_SNDLOWAT


每个套接口都有一个接收低潮限度和一个发送低潮限度。它们是函数selectt使用的, 接收低潮限度是让select返回“可读”而在套接口接收缓冲区中必须有的数据总量。 ——对于一个TCP或UDP套接口,此值缺省为1。发送低潮限度是让select返回“可写” 而在套接口发送缓冲区中必须有的可用空间。对于TCP套接口,此值常缺省为2048。 对于UDP使用低潮限度, 由于其发送缓冲区中可用空间的字节数是从不变化的,只要 UDP套接口发送缓冲区大小大于套接口的低潮限度,这样的UDP套接口就总是可写的。 UDP没有发送缓冲区,只有发送缓冲区的大小。



TCP 套接口选项


TCP_KEEPALIVE


指定TCP开始发送保持存活探测分节前以秒为单位的连接空闲时间。缺省值至少必须


为7200秒,即2小时。此选项仅在SO_KEPALIVEE套接口选项打开时才有效。


TCP_MAXSEG


获取或设置TCP连接的最大分节大小(MSS)。返回值是我们的TCP发送给另一端的最大 数据量,它常常就是由另一端用SYN分节通告的MSS,除非我们的TCP选择使用一个比 对方通告的MSS小些的值。如果此值在套接口连接之前取得,则返回值为未从另·—端 收到Mss选项的情况下所用的缺省值。小于此返回值的信可能真正用在连接上,因为譬 如说使用时间戳选项的话,它在每个分节上占用12字节的TCP选项容量。我们的TcP将 发送的每个分节的最大数据量也可在连接存活期内改变,但前提是TCP要支持路径MTU 发现功能。如果到对方的路径改变了,此值可上下调整。



例程序:


//获得发送缓冲区大小和MSS大小,设置发送缓冲区大小


//获得发送缓冲区大小和MSS大小 #include “unp.h” #include /* for TCP_MAXSEG */ int main(int argc, char **argv) { int sockfd, rcvbuf, mss; socklen_t len; struct sockaddr_in servaddr; if (argc != 2) err_quit(“usage: rcvbuf “); sockfd = Socket(AF_INET, SOCK_STREAM, 0); len = sizeof(rcvbuf); Getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, rcvbuf, len); len = sizeof(mss); Getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len); printf(“defaults: SO_RCVBUF = %d, MSS = %d\n”, rcvbuf, mss); bzero(servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(13); /* daytime server */ Inet_pton(AF_INET, argv[1], servaddr.sin_addr); Connect(sockfd, (SA *) &servaddr, sizeof(servaddr)); len = sizeof(rcvbuf); Getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len); len = sizeof(mss); Getsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len); printf(“after connect: SO_RCVBUF = %d, MSS = %d\n”, rcvbuf, mss); exit(0); }


//设置发送缓冲区大小 #include “unp.h” #include /* for TCP_MAXSEG value */ int main(int argc, char **argv) { int sockfd, mss, sendbuff; socklen_t optlen; float kk; sockfd = Socket(AF_INET, SOCK_STREAM, 0); /* Fetch and print the TCP maximum segment size. */ optlen = sizeof(mss); sendbuff =2048; Setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, sendbuff, sizeof(sendbuff)); optlen = sizeof(sendbuff); Getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, sendbuff, &optlen); printf(“After send buffer size = %d\n”, sendbuff); exit(0); }

2004年10月05日

在VC中使用property关键字

以前从来没有在VC++中使用过,这个关键字。在很多书里面也没有讲过这个关键字。
偶尔在看一个C++ BULIDER一个关于打印端口的程序时,看到其中使用了
_property这样一个关键字。
于是出于本能去MSDN中查找property.在里面看到一些怎么使用该关键字用法:
MSDN中资料:
Microsoft Specific —>

__declspec( property( get=get_func_name ) ) declarator

__declspec( property( put=put_func_name ) ) declarator

__declspec( property( get=get_func_name, put=put_func_name ) ) declarator

This attribute can be applied to non-static “virtual data members” in a class or structure definition. The compiler treats these “virtual data members” as data members by changing their references into function calls.

When the compiler sees a data member declared with this attribute on the right of a member-selection operator (“.” or “->“), it converts the operation to a get or put function, depending on whether such an expression is an l-value or an r-value. In more complicated contexts, such as “+=“, a rewrite is performed by doing both get and put.

This attribute can also be used in the declaration of an empty array in a class or structure definition. For example:

__declspec(property(get=GetX, put=PutX)) int x[];
The above statement indicates that x[] can be used with one or more array indices. In this case, i=p->x[a][b] will be turned into i=p->GetX(a, b), and p->x[a][b] = i will be turned into p->PutX(a, b, i);

//
怎么使用该属性呢?
比如:
一个打印控制类;
打印的最基本的端口有:数据端口(0×378),控制端口(0×37a),状态的端口(0×379),要对那些端口直接读写,除了借助第三方的驱动(如果自己不会写驱动/或者不想写的话,因为在2000/XP中是无法对端口直接读写的)。
于是在一个类中,定义三个缓冲,来存放你读写端口的值(这里利用:port95nt这个免费的第三方的驱动程序可以google下载):
于是类似下面形式
CLpt
{

    //存放寄存器中的内容缓冲
 BYTE m_LPTData;
 BYTE m_LPTControl;
 BYTE m_LPTStatus;
public:
         CLpt();
  ~CLpt();

 //写读数据寄存器
 void write_data_reg(BYTE data)
 { 
        DlPortWritePortUchar(0X378,data);

 }
 BYTE read_data_reg()
      {
          return DlPortReadPortUchar(0X378,data);

 }

       //其他的具体内容类似就不写了。
 //写读控制寄存器
 void write_control_reg(BYTE control);
 BYTE read_control_reg();

 //写读状态寄存器
 void write_status_reg(BYTE status);
 BYTE read_status_reg();
public:

 ////在这里像MSDN中说的一样使用property这个关键子。
        //这样就可以通过data,status,control这写属性,来访问(读/写)端口的内容了。是不是感觉比较方便呢。
 //当用拥护: i = CLpt::Data 它将调用CLpt::read_data_reg();
 //当用户: CLpt::Data = i; 它将调用CLpt::write_data_reg(i);
   __declspec(property(get=read_data_reg, put=write_data_reg)) BYTE Data;
   __declspec(property(get=read_status_reg, put=write_status_reg)) BYTE Status;
   __declspec(property(get=read_control_reg, put=write_control_reg)) BYTE Control;

};

比如 :
CLpt* m_plpt =new CLpt;
往里面写入数据0×56就可以这样:m_plpt->Data =  0×56;
要读出里面的数据就可以这样:BYTE m_bdata =m_lpt->Data; //^_^这样感觉方便多了。


 

2004年09月28日

关于浮点的运算要注意一点:

一日计算进度条的位置.得到的都是0,甚是奇怪,原来,浮点做怪。

类似:
 for(int x= 0; x<128; x++)
{
     int  pos = (100/127*x);
}
那日计算不小心按照了以前学数学的写法,写下了pos = (100/127*x)。-_-
结果当然是pos = 0;-_-

浮点转整型在做怪。由于转整型是,整型数据会截尾。因此。
100/127 如果直接转整型,自然为0,0*X =0 -_-;

修改成:
double db = (dobule)(100/127);
 for(int x= 0; x<128; x++)
{
     int  pos = (db*x);
}

或者是:
 for(int x= 0; x<128; x++)
{
     int  pos = (100*x/127);
}

基本上就没问题了^_^

还有转型截尾的问题:

double a=1.001;
int aaa;
double g=(double)(a*1000);
如果直接将转:
aaa = g; //aaa = 1000
aaa = (int)(g+0.000001); //aaa = 1001;

关于浮点运算的文章:
浅谈浮点型“(1.1+1.2)!=2.3” http://www.cnblogs.com/oldjacky/archive/2004/06/20/14367.aspx

www.google.com  ^_^

 

2004年09月18日

勿忘国耻     奋斗自强

2004年09月01日

一种全屏显示子窗口的方法:
1:基本想法:
  让子窗口脱离父窗口。用SetWindowPos重新设置窗口的大小。
  还原时重新设置子窗口的父窗口。重新设置窗口的大小
 主要代码如下:
//m_hWnd为子窗口的句柄
     static BOOL m_FullScreen = true;
      HWND hwndParentSave;
                //获得整个屏幕的大小
  int cx = ::GetSystemMetrics(SM_CXSCREEN);
  int cy = ::GetSystemMetrics(SM_CYSCREEN);
               
                //获得子窗口的风格
  long style = ::GetWindowLong(m_hWnd,GWL_STYLE);

                //获得子窗口的父窗口
  HWND hwndparent = ::GetParent(m_hWnd);
             
              //判断其父窗口是否存在。
  if(!::IsWindow(hwndparent))
  {
                  //不存在表示已经全屏,重新设置子窗口的风格(主要是加入了WS_CHILD风格
    ::SetWindowLong(m_hWnd,GWL_STYLE,(style | WS_CHILD));
      //重新设置其父窗口(刚才保存的父窗口 
                   ::SetParent(m_hWnd,hwndParentSave);
  }
  else
  {     //父窗口存在,去掉其子窗口的风格
   style &= ~WS_CHILD;
                     //重新设置要脱离父窗口的子窗口的风格
   ::SetWindowLong(m_hWnd, GWL_STYLE, style);
                     //重新设置子窗口的父窗口为NULL(脱离父窗口)
   ::SetParent(m_hWnd,NULL);
                     //把父窗口保存起来
                  hwndParentSave =hwndparent ;
                     把子窗口的大小保存起来
   ::GetWindowRect(m_hWnd,&rc);    
  }
  if(m_FullScreen)
  { 
                     //全屏设置大小 
   ::SetWindowPos(m_hWnd,HWND_TOPMOST,0,0,cx,cy,0);
   m_FullScreen = false; 
  }
  else
  {
         //还原重新设置大小
   ::MoveWindow(m_hWnd,rc.left,rc.top,rc.Width(),rc.Height(),0);
   m_FullScreen = true;
  }

2004年08月24日

 

 

在程序销毁时释放资源,往往比较容忽略在另外一个线程可能还在使用的资源而先行被销毁,导致程序在退出时崩溃.

比如:
你在主线程中分配了一块内存区域:
//假设其中不存在资源竞争的问题.(否则要用同步控制,嫌麻烦,暂时不做这种处理了).
char* pBuf = new char[10];

然后在另外一个后台线程中:
BOOL bEnd = FA.SE//全局或者静态变量.
DWORD WINPAI CMyClass::OnThreadFunc(LPVOID lpvoid)
{
    CMyClass* pTempMyClass = (CMyclass*)lpvoid;
   
    while(!bEnd)
  {
        WaitForSingleObject(m_hWaitEvent,INFINITE);
    if(bEnd == TRUE)
   {
      break;
    }
       //在这里对这个pBuf进行一些操作.
 pTempMyClass->pBuf……..

  }
  return 1;
}
 
假设你需要从别的地方得到数据,然后在开始运行这个线程,在正常情况下,你执行完你所需要的操作后就这样:
bEnd = TRUE;
SetEvent(m_hWaitEvent);
让这个线程自然的结束.

于是你在程序退出销毁时:
可能这样做:
delete [] pBuf;
pBuf = NULL;
if(m_hThread && m_hWaitEvnet)
{
    bEnd = TRUE;
    SetEvent(m_hWaitEvent);
    WaitForSingleObject(m_hThread,1000);
    CloaseHandle(m_hThread);
    m_hThread  = NULL;
    CloseHandle(m_hWaitEvent);
    m_hWaitEvent = NULL;
}
上面这是在正常情况一种处理,方法.还做了当没有得到数据数据时,当程序退出时,你也能正确的让这个线程自然退出.

但是有一种情况:
 当某种时机下:
你得到数据,于是你开始运行这个线程.
当线程刚刚执行完if(bEnd == TRUE)这句的时候,于是你发出退出程序的命令,于是你做上面的销毁动作。而恰好,你运行完
delete [] pBuf,pBuf = NULL;后,于是线程又恰好要执行你在线程对该pBuf的处理,于是问题出现.必然的要出现无效指针的错误。

怎么避免呢?
很简单销毁资源之前,先停止线程就能避免了.
比如:
if(m_hThread && m_hWaitEvnet)
{
    bEnd = TRUE;
    SetEvent(m_hWaitEvent);
    WaitForSingleObject(m_hThread,1000);
    CloaseHandle(m_hThread);
    m_hThread  = NULL;
    CloseHandle(m_hWaitEvent);
    m_hWaitEvent = NULL;
}

delete [] pBuf;
pBuf = NULL;

因此在多线程种要注意的是:当程序退出,销毁资源的时候,一定要最先退出其中跑起来的线程(线程的退出最好是自然的结束,不要强制的结束).

我在工作就碰见过这种情况:
当时在正常的情况:(一般都能得到数据的情况下,跑另外一个线程).线程一般都结束了。今天恰好跑到一种情况,在数据没有得到情况.我退出了程序.
程序老报错.当时没怎么注意,还以为是别的模块有错误。后来测试的时候,我只加载我一个模块测试,发现退出也有错.
一跟踪发现就是上述类似的问题。恰好在某种情况下就出来,而这种情况也许认为不会出现的,往往就出现了。:(

2004年08月16日

屏蔽media player 的右键菜单

采用子类化。替换窗口过程,拦截鼠标右键的消息。

static WNDPROC lpOldProc; //保存以前的窗口过程

WNDPROC  CTestMediaplayDlg::lpOldProc;

static LRESULT CALLBACK NewWindowProc(   /*新的窗口过程*/
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
);

/*替换窗口过程*/
lpOldProc = (WNDPROC)SetWindowLong(m_player.GetSafeHwnd(),GWL_WNDPROC,(long)NewWindowProc);

LRESULT CALLBACK CTestMediaplayDlg::NewWindowProc(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
)
{
        switch(uMsg) //处理右键
 {
 case WM_RBUTTONDOWN:
  TRACE(“TEST\n”);
  return 0;
 case WM_RBUTTONUP:
  TRACE(“TEST2\n”);
  return 0;
 default:
  break;
 }
 return CallWindowProc(lpOldProc,hwnd,uMsg,wParam,lParam); //还原以前的窗口过程

  }

2004年08月07日

上中专时,我们班主任老师长得很丑:一半脸儿白一半脸儿黑。可是,就是这样一位其貌不扬的教书先生,却有一位很漂亮的太太。这让我们大跌眼镜,也让我们对他们夫妻的结合怀有浓厚的兴趣。年终岁末,班里组织一年一度的联欢晚会。在联欢会上,我们就问起老师的恋爱经历,希望他能毫无保留地向我们“坦白”。
班主任一听笑了,说:“我一生下来脸部就有很明显的胎记,而且随着年龄的增长胎记也随之长大,为此,我很伤心,一直对自己缺乏信心,对生活也没有多少热情。惟一能让我欣慰的就是自己的学习成绩还算过得去。就这样,一直到上了大学。大学的生活虽然丰富多彩,但我还是提不起精神。有一天,我的哲学老师找我谈话:“你是怎么回事,哪里还像一个年轻人的样子?”哲学老师说,一个人生得不漂亮可以怨天怨地怨造化弄人,但一个人活得不漂亮,却不可以怨任何人。

  哲学老师的当头棒喝,让他如醍醐灌顶,顿开茅塞。从此以后,他仿佛变了一个人,一扫以往的自卑与忧郁,不但心里充满了阳光,眼角眉梢都洋溢着笑容。除了刻苦学习外,学校所有的活动他都积极参与。几年下来,他不但以优异的成绩令同学们刮目相看,更以自己雄辩的口才,独特的个性,满脸的阳光赢得了“最有魅力的大学生”的称号。而很自然地,他也赢得了一位美丽女生的芳心。

  班主任说:“妻子是我一生的最爱!”这句话获得满堂彩。最后,班主任深情地说:“一直以来,我都很感激我的哲学老师。因为是他告诉我,一个人可以生得不漂亮,但是一定要活得漂亮。无论什么时候,渊博的知识、良好的修养、文明的举止、优雅的谈吐、博大的胸怀,以及一颗充满爱的心灵,一定可以让一个人活得足够漂亮,哪怕你本身长得并不漂亮。”

  活得漂亮,就是活出一种精神、一种品位、一份至真至性的精彩。一个人只要不自弃,相信没有谁可以阻碍你进步。

偶今天在这写文章,发现选择分类的时候,它居然不任以前的分类,老是自己创建一个分类,似乎它不认识分类名称中的”.”,奇怪~~~~~~~~~~~

?

Internet 软件分发单位是“软件包”,它由包含 .inf 文件或软件分发 (.osd) 文件(或两者都包括)
的 CAB 文件 (.cab) 所组成。一个分发单位也可以包含软件组件,如 ActiveX 控件 (.ocx)、.dll
文件、.exe 文件、Java 类文件或小程序。当 Web 页上 OBJECT 元素的 CODEBASE 特性引用包含
?.inf 文件的 CAB 文件时,Internet Explorer 3.0 和更新的版本将自动把 CAB 文件作为软件分发单位。

第一步:创建一个inf文件:
?INF文件是一个文本文件,指定运行控件所需要下载或者呈交的文件(比如DLL或者其它OCX)。
一个INF文件就捆绑了CAB压缩文件所有的必须文件。 缺省情况下,
与现有硬盘中文件版本号相同的文件不被下载。
要了解有关INF文件及其选项(包括如何创建独立于平台的INF文件)的详细情况,
请参考平台软件开发工具包(Platform SDK,)的设置和系统管理服务(Setup and System Management Services)
部分的\Setup API\Overview\INF Files目录下的内容。
下面就以我自己写的一个videoweb.dll为例子。在videoweb.dll中使用到:tdiio.dll,encoder.dll,decoder.dll,tdiio.ini,user.ini,
camer.ini,drivo.ini.
video.inf的例子:
;inf file for videoweb.dll
[version]
;version signature(same fot both NT and Win95) do not remove
signature=”$SCHICAGO$”
AdvancedINF=2.0

[Add.Code]
VideoWeb.dll=VideoWeb.dll
tdiio.dll=tdiio.dll
Decoder.dll=Decoder.dll
encoder.dll=encoder.dll
camer.ini=camer.ini
drvio.ini=drvio.ini
castel.ini=castel.ini
user.ini=user.ini
tdiio.ini=tdiio.ini

;needed DLL
[tdiio.dll]
file-win32-x86=thiscab
FileVersion=2,1,701,1
DestDir=11
RegisterServer=yes

[Decoder.dll]
file-win32-x86=thiscab
FileVersion=
DestDir=11
RegisterServer=yes

[encoder.dll]
file-win32-x86=thiscab
DestDir=11
RegisterServer=yes

;needed ini
[camer.ini]
file-win32-x86=thiscab
DestDir=10

[tdiio.ini]
file-win32-x86=thiscab
DestDir=10

[user.ini]
file-win32-x86=thiscab
DestDir=10

[drvio.ini]
file-win32-x86=thiscab
DestDir=10

[castel.ini]
file-win32-x86=thiscab
DestDir=10
;ActiveX dll
[VideoWeb.dll]
file-win32-x86=thiscab
clsid={4BDA0AA3-2164-44B7-8A88-208A145F51DC}
FileVersion=2, 0, 0, 1
DestDir=11
RegisterServer=yes

;end of INF file

下面解说一下上面的一些关键的东西:
关键字”file-win32-x86″ 指定平台是 x86。
file-win32-x86也可以指向一个url上的dll。比如:file-win32-x86=http://www.castelsz.com/xxx/xxx.dll
FileVersion:版本号(点右键看属性就可以得到,也可以没有)
DestDir:指的是装载目录或者文件的地址: 11 指定为系统目录 WINDOWS/SYSTEM 或者 WINNT/SYSTEM32;
?????? 10 规定为窗口目录、WINDOWS或者WINNT。如果没有指定DestDir(典型情况),则代码从固定的OCCACHE目录装载。
clsid:指的是要安装控件的CLSID
RegisterServer:表示是否需要注册。
当然以上仅仅是在internet上分发软件时一个很简单的inf文件,还有更复杂的inf文件。请看SDK。

第二步:把video.inf, tdiio.dll,encoder.dll,decoder.dll,tdiio.ini,user.ini,
camer.ini,drivo.ini.放在一个文件夹中。
第三步:开始制作cab文件,我是用IExpress工具制作的(可以到wwww.newhua.com去下载pIExpres2工具),其实改工具就是使用了:cabarc这个VC5.0中带一个工具。
???? 1:选择创建新的自解压指令文件。
???? 2:选择仅创建压缩文件(ActiveX安装)
???? 3:点击添加把你要供别人下载的文件添加
???? 4:点击浏览 输入*.cab文件的存放地址(包含文件名)
???? 5:就随便了
好了制作完成。
现在在你的网页中使用该控件,当别人访问你的网页时,如果他的机子上没有你的控件,浏览器就会自动下载安装。因为你的控件可能没有获得签名
因此得在你的浏览器中把浏览器的安全级别降低。在internet 工具中选择安全,选择自定义。找到下载未签名的ActiveX控件。选择其下面的提示或者启用。
在网页中插入:

<OBJECT classid=clsid:4BDA0AA3-2164-44B7-8A88-208A145F51DC
codebase=”video.cab#version=2,0,0,1″
data=data:application/x-oleobject;base64,owraS2Qht0SKiCCKFF9R3AADAAA1SgAAZzQAAA==
id=Video1 style=”HEIGHT: 507px; WIDTH: 718px”></OBJECT>
codebase=”video.cab#version=2,0,0,1″
data=data:application/x-oleobject;base64,owraS2Qht0SKiCCKFF9R3AADAAA1SgAAZzQAAA==
id=Video1 style=”HEIGHT: 507px; WIDTH: 718px”>

该句.
注意:得把*.cab 文件你和的网页放在同一目录下。或者在codebase中指定*.cab的路径。

?