mehtav has asked for the wisdom of the Perl Monks concerning the following question:

I am writing a code that recursively goes through a directory and extracts the title, name of the author and the date from the 1st slide of a powerpoint and outputs that data to an excel sheet. It runs correctly if there is only one ppt in the entire directory. Otherwise, it gives the following error: Win32::OLE(0.1712) error 0x80010108: "The object invoked has disconnected from its clients". I have not changed anything in the code but it suddenly started showing this error. The line causing the error seems to be "foreach $shape (in @placeholders)" but I can't figure out why the error is being caused.

The output is

Scanning the following directories in the tape: \01 Quick Start \02 Syntax \03 Variables \04 Statements \05 Conditionals \06 Loops \07 Special Variables \08 Operators \Directory \09 Regexes Title is Open Powerpoint Date is 11/19/2015 Author is Author \10 Subroutines \11 References \12 Files \13 Builtins \14 Modules \15 POD \16 New Features \17 Web Development \bw-contact \html \twotrees \code \css \images \Scripts \testimonials \BW \DB \XML \data \html Win32::OLE(0.1712) error 0x80010108: "The object invoked has disconnec +ted from its clients" at C:\<path>\open_ppt_out_excel_v1.3.pl line 67 +.

The output should just be the titles, names and the dates. The code is shown below

