幽幽
 
posts - 51,  comments - 28,  trackbacks - 0
  daemon进程是后台守护进程,有时候也叫精灵进程(agent).linux 下server都是daemon进程。相信大部分开发人员都知道如何去写一个daemon进程。但是另一方面,大部分人不知道为什么要这么做,不少人是从某个地方copy一个函数,拿来主义。但是具体为什么这么实现,却不是很透彻。
  见过一些面试官或被面试人。很多人解释daemon进程存在的理由是因为僵死进程。或者输入输出。其实和这些东西一毛钱关系都没有。daemon函数存在的原因是因为控制终端由于某些原因(如断开终端链接)会发送一些信号的原因。而接收进城处理这些信号缺省动作会让进程退出。这些信号会由于终端上敲一些特殊按键而产生。

  贴一个daemon函数常见的实现:
 1 int daemon(void)
 2 {
 3     pid_t pid = fork();
 4 
 5     if( pid != 0 ) exit(0);//parent
 6 
 7     //first children
 8     if(setsid() == -1)
 9     {
10        printf("setsid failed\n");
11        assert(0);
12        exit(-1);
13     }
14 
15     umask(0);
16 
17 
18     pid = fork();
19 
20     if( pid != 0) exit(0);
21   
22     //second children 
23     chdir ("/");
24 
25     for (int i = 0; i < 3; i++)
26     {
27         close (i);
28     }
29 
30 
31     int stdfd = open ("/dev/null", O_RDWR);
32     dup2(stdfd, STDOUT_FILENO);
33     dup2(stdfd, STDERR_FILENO);
34 
35     return 0;
36 }
37 

    1 、第一次fork的作用是让shell 认为本条命令 已经终止,不用挂在终端输入上。还有一个作用是为后面setsid服务。setsid的调用者不能是进程组组长(group leader). 此时父进程是进程组组长。
    
    2 、setsid() 是本函数最重要的一个调用。它完成了daemon函数想要做的大部分事情。调用完整个函数。子进程是会话组长(sid==pid),也是进程组组长(pgid == pid),并且脱离了原来控制终端。到了这一步,基本上不管控制终端如何怎么样。新的进程都不会收到那些信号。
    3  、经过前面2个步骤,基本想要做的都做了。第2次fork不是必须的。也看到很多开源服务没有fork第二次。fork第二次主要目的是。防止进程再次打开一个控制终端。因为打开一个控制终端的前台条件是该进程必须是会话组长。再fork一次,子进程ID != sid(sid是进程父进程的sid)。所以也无法打开新的控制终端。
    daemon目的就是防止终端产生的一些信号让进程退出。上面函数并没有直接调用signal函数去处理它。而是间接通过fork和setsid函数使用更少代码优雅处理。而被有些人误以为是僵死进程的原因需要这样处理。
    当然,也有很多程序不是像上面函数那样去实现。而是直接通过忽略信号方式处理。这样其实也不错,因为这些信号很少会有用到的价值。直接忽略基本上不存在误杀的情况。反正达到最终目的就可以。条条大路通罗马。
   下面罗列一下控制终端会产生哪些信号。程序中只要处理好这些信号,同样能达到上面函数实现的目的。
   //后台进程读取/写入终端输入产生下面两个信号,或者控制终端不存在情况读取和写入会产生
   signal(SIGTTOU, SIG_IGN);
   signal(SIGTTIN, SIG_IGN);
   //按CTRL-C ,CTRL-\ CTRL-Z会向前台进程组发送下面这些信号
   signal(SIGINT,  SIG_IGN );
   signal(SIGQUIT, SIG_IGN );
   signal(SIGTSTP, SIG_IGN );
   
   //终端断开,会给会话组长或孤儿进程组所有成员发送下面信号
   signal(SIGHUP,  SIG_IGN );
   还有有些信号也可以由终端shell产生,需要关注
   signal(SIGCONT, SIG_IGN );
   signal(SIGSTOP, SIG_IGN );
  上面这些信号,应该有些程序缺省处理(SIG_DFL)本身动作就是忽略(SIG_IGN),不是退出进程。不过按照上面写也不会造成什么问题。
posted @ 2014-10-10 14:15 幽幽 阅读(371) | 评论 (0)编辑 收藏
     摘要:     11.在COM组件中调用JavaScript函数  2// 连接点方式页面javascript脚本  3<object classid="CLSID:B568F111-DFE4-4944-B67F-0728AB2AB30F" id="testCom" VIEWASTEXT&...  阅读全文
posted @ 2010-10-09 03:21 幽幽 阅读(3326) | 评论 (2)编辑 收藏
  
转自 http://www.cppblog.com/Zezese/archive/2010/07/25/121247.html?opt=admin

1 
  2 
  3 
  4 
  5 template <class R, class P1, class P2>
  6 class IDelegate
  7 {
  8 public:
  9     virtual R Invoke(P1, P2) = 0;
 10 };
 11 
 12 
 13 template <class T, class R, class P1, class P2>
 14 class CDelegate : public IDelegate<R, P1, P2>
 15 {
 16 protected:
 17 
 18     typedef R (T::*pfnHandle)(P1, P2);
 19 
 20     const pfnHandle m_pfn;
 21 
 22     T* const m_pThis;
 23 
 24 public:
 25 
 26     CDelegate(T* const pThis, const pfnHandle pfn)
 27         :m_pThis(pThis), m_pfn(pfn)
 28     {
 29         if (m_pThis == NULL || m_pfn == NULL)
 30         {
 31             throw;
 32         } 
 33     }
 34 
 35     virtual R Invoke(P1 p1, P2 p2)
 36     {
 37         return (m_pThis->*m_pfn)(p1, p2);
 38     }
 39 
 40 };
 41 
 42 class CDelegateSource
 43 {
 44 public:
 45     CDelegateSource()
 46         : m_lpCallBack(NULL)
 47     {
 48     }
 49 
 50     void SetCallBack(IDelegate<boolintint>* newVal) 
 51     { 
 52         m_lpCallBack = newVal; 
 53     }
 54 
 55     void DoSomething()
 56     {
 57         for (int i = 0; i < 10; i++)
 58         {   
 59             if (m_lpCallBack != NULL)
 60             {
 61                 m_lpCallBack->Invoke(i, i * i);
 62             }
 63         }
 64     }
 65 
 66 private:
 67 
 68     IDelegate<boolintint>* m_lpCallBack;
 69 
 70 };
 71 
 72 class CDelegateTester
 73 {
 74 private:
 75 
 76     bool OnCallBack(int nParam1, int nParam2)
 77     {
 78         printf("OnCallBack -> nParam1:%d, nParam2:%d\r\n", nParam1, nParam2);
 79 
 80         return true;
 81     }
 82 
 83     CDelegate<CDelegateTester, boolintint> m_OnCallBack;
 84 
 85 public:
 86 
 87     CDelegateTester()
 88         : m_OnCallBack(this, OnCallBack)
 89     {
 90     }
 91 
 92     void Execute()
 93     {
 94         CDelegateSource src;
 95         src.SetCallBack(&m_OnCallBack);
 96         src.DoSomething();
 97     }
 98 };
 99 
