• 【VB.NET】提取PPT中的SWF


    学坏了~先贴图~~~~~~~~~~~~~~~~~

    '这几天在网上看到了一些提取PPT内SWF的方法,于是编程实现了一下。其他的几种OFFICE文件内嵌后基本都可以用另存网页的方式解决。。。。。
    '思路是这样的:
    '1、通过PPT的COM对象,遍历幻灯片内容并把找到的内嵌SWF控件复制到剪贴板
    '2、访问剪贴板对象,提取其中的嵌入对象内容得到一个流
    '3、处理这个流前面的多余部分,并保存为文件
    '还存在一些问题:
    '1、如果只有控件,而没有SWF被嵌入,不知道代码会得到怎么样的结果

    经过协商,得到了一个比较满意的结果。。。公开部分代码和软件。但请遵循程序的协议~~~~~~

    请注意,这里所提到的“内嵌”,指将SWF文件内置于OFFICE文件中,即在设计文档时将FLASH AX控件的Embed Move属性设置为True。



    然后保存文件。若要在Excel中观看到动画,请退出设计模式(点那个三角板、铅笔、直尺组成的图标使之处于非选中状态)。

    而后是编码了,首先是一个总体的调用方式:

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            Dim file As New OpenFileDialog
            file.Filter = "*.XLS|*.XLS"
            file.ShowDialog()
            If file.FileName <> "" Then
                AddHandler MSExcel.Progress, AddressOf Progress
                MSExcel.GetSWFFile(file.FileName)
                RemoveHandler MSExcel.Progress, AddressOf Progress
            End If
        End Sub

        Private Sub Progress(ByVal Msg As String)
            lstMsg.Items.Add(Msg)
        End Sub


    在按钮点击事件,调用一个打开文件对话框,以打开要提取SWF文件的XLS文档,而后为了显示进度消息,添加事件处理函数句柄到Progress函数,当使用完毕后,该句柄被移除。而Progress函数是非常简单的,仅仅在一个名为lstMsg的ListBox里面添加得到的消息。
    接下来实现GetSWFFile函数,这个函数被设定在一个仅有共享成员的类(MSExcel)中,该类将有一个共享成员:
    Public Shared Event Progress(ByVal Msg As String)
    而为了以后扩展的需要,把进程和文档引用都放在了这里:
        Private Shared app As Excel.Application
        Private Shared book As Excel.Workbook
    当然,为了使用这些代码,需要在类文件开头导入一个名空间,在这里使用了指定别名的方式:
    Imports Excel = Microsoft.Office.Interop.Excel
    (请在引用对话框添加Microsoft.Office.Interop.Excel.XX及相应版本OFFICE的.NET引用,在这里笔者使用的是OFFICE2003,所以添加的版本都为11,OFFICE的引用当中会包含一些名空间,而对于我们的程序来讲只使用了其中包含一些常量的名空间,所以你完全可以用相应的数字来代替这些常量而不引用名空间,这样做的好处后面还将叙述)
    而后,编写一个实现复制FLASH内嵌对象的函数、一个将剪贴板内容保存为SWF文件的函数(将这个功能分离是因为它可以重用:在其他OFFICE文件格式处理时也用这个函数)。首先描述第一个函数,这个函数要实现的功能就是把XLS文件中的FLASH OLE内容复制出来,简单的做法是利用EXCEL:
    1、打开文档:
            app = New Excel.Application()
            app.Visible = Microsoft.Office.Core.MsoTriState.msoTrue(在VBA、VB6、VB.NET的IDE中这个msoTrue实际上为-1,对于VB.NET的布尔型在IDE和CLR中的关系等等不想赘述。有疑问的可以参考VB.NET核心编程或.NET FRAMEWORK详解等。在我们的程序里“最终”应使用-1来代替这些常量,后面将给予说明)
            book = app.Workbooks.Open(file)
    2、遍历并复制FLASH OLE内容
    这里的叙述可能有些小问题,不深究了。实际上我们的操作是复制了FLASH的控件,其他操作由OFFICE自动完成了。
    A、我们都知道,在EXCEL里,工作簿里面有N个(默认3个)工作表,而我们要的FLASH在某个工作表里面,所以我们先遍历book这个工作簿的Sheets集合,而后遍历每个Sheet的Shapes集合,从而达到遍历Shape的目的。
    For i As Integer = 1 To book.Sheets.Count
                For j As Integer = 1 To book.Sheets(i).Shapes.count
                      ‘这里进行识别
                Next
    Next
    B、当得到Shape引用之后,我们就要识别它是否包含了FLASH的OLE内容。这个内容如何识别呢,我们知道OLE对象都有一个OLEFormat属性,它包含了OLE信息,其中ProgID属性是一个很有用的字符串,包括了OLE对象的类型,我们使用这个属性来识别:
                        If InStr(1, book.Sheets(i).Shapes(j).OLEFormat.ProgID, "ShockwaveFlash.ShockwaveFlash", vbTextCompare) = 1 Then
                            ’这里执行复制代码
                        End If
    C、当我们得到了含有FLASH OLE的Shape对象引用之后,就可以复制它,在不同的OFFICE工具中,复制方法可能有些差异,但在EXCEL中我们的对象是Shape,它支持COPY方法,于是我们直接复制:
                            '是FLASH内嵌文件,则复制到剪贴板
                            book.Sheets(i).Shapes(j).copy()
    D、将处理权交给剪贴板处理函数:
                            Clipboard2File(book.Name, i, j)
    这个函数有三个参数:第一个是处理的文件名,第2、3个是我们遍历时用到的数据,其实这三个参数都是为了命名最终生成的文件而传入的,如果有必要,你可以把这个保存文件功能分离出来——也许这样更好,但是我没有这样做,理由就是……我懒

    3、剪贴板处理:
    就是我们上面的Clipboard2File函数了,我把它放在一个类(Clipboard2FileClass)里面,这个类仅有一个共享成员:我们的处理函数。
    这里,我们使用的是Clipboard对象来读取剪贴板数据:
    Dim iData As IDataObject = Clipboard.GetDataObject
    这都是大家所熟知的东东了,用它就可以读回来我们要的内容,但是内容格式是什么呢?当然是“内嵌对象”了,呵呵
    Dim ms As IO.MemoryStream = iData.GetData("Embedded Object")
    于是我们就得到一个含有SWF文件内容的流——它是我们复制到内存中全部数据的一部分,也正是我们关心的SWF文件所在部分。到这里,有一种即将大功告成的希望了吧?也许没什么好高兴的,我们一共才写了几十行代码?
    接下来处理这个流,如何提取SWF文件是当务之急,如果我们熟知SWF文件的结构该有多好……(……)(*(——&*%……¥%……¥)(()——(
    确实,已经有同志付出了很多努力,给了我们一个母语版的SWF文件结构,对此表示敬意,对原作者表示感谢!
    http://bbs.zhujiangroad.com/html/2008/6/250234.html
    上面的地址,就是了~~~~~我们要用的信息就是前8字节:
    1
    2     FWS,CWS
    3
    4     版本
    5
    6     文件大小
    7
    8
    OK,开始找文件头吧!这个内存流查找我还真不会,转化成数组,而后遍历
    Dim bs As Byte() = ms.ToArray()                                 '将原始流转化为数组以对SWF文件头进行搜索
            Dim point As Integer                                            '文件头的位置
            For index As Integer = 0 To bs.Length - 3
                If bs(index) = CByte(&H46) OrElse bs(index) = CByte(&H43) Then  '有两种情况,可能是SWF,也可能是SWC,视FLASH生成文件时是否压缩
                    If bs(index + 1) = CByte(&H57) AndAlso bs(index + 2) = CByte(&H53) Then
                        point = index
                        Exit For
                    End If
                End If
            Next
    这样,我们的point里面保存的就是SWF文件的开始位置了~~再得到文件长度,就可以把数组写入一个新流,而后保存
            Dim filesize As Integer = System.BitConverter.ToInt32(bs, point + 4)
            Dim by As New IO.MemoryStream(bs, point, filesize)
    此时,by流中就是我们的SWF文件,保存它!
    Dim filename As String = My.Application.Info.DirectoryPath & "\SWFFile\" & name & "-" & i & "-" & j & ".swf"
            If Not My.Computer.FileSystem.DirectoryExists(My.Application.Info.DirectoryPath & "\SWFFile\") Then My.Computer.FileSystem.CreateDirectory(My.Application.Info.DirectoryPath & "\SWFFile\")
            If My.Computer.FileSystem.FileExists(filename) Then My.Computer.FileSystem.DeleteFile(filename)
            '创建一个流以保存内存流到磁盘
            Dim dumpFile As IO.FileStream = New IO.FileStream(filename, IO.FileMode.OpenOrCreate, IO.FileAccess.ReadWrite)
            '保存内存流
            by.WriteTo(dumpFile)
    上面的过程进行了文件夹和文件是否存在等判断和操作,以保证程序的“健壮”——不能因为一个文件操作搞得崩溃了啊。。。多丢人,呵呵,最后擦擦屁屁:
            '退出用过的流
            dumpFile.Close()
            ms.Close()
            by.Close()
            '清除剪贴板
            My.Computer.Clipboard.Clear()
    好了,至此我们的EXCEL中提取SWF的工作告一段落,你可以测试你的代码了……感觉还不错吧?连注释都算进去,不到百行,解决了这个问题。。。
    下面是一个测试文件和写好的主程序。。但使用时有一些限制,如果仔细阅读了上文,相信你已经知道怎么做~~~~

    /Files/zcsor/提取SWF工具测试文件.xls

    /Files/zcsor/复制OFFICE内嵌的SWF.7z


    最后的最后,我们来讨论一下,这个代码存在的问题~~~适应性,为了让更多的OFFICE版本被我们支持,你可以进行以下工作~~~(当然我没做,懒啊)
    0、复制一份解决方案目录,打开这个复制品
    1、用鼠标指着每个OFFICE常量,它怕怕了就把实际数值告诉你,用数值替换这些常量
    2、将APP和BOOK声明为OBJCET,并改用CreateObject 方法创建APP对象
    3、在引用页扫描未使用引用,并删除它们
    OK,你的更通用的版本诞生了~~~~可回到IDE修改代码时就没有那么多提示喽~~~~~~呵呵呵。。。并不是我们不知道怎么做更好怎么才能做到更好,是吧?

  • 相关阅读:
    javascript推荐书籍
    [zt]介绍一本搜索引擎爬虫方面的好书
    一些文章资源和趣闻
    【详细的英语自学指导】黑猫出版社 Easyreads系列13本书,科普英语,入门好选择
    网上邻居疑难问题分析与总结
    关于文件夹权限的十个问答
    net send命令解析
    如何成为一名黑客
    全球最值得模仿的230个网站
    女生最爱不释手的30个网站
  • 原文地址:https://www.cnblogs.com/zcsor/p/1580143.html
Copyright © 2020-2023  润新知