#!/usr/bin/perl #use strict; #use warnings; use Try::Tiny; use FindBin qw( $Bin ); use File::Spec::Functions qw( catfile ); use lib "$FindBin::Bin/../lib"; #use plsubs; use Cwd; use Win32::OLE; use Win32::OLE::Const 'Microsoft PowerPoint'; use Win32::OLE::Enum; use Win32::OLE qw(in valof with); use Win32::OLE::Const 'Microsoft Excel'; $Win32::OLE::Warn = 3; #--------------------------------------------------------------------- +--# # $scriptName= 'Get details from 1st slide of ppt'; # Last Edited: 11/19/2015 # $revision= 1.3; #--------------------------------------------------------------------- +--# # Description: # Searches through all files and folders of the to get details fro +m the first page of the ppt #--------------------------------------------------------------------- +--# # Revision History: # 1.0 Initial revision ###################################################################### +### # Sunroutine opens the ppt file and gets the details sub get_file_data { #print "get_file_data\n"; #debug my $name_file = shift; my $title; my $author; my $date; my $date_found = 0; my $ppt = get_ppt(); $ppt->{Visible} = 1; my $ppt_file = catfile $Bin, $name_file; my $presentation = $ppt->Presentations->Open($ppt_file); my $slides = Win32::OLE::Enum->new( $presentation->Slides ); my $num_slides = $presentation->Slides->Count; SLIDE: my $slide = $slides->Next; my $shapes1 = $slide->Shapes; #printf "%s:\t", $slide->Name; my $shapes = Win32::OLE::Enum->new( $slide->Shapes ); my $num_shapes = $shapes1->Count; #print "Num of shapes is $num_shapes\n"; while ( my $shape = $shapes->Next ) { if ($shape->HasTextFrame) { @placeholders = $slide->Shapes->Placeholders; foreach $shape (in @placeholders) { my $type = $shape->PlaceholderFormat->Type; if ( $type == ppPlaceholderTitle or $type == ppPlaceholderCenterTitle or $type == ppPlaceholderVerticalTitle ) { unless (defined $title) { $title = $shape->TextFrame->TextRange->text; #$title1 = $title =~ s|\W*||m; print "Title is $title\n"; } } else { #$author = $shape->TextFrame->TextRange->text; $text = $shape->TextFrame->TextRange->text; if ($text =~ m|(\d+[/-]\d+[/-]\d+)|) { unless (defined $date) { #print "date1"; $date = $1; print "Date is $date\n"; $date_found = 1; } } if ($date_found == 1) { $text =~ s|\d+[/-]\d+[/-]\d+||m; } if ($text =~ m/^\s*(\w+\s*\S*\w*.+)/ || $text =~ m/^\s*( +\w+\s*\w+.+)/ ) { unless (defined $author) { $author = $1; print "Author is $author\n"; } } } } #$author = $shape->TextFrame->TextRange->text; $text = $shape->TextFrame->TextRange->text; #if ($text =~ m|^\s*(\d+[/-]\d+[/-]\d+)|) if ($text =~ m|(\d+[/-]\d+[/-]\d+)|) { unless (defined $date) { #print "date2"; $date = $1; print "Date is $date\n"; $date_found = 1; } } if ($date_found == 1) { $text =~ s|\d+[/-]\d+[/-]\d+||m; } if (($text =~ m/^\s*(\w+\s*\w+.+)/ || $text =~ m/^\s*(\w+\s*\S +*\w*.+)/ ) && ($title ne $text)) { unless (defined $author) { $author = $1; print "Author is $author\n"; } } } } #print "Number of slides = $num_slides\n"; #print "\n"; $presentation->Close(); return ($title,$author,$date,$num_slides); } ###################################################################### +# #Opens the ppt application sub get_ppt { #print "get_ppt\n"; #debug my $ppt; try { $ppt = Win32::OLE->GetActiveObject('PowerPoint.Application'); } catch { die $_; }; unless ( $ppt ) { $ppt = Win32::OLE->new( 'PowerPoint.Application', sub { $_[0]->Quit } ) or die sprintf( 'Cannot start PowerPoint: %s', Win32::OLE->LastError ); } #print "get_ppt done\n"; #debug return $ppt; } ################################################################### +#### # Scans the product specific directory including all directories and # stores them in variables to be used during the script. sub scan_directory { # print "scan_directory"; #debug my $currentDir=shift; my $level=shift; my $cleanDir; my $relDir; my $file; my @dirContents=(); my $dirNameSave; my $addDir=""; # Change the current directory and obtain all of the files chdir($currentDir); @dirContents=<*>; if($level==0) { print " Scanning the following directories in the tape:\n"; $dirName="ROOT"; } elsif($level==1) { $dirName=$currentDir; $dirName=~s/$origCwd\///; #@cwdSplit=split /[\/\\]/, $currentDir; #$dirName=$cwdSplit[-1]; } else { $addDir=$currentDir; $addDir=~s/$origCwd\/$dirName\///; } foreach $file (@dirContents) { # Check if current file is a directory if(-d $file) { # Skip . and .. directories if(($file eq ".")||($file eq "..")) { next; } # Skip CVS and SVN directories if(($file eq "CVS")||($file eq "SVN")) { next; } # Print directory name to screen print " "." "x$level."\\$file\n"; $dirNameSave=$dirName; # Scan each directory within this directory scan_directory("$currentDir/$file",$level+1); # Reset current directory chdir($currentDir); $dirName=$dirNameSave; } # If the file is binary, skip it #elsif(-B $file) { next; } # If not a directory then search the file else { # Skip all files except for ppts #if($file =~ /^stats_file\w*\.txt$/) { next; } #next unless ($file =~ /\.ppt$/ | $file =~ /\.pptx$/); if ($file =~ /\.pptx$/ | $file =~ /\.ppt$/) { $numDirFiles=$numFiles{$dirName}; if($addDir ne "") { $fileName{$dirName}[$numDirFiles]="$ad +dDir/$file"; } else { $fileName{$dirName}[$numDirFiles]=$fil +e; } ($fileTitle{$dirName}[$numDirFiles], $fileAuthor{$dirName}[$numDirFiles], $fileDate{$dirName}[$numDirFiles], $fileNum_slides{$dirName}[$numDirFiles])=get_file_data +("$file"); # $dirSize{$dirName}+=$fileSize{$dirName}[$numDirFiles]; # $dirCodeLines{$dirName}+=$fileCodeLines{$dirName}[$numDirFiles +]; # $dirBlankLines{$dirName}+=$fileBlankLines{$dirName}[$numDirFil +es]; # $dirCommentLines{$dirName}+=$fileCommentLines{$dirName}[$numDi +rFiles]; $numFiles{$dirName}++; $totalFiles++; } } } if(($numFiles{$dirName}>0) && ($level <= 1)) { if($dirName eq "ROOT") { unshift @dirNames, $dirName; } # Add +to the beginning of the array else { push @dirNames, $dirName; } # Add to +the end of the array } } #################################################################### +### sub print_data { # print "\nprint_data\n"; #debug #### put the directory where you want to put the excel file that you c +reate in <path> my $excel_file = '<path>\ppt_data.xlsx'; # my $excel_file = 'ppt_data.xlsx'; my $Excel = Win32::OLE->GetActiveObject('Excel.Application') || Wi +n32::OLE->new('Excel.Application', 'Quit'); $Excel->{SheetsInNewWorkbook} = 1; my $Book = $Excel->Workbooks->Add; my $Sheet = $Book->Worksheets(1); $Sheet->{Name} = 'Data'; $Sheet->Range("A:H")->{'WrapText'} = "True"; # Insert column titles my $Range = $Sheet->Range("A1:H1"); $Range->{Value} = [qw(Tape Directory #Files File_Name File_Title F +ile_Author File_Date #Slides)]; $Range->Font->{Bold} = 1; my $num = 2; for($dir=0; $dir<@dirNames; $dir++) { $dirName=$dirNames[$dir]; $numDirFiles=$numFiles{$dirName}; for($file=0; $file<$numDirFiles; $file++) { my $range = $Sheet->Range("A".$num.":H".$num); $range->{Value} = [$tapeName, $dirName, $numDirFiles, $fileName{ +$dirName}[$file], $fileTitle{$dirName}[$file], $fileAuthor{$dirName}[ +$file], $fileDate{$dirName}[$file], $fileNum_slides{$dirName}[$file]] +; $num++; } } unlink $excel_file if -f $excel_file; $Book -> SaveAs($excel_file); $Book = $Excel->Workbooks->Close(); } #################################################################### +### sub print_closing { # print "closing\n"; #debug print "\n NUMBER OF FILES = $totalFiles\n"; if($printFile) { print "\n OUTPUT FILE = searchResult.txt\n"; } #print "\n Start= "; #plsubs::print_date_time($scriptTime); #print "\n Stop = "; #plsubs::print_date_time(); #print "\n Script Run Time = "; #plsubs::get_time($scriptTime); #print "\n";*/ } ###################################################################### +### ##---------------------------- MAIN ---------------------------- +-## ###################################################################### +### #$scriptTime=$timer=plsubs::get_time(); #plsubs::print_title($scriptName,$revision); #print "main"; #debug print "\n"; # Get starting directory and create clean root directory $origCwd=cwd(); @cwdSplit=split /[\/\\]/, $origCwd; $tapeName=$cwdSplit[-1]; $totalFiles=0; @numFiles=(); scan_directory($origCwd); print_data(); print_closing();