100 void main()
101 {
102     CDelegateTester Tester;
103     Tester.Execute();
104 
105     getchar();
106 }
107 
posted @ 2010-10-04 09:10 幽幽 阅读(783) | 评论 (0)编辑 收藏
1SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST+SHCNF_FLUSH, 00);
posted @ 2010-09-12 01:19 幽幽 阅读(608) | 评论 (0)编辑 收藏
转一些从shell32.dll导出的函数

外壳对话框

外壳对话框的秘密

常见的Windows的通用对话框被封装在Comdlg32.dll,这给我们的编程提供了很大的便利。但它还不够完整,我们在系统里经常能看到大量的可重复使用的对话框,但在Windows的文档里你却找不到它们的调用方法。而如果我们自己去做这样的界面是非常费时费力的而且也是没有必要的,因为这些对话框实际上很容易得到。这里我要介绍一些已经众所周知或不为认知的对话框,它们可以应用在我们的程序中使程序显得非常友好和专业。

浏览文件夹对话框



图2.23

大多数Delphi程序员都知道如何使用VCL的TOpenDialog控件来让用户浏览将要打开的文件。然而有时你可能只想让用户选择文件夹而不是特定的文件,windows已经提供了一个这样的对话框如图2.23所示。我们可以通过公开的函数SHBrowseForFolder来调用 (这个函数定义在ShlObj单元),函数定义如下:

function SHBrowseForFolder(var BrowseInfo: TBrowseInfo): PItemIDList; stdcall;

这个函数只有一个参数,但这个参数是一个比较复杂的记录类型

    TBrowseInfo = packed record

      hwndOwner: HWND;

      pidlRoot: PItemIDList;

      pszDisplayName: PChar;

      lpszTitle: PChar;

      ulFlags: UINT;

      lpfn: TFNBFFCallBack;

      lParam: LPARAM;

      iImage: Integer;

    end;


hwndOwner数据成员包含对话框的父窗体的窗口句柄,可以把它设成0。PIdlRoot数据成员指向一个PIDL的指针对应于对话框初始化时的根目录。指定了PIdlRoot后,就只有根目录及它的子目录会出现在对话框中。可以设定它为nil,这时缺省的根目录是桌面,pszDisplayName 数据成员指向一个缓冲区可以用来储存被用户选中的文件名,缓冲区的大小至少为MAX_PATH 这个常数那么大,否则遇到特别长的文件名会溢出。lpszTitle 数据对象指向一个以null结尾的字符串,字符串作为对话框的标题来显示。注意标题不要太长,否则显示时会被截断。ulFlags 标志数据对象用来限制在对话框中显示的文件夹类型。可以设定它为0或下列值的组合:

    //在对话框中会包含一个状态区,回调函数可以通过向对话框发送消息来设定状态

BIF_STATUSTEXT

    //只允许选择标准文件系统,若选了非标准的文件夹如打印机,确认按钮会变灰

    BIF_RETURNONLYFSDIRS   = $0001;

    //不选择网络文件夹

    BIF_DONTGOBELOWDOMAIN  = $0002;

    // 给状态条留出空白

    BIF_STATUSTEXT         = $0004;

    // 只选择文件系统的上级目录

    BIF_RETURNFSANCESTORS  = $0008;

    //只选择计算机

    BIF_BROWSEFORCOMPUTER  = $1000;

    //只选择打印机

    BIF_BROWSEFORPRINTER   = $2000;

    //包括文件也可以选

    BIF_BROWSEINCLUDEFILES = $4000;


注意:如果你想对话框显示lpszTitle里的用户定制的状态条信息,必须包括 BIF_STATUSTEXT标识。Lpfn数据对象是一个回调函数类型的指针,函数类型如下:

    TFNBFFCallBack = function(DialogHandle: HWND;

      MessageID: UINT; PIDL: PItemIDList; Data: LPARAM):Integer; stdcall;


这是一个回调函数,可以用来在同用户交互时控制和更新对话框的显示。如果你不想控制对话框,可以把它设成nil,lParam 数据对象允许你在回调函数中以参数lpfn形式返回一个指针(通常我们用它来返回对象),当然也可以把它设成为0。IImage数据成员不需要设置,因为它是用来接收系统中同文件夹相关的图标列表索引的,我们这里设它为0。

SHBrowseForFolder函数返回一个唯一的指向被选择的文件夹的PIDL。如果文件夹是一个传统的文件对象的话,可以用函数SHGetPathFromIDList把PIDL转换为真实的目录。同时,作为调用者,必须负责释放被返回的item identifier list,使用IMalloc COM 接口来释放。

注意:不要用FreeMem或其他方法来释放 PIDL ,这是因为外壳的内存管理是独立的,只能用IMalloc来释放。

现在我们已经可以显示对话框了,那让我们更深入一步看看如何能够控制对用户动作的反应,这就要用到了回调函数 TFNBFFCallBack。 注意回调函数的意思就是,你只是实现了它,系统就知道什么时候去调用它,就好比一个守株待兔的例子。

DialogHandle参数代表对话框窗口句柄。通常可以用这个句柄给对话框发消息,MessageID 参数并不是一个TMessage 结构的记录,它是对话框通过回调函数发给用户消息的,它可以是下面两个值:

    BFFM_INITIALIZED = 1;   // 对话框将要显示

    BFFM_SELCHANGED  = 2; // 用户选中了某项


