在这里发图片比较费事,本文的PDF版本可点此下载.
OCX开发手记
系 统: |
图形平台 |
子 模 块: |
二次开发应用 |
时 间: |
2009-11-23 |
目 录
0. 项目来源................................................................................................................................ 1
1. OCX........................................................................................................................................ 2
1.1 准备工作.......................................................................................................................... 2
1.2 工程设置.......................................................................................................................... 3
1.3 发布相关.......................................................................................................................... 4
1.4 提高效率.......................................................................................................................... 9
2. 拷屏与显示........................................................................................................................... 12
2.1 拷屏................................................................................................................................ 12
2.2 检验................................................................................................................................ 12
2.3 OCX大小调整................................................................................................................. 14
3. 网络传输............................................................................................................................... 16
3.1 基本元件......................................................................................................................... 16
3.2 通信................................................................................................................................ 16
4. 添加OCX属性..................................................................................................................... 19
5. 部署OCX.............................................................................................................................. 22
5.1 服务端与客户端网页....................................................................................................... 22
6. 测试...................................................................................................................................... 24
公元2009年11月20日星期五,晚上,20:05。
我刚上线不久,一位朋友给我发了个消息,说需要帮忙做个小东西。
具体的需求还是用在电力上,需要将员工计算机屏幕上的内容发送给客户计算机,确实是个小东西。
当然,需求需要更细化一些,大概讨论了10分钟,得到结论:
1) 编程实现一个OCX
2) 该OCX嵌入到员工计算机网页内时,将向指定客户计算机网页内的OCX实时发送桌面
3) 该OCX嵌入到客户计算机网页内时,将接收员工计算机网页内的OCX发送桌面数据并显示,以拉伸方式进行缩放
4) 客户机OCX的显示与否由合作方进行控制,因此,对于我这边的任务是,只要OCX活着,就一直工作就OK了
呵呵,不需要控制,是不是有点太简单了。我信心满满,说,一天时间就足够了。明天开始干活。
据我的经验,这个项目根本不值得一做,为什么要做呢?
原因有二:
1) 一是这个项目(美其名曰是项目,KAO,这个曰字还是用拼音敲出来的)是OCX开发,好久不做OCX了,练练手
2) 二是,毕竟还有点效益,前两天,媳妇说,我们家半年时间用了800吨水(我怀疑是不是水表坏了),又要交物业费、车位费,还正准备进行车检,一查,发现5个违章,罚1000元,扣14分,TNND,又得找个驾照去了,不然得学习了。哦,车检顺便还要交保险,这一算,几大千出去了,做个这个小玩意还有点盈余,嘿嘿
从技术角度来分析一下,需要解决以下几个技术难题(不好意思,实在没什么词好说了,总要好过说技术创新点吧)
1) 拷屏,也就是把屏幕内容抓下来,结果为BMP或MemoryStream形式。这点,网上应该是一大堆的代码,好象用不着写
2) 网络传输。把MemoryStream中的数据通过网络传送到目标计算机上,这点,好象用Socket之类的技术就OK了,哦,好久没用INDY了,这次就用它吧
3) 显示,也就是把接收到的MemoryStream中的内容以图形形式显示出来,呃,好象不难哈
4) OCX新属性设置,在这个项目里,需要设计通信端口、目标IP、启动停止等一些属性,通过TLB就可以了哈
5) OCX部署,好象这一点比较难,要注意的东东多一些
说时迟,那时快,当时这些想法在我的头脑里也就电光火石般地过了一下,我就应承了这件事。
结果如何呢?懒得去想。只是想起以前一些朋友问我如何开发OCX,干脆把这个过程记录下来,不敢说是教材,但应该有所作用。
开始干活。首先得做出一个可以在网页中看得到的OCX。
打开BCB6,看着那熟悉的LOGO,轻轻抚摸它一下,乖,今天争点气哈。
以下的步骤,轻车熟路,驾轻就熟,嘿嘿,没什么好说的。
第一步,当然是新建一个ActiveForm了
再下来,改ActiveX名字为CbwScreen,选中Make Control Licensed与Include Version Information选项,About Box没啥用,也就不选Include About Box选项了。
OK之后,全部保存,改一堆文件名以及工程名,改成下面这个效果就好了。
下一步,设置工程的属性,为打个包准备。也就是在Packages标签页面下不选中Build with runtime packages,在Linker标签下不选中Use dynamic RTL选项,这样编译出来的EXE或DLL才可以单独运行,而不需要这个那个DLL。
为使编译结果与源代码文件不混杂在一起,设置一下各个输出目录。
现在,编译,运行,对前者,BCB当然无条件支持的了,但一运行,它不干了
需要设置一下才能直接运行。
当然,这还需要做一点铺垫,因为OCX要嵌入到网页中,所以得先有个网页。
这个网页需要自己设计吗?先还是免了吧,BCB能为我们做这件事。
选择菜单项ProjectàWeb Deployment Options,按如下设置
其中,10.210.9.2是我本机的IP地址。注意URL地址设置为http://10.210.9.2/DoubleScreen/,其中有个虚拟目录DoubleScreen,稍后设置。
然后Web Deploy进行发布,到目标目录下一看,嘿,一堆文件
为了刚才的URL即http://10.210.9.2/DoubleScreen/起作用,需要设置一下虚拟目录DoubleScreen,在目录DoubleScreen上右键点击,选择“共享和安全”,在Web共享页面中共添加共享。
当然,这点需要有IIS的支持,在此不对其多加说明。
现在进行IIS信息服务窗口中,可以看到DoubleScreen虚拟目录
右键选择其属性,设置其身份验证方法为匿名访问。
现在到资源管理器中,双击打开CbwScreen.htm,网页出来了哈
一切顺利,点击提示项,选择“允许阻止的内容”,IE没办法了,只好再提示一下
还有什么说的,确定Y,这下该出来了吧。
这是有点傻眼了。哪里做得不对呢。网上有一大堆的建议。花了大约15分钟,才想起来可以参考一下我原来做的OCX,一看,哦,需要屏蔽一下LICENSE,在CbwScreenImpl1.h中,屏蔽以下代码
/*
//$$---- activex control license support (stActiveXControlLicensing)
// Licensing support
//
typedef TLicenseString
DECLARE_CLASSFACTORY2(TLicenseClassImpl)
// Add logic to determine whether this Control is properly licensed on this machine
// in the following method..
//
static const WCHAR* GetLicenseString()
{
return L"{9DEB874C-56CF-4A4E-B247-CEB9152488DC}";
}
static const TCHAR* GetLicenseFileName()
{
return _T("DoubleScreen.lic");
}
static BOOL IsLicenseValid()
{
// By default we validate the license by verifying that the
// license string GUID is in the .LIC file generated by the Wizard.
//
// You may replace the logic of this routine to implement another
// method to verify that your control is properly licensed.
//
return TValidateLicense::IsGUIDInFile(GetLicenseString(), GetLicenseFileName());
}
*/
再次发布,打开CbwScreen.htm,OK了
哦,每次都要发布再打开文件看,多麻烦,设置一下吧,提高效率。
在RunàParameters调出对话框中,设置如下
现在在窗口中加一个按钮,直接按F9,嘿嘿,又出来了。
这下,工作应该完成1/3了,OCX已经能显示了。哦,等等,还需要有个什么安全性的处理,呃…,参考以前OCX的代码,哦,IObjectSafety接口,加入几句代码就OK了。也就是CbwScreenImpl1.h中要修改成以下模样,怎么修改呢,自己琢磨哈。
class ATL_NO_VTABLE TCbwScreenImpl:
VCLCONTROL_IMPL(TCbwScreenImpl, CbwScreen, TCbwScreen, ICbwScreen, DIID_ICbwScreenEvents)
,public IObjectSafetyImpl
。。。。。。。。。
BEGIN_CATEGORY_MAP(TCbwScreenImpl)
IMPLEMENTED_CATEGORY(CATID_SafeForScripting)
IMPLEMENTED_CATEGORY(CATID_SafeForInitializing)
END_CATEGORY_MAP()
BEGIN_COM_MAP(TCbwScreenImpl)
VCL_CONTROL_COM_INTERFACE_ENTRIES(ICbwScreen)
COM_INTERFACE_ENTRY(IObjectSafety)
END_COM_MAP()
时间嘀嗒嘀嗒不停转,一抬头,已经8:30了。过去了半个小时,唉,要是不上网转那10多分钟就好了。起来转会吧。
拷屏,也就是将屏幕内容拷贝到一个MemoryStream中,这个,呃,还是上网搜一下吧,确实一大堆代码。
整理一下,核心代码如下:
if(HDC hdcScreen = GetWindowDC(GetDesktopWindow()))
{
int w = Screen->Monitors[0]->Width;
int h = Screen->Monitors[0]->Height;
BitBlt(MoniterBitmap->Canvas->Handle, 0, 0, w, h, hdcScreen, 0, 0, SRCCOPY);
ReleaseDC(GetDesktopWindow(), hdcScreen);
BmpStream->Clear();
MoniterBitmap->SaveToStream(BmpStream);
}
呵呵,上面代码要运行,需要声明一些辅助变量,主要是MoniterBitmap和BmpStream,这点工作应该能搞定吧。
效果如何呢?需要检验一下,用什么来检验呢?图形化效果那是最好了,干脆,自己先显示一下,看看对不对,放置一个Image1,alClient,拉伸效果,然后代码为
if(HDC hdcScreen = GetWindowDC(GetDesktopWindow()))
{
int w = Screen->Monitors[0]->Width;
int h = Screen->Monitors[0]->Height;
BitBlt(MoniterBitmap->Canvas->Handle, 0, 0, w, h, hdcScreen, 0, 0, SRCCOPY);
ReleaseDC(GetDesktopWindow(), hdcScreen);
BmpStream->Clear();
MoniterBitmap->SaveToStream(BmpStream);
Image1->Picture->Bitmap->LoadFromStream(BmpStream);
}
运行后,居然没变化!!什么问题,调试看看BmpStream的Size,啊?居然每次的值不一样!!
想来想去,不得其解。
难道拷屏方法有误?翻来覆去,辗转反侧,没发现什么问题。
查看一下TMemoryStream的帮助,随手点开其属性,简单至极
Derived from TCustomMemoryStream
Memory
Derived from TStream
Position
Size
猛然想起,其Position是位置,LoadFromStream是从当前位置开始读的,是不是需要手动从头开始呢。先不管了,强制置其位置为0试试:
MoniterBitmap->SaveToStream(BmpStream);
BmpStream->Position = 0;
Image1->Picture->Bitmap->LoadFromStream(BmpStream);
F9运行一下,啊哈,期望结果出来了
好象有点美中不足哈,窗口太小了,这点可在网页中修改,现在的网页源文本内容为:
C++Builder 6 ActiveX Test Page
You should see your C++Builder 6 forms or controls embedded in the form below.
classid="clsid:0680890D-4D0A-45FB-BF44-3AF41DC04165"
codebase="http://10.210.9.2/DoubleScreen/CbwScreen.inf"
width=350
height=250
align=center
hspace=0
vspace=0
>
把width与height改成100%就OK了,即
C++Builder 6 ActiveX Test Page
You should see your C++Builder 6 forms or controls embedded in the form below.
classid="clsid:0680890D-4D0A-45FB-BF44-3AF41DC04165"
codebase="http://10.210.9.2/DoubleScreen/CbwScreen.inf"
width=100%
height=100%
align=center
hspace=0
vspace=0
>
再次运行,这下满意了
对于网络传输的需求是早就想好了的,直接用INDY。
在员工机端,放置一个TIdTCPClient
object IdTCPClient: TIdTCPClient
Host = '10.210.9.1'
Port = 6001
Left = 424
Top = 232
end
再用一个TIdAntiFreeze增加其健壮性
object IdAntiFreeze1: TIdAntiFreeze
Left = 464
Top = 232
end
而在客户端,也就是目标计算机上,放置一个TIdTCPServer
object IdTCPServer: TIdTCPServer
Active = True
Bindings = <10.210.9.2:6001>
DefaultPort = 6001
OnExecute = IdTCPServerExecute
Left = 40
Top = 152
end
为了在同一个OCX中支持发送与接收端,因此可加入两个基本FORM,分别为处理员工端与客户端。
先固定端口为6001吧,其修改留待后续开放属性时再处理
同样,目标计算机的IP地址即Host也先固定为10.210.9.1,开放属性后由网页的JS进行修改为宜。
在实现通信之前,我想是否需要先实现一个文本方式通信呢?这时,头脑里出现正反双方两个声音:
“需要吗?”
“不需要吗?”
“需要吗?”
“不需要吗?”
“需要吗?”
“不需要吗?”
“呃,研究研究嘛,何必那么认真呢?需要吗?”
嗨,哪跟哪呀。就俺目前这水平,还需要这点小玩意来验证吗?不做也该知道做出来的结果是什么样子的了。直接上图片,不然不足以显示出水平,上!
自己YY得热血沸腾,顺手就写了个发送端:
AnsiString errorPrompt = "";
try
{ // 各种容错处理
IdTCPClient->Connect();
try
{
int length = BmpStream->Size;
AnsiString protocol = bmpProtocol; // 为避免错误,再加一层握手协议
IdTCPClient->WriteLn(protocol); // 以流的方式进行发送,将减小出错的概率
IdTCPClient->WriteInteger(length);
IdTCPClient->WriteStream(BmpStream, true, false);
IdTCPClient->Disconnect();
}
catch(Exception& ex)
{
IdTCPClient->Disconnect();
if(!SameText(ex.Message, "Connection Closed Gracefully."))
{
errorPrompt.printf("通信出错 ==> 向目的服务器 '%s' 发送数据失败!/n/n网络信息:/n", cPromptHost.c_str());
errorPrompt += ex.Message;
throw Exception(errorPrompt);
}
}
}
catch(Exception& ex)
{
errorPrompt.printf("网络连接出错,无法发送数据,请检查!/
/r/n/r/n%s/
/r/n/r/n可能出现的情况:/
/r/n/r/n/t1. %s的地址或端口设置出错;/
/r/n/r/n/t2. %s程序没有运行;/
/r/n/r/n/t3. 网络不通 /
/n/n网络信息:/n",
(cPromptHost.Length() ?
(AnsiString("目的服务器名: ") + cPromptHost).c_str() :
(AnsiString("目的主机地址: ") + IdTCPClient->Host).c_str()),
(cPromptHost.Length() ?
cPromptHost.c_str() : "目的主机"),
(cPromptHost.Length() ?
cPromptHost.c_str() : "服务器端")
);
errorPrompt += ex.Message;
throw Exception(errorPrompt);
}
手随心动,看着手指头在键盘上飞舞,编程和打字一样惬意,心里那个爽啊。一边敲一边想,幸亏咱经验多,还知道try…catch来捕捉异常哈。又想,这个小项目,收人家的费用是不是有点多了呢。猛地,一首歌调浮现了出来:您看这道菜,群英荟萃…,收您老八十一点都不多。呵呵,摇头兼晃脑,正想着间,代码写完了,按个Ctrl+F9编译一下,看看有没有手误敲错的。糊了。
这不,打铁要趁热,俺手一抖,服务端代码就跃然于屏幕之上了。
void __fastcall TCbwScreen::IdTCPServerExecute(
TIdPeerThread *AThread)
{
AnsiString protocol = AThread->Connection->ReadLn(EOL);
bool bmpFlag = SameText(protocol, bmpProtocol);
if(!bmpFlag) return;
// 以流的方式读取
int number = AThread->Connection->ReadInteger();
AThread->Connection->ReadStream(MemoryStream, number, false);
if(MemoryStream->Size == 0)
return;
// 获取通信内容
try
{
EnterCriticalSection(&g_CriticalSection); // 串行处理
MemoryStream->Position = 0;
Image1->Picture->Bitmap->LoadFromStream(MemoryStream);
}
__finally
{
LeaveCriticalSection(&g_CriticalSection); // 可以处理下一个请求了
}
我的思绪飞呀飞,歌曲调调已经是笑脸中的有情人千里共婵娟了。顺便把CriticalSection处理完毕。
运行,OK,啊啊,太不把人当腕了。
剩下的事情是不是该扫尾了,发布一下OCX属性,供网页中的JS调用以方便设置,要不然,部署到不同的计算机上还要改一下源代码,可羞死人了。
在BCB中,添加OCX属性是如此的简单,当然首先需要知道要添加什么属性,想干什么?这个很简单,一个属性Client,表示是否为客户机,一个属性为Host,表示客户机的IP地址,一个属性为Monitor,表示启动/停止发送桌面。
呃,你说,上面各属性的类型都是些什么呢,用脚趾头想,使劲想。
添加的结果如上图所示。
其实现代码没啥好说的,直接看吧。
STDMETHODIMP TCbwScreenImpl::set_Monitor(VARIANT_BOOL Value)
{
try
{
bool v = Value;
GlobalCbwScreen->Timer1->Enabled = v;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_ICbwScreen);
}
return S_OK;
};
STDMETHODIMP TCbwScreenImpl::set_Host(BSTR Value)
{
try
{
GlobalCbwScreen->IdTCPClient->Host = Value;
GlobalCbwScreen->Label1->Caption = Format("机型:%s/r/n发送目标IP: %s, 端口:%d/r/n本机监听端口:%d, 激活态:%s",
ARRAYOFCONST(((ClientFlag ? "客户机" : "员工机"),
GlobalCbwScreen->IdTCPClient->Host.c_str(),
GlobalCbwScreen->IdTCPClient->Port,
GlobalCbwScreen->IdTCPServer->DefaultPort,
GlobalCbwScreen->IdTCPServer->Active ? "true" : "false")));
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_ICbwScreen);
}
return S_OK;
};
STDMETHODIMP TCbwScreenImpl::set_Client(VARIANT_BOOL Value)
{
try
{
ClientFlag = Value;
int sendPort = 6001;
int recievePort = 6010;
if(ClientFlag)
{ // 客户机
int temp = sendPort;
sendPort = recievePort;
recievePort = temp;
}
GlobalCbwScreen->IdTCPClient->Port = sendPort;
GlobalCbwScreen->IdTCPServer->DefaultPort = recievePort;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_ICbwScreen);
}
return S_OK;
};
为了完成项目需求,还需要设计一个服务端网页(部署在员工机上)和一个客户端网页(部署在客户机上)。服务端负责向客户端发送桌面。
OCX嵌入网页嘛,具体可参照BCB替我们生成的标准网页。
下面是员工机的网页ServerScreen.htm
classid="clsid:0680890D-4D0A-45FB-BF44-3AF41DC04165"
codebase="DoubleScreen/CbwScreen.inf"
id="doublescreen"
width=0
height=0
align=center
hspace=0
vspace=0
>
>
function Init()
{// 设置图形控件访问的WebService地址,其最重要的是要对图形控件设置其访问的WebService地址
doublescreen.Client=0; // 1为客户机,0为员工机
doublescreen.Host="10.210.9.2"; // 客户机IP地址
}
发送">
其中,OCX的宽度高度设置为0,表示该OCX不可见。嗯,笨就一个字,我只说一次。
直接在网页onload事件中调用Init()初始化函数,主要是设置OCX工作在员工机模式(Client=0),目标客户机IP地址为10.210.9.2(本机,呵呵,测试一下)。发送按钮将触发发送事件。
客户机网页ClientScreen.htm如下:
classid="clsid:0680890D-4D0A-45FB-BF44-3AF41DC04165"
codebase=" DoubleScreen/CbwScreen.inf"
id="doublescreen"
width=100%
height=100%
align=center
hspace=0
vspace=0
>
>
function SetClient()
{ // 设置图形控件访问的WebService地址,其最重要的是要对图形控件设置其访问的WebService地址
doublescreen.Client=1; // 1为客户机,0为员工机
}
万事俱备了,想直接拷贝给朋友,作一了断,转念一想,还是简单测试一下吧。
换台计算机,环境配置完毕,测试。
没想到啊没想到,真傻眼了,那个小小的红叉叉它硬是要出来。
嗨,这不是还没有配置新计算机的IE吗。立马设置一下。
首先,IE的菜单 工具àInternet选项,设置Internet的安全级别,简单点,直接设置为低安全级吧。啊?人倒霉了,喝凉水都塞牙,那个该死的Windows不让我设置更低的级别,气得我连图都懒得贴出来了。
内事不决问百度,外事不决找GOOGLE,这是微软的事,那就G吧。
找了一大圈,找到了解决方法:修改注册表,额的神啊。
不过改注册表这种行为,俺也不是一次两次的了,进入HKEY_CURRENT_USER、SOFTWARE、MICROSOFT、WINDOWS、CURRENTVERSION、INTERNET SETTINGS、ZONES、3(迷糊了吗?没迷糊我再讲一遍哈),修改CurrentLevel值为10000,确定。
嘿嘿,可以设置为低安全级了。脸上都笑开了花。
刷新一下,笑容就僵在脸上了,可要了亲命了,还是不行。
重…重…重来
这次老子也不信安全级别了,直接进入自定义级别,呃,要是你不清楚怎么整出个自定义级别的话,说明你是新来的,还是直接从这段才开始看的。
瞪圆了双眼,愣是把Active控件和插件下的所有选项一个一个地、挨个地、逐个地设置为启用了。再次打开网页,OMG,外甥打灯笼了。
GOOGLE,你这次再不好好表现一下,以后你就给我爬一边去。
GOOGLE还是好啊,它也不生气,告诉了我另外一个方式:Internet选项中还有个高级标签呢,在安全下面的复选框中,有一项,它名叫,哦,专业一点,应该是标题是“允许运行或安装软件,即使签名无效”。我一看,哟,果真呢,我这项没有选,选上,赶紧的。
办法都想尽了,这下该好了吧。想咱没有功劳也有苦劳,没有苦劳也有疲劳。唉,又是一个杨白劳。
GOOGLE又说,可以设置一下本地Intranet的安全级别,呜呜呜呜…,结果呢,地球人都知道了。
GOOGLE还说,应该加入受信任的站点,我也加了的呀。
没办法了,再G一下吧,GOOGLE也没啥新意了,东说西说,都是上面的这些解决方法,唉,中文的博客、文章之类的,抄来抄去的,再看个开头就知道结尾,不看也罢。
突然,眼前一亮,发现一个新方法,说是系统属性中有个硬件,通过“驱动程序签名”按钮进行签名选项设置,应选择为“忽略 – 安装软件,不用征求我的同意”。哦,我什么时候说过需要我的同意了,你赶紧出来吧。
我想,我当时的眼睛可能都亮得发出绿光了,但过了5秒钟,正常了。
我都有点想撞墙了,换换脑子吧。前两天听一个小朋友成天给我吹风说现在蜗居很热很真实,呃,要不看上一集?先上网看看评论,嗬,那是人山人海,锣鼓喧天,相当的热闹。有两个标题党,一个说是史上最淫荡台词的电视剧,附了一大堆的截图,另一个说是让80后有当二奶的冲动。啊?这哪跟哪呀?看一集吧。
结果看了三集,中间吃了中午饭。我看时间不早了,肠子都悔青了。今天咋给朋友交待呢。
一不做,二不休,再上网溜溜,一会儿就跑题了,看到了北大两名学生关机山事件的各种版本,笑死俺了。在此借用一下易中天版本哈。
上一讲我们说到了OCX控件在IE中无法显示的问题。为什么无法显示呢?史书上没有记载,但我们可以从一些野史上找到一些蛛丝马迹。根据我们的分析,这其中的原因可能有三个,一是IE安全级别设置得不对,导致OCX无法下载;二是OCX的包没打全;三是OCX行了一些苟且之事,编得有问题。对于第一个原因,基本可以排除,因为我们已经系统地试验过了。第二个原因倒是有可能,因为在开发的计算机上可以运行,换台机子就不行了。让我们参考一下《三国志》OCX传裴松注中的说法,“橘生南国为橘,北国为桔”,换台机子后它水土不服,这只是表象,究其根本原因在于自己的毛还没有长全,打包的时候少打了些东东。但仔细一想,不对呀,已经全部设置清楚了呀,该干的事都做了,偷鸡摸狗的事一件没做呀,因此,这第二个原因也可以排除。那现在只剩下第三个原因,也就是说,OCX行了一些苟且之事,编得有问题。
哦,易中天教授,您太伟大了,按照您的思路,我一下子就找到了问题的根源。我知道该怎么做了。
首先,我就只做一个空白的OCX,经过接口处理,发布,部署,拷贝到新的计算机上,一看,哟嗬,你猜怎么着,小红叉叉不见了,OCX控件出来了。呃,有戏。
然后,我就逐个模块往上添加,添加一个,发布一版,只见那文件版本,从1.0.1.0噌噌上升到了1.0.52.0,这中间,我就象个机器人,修改、编译、发布、插U盘、拷贝文件、拔U盘、拷贝文件、修改版本号、拔U盘,一系列工作,我把它流程化了,幸亏我的椅子带有滚轮,不然,我能跑个5公里。
你又猜怎么着,嘿,我找到问题所在了,是IdTCPServer出错了,我反复试了下,把原来的IdTCPServer复制粘贴到新的OCX中就不行,小红叉叉如约守候在网页中,不要它就OK。
呃,不要可就不行了,网络传输还要靠它呢,怎么办呢?
仔细看了看IdTCPServer
object IdTCPServer: TIdTCPServer
Active = True
Bindings = <10.210.9.2:6001>
DefaultPort = 6001
OnExecute = IdTCPServerExecute
Left = 40
Top = 152
end
哦,把它的Active改为false,并将Bindings清空试试。
天可怜见,一切正常了。
原来,在本机上,Bindings绑定了本机IP与端口,所以初始化时,置其Active为true,它能正确处理。但换到另一台计算机上,它不在本局域网内,10.210.9.2这个IP地址它找不到,所以IdTCPServer为true时将会失败,可能抛出异常,导致OCX不能正常初始化,结果失败。将其Active属性改为false并将Bindings清空后,它的初始化过程不会出错,一切正常,OCX就OK了。
我恍然大悟。
我想我快是什么教徒了:“主啊,原谅我的无知吧,我再也不妄自菲薄了。”
再一看时间,都下午5点了,赶紧的,把一切弄得看起来专业一点,把版本定为1.1.1.0,打了个包,发给了朋友。呃,不好意思,顺便再把账号也发了一遍。
朋友在下班前测试了一下,一切正常,说,明天就去给客户演示。我说,好,你去吧。
本来想总结一下的,结果一想,值得总结吗?至少在一段时间内,我需要谦虚了,谨慎了,低调了,这是今天的最大收获。你呢?