Replies are listed 'Best First'.
Re: Error in Extracting Data from the 1st slide of a ppt
by poj (Abbot) on Nov 23, 2015 at 21:23 UTC

    Try this cut down version of your code

    #!perl use strict; use warnings; use Win32::OLE; use Win32::OLE::Const 'Microsoft PowerPoint'; use Win32::OLE::Enum; use File::Find; use Cwd; my $ppt = Win32::OLE->new( 'PowerPoint.Application', sub { $_[0]->Quit + } ) or die "Cannot start PowerPoint: ".Win32::OLE->LastError; find(\&wanted, cwd()); sub wanted { return unless (/pptx?$/); scan($File::Find::name); } sub scan { my $file = shift; print "Scanning $file\n"; my $p = $ppt->Presentations->Open($file); my $slides = Win32::OLE::Enum->new( $p->Slides ); my $slide = $slides->Next; my $shapes = Win32::OLE::Enum->new( $slide->Shapes ); while ( my $shape = $shapes->Next ){ my $type = $shape->PlaceholderFormat->Type; if ( $type == ppPlaceholderTitle or $type == ppPlaceholderCenterTitle or $type == ppPlaceholderVerticalTitle){ my $title = $shape->TextFrame->TextRange->text; print "Title is $title\n"; } } }
    poj

      Hi Poj,

      Thanks a lot for your help. This code works great while reading the titles. However, I am also trying to extract the author and the date from the 1st slide. If these values are put in placeholders then the code works fine. However, these are put in a textbox then the line

      " my $type = $shape->Placeholderformat->Type; "

      gives a type mismatch error. I am assuming thats because a textbox does not count as a placeholder. How do I try to fix that?

        Deal with different shape types with if/else statements. (You can use the object browser to inspect the msoShapeType constant values)

        sub scan { my $file = shift; print "Scanning $file\n"; my $p = $ppt->Presentations->Open($file); my $slides = Win32::OLE::Enum->new( $p->Slides ); my $slide = $slides->Next; my $shapes = Win32::OLE::Enum->new( $slide->Shapes ); while ( my $shape = $shapes->Next ){ my $stype = $shape->type; print "Shape type is $stype\n"; if ($stype == 17){ my $text = $shape->TextFrame->TextRange->text; print "TextBox is $text\n"; } elsif ($stype == 14){ my $type = $shape->PlaceholderFormat->Type; if ( $type == ppPlaceholderTitle or $type == ppPlaceholderCenterTitle or $type == ppPlaceholderVerticalTitle ){ my $title = $shape->TextFrame->TextRange->text; print "Title is $title\n"; } } } }
        poj
Re: Error in Extracting Data from the 1st slide of a ppt
by stevieb (Canon) on Nov 23, 2015 at 19:48 UTC

    Welcome to the Monastery, mehtav!

    When cross-posting to other sites, it's polite to mention that near the top of your post. This prevents wasteful duplicate efforts.

      Will do so next time! thanks!