PIDL参数包含其他的额外信息。如果MessageID 是 BFFM_INITIALIZED,PIDL将等于nil。如果MessageID是BFFM_SELCHANGED,PIDL的值将是一个PIDL 对应于用户选择的文件夹。Data 参数包含用户付给TbrowseInfo记录中的Lparam数据成员的值,通常可以传递一个对象指针。下面是一个简单的回调函数的例子:

    function BrowseForFolderCallback(DialogHandle: HWND;

      MessageID: UINT; PIDL: PItemIDList; Data: LPARAM):

      Integer;

    begin

      //响应对话框的通知消息

      case (MessageID) of

        BFFM_INITIALIZED:

          DialogInitialized(DialogHandle, Data);

        BFFM_SELCHANGED:

          HandleNewSelection(DialogHandle, PIDL, Data);

      end;

      Result := 0; // 总返回0.

    end;


在回调函数里,可以根据用户的输入发送三个用户的消息给对话框,下面是消息ID:

    // 改变对话框的状态信息

    BFFM_SETSTATUSTEXT = WM_USER + 100;

    //控制确定按钮失效与否

    BFFM_ENABLEOK      = WM_USER + 101;

    //改变选择的文件夹

    BFFM_SETSELECTION  = WM_USER + 102;


通常,这些消息发送给对话框使之根据用户的选择更新显示,当然你也可以发送其他的消息给对话框,比如可以发送WM_SETTEXT消息来改变对话框的标题。

下面是一个发送消息的例子(见表2.11):

PostMessage(DialogHandle, BFFM_SETSELECTION, True, LPARAM(PChar (NewPath)));

表2.11

Message ID WParam LParam
BFFM_SETSTATUSTEXT 没有使用 一个指向新的状态信息的Pchar
BFFM_ENABLEOK 没有使用 True使得确认按钮有效,False无效
BFFM_SETSELECTION 如果Lparam是路径则为True,若Lparam是PIDL则为False 指向被选择的文件路径或PIDL的Pchar



另外要提到的是,Delphi也提供了对这个函数的封装,那就是SelectDirectory函数。

关于对话框

通常我们都要在自己的程序里加上一个关于对话框来显示一些版本信息等等,Windows为我们提供了一个标准的对话框如图2.24所示,可以在一定范围内对它定制,不过它只适合显示简单的标识和文本(我觉得用处极小)。我们可以通过函数ShellAbout来调用它(声明在ShellAPI单元里),函数定义如下:

    function ShellAbout(Owner: HWND; ApplicationName: PChar;

      OtherText: PChar; IconHandle: HICON): Integer; stdcall;


Owner参数标识了拥有对话框的父窗体句柄,通常设为0,表明没有父窗体。ApplicationName 参数包含对话框的标题,字符串中可以包含“#”字符,它能起到分割符的作用。这种情况下,函数会把分割符前的字符串作为标题栏,分割符后的部分作为 "Microsoft"字符串后的第一行。OtherText参数包含了打算显示在Microsoft 版本和版权信息后的字符串。IconHandle 参数标识了打算显示在对话框上的图标标识,如果设为0,函数会显示Windows缺省的图标。



图2.24




图2.25

格式化对话框

SHFormatDrive函数会显示一个格式化对话框,如图2.25所示,它是一个半公开的函数。但现在它不在微软的SDK里。然而微软承认它的存在并把它从Shell32.dll里用名字公开声明,Delphi中的函数定义如下:

    function SHFormatDrive(Owner: HWND; Drive: UINT;

      FormatID: UINT; OptionFlags: UINT): DWORD; stdcall;


Owner参数标识拥有对话框的窗体句柄,文档中推荐不要设为0,但实际上好像没什么影响。Drive参数是用来标识打算格式化的驱动器的数值,它是以0为底的,从A开始 A:=0,B:=1依此类推。FormatID 参数允许我们指定一个格式化的模板,通常情况下,只要赋值为SHFMT_ID_DEFAULT就可以了。OptionFlags 参数是一个选项掩码,来确定格式化的选项。当前有两个选项:

    SHFMT_OPT_FULL    = $0001; // 快速格式化

    SHFMT_OPT_SYSONLY = $0002; // 复制系统文件


如果函数调用失败,会返回下列错误中的一种来表明错误原因,错误常数如下:

    SHFMT_NOFORMAT = $FFFFFFFD;   // 驱动器无法格式化

    SHFMT_CANCEL   = $FFFFFFFE;   //格式化被取消了

    SHFMT_ERROR    = $FFFFFFFF; //其他错误


Windows NT 和 WideChar

在进一步研究未公开的函数前, 我们必须清楚一点,对于未公开的函数来说以null结尾的字符串类型参数大多数被声明为类型指针而不是PChar。这有点像陷阱,但必须承认这是事实。在Win 9X上所有的字符串类型参数声明为PAnsiChar,而在Windows NT上被声明为PWideChar。如果你想你的应用程序适应所有平台,你必须考虑两种情况,在运行时要判断平台类型,这是很讨厌的,但这也是使用未公开的API的代价。

选择图标对话框



图2.26

我们要讨论的第一个完全未公开的函数是PickIconDlg。如图2.26所示这个函数会显示一个对话框,用户可以用来从文件中选择一个图标资源。它通常是用文件类型编辑器来关联图标和某一文件类型的,也会在快捷方式对话框中被调用来修改快捷方式的图标。这个函数从Shell32.dll 用值62来公开出来,函数定义如下:

    function PickIconDlg(Owner: HWND; FileName: Pointer;

      MaxFileNameChars: DWORD; var IconIndex: DWORD):LongBool; stdcall;


Owner参数和上面的意义类似。FileName 参数指向一个缓冲区,包含了被浏览图标的文件名,缓冲区要不小于MAX_PATH+1。MaxFileNameChars 指定字符数量大小。IconIndex 常数是以0为底的图标索引,当对话框打开时会把焦点定在IconIndex对应的图标上,函数返回后,IconIndex指向最后被选的图标索引。如果用户点了取消按钮,函数返回False。

