精华内容
下载资源
问答
  • ” 呵呵,以前我也没处理过,通过测试,发现问题关键是如何知道影音文件路径,通过分析,发现其实并不是很难,主要是以前没有注意LinkFormat对象,其实MSDN中有如下范例: WithActivePresentation.Slides...

    几天前有个朋友(simon)来信问“如果在PPT里边插入影音文件,如何在保存文件的时候将影音文件也保存出来?”

    呵呵,以前我也没处理过,通过测试,发现问题的关键是如何知道影音文件的路径,通过分析,发现其实并不是很难,主要是以前没有注意LinkFormat对象,其实在MSDN中有如下的范例:

    None.gifWith ActivePresentation.Slides(1).Shapes(1)
    None.gif    
    If .Type = msoLinkedOLEObject Then
    None.gif        
    With .LinkFormat
    None.gif            .SourceFullName 
    = "c:\my documents\wordtest.doc"
    None.gif            .AutoUpdate 
    = ppUpdateOptionAutomatic
    None.gif        
    End With
    None.gif    
    End If
    None.gif
    End With

    其中对SourceFullName 属性解释如下:

    应用于示例特性返回或设置链接 OLE 对象的源文件的名称或路径。可读写。String 类型。

    使用如下的代码测试了一下:

    None.gif'<summary>
    None.gif
        'Test过程就是在在幻灯片里边插入一个空白幻灯片
    None.gif
        '然后在幻灯片中插入一个影片文件,然后查看Shape对象的LinkFormat对象的FullName属性就行了
    None.gif
        '至于保存的时候如何处理,这个简单,你写一段宏就可以了
    None.gif
        '剩下的自己发挥吧
    None.gif'
    </summary>
    None.gif
    Sub InsertAvi()
    None.gif    ActiveWindow.Selection.SlideRange.Shapes.AddMediaObject(FileName:
    ="F:\clock.avi"Left:=239.625, Top:=149.625).Select
    None.gif    ActiveWindow.Selection.Unselect
    None.gif
    End Sub
    None.gif
    None.gif
    Sub ShowPath()
    None.gif    
    If Application.ActivePresentation.Slides(1).Shapes(1).MediaType = ppMediaTypeMovie Then
    None.gif        
    '关键是你理解一下LinkFormat对象
    None.gif
            'LinkFormat.SourceFullName就是文件的保存路径
    None.gif
            MsgBox Application.ActivePresentation.Slides(1).Shapes(1).LinkFormat.SourceFullName
    None.gif    
    End If
    None.gif
    End Sub


    这些是在VBA里边测试的,通过了,我想最主要的就是这个LinkFormat.SourceFullName这句话了,其实simon的想法很好,因为如果PPT中的影音文件如果不保存的话,如果放到别的机器上,就不能正常播放了,AVI就变成了一个图片了,呵呵,所以还是有用的。

    我想余下的最重要的就是Update这个方法了,如下:

    For Each sld In ActivePresentation.Slides
        For Each sh In sld.Shapes
            If sh.Type = msoLinkedOLEObject Then
                sh.LinkFormat.Update
            End If
        Next
    Next

    通过这个修改绝对路径为相对路径,应该就不是问题了。

    转载于:https://www.cnblogs.com/Duiker/archive/2005/11/09/272404.html

    展开全文
  • 之前做了一个PPT导出功能,去网上找了一些教程 发现都不是很详细,这里详细说一下。 GitHub地址:https://github.com/PHPOffice/PHPPresentation 首先加载类是必须,这里就不详细说了,直接进入代码://新建一...

    之前做了一个PPT导出的功能,去网上找了一些教程 发现都不是很详细,在这里详细说一下。 GitHub地址:https://github.com/PHPOffice/PHPPresentation 首先加载类是必须的,这里就不详细说了,直接进入代码:

    //新建一个对象,每个人放的位置不同,用命名空间也可以

    $objPHPPowerPoint = new \PhpOffice\PhpPresentation\PhpPresentation();

    //获取当前页的幻灯片(也可以看做第一页)

    $shape = $currentSlide->createDrawingShape();

    // 添加文本框

    $shape = $currentSlide->createRichTextShape()

    ->setHeight(150) //宽 (设置宽度后 自动缩放 )

    ->setOffsetX(370) // X 位置

    ->setOffsetY(180); // Y 位置

    $shape->getActiveParagraph()->getAlignment()->setHorizontal( Alignment::HORIZONTAL_CENTER ); //水平居中

    $textRun = $shape->createTextRun("XXXXXX\n\n检测报告"); //内容

    $textRun->getFont()->setBold(false) //加粗

    ->setSize(38) //字号

    ->setName("黑体") //字体

    ->setColor( new Color( 'FF000000' ) ); //颜色

    //添加一个图片

    $shape = $currentSlide->createDrawingShape();

    $shape->setName('社区图片') //图片名

    ->setDescription('社区图片') //描述

    ->setPath('111.jpg') //路径

    ->setWidth(250) //宽

    ->setOffsetX(100)

    ->setOffsetY(200);

    //设置图片显示状态

    $shape->getShadow()->setVisible(true)

    ->setDirection(45)

    ->setDistance(10);

    简单的先说这两个吧,如果你添加多张的话可以用下面这个方法

    $currentSlide = $objPHPPowerPoint->createSlide(); //在这行代码的后边直接添加图片或者文本框,多张幻灯片就循环一下

    //然后保存

    $oWriterPPTX = IOFactory::createWriter($objPHPPowerPoint, 'PowerPoint2007');

    $oWriterPPTX->save(ROOT . 'myPhpPpt.pptx');

    这种方式不会提示下载直接就到save()的路径下面了,若果提示保存的话应该在调用createWriter()前加上这样一段代码

    header("Content-Type: application/force-download");

    header("Content-Type: application/octet-stream");

    header("Content-Type: application/download");

    header("Content-Disposition:inline;filename=\"" . urlencode($order['order_name']). date('YmdHis') . ".pptx\"");

    header("Content-Transfer-Encoding: binary");

    header("Expires: " . gmdate("D, d M Y H:i:s") . " GMT");

    header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");

    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

    header("Pragma: no-cache");

    最后的save()里的内容应该是‘php://output’; 需要注意的一点是,我列出来的这段代码最后不能

    return $oWriterPPTX->save(‘php://output’);

    return后页面会有一大串乱码,可以exit;

    $oWriterPPTX->save(‘php://output’);

    exit;

    也可以读取某个PPT文件,用这个文件的模板生成ppt

    //读取ppt

    //ppt版本 2007

    $pptReader = IOFactory::createReader('PowerPoint2007');

    $oPHPPresentation =new \PhpOffice\PhpPresentation\PhpPresentation();

    //读取文件

    $oPHPPresentation = $pptReader->load(ROOT_PATH.'123.pptx');

    //幻灯片的个数

    $slidecount = $oPHPPresentation->getSlideCount();die;

    //获取某张幻灯片 从0开始

    $currentSlide = $oPHPPresentation->setActiveSlideIndex(2);

    //这后面可以对此页的幻灯片进行操作,代码和新建一样,就不贴出来了

    就说这么多吧,不是高玩(请轻喷),刚做的可以看下。

    展开全文
  • PowerPoint.2007宝典 8/10

    2012-04-01 18:39:23
    3.6 在PowerPoint内管理文件 65 3.6.1 创建新文件夹 66 3.6.2 复制演示文稿 67 3.6.3 删除演示文稿 67 3.6.4 重命名演示文稿 67 3.6.5 映射网络驱动器 67 3.7 小结 68 第4章 创建幻灯片和文本框 69 ...
  • PowerPoint.2007宝典 10/10

    2012-04-01 18:45:00
    3.6 在PowerPoint内管理文件 65 3.6.1 创建新文件夹 66 3.6.2 复制演示文稿 67 3.6.3 删除演示文稿 67 3.6.4 重命名演示文稿 67 3.6.5 映射网络驱动器 67 3.7 小结 68 第4章 创建幻灯片和文本框 69 ...
  • PowerPoint.2007宝典 9/10

    2012-04-01 18:42:38
    3.6 在PowerPoint内管理文件 65 3.6.1 创建新文件夹 66 3.6.2 复制演示文稿 67 3.6.3 删除演示文稿 67 3.6.4 重命名演示文稿 67 3.6.5 映射网络驱动器 67 3.7 小结 68 第4章 创建幻灯片和文本框 69 ...
  • PowerPoint.2007宝典 7/10

    2012-04-01 18:36:57
    3.6 在PowerPoint内管理文件 65 3.6.1 创建新文件夹 66 3.6.2 复制演示文稿 67 3.6.3 删除演示文稿 67 3.6.4 重命名演示文稿 67 3.6.5 映射网络驱动器 67 3.7 小结 68 第4章 创建幻灯片和文本框 69 ...
  • PowerPoint.2007宝典 6/10

    2012-04-01 18:34:41
    3.6 在PowerPoint内管理文件 65 3.6.1 创建新文件夹 66 3.6.2 复制演示文稿 67 3.6.3 删除演示文稿 67 3.6.4 重命名演示文稿 67 3.6.5 映射网络驱动器 67 3.7 小结 68 第4章 创建幻灯片和文本框 69 ...
  • PowerPoint.2007宝典 5/10

    2012-04-01 18:32:23
    3.6 在PowerPoint内管理文件 65 3.6.1 创建新文件夹 66 3.6.2 复制演示文稿 67 3.6.3 删除演示文稿 67 3.6.4 重命名演示文稿 67 3.6.5 映射网络驱动器 67 3.7 小结 68 第4章 创建幻灯片和文本框 69 ...
  • PowerPoint.2007宝典 4/10

    2012-04-01 18:30:04
    3.6 在PowerPoint内管理文件 65 3.6.1 创建新文件夹 66 3.6.2 复制演示文稿 67 3.6.3 删除演示文稿 67 3.6.4 重命名演示文稿 67 3.6.5 映射网络驱动器 67 3.7 小结 68 第4章 创建幻灯片和文本框 69 ...
  • PowerPoint.2007宝典 3/10

    2012-04-01 18:27:45
    3.6 在PowerPoint内管理文件 65 3.6.1 创建新文件夹 66 3.6.2 复制演示文稿 67 3.6.3 删除演示文稿 67 3.6.4 重命名演示文稿 67 3.6.5 映射网络驱动器 67 3.7 小结 68 第4章 创建幻灯片和文本框 69 ...
  • PowerPoint.2007宝典 2/10

    2012-04-01 18:25:11
    3.6 在PowerPoint内管理文件 65 3.6.1 创建新文件夹 66 3.6.2 复制演示文稿 67 3.6.3 删除演示文稿 67 3.6.4 重命名演示文稿 67 3.6.5 映射网络驱动器 67 3.7 小结 68 第4章 创建幻灯片和文本框 69 ...
  • PowerPoint.2007宝典 1/10

    2012-04-01 18:21:50
    3.6 在PowerPoint内管理文件 65 3.6.1 创建新文件夹 66 3.6.2 复制演示文稿 67 3.6.3 删除演示文稿 67 3.6.4 重命名演示文稿 67 3.6.5 映射网络驱动器 67 3.7 小结 68 第4章 创建幻灯片和文本框 69 ...
  • DELPHI中消息处理机制 Delphi是Borland公司提供一种全新WINDOWS编程开发工具。由于它采用了具有弹性和可重用面向对象Pascal(object-...众多编程语言(如VB,PowerBuilder,Powerpoint等)中脱颖

    DELPHI中的消息处理机制

    Delphi是Borland公司提供的一种全新的WINDOWS编程开发工具。由于它采用了具有弹性的和可重用的面向对象Pascal(object-orientedpascal)语言,并有强大的数据库引擎(BDE),快速的代码编译器,同时又提供了众多出色的构件。受到广大编程人员的青睐。在众多的编程语言(如VB,PowerBuilder,Powerpoint等)中脱颖而出。其中一个DELPHI强于其他编程语言(如VB4.0)的地方就是在DELPHI中可自定义消息,并可直接处理消息。这对于那些希望编写自己的构件(Component),或者希望截获。过滤消息的用户来说是必不可少的。因为编写构件一般要对相应的消息进行处理。下面就对Delphi中消息处理机制进行一下介绍。

    一。DELPHIVCL中消息的传递

    Delphi中每一个VCL(VisualComponentLibrary)构件(如Tbutton,Tedit等)都有一内在的消息处理机制,其基本点就是构件类接收到某些消息并把它们发送给适当的处理方法,如果没有特定的处理方法,则调用缺省的消息处理句柄。

    其中MainWndProc是定义在Twincontrol类中的一个静态方法,不能被重载(Override)。它不直接处理消息,而是交由wndproc方法处理,并为wndproc方法提供一个异常处理模块。Mainwndproc方法声明如下:
    procedure MainWndProc(varMessage: TMessage);

    WndProc是在Tcontrol类中定义的一个虚拟方法,由它调用dispatch方法来进行消息的分配,wndproc方法声明如下:
    procedure WndProc(varMessage: TMessage); virtual;

    dispatch方法是在Tobject根类中定义的,其声明如下:
    procedure Tobject。dispatch(varMessage);传递给dispatch的消息参数必须是一个记录类型,且这个记录中第一个入点必须是一个Cardinal类型的域(field),它包含了要分配的消息的消息号码。例如:

    type
    Tmessage=record
    Msg:cardinal;
    wparam:word;
    lparam:longint; 
    result:longint;
    end;

    而Dispatch方法会根据消息号码调用构件的最后代类中处理此消息的句柄方法。如果此构件和它的祖先类中都没有对应此消息的处理句柄,Dispatch方法便会调用DefaultHandler方法。DefaultHandler方法是定义于TObject中的虚拟方法,其声明如下:
    Procedure DefaultHandler(varMessage); virtual;

    Tobject类中的DefaultHandler方法只是实现简单的返回而不对消息进行任何的处理。我们可以通过对此虚拟方法的重载,在子类中实现对消息的缺省处理。对于VCL中的构件而言,其DefaultHandler方法会启动windows API函数DefWindowProc对消息进行处理。

    二。DELPHI中的消息处理句柄

    在DELPHI中用户可以自定义消息及消息处理句柄。消息处理句柄的定义有如下几个原则:

    1。消息处理句柄方法必须是一个过程,且只能传递一个Tmessage型变量参数。
    2。方法声明后要有一个message命令,后接一个在0到32767之间的消息标号(整型常数)。
    3。消息处理句柄方法不需要用override命令来显式指明重载祖先的一个消息处理句柄,另外它一般声明在构件的protected或private区。
    4。在消息处理句柄中一般先是用户自己对消息的处理,最后用inherited命令调用祖先类中对应此消息的处理句柄(有些情况下可能正相反)。由于可能对祖先类中对此消息的处理句柄的名字和参数类型不清楚,而调用命令inherited可以避免此麻烦,同样如果祖先类中没有对应此消息的处理句柄,inherited就会自动调用Defaulthandler方法(当然如果要屏蔽掉此消息,就不用inherited命令了)。

    消息处理句柄方法声明为:
    procedure Mymsgmethod(var message:Tmessage);message Msgtype;

    同样用户也可以定义自己的消息,用户自定义消息应从WM_USER开始。
    自定义消息及消息处理句柄举例如下:

    const my_paint=Wm_user+1;

    type 
    Tmypaint=record
    msgid:cardinal;
    msize:word;
    mcolor:longint;
    msgresult:longint;
    end;

    type

    Tmycontrol=class(TCustomControl)
    protected 
    procedure change(var message:Tmypaint); message my_paint;
    end;

    procedure Tmycontrol.change(var message:Tmypaint);
    begin
    size:=message.msize; {设置Tmybutton尺寸属性}
    color:=message.mcolor; {设置Tmybutton颜色属性}
    {do something else}
    inherited; {交由Tcustomcontrol处理}
    end;

     


    三。过滤消息

    过滤消息又称消息陷阱。在一定情况下,用户可能需要屏蔽某些消息。或者截获某些消息进行处理。由以上介绍可以看出过滤消息一般有三种途径:
    (1)。重载构件继承的虚拟方法wndproc。
    (2)。针对某消息编写消息处理句柄。
    (3)。重载构件继承的虚拟方法DefHandler,在其中对消息进行处理。其中常用的方法是方法(2),在上节中已介绍过了,方法(1)与方法(3)相似,这里只简单介绍一下方法(1)。

    重载虚拟方法wndproc的一般过程如下:
    procedure Tmyobject.wndproc(var message: Tmessage);
    begin
    {判断此消息是否该处理}
    inherited wndproc(message);
    {未处理的消息交由父辈wndproc方法处理}
    end;


    由此可以看出在wndproc方法中处理消息的优势是可以过滤整个范围内的消息,而不必为每个消息指定一个处理句柄,事实上Tcontrol构件中就是利用它来过滤并处理所有的鼠标消息的(从WM_mousefirst到WM_mouselast,如下代码示)。同样利用它也可以阻止某些消息被发送给处理句柄。

    procedure TControl.WndProc(var Message: TMessage);
    begin

    if (Message.Msg>=WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST)
    then
    if Dragging then DragMouseMsg(TWMMouse(Message)) {处理拖曳事件}
    else {处理其他鼠标消息}
    end;

    Dispatch(Message);

    {否则正常发送消息}

    end;


    下例为一简单的自定义构件例子:
    Tmyedit类是从Tedit类派生出的一个新类,它的特点是在运行中不能获得焦点,不能由键盘输入(有点类似Tlabel构件)。我们可在其wndproc方法中过滤出WM_setfocus,WM_mousemove消息并进行处理来达到上述要求,源程序如下:

    unit myedit;

    interface

    uses

    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,StdCtrls;

    type

    Tmyedit = class(TEdit)
    protected
    procedure wndproc(var message:Tmessage);override;
    end;

    procedure Register;

    implementation

    procedure Register;
    begin
    RegisterComponents('Samples', [Tmyedit]);
    end;

    procedure Tmyedit.WndProc(var message:tmessage);
    begin
    if message.msg=wm_mousemove then 
    begin 
    cursor:=crarrow; { 设置光标为crarrow, 而不是缺省的crBeam 光 标}
    exit; 
    end;

    if message.msg=wm_SetFocus then exit; 
    {屏蔽掉WM_setfocus消息,不让Tmyedit控件获得输入焦点}

    inherited wndproc(message);
    {其他消息交父辈wndproc处理}

    end;

    end.


    您可以将Tmyedit 加 到Component Palette中检验其性能。

    由以上介绍可以看出,只有清楚了DelphiVCL中的消息处理机制,掌握好处理各种消息的方法和时机(必要时要借助各种工具,如winsight32,spy等),并结合OOP语言的特点,我们才可能编出高质量的构件。这当然要靠读者在实践中不断摸索,积累经验。

    ===============================================================

    上文写的很不错,但是读完之后,引发一个问题,就是三种消息函数的触发顺序。经过我的研究,应该是WndProc最有优先权,它处理完以后,还要看它是否继续传递。如果继续传递,可以继续触发下一个消息函数。其中消息处理句柄函数优先权更高,如果它存在,就触发它,然后看它是否继续传递。如果继续传递还可以传递给DefaultHandler方法。这样一条消息就可以同时触发三个消息处理函数。

    但是如果消息处理句柄函数不存在,那么WndProc会直接传递消息给DefaultHandler(其实是WndProc找不到消息处理句柄函数之后,靠TObject的Dispatch发送消息到DefaultHandler的)。

    复制代码
    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
    
    const
      WM_TEST=WM_USER+100;
        
    type
      TForm1 = class(TForm)
        Edit1: TEdit;
        Edit2: TEdit;
        Button1: TButton;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
      private
        { Private declarations }
        procedure WndProc(var Message: TMessage); override;  // 第一优先权
        procedure MyMessage(var Msg: TMessage); message WM_TEST; // 第二优先权
        procedure DefaultHandler(var Message); override; // 第三优先权
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm1.WndProc(var Message: TMessage);
    begin
      case Message.Msg of
        WM_TEST: begin ShowMessage('message in wndproc'); end; // 这里加上Exit就不再继续传递了
      end;
      // 一定要加上这句,否则编译通不过。因为绝大部分消息没人处理了
      inherited WndProc(Message); // 会一路向上调用,直到TControl.WndProc调用Dispatch来寻找消息处理函数
    end;
    
    procedure TForm1.MyMessage(var Msg: TMessage);
    begin
      // 消息编号处理函数会首先响应Dispatch函数
      ShowMessage('message in MyMessage function');
      // 处理完以后,可选择是否继续传递给DefaultHandler
      inherited; // 加上这句就会继续传递,否则就到此为止了
    end;
    
    procedure TForm1.DefaultHandler(var Message);
    var
      P: PChar;
    begin
      with TMessage(Message) do
        case Msg of
          WM_TEST: begin ShowMessage('message in DefaultHandler'); Exit; end; // 这里加不加Exit都不影响
        else
          // 一定要加上这句,否则编译通不过。因为一些基本的消息没人处理,程序就没法正常运行了。(没有默认的消息处理函数了,消息没法自然终止)
          inherited DefaultHandler(Message); // 它会调用TCustomForm.DefaultHandler,然后继续向上传递
        end;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      SendMessage(Handle, WM_TEST, 0, 0);  // 第一种发消息方法
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    begin
      Perform(WM_TEST, 0, 0); // 第二种发消息方法
    end;
    
    end.
    复制代码

     但是如果这样写:

    复制代码
    procedure TForm1.WndProc(var Message: TMessage);
    begin
      // 一定要加上这句,否则编译通不过。因为绝大部分消息没人处理了
      inherited WndProc(Message); // 会一路向上调用,直到TControl.WndProc调用Dispatch来寻找消息处理函数
    
      case Message.Msg of
        WM_TEST: begin ShowMessage('message in wndproc'); end; // 这里加上Exit就不再继续传递了
      end;
    end;
    复制代码

    虽然WndProc仍然具有最高优先权,但是因为调用关系,它自身处理消息的结果会在最后显示。

    疑问:那两个编译不过的地方,本来应该不是问题。但Delphi在建立窗体和初始化的过程中,使用了某些消息处理,而且还是需要返回值的,所以不处理就会出问题了。有空做个测试研究一下。

    ============================================================

    新的方法:可以这样改变现有控件的WndProc指针(不修改控件的源代码):截获DBGrid的滚动条消息。这种方法其实相当于就是windows的子类化技术。之所以说“相当于”是因为并没有直接替换窗口类的回调函数,而是替换了Delphi函数之间的替换而已。

    复制代码
    FOldProc : TWndMethod;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FOldProc := dbgrid1.WindowProc;
      dbgrid1.WindowProc := MyProc; // 简单替换窗口函数
    end;
    
    procedure TForm1.MyProc(var message: TMessage);
    begin
      if message.Msg = WM_VSCROLL then
        showmessage('vscroll')
      else if message.Msg = WM_HSCROLL then
        showmessage('hscroll');
      FOldProc(message); // 当场使用旧窗口函数的函数指针,简单又完美
    end;
    复制代码

    总结:Delphi的魅力真是无穷呀,除了WndProc,另两种方法都是Delphi自己提供的。就算是WndProc也变得有所不同——换成了Delphi自己的函数,不带句柄,且可相互替换和传递,空前灵活且威力巨大。甚至还能加上try catch语句保护,从而导致程序不会动不动就崩溃。

    展开全文
  • DELPHI中消息处理机制 Delphi是Borland公司提供一种全新WINDOWS编程开发工具。由于它采用了具有弹性和可重用面向对象Pascal(object-...众多编程语言(如VB,PowerBuilder,Powerpoint等)...

    DELPHI中的消息处理机制

    Delphi是Borland公司提供的一种全新的WINDOWS编程开发工具。由于它采用了具有弹性的和可重用的面向对象Pascal(object-orientedpascal)语言,并有强大的数据库引擎(BDE),快速的代码编译器,同时又提供了众多出色的构件。受到广大编程人员的青睐。在众多的编程语言(如VB,PowerBuilder,Powerpoint等)中脱颖而出。其中一个DELPHI强于其他编程语言(如VB4.0)的地方就是在DELPHI中可自定义消息,并可直接处理消息。这对于那些希望编写自己的构件(Component),或者希望截获。过滤消息的用户来说是必不可少的。因为编写构件一般要对相应的消息进行处理。下面就对Delphi中消息处理机制进行一下介绍。

     

    一. DELPHIVCL中消息的传递

    Delphi中每一个VCL(VisualComponentLibrary)构件(如TbuttonTedit等)都有一内在的消息处理机制,其基本点就是构件类接收到某些消息并把它们发送给适当的处理方法,如果没有特定的处理方法,则调用缺省的消息处理句柄。

    其中MainWndProc是定义在Twincontrol类中的一个静态方法,不能被重载(Override)。它不直接处理消息,而是交由wndproc方法处理,并为wndproc方法提供一个异常处理模块。Mainwndproc方法声明如下:

    procedure MainWndProc(varMessage: TMessage);

     

    WndProc是在Tcontrol类中定义的一个虚拟方法,由它调用dispatch方法来进行消息的分配,wndproc方法声明如下:

    procedure WndProc(varMessage: TMessage); virtual;

     

    dispatch方法是在Tobject根类中定义的,其声明如下:

    procedure Tobject。dispatch(varMessage);

     

    传递给dispatch的消息参数必须是一个记录类型,且这个记录中第一个入点必须是一个Cardinal类型的域(field),它包含了要分配的消息的消息号码。例如:

    type
    Tmessage=record
    Msg:cardinal;
    wparam:word;
    lparam:longint; 
    result:longint;
    end;

     

    Dispatch方法会根据消息号码调用构件的最后代类中处理此消息的句柄方法。如果此构件和它的祖先类中都没有对应此消息的处理句柄,Dispatch方法便会调用DefaultHandler方法。DefaultHandler方法是定义于TObject中的虚拟方法,其声明如下:

    Procedure DefaultHandler(varMessage); virtual;

     

    Tobject类中的DefaultHandler方法只是实现简单的返回而不对消息进行任何的处理。我们可以通过对此虚拟方法的重载,在子类中实现对消息的缺省处理。对于VCL中的构件而言,其DefaultHandler方法会启动windows API函数DefWindowProc对消息进行处理。

     

    二. DELPHI中的消息处理句柄

    在DELPHI中用户可以自定义消息及消息处理句柄。消息处理句柄的定义有如下几个原则:

    1。消息处理句柄方法必须是一个过程,且只能传递一个Tmessage型变量参数。
    2。方法声明后要有一个message命令,后接一个在0到32767之间的消息标号(整型常数)。
    3。消息处理句柄方法不需要用override命令来显式指明重载祖先的一个消息处理句柄,另外它一般声明在构件的protectedprivate区。
    4。在消息处理句柄中一般先是用户自己对消息的处理,最后用inherited命令调用祖先类中对应此消息的处理句柄(有些情况下可能正相反)。由于可能对祖先类中对此消息的处理句柄的名字和参数类型不清楚,而调用命令inherited可以避免此麻烦,同样如果祖先类中没有对应此消息的处理句柄,inherited就会自动调用Defaulthandler方法(当然如果要屏蔽掉此消息,就不用inherited命令了)。

    消息处理句柄方法声明为:

    procedure Mymsgmethod(var message:Tmessage);message Msgtype;

     

    同样用户也可以定义自己的消息,用户自定义消息应从WM_USER开始。
    自定义消息及消息处理句柄举例如下:

    const my_paint=Wm_user+1;
    
    type 
    Tmypaint=record
    msgid:cardinal;
    msize:word;
    mcolor:longint;
    msgresult:longint;
    end;
    
    type
    
    Tmycontrol=class(TCustomControl)
    protected 
    procedure change(var message:Tmypaint); message my_paint;
    end;
    
    procedure Tmycontrol.change(var message:Tmypaint);
    begin
    size:=message.msize; {设置Tmybutton尺寸属性}
    color:=message.mcolor; {设置Tmybutton颜色属性}
    {do something else}
    inherited; {交由Tcustomcontrol处理}
    end;
    
     

     


    三. 过滤消息

    过滤消息又称消息陷阱。在一定情况下,用户可能需要屏蔽某些消息。或者截获某些消息进行处理。由以上介绍可以看出过滤消息一般有三种途径:
    (1)。重载构件继承的虚拟方法wndproc
    (2)。针对某消息编写消息处理句柄。
    (3)。重载构件继承的虚拟方法DefHandler,在其中对消息进行处理。其中常用的方法是方法(2),在上节中已介绍过了,方法(1)与方法(3)相似,这里只简单介绍一下方法(1)。

    重载虚拟方法wndproc的一般过程如下:

    procedure Tmyobject.wndproc(var message: Tmessage);
    begin
    {判断此消息是否该处理}
    inherited wndproc(message);
    {未处理的消息交由父辈wndproc方法处理}
    end;

     

    由此可以看出在wndproc方法中处理消息的优势是可以过滤整个范围内的消息,而不必为每个消息指定一个处理句柄,事实上Tcontrol构件中就是利用它来过滤并处理所有的鼠标消息的(从WM_mousefirstWM_mouselast,如下代码示)。同样利用它也可以阻止某些消息被发送给处理句柄。

    procedure TControl.WndProc(var Message: TMessage);
    begin
    
    if (Message.Msg>=WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST)
    then
    if Dragging then DragMouseMsg(TWMMouse(Message)) {处理拖曳事件}
    else {处理其他鼠标消息}
    end;
    
    Dispatch(Message);
    
    {否则正常发送消息}
    
    end;

     

    下例为一简单的自定义构件例子:
    Tmyedit类是从Tedit类派生出的一个新类,它的特点是在运行中不能获得焦点,不能由键盘输入(有点类似Tlabel构件)。我们可在其wndproc方法中过滤出WM_setfocusWM_mousemove消息并进行处理来达到上述要求,源程序如下:

    unit myedit;
    
    interface
    
    uses
    
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,StdCtrls;
    
    type
    
    Tmyedit = class(TEdit)
    protected
    procedure wndproc(var message:Tmessage);override;
    end;
    
    procedure Register;
    
    implementation
    
    procedure Register;
    begin
    RegisterComponents('Samples', [Tmyedit]);
    end;
    
    procedure Tmyedit.WndProc(var message:tmessage);
    begin
    if message.msg=wm_mousemove then 
    begin 
    cursor:=crarrow; { 设置光标为crarrow, 而不是缺省的crBeam 光 标}
    exit; 
    end;
    
    if message.msg=wm_SetFocus then exit; 
    {屏蔽掉WM_setfocus消息,不让Tmyedit控件获得输入焦点}
    
    inherited wndproc(message);
    {其他消息交父辈wndproc处理}
    
    end;
    
    end.

     


    您可以将Tmyedit 加 到Component Palette中检验其性能。

    由以上介绍可以看出,只有清楚了DelphiVCL中的消息处理机制,掌握好处理各种消息的方法和时机(必要时要借助各种工具,如winsight32spy等),并结合OOP语言的特点,我们才可能编出高质量的构件。这当然要靠读者在实践中不断摸索,积累经验。

    ===============================================================

    上文写的很不错,但是读完之后,引发一个问题,就是三种消息函数的触发顺序。经过我的研究,应该是WndProc最有优先权,它处理完以后,还要看它是否继续传递。如果继续传递,可以继续触发下一个消息函数。其中消息处理句柄函数优先权更高,如果它存在,就触发它,然后看它是否继续传递。如果继续传递还可以传递给DefaultHandler方法。这样一条消息就可以同时触发三个消息处理函数。

    但是如果消息处理句柄函数不存在,那么WndProc会直接传递消息给DefaultHandler(其实是WndProc找不到消息处理句柄函数之后,靠TObjectDispatch发送消息到DefaultHandler的)。

    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
    
    const
      WM_TEST=WM_USER+100;
        
    type
      TForm1 = class(TForm)
        Edit1: TEdit;
        Edit2: TEdit;
        Button1: TButton;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
      private
        { Private declarations }
        procedure WndProc(var Message: TMessage); override;  // 第一优先权
        procedure MyMessage(var Msg: TMessage); message WM_TEST; // 第二优先权
        procedure DefaultHandler(var Message); override; // 第三优先权
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm1.WndProc(var Message: TMessage);
    begin
      case Message.Msg of
        WM_TEST: begin ShowMessage('message in wndproc'); end; // 这里加上Exit就不再继续传递了
      end;
      // 一定要加上这句,否则编译通不过。因为绝大部分消息没人处理了
      inherited WndProc(Message); // 会一路向上调用,直到TControl.WndProc调用Dispatch来寻找消息处理函数
    end;
    
    procedure TForm1.MyMessage(var Msg: TMessage);
    begin
      // 消息编号处理函数会首先响应Dispatch函数
      ShowMessage('message in MyMessage function');
      // 处理完以后,可选择是否继续传递给DefaultHandler
      inherited; // 加上这句就会继续传递,否则就到此为止了
    end;
    
    procedure TForm1.DefaultHandler(var Message);
    var
      P: PChar;
    begin
      with TMessage(Message) do
        case Msg of
          WM_TEST: begin ShowMessage('message in DefaultHandler'); Exit; end; // 这里加不加Exit都不影响
        else
          // 一定要加上这句,否则编译通不过。因为一些基本的消息没人处理,程序就没法正常运行了。(没有默认的消息处理函数了,消息没法自然终止)
          inherited DefaultHandler(Message); // 它会调用TCustomForm.DefaultHandler,然后继续向上传递
        end;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      SendMessage(Handle, WM_TEST, 0, 0);  // 第一种发消息方法
    end;
    
    procedure TForm1.Button2Click(Sender: TObject);
    begin
      Perform(WM_TEST, 0, 0); // 第二种发消息方法
    end;
    
    end.

     但是如果这样写:

    procedure TForm1.WndProc(var Message: TMessage);
    begin
      // 一定要加上这句,否则编译通不过。因为绝大部分消息没人处理了
      inherited WndProc(Message); // 会一路向上调用,直到TControl.WndProc调用Dispatch来寻找消息处理函数
    
      case Message.Msg of
        WM_TEST: begin ShowMessage('message in wndproc'); end; // 这里加上Exit就不再继续传递了
      end;
    end;

    虽然WndProc仍然具有最高优先权,但是因为调用关系,它自身处理消息的结果会在最后显示。

    疑问:那两个编译不过的地方,本来应该不是问题。但Delphi在建立窗体和初始化的过程中,使用了某些消息处理,而且还是需要返回值的,所以不处理就会出问题了。有空做个测试研究一下。

    ============================================================

    新的方法:可以这样改变现有控件的WndProc指针(不修改控件的源代码):截获DBGrid的滚动条消息。这种方法其实相当于就是windows的子类化技术。之所以说“相当于”是因为并没有直接替换窗口类的回调函数,而是替换了Delphi函数之间的替换而已。

    FOldProc : TWndMethod;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FOldProc := dbgrid1.WindowProc;
      dbgrid1.WindowProc := MyProc; // 简单替换窗口函数
    end;
    
    procedure TForm1.MyProc(var message: TMessage);
    begin
      if message.Msg = WM_VSCROLL then
        showmessage('vscroll')
      else if message.Msg = WM_HSCROLL then
        showmessage('hscroll');
      FOldProc(message); // 当场使用旧窗口函数的函数指针,简单又完美
    end;

    总结:Delphi的魅力真是无穷呀,除了WndProc,另两种方法都是Delphi自己提供的。就算是WndProc也变得有所不同——换成了Delphi自己的函数,不带句柄,且可相互替换和传递,空前灵活且威力巨大。甚至还能加上try catch语句保护,从而导致程序不会动不动就崩溃。

    转载于:https://www.cnblogs.com/YiShen/p/9720245.html

    展开全文
  • Delphi是Borland公司提供一种全新WINDOWS编程开发工具.由于它采用了具有弹性和可重用面向对象Pascal(object-...众多编程语言(如VB,PowerBuilder,Powerpoint等)中脱颖而出.其中一个DELPHI强于其他编
  • HTML中显示可以Office 2003中处理吗?没有这么好!你还必须支持Office 97!  你只能去找一个符合当前需求工具了,但是接着收到更多要求了。"这能Word中做到吗?Powerpoint能做到吗?能不能用调制解调...
  • 给你PPT瘦个身吧

    2014-10-16 08:51:48
    在PowerPoint演示文稿中,图片和视频会占用很大的存储空间。因此,要为演示文稿减肥,主要处理的对象便是这二者。PowerPoint 2013为用户提供了完美的压缩手段。 (1)在文档属性窗口中,查看未经减...
  • DELPHI中自定义消息发送和接收

    千次阅读 2015-07-31 15:27:55
    DELPHI中消息处理机制 Delphi是Borland公司提供一种全新WINDOWS编程开发工具。由于它采用了具有弹性和可重用面向对象Pascal(object-...众多编程语言(如VB,PowerBuilder,Powerpoint等)中脱颖而出
  • 您再也不必担心文件格式的兼容性:用Word 97、写字板或过去版本的WPS 处理的文件,WPS 2000 都能很好地读入,最大程度地保护您的利益。 以往的WPS 着重处理文字,而现在的WPS 2000 极大地丰富了文字处理软件的内涵,...
  • 第一部分讲述事务处理的概念、并发控制技术和数据库恢复技术;第二部分讲述面向数据库的概念、语言、设计及对象-关系和扩展-关系系统;第三部分主要描述了数据库安全和客户-服务器体系结构;第四部分简要介绍了xml、...
  • 企业级应用开发中,Excel报表是一种最常见报表需求,POI 是apache子项目,目标是处理ole2对象。它提供了一组操纵Windows文档Java API目前比较成熟是HSSF接口,处理MS Excel(97-2002)对象。它不象我们...
  • 根据当前要操作的对象的形状,支持六种捕捉方式:捕捉活动窗口、捕捉窗口/对象、捕捉矩形区域、捕捉手绘区域、捕捉整个屏幕、捕捉滚动窗口。它还支持屏幕录像、屏幕放大镜、拾取屏幕颜色、屏幕标尺、快捷键等诸多...
  • 我这里用是ApachePOI项目,它是目前比较成熟HSSF接口,用来处理Excel对象。其实POI不仅仅只能处理excel,它还可以处理word、PowerPoint、Visio、甚至Outlook。 一.首先介绍利用POI如何生成excel。 首先生成...
  • springMVC(4)---生成excel文件并导出 ...我这里用是ApachePOI项目,它是目前比较成熟HSSF接口,用来处理Excel对象。其实POI不仅仅只能处理excel,它还可以处理word、PowerPoint、Visio、甚至Outlook。 一....

空空如也

空空如也

1 2 3 4 5 6
收藏数 111
精华内容 44
关键字:

在powerpoint处理的对象是