#大鱼吃小鱼# 请教做过service Application的“大鱼”!(200分)

  • 主题发起人 shadow_x
  • 开始时间
S

shadow_x

Unregistered / Unconfirmed
GUEST, unregistred user!
“小鱼”我最近在做后台服务。很不称心! :(
1,我第一步把要在服务做的事情,用应用程序先做了出来(完全成功)
2,移植到服务里。这下子出了大乱子。具体表现是:
1>所用引用的DLL用不得(用...try ...except...可以发现DLL的对象异常)
2>好想ADO也出现连接异常!
3> 用
try
...
except
On E:Exceptiondo
ShowMessage(E.Message);
...
报出错误:“标记没有引用存储”
嘿嘿,我还没有碰见过中文的报错信息。好奇怪!!!!
========================================
申明
1。编译时没有错误。
2。我在“开始->运行。。。”里安装了编译生成的EXE文件。project1.exe -install
3。我也在“开始->程序->管理工具->服务” 里找到了相关的服务项!可以启动
4。环境:Delphi 6.0,Windows 2000 server family
5。我用了好多showmessage。“开始->程序->管理工具->服务”启动后弹出message(很蠢调试方法喔)


 
不知道你的project1.exe是不是有COM模块,最好先独立运行调试看看。
 
TO:SXWH.
用应用程序先做了出来(完全成功)!!!!!
 
根据我的经验,许多DELPHI的控件在写SERVICE时不好用,最好在里面尽量直接调用WINDOWS API,此外调用DLL时会有一个启动顺序的问题,最好加上一个依赖的服务,延迟启动时间。
 
:(
那我是不是等着死?
555~~~~555555555555555~~~~~~~``
 
"标记没有引用存储"是中文WINDOWS的系统提示,你最好把Try了什么写出来
 
我最近正好做了一个这样的程序!也是service中调用ADO没有错误!
你不ADO的状态在初始连接状态就设为true;试试!
 
1。我的ADO当然是设置为不连接的。
2。为什么我调用安装到delphi的DLL也用不了?
3。ADO和DLL都是报同样的错误!
[red]报出错误:“标记没有引用存储”[/red]
4。这个服务一定没有依赖的服务, 我测试时所用的流程和应用程序的流程一样
===================================
请DFW的高手们抽空看看。 如果要看代码我可以贴出来的!!!!

 
请问你的开发平台(WINDOWS)系统和现有的系统平台是不是不同,
我的经验是WINDOWS 2000 server family 经常出问题,对DELPHI
有那种不兼容的问题,我建议改用PROFESSIONAL 版试一试!
 
我是在同一台机器上测试的。。。

:(
到底有人做过后台服务吗?
等待回答。。。。。。。。。。。。。。。。。。。。。。。
 
“标记没有引用存储”的另一个意思就是有名的“未调用Coinitialize”,因此你只需要
在创建ADO连接的线程中调用Coinitialize和Couninitialize即可。
 
终于有人肯回答啦,高兴!!!!
*_*

TO:Huzzz
1。作应用程序时没有这种错误啊!为什么做成服务后有这种错误啊?
2。[red]Coinitialize[/red]什么地方调用?
===============================
我是“小鱼”

 
报中文的错误信息一般都是与COM相关的。我没有遇到过你的那个问题,你用ADO连接的是Oracle
数据库吗?如果是的话,建议换为BDE,ADO在COM+中连接Oracle会有问题,而在一般的C/S程序
中则很正常。
“安装到Delphi的DLL用不了”?这是什么意思?这个DLL是个普通的DLL还是一个COM组件的
DLL,你是怎么用的?
 
1.我连接的不是Oracle,我连接的数据库是SQL server 2000
2.我用的DLL是项目组的同事用VB编写的。
应该是些普通的DLL,
它们主要封装了读写注册表和操作FTP的功能!
3.我就是纳闷:为什么它们转到服务里就有错呢?而且ADO和DLL对象的错误还一样!
===============
哎,郁闷。。。。。。。。。
 
这个帖子 我怎么看不了?????????今天都欺负人!
 
TO:D_Q
兄弟,怎么啦?
 
服务程序与普通程序是有区别的:
1。引用顺序不一样,服务程序一般会最先引用SvcMgr,当然一般不会引起问题
2。服务程序一般会自动创建一个服务线程(另外还有一个启动线程),一般也不会引起问题
3。窗体的消息循环仍在主线程中处理,但服务的消息则在服务线程中处理
4。如果你在主线程中使用了COM对象,则主线程应该调用CoInitialize(一般来说,主线程在
初始化时会自动调用)
5。如果你在服务线程中使用了COM对象(在OnStart, OnStop, OnExecute中执行COM代码),
则服务线程应该调用CoInitialize(可以在ONSTART时调用)
6。如果你的MIDAS服务是多线程多实例的,则每个实例都会创建一个线程,因此,如果远程数
据模块的代码(如DATASET.ONOPEN事件代码)有使用COM,则也应该调用CoInitialize
7。DLL也是一样,如果你调用的DLL中有使用COM,则调用的线程应该调用CoInitialize
总之,无论哪个线程使用了COM接口,则这个线程需要调用CoInitialize(要与CoUninitialize
配对使用,可多次调用,但一定要配对)。
如果你想证明一下主线程是否调用CoInitialize,只需要在工程中把Application.Initialize
这一句删除或注释掉,然后再放一个ADOCONNECTION在主FORM上,就可以运行出错了。你的问题
正是因为执行代码可能不在主线程中了。
至于为什么有时提示“尚未调用 CoInitialize”,有时又是“标记没有引用存储”,这就是另
一个问题了,你可以不理。
 
看来问题复杂啦!

干脆等会儿我把我的代码贴出来。(不是很多,只有344行,包括注释)
结构不是很复杂,就是通过timer的时间调用处理的逻辑。
没有用到线程
=================
谢谢“大鱼”们的捧场,问题over后,狂放分(不过只有400[:D])
 
to:shadow_x
Huzzz 老兄讲的已经很明白(应该多给分)。
你的问题至少涉及到两个方面:com的线程模型和winnt/win2K的service。
com的线程模型保证了多线程调用com的数据安全,又使多线程使用com变得简单。我们
通常采用Apartment线程模型,即:同时只能有一个线程访问com数据。所以需要在
线程中调用Coinitialize和Couninitialize保证线程公寓的建立。(另外,“标记没有引用存储”
就是“未调用Coinitialize”的意思,与你的系统环境有关,这是Microsoft的问题,与
borland无关。
至于windows的服务,应该先了解win32内核对象,(如Process和thread)。Win32
的Process只是一个载体,CPU调度的单位是线程,通过宿主进程的优先级和线程优先级
分配cpu时间片(这就是“抢占式多任务”),Service类进程与Window类进程的区别就在于
Window类进程的主线程需要处理窗口消息,Service不用。当然交互式服务至少又两个线程。
说白了Service就是一个工作线程。当然主线程处理窗口消息不需要Coinitialize。
请看delphi Service类Application.run的源代码与Form类Application.run的不同:
注:Delphi自己编写的scktsrvr是最经典的Service 例子(源代码在source/vcl)
procedure TServiceApplication.Run;
function FindSwitch(const Switch: string): Boolean;
begin
Result := FindCmdLineSwitch(Switch, ['-', '/'], True);
end;

var
ServiceStartTable: TServiceTableEntryArray;
ServiceCount, i, J: Integer;
StartThread: TServiceStartThread;
begin
AddExitProc(DoneServiceApplication);
if FindSwitch('INSTALL') then
RegisterServices(True, FindSwitch('SILENT')) else
if FindSwitch('UNINSTALL') then
RegisterServices(False, FindSwitch('SILENT')) else
begin
Forms.Application.OnException := OnExceptionHandler;
ServiceCount := 0;
for i := 0 to ComponentCount - 1do
if Components is TService then
Inc(ServiceCount);
SetLength(ServiceStartTable, ServiceCount + 1);
FillChar(ServiceStartTable[0], SizeOf(TServiceTableEntry) * (ServiceCount + 1), 0);
J := 0;
for i := 0 to ComponentCount - 1do
if Components is TService then
begin
ServiceStartTable[J].lpServiceName := PChar(Components.Name);
ServiceStartTable[J].lpServiceProc := @ServiceMain;
Inc(J);
end;
StartThread := TServiceStartThread.Create(ServiceStartTable);
try
while not Forms.Application.Terminateddo
Forms.Application.HandleMessage;
Forms.Application.Terminate;
if StartThread.ReturnValue <> 0 then
FEventLogger.LogMessage(SysErrorMessage(GetLastError));
finally
StartThread.Free;
end;
end;
end;

procedure TApplication.Run;
begin
FRunning := True;
try
AddExitProc(DoneApplication);
if FMainForm <> nil then
begin
case CmdShow of
SW_SHOWMINNOACTIVE: FMainForm.FWindowState := wsMinimized;
SW_SHOWMAXIMIZED: MainForm.WindowState := wsMaximized;
end;
if FShowMainForm then
if FMainForm.FWindowState = wsMinimized then
Minimize else
FMainForm.Visible := True;
repeat
HandleMessage
until Terminated;
end;
finally
FRunning := False;
end;
end;

 
//这是全部的代码,这个服务的主要目的是:
每天零时执行一次删除数据库记录和FTP目录下对应的物理文件
每小时(timer设置的是一小时)判断当前时间是不是零时。
1。EXE()是我直接从应用程序里搬出来的。
2。错误发生在[red]红色[/red]标记块(在应用程序中正确)

unit DeleteService;
interface
uses
Windows, Messages, SysUtils, Classes, Controls, SvcMgr,Psock, DB, ADODB,Dialogs,
ExtCtrls,OleServer, YWQftpclass_TLB, Parameter_TLB;//两个DLL
type
Tmain = class(TService)
ADOConnection1: TADOConnection;
Timer1: TTimer;
cFtpApp1: TcFtpApp;
RegDataConn1: TRegDataConn;
ADOQuery1: TADOQuery;
procedure Timer1Timer(Sender: TObject);
procedure ServiceContinue(Sender: TService;
var Continued: Boolean);
procedure ServicePause(Sender: TService;
var Paused: Boolean);
procedure ServiceStart(Sender: TService;
var Started: Boolean);
procedure ServiceStop(Sender: TService;
var Stopped: Boolean);
procedure ServiceExecute(Sender: TService);
private
{ Private declarations }
//=======公共自定义部分
function GetFileSize(filename: String): Integer;
//取得文件大小
function WriteLogFile(StrLog: String): Boolean;
//把字符串写入日志文件中
//=======资源库
procedure ResourceLib();
procedure DeleteRecordes(ID:string);
procedure DeleteResourceFile(ComplexFlag,ResourcePath:widestring);

public
function GetServiceController: TServiceController;
override;
procedure Exe();
//执行流程
{ Public declarations }
end;

var
main: Tmain;

implementation
{$R *.DFM}
procedure ServiceController(CtrlCode: DWord);
stdcall;
begin
main.Controller(CtrlCode);
end;

function Tmain.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;

procedure Tmain.Exe();//执行流程
begin
ResourceLib();
//showmessage('ResourceLib');
end;

//------------------------------------------------------------------------------
procedure Tmain.Timer1Timer(Sender: TObject);
var
Present: TDateTime;
Hour, Min, Sec, MSec: Word;
begin
Present:= Now;
DecodeTime(Present, Hour, Min, Sec, MSec);
//showmessage('start!');
WriteLogFile(DateTimeToStr(Now)+':执行开始');
//showmessage('执行开始')
if (Hour=0) then
//零点执行
begin
Exe();
end;
end;

//------------------------------------------------------------------------------
function Tmain.GetFileSize(filename: String): Integer;
var
F: file of Byte;
begin
try
AssignFile(F,filename);
Reset(F);
Result := FileSize(F);
CloseFile(F);
except
CloseFile(F);
Result := 0;
end;
end;

//------------------------------------------------------------------------------
function Tmain.WriteLogFile(StrLog: String): Boolean;
//把字符串写入日志文件中
var
CurrentPath: String;
v_result: Boolean;
LogF: TextFile;
//日志文件
begin
v_result := True;
try
if [red]RegDataConn1.GetRegData [/red]then
CurrentPath:= RegDataConn1.SoftwarePath
//else
// showmessage(RegDataConn1.Error);
if CurrentPath='' then
begin
Result:= False;
Exit;
end
else
;
if CurrentPath[Length(CurrentPath)] <> '/' then
CurrentPath := CurrentPath + '/';
if not DirectoryExists(CurrentPath+'App') then
CreateDir(CurrentPath+'App');
CurrentPath := CurrentPath + 'App/';

if not DirectoryExists(CurrentPath+'Log') then
CreateDir(CurrentPath+'Log');
try
if FileExists(CurrentPath+'Log/DeleteService.txt') then
if GetFileSize(CurrentPath+'Log/DeleteService.txt') > 1*1024 then
//判断日志文件是否大于1M
RenameFile(CurrentPath+'Log/DeleteService.txt',CurrentPath+'Log/DeleteService'+DateTimeToStr(Now)+'.log');
except
On E:Exceptiondo
ShowMessage(E.Message);
end;
AssignFile(LogF,CurrentPath+'Log/DeleteService.txt');
//查找是否存在文件DeleteService.txt
if FileSearch('DeleteService.txt',CurrentPath+'Log') = '' then
begin
//文件不存在
Rewrite(LogF);
end
else
begin
//文件存在
Append(LogF);
end;
Writeln(LogF,StrLog);
Flush(LogF);
except
v_result := False;
if (@LogF <> nil) then
CloseFile(LogF);
end;
Result := v_result;
end;

//------------------------------------------------------------------------------
// 删除资源表中已经标记为删除的资源记录和物理文件
//
//------------------------------------------------------------------------------
procedure Tmain.ResourceLib();
var
DSN,UID,PWD: widestring;
ConStr,SQLStr: string;
rcd: _recordset;
vStr1,vStr2,vStr3,vStr4:widestring;
begin
SQLStr:='select ResourceTable.ResourceID, '
+ 'ResourceTable.URIType, '
+ 'Rtrim(LTRIM(DiskTable.VirtualPath))+ Rtrim(LTRIM(ResourceTable.[Identifier])) as Path,'
+ 'ResourceTable.ComplexFlag '
+'from ResourceTable,Disktable '
+'where (ResourceTable.VirtualPathID=DiskTable.VirtualPathID) and '
+ 'ResourceTable.ResourceID in '
+ '(select ResourceID from ResourceReferenceView where Counter=0 ) ';
if [red]RegDataConn1.GetRegData[/red] then
//读注册表参数
begin
DSN:=RegDataConn1.ODBCDSN ;
//======ODBC数据库配置参数
UID:=RegDataConn1.ODBCUID;
PWD:=RegDataConn1.ODBCPWD;
ConStr:='Provider=MSDASQL.1;Password='
+PWD+';Persist Security Info=False;User ID='
+UID+';Data Source='
+DSN ;
end
else
begin
WriteLogFile('注册表读取失败:时间='+DateTimeToStr(Now)+'信息:ODBC参数不能获得。'+trim(RegDataConn1.Error));
exit;
end;
}
ConStr:='Provider=MSDASQL.1;Password='
+'love'+';Persist Security Info=False;User ID='
+'sa'+';Data Source=ChinaSchool_RS40';
ADOConnection1.ConnectionString:=ConStr;
try
[red]ADOConnection1.Open[/red];
except
On E:Exceptiondo
ShowMessage(E.Message);
ADOConnection1.Close;
WriteLogFile('数据库连接失败:时间='+DateTimeToStr(Now)+'信息:连接串='+ConStr);
exit;
end;
showmessage('ADOConnection1.Open;');
try
ADOQuery1.Close;
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Add(SQLStr);
ADOQuery1.Open;
rcd:=ADOQuery1.Recordset;
ADOQuery1.Close;
except
On E:Exceptiondo
ShowMessage(E.Message);
ADOQuery1.Close;
WriteLogFile('数据库查询失败:时间='+DateTimeToStr(Now)+'信息:查询SQL='+SQLStr);
exit;
end;
if rcd.RecordCount <>0 then
begin
rcd.MoveFirst;
while not rcd.EOFdo
begin
//------------------------
vStr1:=rcd.Fields[0].Value;
// 资源ID
vStr2:=rcd.Fields[1].Value;
// 资源类型
vStr3:=rcd.Fields[2].Value;
// 路径
vStr4:=rcd.Fields[3].Value;
//复合标记
//------------------------
DeleteRecordes(vStr1);
if vStr2='L' then
DeleteResourceFile(vStr4,vStr3);
rcd.MoveNext;
end;
end;

end;

//------------------------------------------------------------------------------
// 删除记录
// ID-> 资源表的ResourceID
//------------------------------------------------------------------------------
procedure Tmain.DeleteRecordes(ID:string);
var
SQlStr:string;
begin
SQlStr:='DELETE FROM ResourceTable where ResourceID='+ID;
try
ADOQuery1.Close;
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Add(SQlStr);
ADOQuery1.ExecSQL;
ADOQuery1.Close;
WriteLogFile('记录删除成功:时间='+DateTimeToStr(Now)+'信息:资源ID='+ID);
//应该写入日志文件
except
ADOQuery1.Close;
WriteLogFile('记录删除失败:时间='+DateTimeToStr(Now)+'信息:资源ID='+ID);
//应该写入日志文件
exit;
end;
end;

//------------------------------------------------------------------------------
// 删除物理文件
// ComplexFlag -> 资源文件复合标记
// ResourcePath -> 资源的绝对路径
//------------------------------------------------------------------------------
procedure Tmain.DeleteResourceFile(ComplexFlag,ResourcePath:widestring);
begin
if [red]RegDataConn1.GetRegData[/red] then
begin
cFtpApp1.FtpServerName:=RegDataConn1.FTPServerIP;
cFtpApp1.FtpUserID:=RegDataConn1.FTPUser;
cFtpApp1.FtpPassword:=RegDataConn1.FTPPwd;
cFtpApp1.FtpPort:=RegDataConn1.FtpPort;
end
else
begin
WriteLogFile('注册表读取失败:时间='+DateTimeToStr(Now)+'信息:FTP参数不能获得。'+trim(RegDataConn1.Error));
//写入日志文件
exit;

end;
}
cFtpApp1.URIType:='L';
cFtpApp1.ComplexFlag:=ComplexFlag;
cFtpApp1.SourceRemoteMainURI:=ResourcePath;
if cFtpApp1.DeleteResoure then
//删除资源文件
WriteLogFile('资源文件删除成功:时间='+DateTimeToStr(Now)+'信息:资源路径='+ResourcePath)
//showmessage('资源文件删除成功:时间='+DateTimeToStr(Now)+'信息:资源路径='+ResourcePath) //写入日志文件
else
WriteLogFile('资源文件删除失败:时间='+DateTimeToStr(Now)+'信息:'+trim(cFtpApp1.GetErrString));
//写入日志文件
cFtpApp1.Disconnect;
end;

procedure Tmain.ServiceContinue(Sender: TService;
var Continued: Boolean);
begin
Timer1.Enabled :=True;
WriteLogFile('Service has been Continued at '+DateTimeToStr(Now));
Continued := True;
end;

procedure Tmain.ServicePause(Sender: TService;
var Paused: Boolean);
begin
Timer1.Enabled := False;
WriteLogFile('Service has been Paused at '+DateTimeToStr(Now));
Paused := True;
end;

procedure Tmain.ServiceStart(Sender: TService;
var Started: Boolean);
begin
Timer1.Interval:=1000;
showmessage('服务开始'+DateTimeToStr(Now));
WriteLogFile('Service has been Started at '+DateTimeToStr(Now));
Started:=True;
end;

procedure Tmain.ServiceStop(Sender: TService;
var Stopped: Boolean);
begin
Timer1.Enabled := False;
WriteLogFile('Service has been Stopped at '+DateTimeToStr(Now));
Stopped:= True;
end;

procedure Tmain.ServiceExecute(Sender: TService);
begin
WriteLogFile('Service has been Started at '+DateTimeToStr(Now));
Timer1.Enabled := True;
//showmessage(' Timer1.Enabled := True;')
end;
end.
 
顶部