运行程序对话框




图2.27

RunFileDlg函数是相当灵活的,如图2.27所示就是调用开始菜单的运行子菜单后会显示的对话框,我们通过值61把它从Shell32.dll暴露出来。下面是函数声明:

    procedure RunFileDlg(Owner: HWND; IconHandle: HICON;

      WorkPath: Pointer; Caption: Pointer; Description: Pointer; Flags: UINT); stdcall;


Owner参数就不用再说了。IconHandle参数是显示在对话框上的图标句柄,如果为nil,缺省的icon将会使用。WorkPath 参数指向一个字符串来指定应用程序运行的工作路径。 Title 参数指向作为对话框标题的字符串,如果为nil,就使用缺省的标题。Description 参数指向一个描述字符串,主要是告诉用户如何去做,可以设为nil,这时使用缺省的描述。 Flags参数用一组位掩码来设定对话框的属性。下面是定义:

    RFF_NOBROWSE      = $01;   // 移去浏览按钮

    RFF_NODEFAULT     = $02;   // 无缺省的选项

    RFF_CALCDIRECTORY = $04;   // 由文件名确定工作路径

    RFF_NOLABEL       = $08;   // 去掉编辑框标签

    RFF_NOSEPARATEMEM = $20; // 去掉在单独的内存空间运行的复选框 (只对NT有效)


这个对话框一个很好的特性是允许你控制用户可以运行的应用程序。当用户选择了确认按钮,对话框的父窗体会发送一个通知消息来传递将要运行的程序信息。通知消息是一个 WM_NOTIFY消息,它的通知代码设定为RFN_VALIDATE (-510),然后lParam指向一个 TNM_RunFileDlg记录。定义如下:

    TNM_RunFileDlg = packed record

      hdr: TNMHdr;

      lpFile: Pointer;

      lpDirectory: Pointer;

      nShow: LongBool;

    end;


hdr数据对象是TNMHdr类型,它是一种标准的Windows数据类型,每个WM_NOTIFY消息的lParam参数都会指向这个数据成分。同时根据不同的消息类型,可能一些额外的数据跟在记录后面,标准的TNMHdr记录定义如下:

    TNMHdr = packed record

      hwndFrom: HWND;

      idFrom: UINT;

      code: UINT;

    end;


记录中的hwndFrom包含发送消息的窗口句柄,idFrom则包含发送消息的控件标示符,code 中包含标识被发送的消息的通知代码。

在TNMHdr记录后被打包的额外数据包含三个数据成分:LpFile指向一个包含将要运行的文件的路径字符串;LpDirectory指向正在运行程序的工作目录字符串;最后,nShow 用来指定将要运行的应用程序是否可见。

对于本文中特定的消息,只对TNMHdr记录中的Code感兴趣,通过检验Code可以确保我们收到一个运行文件校验消息,同时使我们可以存取额外的TNM_RunFileDlg数据成员。当TNMHdr记录中的code等于RFN_VALIDATE(-510)时,可以获得一个TNM_RunFileDlg记录。下面是校验消息的代码:

    var

      FileToRun: String;

      ...

      if TheMessage.Msg = WM_NOTIFY then

        if PNMHdr(TheMessage.LParam).code = RFN_VALIDATE then

          WideCharToStrVar(PNM_RUNFILEDLG(

          TheMessage.LParam).lpFile, FileToRun);

    ...


注意只有当我们已经检验TNMHdr的Code为RFN_VALIDATE后,才映射LParam 参数为PNM_RunFileDlg类型。

通知消息的返回值决定了应用程序是否能够运行,下面是可能的值:

    RF_OK     = $00; //允许程序运行

    RF_CANCEL = $01;   //取消操作,关闭对话框

    RF_RETRY  = $02; //取消操作,对话框仍然打开


查找文件对话框




图2.28

调用查找文件对话框的函数是SHFindFiles,对话框如图2.28所示。它是从Shell32.dll按索引值90公开出来的:

    function SHFindFiles(SearchRoot: PItemIDList;

      SavedSearchFile: PItemIDList): LongBool; stdcall;


SearchRoot 参数允许从一个特定的文件夹开始查找,同在资源管理器中在文件夹上用右键点击查找菜单的效果是一样的。如果设为nil,那么查找是从桌面开始的。 SavedSearchFile 参数让你指定一个以前查询保存的查找策略文件(*.fnd文件),根据以前的设定来查找,若不需要的话可以设定为nil。如果你指定了一个非空值的SearchRoot PIDL,那么在调用完SHFindFiles后必须负责释放掉。但是有点奇怪的是,如果你指定了一个非空的SavedSearchFile PIDL参数,函数成功调用的话,你不能去释放这个 PIDL,否则会出错,但如果调用失败了的话,你必须释放它。

同大多数对话框函数不一样,这个函数是非模态的,也就是系统在另外一个独立的线程中启动对话框,然后立即返回,对话框会在你的程序结束后自动关闭。也就是说你没有任何直接的方法来告诉用户如何使用查找到的结果,所以要想知道用户找到的文件的话,最好是让你的程序支持文件拖放,以便让用户把找到的文件拖放给你。

查找电脑对话框

同SHFindFiles比较接近的一个函数是SHFindComputer,这个函数调用的结果同开始菜单上查找电脑菜单调用的结果是一样的。它的参数同SHFindFiles完全一样,不同之处在于它完全忽略传递给它的参数,很显然是保留起来为了将来扩展的需要。这里我们只要把参数都设成nil就可以了,另外注意这个对话框也是非模态的。 SHFindComputer 是从Shell32.dll 以索引号91公开出来的:

    function SHFindComputer(Reserved1: PItemIDList;

      Reserved2: PItemIDList): LongBool; stdcall;



查找文件对话框

通过调用GetFileNameFromBrowse函数可以调出这个对话框,不过说实在的,它实际上只是GetOpenFileName 函数的简单封装。而我们常用的TOpenDialog控件也是对GetOpenFileName 函数封装,这个函数我们很少会去直接用它。不过还是写出来吧,它是从Shell32.dll里按索引值63公开出来的:

    function GetFileNameFromBrowse(Owner: HWND;

      FileName: Pointer; MaxFileNameChars: DWORD;

      InitialDirectory: Pointer; DefaultExtension: Pointer;

      Filter: Pointer; Caption: Pointer): LongBool; stdcall;




图2.29

大多数参数对应于OPENFILENAME 结构的成员。Owner参数我想就不用再重复了, FileName 参数指向一个初始化对话框编辑控制文件名的缓冲区,函数返回后FileName包含被选择的文件路径,它的大小一般设成MAX_PATH+1那么大。MaxFileNameChars 参数用来指定FileName缓冲区的大小。 InitialDirectory参数指向对话框初始化的目录名,但如果FileName参数被指定了,InitialDirectory就会被忽略而使用FileName参数中的路径。DefaultExtension参数指向一个包含要搜索的缺省扩展名的字符串。Filter参数指向一个以null结尾的可以用来在下拉列表中限定文件类型的过滤字符串。Caption参数指向对话框标题字符串。

如果用户选择了一个要打开的文件,函数返回True,当有错误发生,用户选择取消按钮或关闭对话框的话会返回False。

外壳对象属性对话框

另一个未公开的对话框函数是SHObjectProperties,它可以用来显示外壳对象的属性,比如驱动器、文件夹或文件等,运行效果如图2.29所示。函数可以从Shell32.dll中按索引值178公开出来,定义如下:

    function SHObjectProperties(Owner: HWND; Flags: UINT;

      ObjectName: Pointer; InitialTabName: Pointer):LongBool; stdcall;


Flags参数用来指定ObjectName参数对应对象的类型,它可以是下列标识:

    //打印机

    OPF_PRINTERNAME = $01;

    //路径

    OPF_PATHNAME    = $02;


ObjectName参数指向一个包含路径名的字符串或是要显示属性的打印机名。如果打印机是本地的,可以使用实际的打印机名,如果是网络打印机,就需要使用完整的UNC样式名称,比如\\COMPUTERNAME\PRINTERNAME。InitialTabName参数指向一个属性对话框中页面名称字符串,用来指定要显示的缺省页面。如果InitialTabName参数为nil,或不匹配任何页面的名称,第一个属性页面将会被显示。

如果函数调用成功会返回True,如果失败会返回False。要想获得扩展的错误信息,可以调用API函数GetLastError。要注意的是这个对话框是非模态的,类似于查找文件对话框,所以函数一被调用,就肯定会显示一个对话框,同时我们没有办法知道用户什么时候关闭了对话框。

映射网络驱动对话框





图2.30

图2.30显示了映射网络驱动器的对话框,我们通过SHNetConnectionDialog函数调用它(win 9x和Win NT上都支持),它可以按索引值160从Shell32.dll暴露出来,函数定义如下:

    function SHNetConnectionDialog(Owner: HWND;

      ResourceName: Pointer; ResourceType: DWORD): DWORD; stdcall;


SHStartNetConnectionDialog函数也会显示同样的对话框,但它显示的对话框是非模态的,同时只在NT上才支持。它可以按索引值215从Shell32.dll中公开出来,函数定义如下:

    function SHStartNetConnectionDialog(Owner: HWND;

      ResourceName: PWideChar; ResourceType: DWORD):DWORD; stdcall;


上面两个函数的参数完全相同。其中ResourceName参数指向一个要连接的网络资源UNC路径名。指定了这个参数的话,显示的对话框中被预设的连接资源就不可改变了。如果这个参数为nil,则在对话框中用户可以指定要连接的资源。ResourceType参数可以是下面的值之一:RESOURCETYPE_DISK或RESOURCETYPE_PRINT。它的不同将会生成不同的对话框。参数为RESOURCETYPE_DISK允许我们为网络驱动资源指定一个盘符,另一个参数允许我们映射一个并行口名比如LPT2为一个网络打印机。然而,不知道为什么RESOURCETYPE_PRINT参数在NT上无效。




图2.31

如果函数调用成功的话,返回值是NO_ERROR,如果用户取消的对话框,则返回 -1($FFFFFFFF),如果调用失败则返回其他的错误代码,具体错误信息可以用GetLastError API调用获得。

关闭系统对话框

ExitWindowsDialog和RestartDialog函数可以用来显示关闭和重启系统对话框(如图2.31),它们同公开的ExitWindowsEx API函数没有什么太大的不同,但在其过程中都会产生一个对话框。ExitWindowsDialog函数可以按索引值60从Shell32.dll中公开出来,RestartDialog函数的在Shell32.dll中的索引值则是59,两个函数的定义如下:

    procedure ExitWindowsDialog(Owner: HWND); stdcall;

    function RestartDialog(Owner: HWND; Reason: Pointer; ExitType: UINT): DWORD; stdcall;


对ExitWindowsDialog函数来说,对话框好像并不使用Owner参数作为父窗口,在Windows 95上,当操作成功的话owner窗口会收到一个WM_QUIT消息。在Windows NT上,owner 窗口根本不被使用。同时这个函数没有返回值,所以没有办法知道用户选择了什么操作以及操作是否被取消了。

RestartDialog函数更有用一些,当我们修改了系统的设置,并希望重新启动系统使修改生效的时候可以使用这个函数。Reason参数指向一个要显示在对话框中的字符串,用来解释关闭系统的原因。ExitType参数指定关闭类型,可以使用ExitWindowsEX函数使用值的一个子集及额外的几个新值,下面是它们的完全列表:

    EWX_LOGOFF        = $00;

    EWX_SHUTDOWN      = $01;

    EWX_REBOOT        = $02;

    EW_RESTARTWINDOWS = $42;

    EW_REBOOTSYSTEM   = $43;

    EW_EXITANDEXECAPP = $44;


如果用户选择执行关闭操作,函数返回IDYES,否则返回IDNO。

要注意的是显示在对话框中的原因字符串后总会跟着一个系统缺省提供的字符串用来显示确认信息,所以应该在我们的Reason字符串后附上空格或回车换行字符。另外返回值不能用于确定操作的成功性,它只表明用户的选择,如果重启操作由于某些原因失败了,返回值仍然是IDYES。同时要注意的是要想调用成功,用户还必须有SE_SHUTDOWN_NAME权限(在NT上)。

缺少内存对话框

SHOutOfMemoryMessageBox是一个未公开的函数,当系统内存不足时可以用来显示标准的外壳信息对话框,它在Shell32.dll中的索引值是126,函数定义如下:

    function SHOutOfMemoryMessageBox(Owner: HWND;

      Caption: Pointer; Style: UINT): Integer; stdcall;


它会调用MessageBox API,同时传递3个标准的参数和ERROR_OUTOFMEMORY错误消息。Caption参数指向对话框标题字符串。如果Caption为nil,父窗口的标题就会被使用。Style参数可以被设置为任意MessageBox函数使用的MB_XXX常数的组合,通常设置它为MB_OK或MB_ICONHAND。函数调用返回值参见SDK中MessageBox函数说明。

当MessageBox函数被调用时,MB_SETFOREGROUND标识会被添加到Style参数中,但如果第一次调用失败了的话,MessageBox函数会被再次调用,这次MB_SYSTEMMODAL 标识会被添加到Style参数中。MB_SYSTEMMODAL同MB_ICONHAND标识结合后会忽略内存状况来显示消息对话框。当内存确实不足时,函数不会显示任何东西,然而它仍然会返回MessageBox函数调用结果。所以我们可以根据返回值判断函数是否调用成功了。

空间不足对话框




图2.32

另一个资源相关的函数是SHHandleDiskFull,它会显示磁盘不足的信息对话框(如图2.32)。我们可以在由于没有足够磁盘空间时导致程序无法运行的条件下调用这个函数,调用后,如果回收站中有什么东西没有删除的话,对话框允许用户清空回收站来释放磁盘空间。它在Shell32.dll中的索引值为185,函数的定义如下:

    procedure SHHandleDiskFull(Owner: HWND; Drive: UINT); stdcall;


Drive参数用于指定以0为底的驱动器盘符。0代表A:\,1代表B:\,依此类推。这个函数的应用比较困难,因为当回收站中没有任何东西时对话框不会显示,同时也没有任何返回值表示对话框是否显示,还无法知道用户的操作,比如它是否真的清空了。看起来比较可行的应用只能是程序自行监视磁盘剩余空间,只是使用这个对话框作为一个快速修复的工具。

一般外壳消息对话框

ShellMessageBox函数仅仅是一个对MessageBox函数的简单封装函数,它允许使用字符串资源标识符或标准的以null结尾的字符串,同时还允许加入支持格式化ForamtMessage函数的控制符。ShellMessageBox函数在Shell32.dll中的索引值为183:

    function ShellMessageBoxA(Module: THandle; Owner: HWND;

      Text: PChar; Caption: PChar; Style: UINT;

      Parameters: array of Pointer): Integer; cdecl;


更确切地说这个函数应该叫ShellMessageBoxA因为它只支持ANSI字符串,还有一个UNICODE的版本的函数ShellMessageBoxW,它的索引值为182,但它只在Windows NT上才有,函数定义如下:

    function ShellMessageBoxW(Module: THandle; Owner: HWND;

      Text: PWideChar; Caption: PWideChar; Style: UINT;

      Parameters: array of Pointer): Integer; cdecl;


Module参数是提供字符串资源的模块句柄,句柄可以用GetModuleHandle函数获得。顾名思义Text 参数指向一个要显示在对话框中的文本,它也可以是资源字符串ID,文本中可以包括格式控制序列,它将会被在Parameters中提供的额外字符串替代。控制符格式为“%#”,其中“#”是额外字符串在参数中的位置,比如“%1”将被第一个Parameters数组中的字符串元素替代,“%3”将会被第三个元素替代,依此类推。Caption参数指向对话框标题文本,同样它也可以是资源ID,如果参数为nil,Owner指定的窗口标题将被用于对话框标题。Style参数是由位掩码标识组成的,可以设置成MessageBox函数支持的MB_XXX常数的组合。返回值同MessageBox完全一样。

对于这个函数很重要的一点是微软公司使用cdecl来输出这个函数而不是通常的 stdcall。此外,Parameters参数使用了C语言中的可变参数列表,这意味着这个函数不是语言无关的,这使得调用起来非常麻烦,因为Delphi并不直接支持cdecl和可变参数列表。为了解决这个问题,Parameters参数被映射为一个动态指针列表。同时我们还需要使用嵌入式汇编来建立cdecl样式的堆栈。由于动态指针列表的性质,我们必须至少指定一个指针值。如果不想指定要替代的字符串,简单设置为nil就可以了。
posted @ 2009-12-18 09:20 幽幽 阅读(782) | 评论 (0)编辑 收藏

首先,先简单介绍一下MD5

MD5的全称是message-digest algorithm 5(信息-摘要算法,在90年代初由mit laboratory for computer science和rsa data security inc的ronald l. rivest开发出来, 经md2、md3和md4发展而来。

MD5具有很好的安全性(因为它具有不可逆的特征,加过密的密文经过解密后和加密前的东东相同的可能性极小)

引用
using System.Security.Cryptography;
using System.Text;

具体代码如下(写在按钮的Click事件里):
byte[] result = Encoding.Default.GetBytes(this.tbPass.Text.Trim());    //tbPass为输入密码的文本框
MD5 md5 = new MD5CryptoServiceProvider();
byte[] output = md5.ComputeHash(result);
this.tbMd5pass.Text = BitConverter.ToString(output).Replace("-","");  //tbMd5pass为输出加密文本的文本框

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lykycs/archive/2006/06/16/802873.aspx


posted @ 2009-12-07 08:29 幽幽 阅读(432) | 评论 (0)编辑 收藏

再谈异常――谈C++与Object Pascal中的构造函数与异常

作者:Nicrosoft(nicrosoft@sunistudio.com) 2001.9.15
个人主页:http://www.sunistudio.com/nicrosoft/
东日文档:http://www.sunistudio.com/asp/sunidoc.asp

  我们知道,类的构造函数是没有返回值的,如果构造函数构造对象失败,不可能依靠返回错误代码。那么,在程序中如何标识构造函数的失败呢?最“标准”的方法就是:抛出一个异常。

  构造函数失败,意味着对象的构造失败,那么抛出异常之后,这个“半死不活”的对象会被如何处理呢?这就是本文的主题。

在C++中,构造函数抛出异常后,析构函数不会被调用。这是合理的,因为此时对象并没有被完整构造。也就是说,如果构造函数已经做了一些诸如分配内存、 打开文件等操作的话,那么类需要有自己的成员来记住做过哪些动作。在C++中,经典的解决方案是使用STL的标准类auto_ptr,这在每一本经典 C++著作中都有介绍,我在这里就不多说了。在这里,我想再介绍一种“非常规”的方式,其思想就是避免在构造函数中抛出异常。我们可以在类中增加一个 Init(); 以及 UnInit();成员函数用于进行容易产生错误的资源分配工作,而真正的构造函数中先将所有成员置为NULL,然后调用 Init(); 并判断其返回值(或者捕捉 Init()抛出的异常),如果Init();失败了,则在构造函数中调用 UnInit(); 并设置一个标志位表明构造失败。UnInit()中按照成员是否为NULL进行资源的释放工作。示例代码如下:
class A
{
private:
 char* str;
 int failed;

public:
 A();
 ~A();
 int Init();
 int UnInit();
 int Failed();
};

A::A()
{
 str = NULL;
 try
 {
  Init();
  failed = 0;
 }
 catch(...)
 {
  failed = 1;
  UnInit();
 }
}

A::~A()
{
 UnInit();
}

int A::Init()
{
 str = new char[10];
 strcpy(str, "ABCDEFGHI");
 throw 10;

 return 1;
}

int A::UnInit()
{
 if (!str)
 {
  delete []str;
  str = NULL;
 }

 printf("Free Resource

");
 return 1;
}

int A::Failed()
{
 return failed;
}

int main(int argc, char* argv[])
{
 A* a = new A;
 if ( a->Failed() )
  printf("failed

");
 else
  printf("succeeded

");

 delete a;

 getchar();
 return 0;
}

  你会发现,在int A::Init()中包含了throw 10;的代码(产生一个异常,模拟错误的发生),执行结果是:
  Free Resource
  failed
  Free Resource
  虽然 UnInit();被调用了两次,但是由于UnInit();中做了判断(if (!str)),因此不会发生错误。而如果没有发生异常(去掉 int A::Init()中的throw 10;代码),执行结果是:
  Succeeded
  Free Resource
  和正常的流程没有任何区别。

在Object Pascal(Delphi/VCL)中,这个问题就变得非常的简单了,因为 OP 对构造函数的异常的处理与C++不同,在Create时抛出异常后,编译器会自动调用析构函数Destroy,并且会判断哪些资源被分配了,实行自动回 收。因此,其代码也变得非常简洁,如下:
type
  A = class
  private
  str : PChar;
  public
  constructor Create();
  destructor Destroy(); override;
  end;

constructor A.Create();
begin
  str := StrAlloc(10);
  StrCopy(str, 'ABCDEFGHI');
  raise Exception.Create('error');
end;

destructor A.Destroy();
begin
  StrDispose(str);
  WriteLn('Free Resource');
end;

var oa : A;
  i : integer;
begin
  try
      oa := A.Create();
      WriteLn('Succeeded');
      oa.Free();
  except
      oa := nil;
      WriteLn('Failed');
  end;

  Read(i);
end.

  在这段代码中,如果构造函数抛出异常(即Create中含有raise Exception.Create('error');),执行的结果是:
  Free Resource
  Failed
  此时的“Free Resource”输出是由编译器自动调用析构函数所产生的。而如果构造函数正常返回(即不抛出异常),则执行结果是:
  Succeeded
  Free Resource
  此时的“Free Resource”输出是由 oa.Free()的调用产生的。

综上,C++与Object Pascal对于构造函数抛出异常后的不同处理方式,其实正是两种语言的设计思想的体现。C++秉承C的风格,注重效率,一切交给程序员来掌握,编译器不 作多余动作。Object Pascal继承Pascal的风格,注重程序的美学意义(不可否认,Pascal代码是全世界最优美的代码),编译器帮助程序员完成复杂的工作。两种语 言都有存在的理由,都有存在的必要!而掌握它们之间的差别,能让你更好地控制它们,达到自由的理想王国。


posted @ 2009-05-20 00:42 幽幽 阅读(628) | 评论 (1)编辑 收藏

串口通讯中的DCB结构

typedef struct _DCB {// dcb

DWORD DCBlength; // sizeof(DCB)

DORD BaudRate; // current baud rate 指定当前的波特率

DWORD fBinary: 1; // binary mode, no EOF check 指定是否允许二进制模式WIN95中须为TRUE
       

DWORD fParity: 1; // enable parity checking 指定奇偶校验是否允许

DWORD fOutxCtsFlow:1; // CTS output flow control 指定CTS是否用于检测发送控制。当为TRUE是CTS为OFF,发送将被挂起。


DWORD fOutxDsrFlow:1; // DSR output flow control   指定CTS是否用于检测发送控制。当为TRUE是CTS为OFF,发送将被挂起。


DWORD fDtrControl:2; // DTR flow control type

DTR_CONTROL_DISABLE 值将DTR置为OFF,DTR_CONTROL_ENABLE值将DTR置为ON, 

DTR_CONTROL_HANDSHAKE 允许DTR"握手",

 

DWORD fDsrSensitivity:1;// DSR sensitivity 当该值为TRUE时DSR为OFF时接收的字节被忽略

 

DWORD fTXContinueOnXoff:1; // XOFF continues Tx
    指定当接收缓冲区已满,并且驱动程序已经发送出XoffChar字符时发送是否停止。

    TRUE时,在接收缓冲区接收到缓冲区已满的字节XoffLim且驱动程序已经发送出XoffChar字      符中止接收字节之后,发送继续进行。
    FALSE时,在接收缓冲区接收到代表缓冲区已空的字节XonChar且驱动程序已经发送出恢复发送的XonChar之后,发送继续进行。


DWORD fOutX: 1; // XON/XOFF out flow control    TRUE时,接收到XoffChar之后便停止发送接收到XonChar之后将重新开始

 

DWORD fInX: 1; // XON/XOFF in flow control
             TRUE时,接收缓冲区接收到代表缓冲区满的XoffLim之后,XoffChar发送出去接收缓冲区接收到代表缓冲区空的XonLim之后,XonChar发送出去


DWORD fErrorChar: 1; // enable error replacement
         该值为TRUE且fParity为TRUE时,用ErrorChar 成员指定的字符代替奇偶校验错误的接收字符


DWORD fNull: 1; // enable null stripping  TRUE时,接收时去掉空(0值)字节

 

DWORD fRtsControl:2; // RTS flow control


DWORD fAbortOnError:1; // abort reads/writes on error  TRUE时,有错误发生时中止读和写操作RTS_CONTROL_DISABLE时,RTS置为OFFRTS_CONTROL_ENABLE时, RTS置为ON

 

RTS_CONTROL_HANDSHAKE时,当接收缓冲区小于半满时RTS为ON当接收缓冲区超过四分之三满时RTS为OFF


RTS_CONTROL_TOGGLE时,当接收缓冲区仍有剩余字节时RTS为ON ,否则缺省为OFF


DWORD fDummy2:17; // reserved  未使用

 

WORD wReserved; // not currently used  未使用,必须为0

 

WORD XonLim; // transmit XON threshold   指定在XON字符发送这前接收缓冲区中可允许的最小字节数


WORD XoffLim; // transmit XOFF threshold  指定在XOFF字符发送这前接收缓冲区中可允许的最小字节数


BYTE ByteSize; // number of bits/byte, 4-8  指定端口当前使用的数据位

 

BYTE Parity; // 0-4=no,odd,even,mark,space 指定端口当前使用的奇偶校验方法,可能为:
                            EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY BYTE

 

StopBits;      0,1,2 = 1, 1.5, 2   指定端口当前使用的停止位数,可能为:
                          ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS


char XonChar; // Tx and Rx XON character  指定用于发送和接收字符XON的值

 

char XoffChar; // Tx and Rx XOFF character  指定用于发送和接收字符XOFF值

 

char ErrorChar; // error replacement character本字符用来代替接收到的奇偶校验发生错误时的值

 

char EofChar; // end of input character 当没有使用二进制模式时,本字符可用来指示数据的结束

 

char EvtChar; // received event character 当接收到此字符时,会产生一个事件

 

WORD wReserved1; // reserved; do not use 未使用

} DCB;

posted @ 2009-05-05 02:35 幽幽 阅读(1874) | 评论 (0)编辑 收藏

CSS切换皮肤切换代码:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
      <head>
       
  <meta http-equiv="content-type" content="text/html;charset=gb2312" />
  <meta name="generator" content="" />
  <meta name="author" content="" />
        <meta name="keywords" content="" />
        <meta name="robots" content="index, follow" />
        <meta name="googlebot" content="index, follow" />
  <title>     </title>
  <link rel="stylesheet" type="text/css" href="code/changestyle/styles1.css" title="styles1"  media="screen" />
 <link rel="alternate stylesheet" type="text/css" href="code/changestyle/styles2.css" title="styles2" media="screen" />
 <link rel="alternate stylesheet" type="text/css" href="code/changestyle/styles3.css" title="styles3" media="screen" />
  <script type="text/javascript">
window.onload=function(){ var c = readCookie('style');
 if (c) switchStylestyle(c);
};
function switchStylestyle(styleName)
{  aa=document.styleSheets;
for(i=0;i<aa.length;i++)
 {
  aa[i].disabled = true;
  if (aa[i].title== styleName){aa[i].disabled = false;}
 };
 createCookie('style', styleName, 365);
}
function createCookie(name,value,days)
{
 if (days)
 {
  var date = new Date();
  date.setTime(date.getTime()+(days*24*60*60*1000));
  var expires = "; expires="+date.toGMTString();
 }
 else var expires = "";
 document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name)
{
 var nameEQ = name + "=";
 var ca = document.cookie.split(';');
 for(var i=0;i < ca.length;i++)
 {
  var c = ca[i];
  while (c.charAt(0)==' ') c = c.substring(1,c.length);
  if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
 }
 return null;
}
function eraseCookie(name)
{
 createCookie(name,"",-1);
}
// /cookie functions
  </script>
 </head>
 <body id="">
  <div id="wrapper">
   <ul>
  <li><a href="javascript:void(0);" onclick="switchStylestyle('styles1')">styles1</a></li>
  <li><a href="javascript:void(0);" onclick="switchStylestyle('styles2')">styles2</a></li>
  <li><a href="javascript:void(0);" onclick="switchStylestyle('styles3')">styles3</a></li>
 </ul
  </div>
 </body>
</html>


posted @ 2009-04-20 03:22 幽幽 阅读(799) | 评论 (0)编辑 收藏
DLL注入程序的一般步骤:

(1)取得宿主进程(即要注入木马的进程)的进程ID dwRemoteProcessId;

(2)取得DLL的完全路径,并将其转换为宽字符模式pszLibFileName;

(3)利用Windows API OpenProcess打开宿主进程,应该开启下列选项:

a.PROCESS_CREATE_THREAD:允许在宿主进程中创建线程;

b.PROCESS_VM_OPERATION:允许对宿主进程中进行VM操作;

c.PROCESS_VM_WRITE:允许对宿主进程进行VM写。

(4)利用Windows API VirtualAllocEx函数在远程线程的VM中分配DLL完整路径宽字符所需的存储空间,并利用Windows API WriteProcessMemory函数将完整路径写入该存储空间;

(5)利用Windows API GetProcAddress取得Kernel32模块中LoadLibraryW函数的地址,这个函数将作为随后将启动的远程线程的入口函数;

(6)利用Windows API CreateRemoteThread启动远程线程,将LoadLibraryW的地址作为远程线程的入口函数地址,将宿主进程里被分配空间中存储的完整DLL路径作为线程入口函数的参数以另其启动指定的DLL;

(7)清理现场。
posted @ 2009-03-13 19:56 幽幽 阅读(356) | 评论 (0)编辑 收藏
仅列出标题
共6页: 1 2 3 4 5 6 

<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

常用链接

留言簿(5)

随笔分类(35)

随笔档案(51)

文章分类(3)

文章档案(3)

相册

我的链接

搜索

  •  

最新评论

阅读排行榜

评论排行榜