remove ppt viewer

This commit is contained in:
Tim Bentley 2018-02-02 21:33:41 +00:00
parent cffd8e603d
commit f7169ee708
12 changed files with 4 additions and 2070 deletions

View File

@ -259,7 +259,8 @@ class Settings(QtCore.QSettings):
('media/last directory', 'media/last directory', [(str_to_path, None)]),
('songuasge/db password', 'songusage/db password', []),
('songuasge/db hostname', 'songusage/db hostname', []),
('songuasge/db database', 'songusage/db database', [])
('songuasge/db database', 'songusage/db database', []),
('presentations / Powerpoint Viewer', '', [])
]
@staticmethod

View File

@ -98,7 +98,7 @@ class UiAboutDialog(object):
'OpenLP is free church presentation software, or lyrics '
'projection software, used to display slides of songs, Bible '
'verses, videos, images, and even presentations (if '
'Impress, PowerPoint or PowerPoint Viewer is installed) '
'Impress or PowerPoint is installed) '
'for church worship using a computer and a data projector.\n'
'\n'
'Find out more about OpenLP: http://openlp.org/\n'

View File

@ -1,307 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
import re
import zipfile
from xml.etree import ElementTree
from openlp.core.common import is_win
from openlp.core.common.applocation import AppLocation
from openlp.core.display.screens import ScreenList
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
if is_win():
from ctypes import cdll
from ctypes.wintypes import RECT
log = logging.getLogger(__name__)
class PptviewController(PresentationController):
"""
Class to control interactions with PowerPoint Viewer Presentations. It creates the runtime Environment , Loads the
and Closes the Presentation. As well as triggering the correct activities based on the users input
"""
log.info('PPTViewController loaded')
def __init__(self, plugin):
"""
Initialise the class
"""
log.debug('Initialising')
self.process = None
super(PptviewController, self).__init__(plugin, 'Powerpoint Viewer', PptviewDocument)
self.supports = ['ppt', 'pps', 'pptx', 'ppsx', 'pptm']
def check_available(self):
"""
PPT Viewer is able to run on this machine.
"""
log.debug('check_available')
if not is_win():
return False
return self.check_installed()
if is_win():
def check_installed(self):
"""
Check the viewer is installed.
"""
log.debug('Check installed')
try:
self.start_process()
return self.process.CheckInstalled()
except OSError:
return False
def start_process(self):
"""
Loads the PPTVIEWLIB library.
"""
if self.process:
return
log.debug('start PPTView')
dll_path = AppLocation.get_directory(AppLocation.AppDir) \
/ 'plugins' / 'presentations' / 'lib' / 'pptviewlib' / 'pptviewlib.dll'
self.process = cdll.LoadLibrary(str(dll_path))
if log.isEnabledFor(logging.DEBUG):
self.process.SetDebug(1)
def kill(self):
"""
Called at system exit to clean up any running presentations
"""
log.debug('Kill pptviewer')
while self.docs:
self.docs[0].close_presentation()
class PptviewDocument(PresentationDocument):
"""
Class which holds information and controls a single presentation.
"""
def __init__(self, controller, document_path):
"""
Constructor, store information about the file and initialise.
:param openlp.core.common.path.Path document_path: File path to the document to load
:rtype: None
"""
log.debug('Init Presentation PowerPoint')
super().__init__(controller, document_path)
self.presentation = None
self.ppt_id = None
self.blanked = False
self.hidden = False
def load_presentation(self):
"""
Called when a presentation is added to the SlideController. It builds the environment, starts communication with
the background PptView task started earlier.
"""
log.debug('LoadPresentation')
temp_path = self.get_temp_folder()
size = ScreenList().current['size']
rect = RECT(size.x(), size.y(), size.right(), size.bottom())
preview_path = temp_path / 'slide'
# Ensure that the paths are null terminated
file_path_utf16 = str(self.file_path).encode('utf-16-le') + b'\0'
preview_path_utf16 = str(preview_path).encode('utf-16-le') + b'\0'
if not temp_path.is_dir():
temp_path.mkdir(parents=True)
self.ppt_id = self.controller.process.OpenPPT(file_path_utf16, None, rect, preview_path_utf16)
if self.ppt_id >= 0:
self.create_thumbnails()
self.stop_presentation()
return True
else:
return False
def create_thumbnails(self):
"""
PPTviewLib creates large BMP's, but we want small PNG's for consistency. Convert them here.
"""
log.debug('create_thumbnails')
if self.check_thumbnails():
return
log.debug('create_thumbnails proceeding')
for idx in range(self.get_slide_count()):
path = self.get_temp_folder() / 'slide{index:d}.bmp'.format(index=idx + 1)
self.convert_thumbnail(path, idx + 1)
def create_titles_and_notes(self):
"""
Extracts the titles and notes from the zipped file
and writes the list of titles (one per slide)
to 'titles.txt'
and the notes to 'slideNotes[x].txt'
in the thumbnails directory
"""
titles = None
notes = None
# let's make sure we have a valid zipped presentation
if self.file_path.exists() and zipfile.is_zipfile(str(self.file_path)):
namespaces = {"p": "http://schemas.openxmlformats.org/presentationml/2006/main",
"a": "http://schemas.openxmlformats.org/drawingml/2006/main"}
# open the file
with zipfile.ZipFile(str(self.file_path)) as zip_file:
# find the presentation.xml to get the slide count
with zip_file.open('ppt/presentation.xml') as pres:
tree = ElementTree.parse(pres)
nodes = tree.getroot().findall(".//p:sldIdLst/p:sldId", namespaces=namespaces)
# initialize the lists
titles = ['' for i in range(len(nodes))]
notes = ['' for i in range(len(nodes))]
# loop thru the file list to find slides and notes
for zip_info in zip_file.infolist():
node_type = ''
index = -1
list_to_add = None
# check if it is a slide
match = re.search(r'slides/slide(.+)\.xml', zip_info.filename)
if match:
index = int(match.group(1)) - 1
node_type = 'ctrTitle'
list_to_add = titles
# or a note
match = re.search(r'notesSlides/notesSlide(.+)\.xml', zip_info.filename)
if match:
index = int(match.group(1)) - 1
node_type = 'body'
list_to_add = notes
# if it is one of our files, index shouldn't be -1
if index >= 0:
with zip_file.open(zip_info) as zipped_file:
tree = ElementTree.parse(zipped_file)
text = ''
nodes = tree.getroot().findall(".//p:ph[@type='" + node_type + "']../../..//p:txBody//a:t",
namespaces=namespaces)
# if we found any content
if nodes and len(nodes) > 0:
for node in nodes:
if len(text) > 0:
text += '\n'
text += node.text
# Let's remove the \n from the titles and
# just add one at the end
if node_type == 'ctrTitle':
text = text.replace('\n', ' ').replace('\x0b', ' ') + '\n'
list_to_add[index] = text
# now let's write the files
self.save_titles_and_notes(titles, notes)
def close_presentation(self):
"""
Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
shut down.
"""
log.debug('ClosePresentation')
if self.controller.process:
self.controller.process.ClosePPT(self.ppt_id)
self.ppt_id = -1
self.controller.remove_doc(self)
def is_loaded(self):
"""
Returns true if a presentation is loaded.
"""
if self.ppt_id < 0:
return False
if self.get_slide_count() < 0:
return False
return True
def is_active(self):
"""
Returns true if a presentation is currently active.
"""
return self.is_loaded() and not self.hidden
def blank_screen(self):
"""
Blanks the screen.
"""
self.controller.process.Blank(self.ppt_id)
self.blanked = True
def unblank_screen(self):
"""
Unblanks (restores) the presentation.
"""
self.controller.process.Unblank(self.ppt_id)
self.blanked = False
def is_blank(self):
"""
Returns true if screen is blank.
"""
log.debug('is blank OpenOffice')
return self.blanked
def stop_presentation(self):
"""
Stops the current presentation and hides the output.
"""
self.hidden = True
self.controller.process.Stop(self.ppt_id)
def start_presentation(self):
"""
Starts a presentation from the beginning.
"""
if self.hidden:
self.hidden = False
self.controller.process.Resume(self.ppt_id)
else:
self.controller.process.RestartShow(self.ppt_id)
def get_slide_number(self):
"""
Returns the current slide number.
"""
return self.controller.process.GetCurrentSlide(self.ppt_id)
def get_slide_count(self):
"""
Returns total number of slides.
"""
return self.controller.process.GetSlideCount(self.ppt_id)
def goto_slide(self, slide_no):
"""
Moves to a specific slide in the presentation.
:param slide_no: The slide the text is required for, starting at 1
"""
self.controller.process.GotoSlide(self.ppt_id, slide_no)
def next_step(self):
"""
Triggers the next effect of slide on the running presentation.
"""
self.controller.process.NextStep(self.ppt_id)
def previous_step(self):
"""
Triggers the previous slide on the running presentation.
"""
self.controller.process.PrevStep(self.ppt_id)

View File

@ -1,121 +0,0 @@
PPTVIEWLIB - Control PowerPoint Viewer 2003/2007 (for openlp.org)
Copyright (C) 2008-2018 Jonathan Corwin (j@corwin.co.uk)
This library wrappers the free Microsoft PowerPoint Viewer (2003/2007) program,
allowing it to be more easily controlled from another program.
The PowerPoint Viewer must already be installed on the destination machine, and is
freely available at microsoft.com.
The full Microsoft Office PowerPoint and PowerPoint Viewer 97 have a COM interface allowing
automation. This ability was removed from the 2003+ viewer offerings.
To developers: I am not a C/C++ or Win32 API programmer as you can probably tell.
The code and API of this DLL could certainly do with some tidying up, and the
error trapping, where it exists, is very basic. I'll happily accept patches!
This library is covered by the GPL (http://www.gnu.org/licenses/)
It is NOT covered by the LGPL, so can only be used in GPL compatable programs.
(http://www.gnu.org/licenses/why-not-lgpl.html)
This README.TXT must be distributed with the pptviewlib.dll
This library has a limit of 50 PowerPoints which can be opened simultaneously.
This project can be built with the free Microsoft Visual C++ 2008 Express Edition.
USAGE
-----
BOOL CheckInstalled(void);
Returns TRUE if PowerPointViewer is installed. FALSE if not.
int OpenPPT(char *filename, HWND hParentWnd, RECT rect, char *previewpath);
Opens the PowerPoint file, counts the number of slides, sizes and positions accordingly
and creates preview images of each slide. Note PowerPoint Viewer only allows the
slideshow to be resized whilst it is being loaded. It can be moved at any time however.
The only way to count the number of slides is to step through the entire show. Therefore
there will be a delay whilst opening large presentations for the first time.
For pre XP/2003 systems, the slideshow will flicker as the screen snapshots are taken.
filename: The PowerPoint file to be opened. Full path
hParentWnd: The window which will become the parent of the slideshow window.
Can be NULL.
rect: The location/dimensions of the slideshow window.
If all properties of this structure are zero, the dimensions of the hParentWnd
are used.
previewpath If specified, the prefix to use for snapshot images of each slide, in the
form: previewpath + n + ".bmp", where n is the slide number.
A file called previewpath + "info.txt" will also be created containing information
about the PPT file, to speed up future openings of the unmodified file.
Note it is up the calling program to directly access these images if they
are required.
RETURNS: An unique identifier to pass to other methods in this library.
If < 0, then the PPT failed to open.
If >=0, ClosePPT must be called when the PPT is no longer being used
or when the calling program is closed to release resources/hooks.
void ClosePPT(int id);
Closes the presentation, releasing any resources and hooks.
id: The value returned from OpenPPT.
int GetCurrentSlide(int id);
Returns the current slide number (from 1)
id: The value returned from OpenPPT.
int GetSlideCount(int id);
Returns the total number of slides.
id: The value returned from OpenPPT.
void NextStep(int id);
Advances one step (animation) through the slideshow.
id: The value returned from OpenPPT.
void PrevStep(int id);
Goes backwards one step (animation) through the slideshow.
id: The value returned from OpenPPT.
void GotoSlide(int id, int slideno);
Goes directly to a specific slide in the slideshow
id: The value returned from OpenPPT.
slideno: The number of the slide (from 1) to go directly to.
If the slide has already been displayed, then the completed slide with animations performed
will be shown. This is how the PowerPoint Viewer works so have no control over this.
void RestartShow(int id);
Restarts the show from the beginning. To reset animations, behind the scenes it
has to travel to the end and step backwards though the entire show. Therefore
for large presentations there might be a delay.
id: The value returned from OpenPPT.
void Blank(int id);
Blanks the screen, colour black.
id: The value returned from OpenPPT.
void Unblank(int id)
Unblanks the screen, restoring it to it's pre-blank state.
id: The value returned from OpenPPT.
void Stop(int id)
Moves the slideshow off the screen. (There is no concept of stop show in the PowerPoint Viewer)
id: The value returned from OpenPPT.
void Resume(int id)
Moves the slideshow display back onto the screen following a Stop()
id: The value returned from OpenPPT.

View File

@ -1,210 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import sys
from ctypes import *
from ctypes.wintypes import RECT
from PyQt5 import QtWidgets
class PPTViewer(QtWidgets.QWidget):
"""
Standalone Test Harness for the pptviewlib library
"""
def __init__(self, parent=None):
super(PPTViewer, self).__init__(parent)
self.pptid = -1
self.setWindowTitle('PowerPoint Viewer Test')
ppt_label = QtWidgets.QLabel('Open PowerPoint file')
slide_label = QtWidgets.QLabel('Go to slide #')
self.pptEdit = QtWidgets.QLineEdit()
self.slideEdit = QtWidgets.QLineEdit()
x_label = QtWidgets.QLabel('X pos')
y_label = QtWidgets.QLabel('Y pos')
width_label = QtWidgets.QLabel('Width')
height_label = QtWidgets.QLabel('Height')
self.xEdit = QtWidgets.QLineEdit('100')
self.yEdit = QtWidgets.QLineEdit('100')
self.widthEdit = QtWidgets.QLineEdit('900')
self.heightEdit = QtWidgets.QLineEdit('700')
self.total = QtWidgets.QLabel()
ppt_btn = QtWidgets.QPushButton('Open')
ppt_dlg_btn = QtWidgets.QPushButton('...')
folder_label = QtWidgets.QLabel('Slide .bmp path')
self.folderEdit = QtWidgets.QLineEdit('slide')
slide_btn = QtWidgets.QPushButton('Go')
prev = QtWidgets.QPushButton('Prev')
next = QtWidgets.QPushButton('Next')
blank = QtWidgets.QPushButton('Blank')
unblank = QtWidgets.QPushButton('Unblank')
restart = QtWidgets.QPushButton('Restart')
close = QtWidgets.QPushButton('Close')
resume = QtWidgets.QPushButton('Resume')
stop = QtWidgets.QPushButton('Stop')
grid = QtWidgets.QGridLayout()
row = 0
grid.addWidget(folder_label, 0, 0)
grid.addWidget(self.folderEdit, 0, 1)
row += 1
grid.addWidget(x_label, row, 0)
grid.addWidget(self.xEdit, row, 1)
grid.addWidget(y_label, row, 2)
grid.addWidget(self.yEdit, row, 3)
row += 1
grid.addWidget(width_label, row, 0)
grid.addWidget(self.widthEdit, row, 1)
grid.addWidget(height_label, row, 2)
grid.addWidget(self.heightEdit, row, 3)
row += 1
grid.addWidget(ppt_label, row, 0)
grid.addWidget(self.pptEdit, row, 1)
grid.addWidget(ppt_dlg_btn, row, 2)
grid.addWidget(ppt_btn, row, 3)
row += 1
grid.addWidget(slide_label, row, 0)
grid.addWidget(self.slideEdit, row, 1)
grid.addWidget(slide_btn, row, 2)
row += 1
grid.addWidget(prev, row, 0)
grid.addWidget(next, row, 1)
row += 1
grid.addWidget(blank, row, 0)
grid.addWidget(unblank, row, 1)
row += 1
grid.addWidget(restart, row, 0)
grid.addWidget(close, row, 1)
row += 1
grid.addWidget(stop, row, 0)
grid.addWidget(resume, row, 1)
ppt_btn.clicked.connect(self.openClick)
ppt_dlg_btn.clicked.connect(self.openDialog)
slide_btn.clicked.connect(self.gotoClick)
prev.clicked.connect(self.prevClick)
next.clicked.connect(self.nextClick)
blank.clicked.connect(self.blankClick)
unblank.clicked.connect(self.unblankClick)
restart.clicked.connect(self.restartClick)
close.clicked.connect(self.closeClick)
stop.clicked.connect(self.stopClick)
resume.clicked.connect(self.resumeClick)
self.setLayout(grid)
self.resize(300, 150)
def prevClick(self):
if self.pptid < 0:
return
self.pptdll.PrevStep(self.pptid)
self.updateCurrSlide()
app.processEvents()
def nextClick(self):
if self.pptid < 0:
return
self.pptdll.NextStep(self.pptid)
self.updateCurrSlide()
app.processEvents()
def blankClick(self):
if self.pptid < 0:
return
self.pptdll.Blank(self.pptid)
app.processEvents()
def unblankClick(self):
if self.pptid < 0:
return
self.pptdll.Unblank(self.pptid)
app.processEvents()
def restartClick(self):
if self.pptid < 0:
return
self.pptdll.RestartShow(self.pptid)
self.updateCurrSlide()
app.processEvents()
def stopClick(self):
if self.pptid < 0:
return
self.pptdll.Stop(self.pptid)
app.processEvents()
def resumeClick(self):
if self.pptid < 0:
return
self.pptdll.Resume(self.pptid)
app.processEvents()
def closeClick(self):
if self.pptid < 0:
return
self.pptdll.ClosePPT(self.pptid)
self.pptid = -1
app.processEvents()
def openClick(self):
oldid = self.pptid
rect = RECT(int(self.xEdit.text()), int(self.yEdit.text()),
int(self.widthEdit.text()), int(self.heightEdit.text()))
filename = str(self.pptEdit.text().replace('/', '\\'))
folder = str(self.folderEdit.text().replace('/', '\\'))
print(filename, folder)
self.pptid = self.pptdll.OpenPPT(filename, None, rect, folder)
print('id: ' + str(self.pptid))
if oldid >= 0:
self.pptdll.ClosePPT(oldid)
slides = self.pptdll.GetSlideCount(self.pptid)
print('slidecount: ' + str(slides))
self.total.setNum(self.pptdll.GetSlideCount(self.pptid))
self.updateCurrSlide()
def updateCurrSlide(self):
if self.pptid < 0:
return
slide = str(self.pptdll.GetCurrentSlide(self.pptid))
print('currslide: ' + slide)
self.slideEdit.setText(slide)
app.processEvents()
def gotoClick(self):
if self.pptid < 0:
return
print(self.slideEdit.text())
self.pptdll.GotoSlide(self.pptid, int(self.slideEdit.text()))
self.updateCurrSlide()
app.processEvents()
def openDialog(self):
self.pptEdit.setText(QtWidgets.QFileDialog.getOpenFileName(self, 'Open file')[0])
if __name__ == '__main__':
pptdll = cdll.LoadLibrary(r'pptviewlib.dll')
pptdll.SetDebug(1)
print('Begin...')
app = QtWidgets.QApplication(sys.argv)
window = PPTViewer()
window.pptdll = pptdll
window.show()
sys.exit(app.exec())

View File

@ -1,920 +0,0 @@
/******************************************************************************
* OpenLP - Open Source Lyrics Projection *
* --------------------------------------------------------------------------- *
* Copyright (c) 2008-2018 OpenLP Developers *
* --------------------------------------------------------------------------- *
* This program is free software; you can redistribute it and/or modify it *
* under the terms of the GNU General Public License as published by the Free *
* Software Foundation; version 2 of the License. *
* *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
* more details. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., 59 *
* Temple Place, Suite 330, Boston, MA 02111-1307 USA *
******************************************************************************/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <io.h>
#include <direct.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "pptviewlib.h"
// Because of the callbacks used by SetWindowsHookEx, the memory used needs to
// be sharable across processes (the callbacks are done from a different
// process) Therefore use data_seg with RWS memory.
//
// See http://msdn.microsoft.com/en-us/library/aa366551(VS.85).aspx for
// alternative method of holding memory, removing fixed limits which would allow
// dynamic number of items, rather than a fixed number. Use a Local\ mapping,
// since global has UAC issues in Vista.
#pragma data_seg(".PPTVIEWLIB")
PPTVIEW pptView[MAX_PPTS] = {NULL};
HHOOK globalHook = NULL;
BOOL debug = FALSE;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.PPTVIEWLIB,RWS")
HINSTANCE hInstance = NULL;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReasonForCall,
LPVOID lpReserved)
{
hInstance = (HINSTANCE)hModule;
switch(ulReasonForCall)
{
case DLL_PROCESS_ATTACH:
DEBUG(L"PROCESS_ATTACH\n");
break;
case DLL_THREAD_ATTACH:
//DEBUG(L"THREAD_ATTACH\n");
break;
case DLL_THREAD_DETACH:
//DEBUG(L"THREAD_DETACH\n");
break;
case DLL_PROCESS_DETACH:
// Clean up... hopefully there is only the one process attached?
// We'll find out soon enough during tests!
DEBUG(L"PROCESS_DETACH\n");
for (int i = 0; i < MAX_PPTS; i++)
ClosePPT(i);
break;
}
return TRUE;
}
DllExport void SetDebug(BOOL onOff)
{
printf("SetDebug\n");
debug = onOff;
DEBUG(L"enabled\n");
}
DllExport BOOL CheckInstalled()
{
wchar_t cmdLine[MAX_PATH * 2];
DEBUG(L"CheckInstalled\n");
BOOL found = GetPPTViewerPath(cmdLine, sizeof(cmdLine));
if(found)
{
DEBUG(L"Exe: %s\n", cmdLine);
}
return found;
}
// Open the PointPoint, count the slides and take a snapshot of each slide
// for use in previews
// previewpath is a prefix for the location to put preview images of each slide.
// "<n>.bmp" will be appended to complete the path. E.g. "c:\temp\slide" would
// create "c:\temp\slide1.bmp" slide2.bmp, slide3.bmp etc.
// It will also create a *info.txt containing information about the ppt
DllExport int OpenPPT(wchar_t *filename, HWND hParentWnd, RECT rect,
wchar_t *previewPath)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
wchar_t cmdLine[MAX_PATH * 2];
int id;
DEBUG(L"OpenPPT start: %s; %s\n", filename, previewPath);
DEBUG(L"OpenPPT start: %u; %i, %i, %i, %i\n", hParentWnd, rect.top,
rect.left, rect.bottom, rect.right);
if (GetPPTViewerPath(cmdLine, sizeof(cmdLine)) == FALSE)
{
DEBUG(L"OpenPPT: GetPPTViewerPath failed\n");
return -1;
}
id = -1;
for (int i = 0; i < MAX_PPTS; i++)
{
if (pptView[i].state == PPT_CLOSED)
{
id = i;
break;
}
}
if (id < 0)
{
DEBUG(L"OpenPPT: Too many PPTs\n");
return -1;
}
memset(&pptView[id], 0, sizeof(PPTVIEW));
wcscpy_s(pptView[id].filename, MAX_PATH, filename);
wcscpy_s(pptView[id].previewPath, MAX_PATH, previewPath);
pptView[id].state = PPT_CLOSED;
pptView[id].slideCount = 0;
pptView[id].currentSlide = 0;
pptView[id].firstSlideSteps = 0;
pptView[id].lastSlideSteps = 0;
pptView[id].guess = 0;
pptView[id].hParentWnd = hParentWnd;
pptView[id].hWnd = NULL;
pptView[id].hWnd2 = NULL;
for (int i = 0; i < MAX_SLIDES; i++)
{
pptView[id].slideNos[i] = 0;
}
if (hParentWnd != NULL && rect.top == 0 && rect.bottom == 0
&& rect.left == 0 && rect.right == 0)
{
LPRECT windowRect = NULL;
GetWindowRect(hParentWnd, windowRect);
pptView[id].rect.top = 0;
pptView[id].rect.left = 0;
pptView[id].rect.bottom = windowRect->bottom - windowRect->top;
pptView[id].rect.right = windowRect->right - windowRect->left;
}
else
{
pptView[id].rect.top = rect.top;
pptView[id].rect.left = rect.left;
pptView[id].rect.bottom = rect.bottom;
pptView[id].rect.right = rect.right;
}
wcscat_s(cmdLine, MAX_PATH * 2, L" /F /S \"");
wcscat_s(cmdLine, MAX_PATH * 2, filename);
wcscat_s(cmdLine, MAX_PATH * 2, L"\"");
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
BOOL gotInfo = GetPPTInfo(id);
/*
* I'd really like to just hook on the new threadid. However this always
* gives error 87. Perhaps I'm hooking to soon? No idea... however can't
* wait since I need to ensure I pick up the WM_CREATE as this is the only
* time the window can be resized in such away the content scales correctly
*
* hook = SetWindowsHookEx(WH_CBT,CbtProc,hInstance,pi.dwThreadId);
*/
if (globalHook != NULL)
{
UnhookWindowsHookEx(globalHook);
}
globalHook = SetWindowsHookEx(WH_CBT, CbtProc, hInstance, NULL);
if (globalHook == 0)
{
DEBUG(L"OpenPPT: SetWindowsHookEx failed\n");
ClosePPT(id);
return -1;
}
pptView[id].state = PPT_STARTED;
Sleep(10);
if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi))
{
DEBUG(L"OpenPPT: CreateProcess failed: %s\n", cmdLine);
ClosePPT(id);
return -1;
}
pptView[id].dwProcessId = pi.dwProcessId;
pptView[id].dwThreadId = pi.dwThreadId;
pptView[id].hThread = pi.hThread;
pptView[id].hProcess = pi.hProcess;
while (pptView[id].state == PPT_STARTED)
Sleep(10);
if (gotInfo)
{
DEBUG(L"OpenPPT: Info loaded, no refresh\n");
pptView[id].state = PPT_LOADED;
Resume(id);
}
else
{
DEBUG(L"OpenPPT: Get info\n");
pptView[id].steps = 0;
int steps = 0;
while (pptView[id].state == PPT_OPENED)
{
if (steps <= pptView[id].steps)
{
Sleep(100);
DEBUG(L"OpenPPT: Step %d/%d\n", steps, pptView[id].steps);
steps++;
NextStep(id);
}
Sleep(10);
}
DEBUG(L"OpenPPT: Slides %d, Steps %d, first slide steps %d\n",
pptView[id].slideCount, pptView[id].steps,
pptView[id].firstSlideSteps);
for(int i = 1; i <= pptView[id].slideCount; i++)
{
DEBUG(L"OpenPPT: Slide %d = %d\n", i, pptView[id].slideNos[i]);
}
SavePPTInfo(id);
if (pptView[id].state == PPT_CLOSING
|| pptView[id].slideCount <= 0)
{
ClosePPT(id);
id=-1;
}
else
{
RestartShow(id);
}
}
if (id >= 0)
{
if (pptView[id].msgHook != NULL)
{
UnhookWindowsHookEx(pptView[id].msgHook);
}
pptView[id].msgHook = NULL;
}
DEBUG(L"OpenPPT: Exit: id=%i\n", id);
return id;
}
// Load information about the ppt from an info.txt file.
// Format:
// version
// filedate
// filesize
// slidecount
// first slide steps
BOOL GetPPTInfo(int id)
{
struct _stat fileStats;
wchar_t info[MAX_PATH];
FILE* pFile;
wchar_t buf[100];
DEBUG(L"GetPPTInfo: start\n");
if (_wstat(pptView[id].filename, &fileStats) != 0)
{
return FALSE;
}
swprintf_s(info, MAX_PATH, L"%sinfo.txt", pptView[id].previewPath);
int err = _wfopen_s(&pFile, info, L"r");
if (err != 0)
{
DEBUG(L"GetPPTInfo: file open failed - %d\n", err);
return FALSE;
}
fgetws(buf, 100, pFile); // version == 1
fgetws(buf, 100, pFile);
if (fileStats.st_mtime != _wtoi(buf))
{
DEBUG(L"GetPPTInfo: date changed\n");
fclose (pFile);
return FALSE;
}
fgetws(buf, 100, pFile);
if (fileStats.st_size != _wtoi(buf))
{
DEBUG(L"GetPPTInfo: size changed\n");
fclose (pFile);
return FALSE;
}
fgetws(buf, 100, pFile); // slidecount
int slideCount = _wtoi(buf);
fgetws(buf, 100, pFile); // first slide steps
int firstSlideSteps = _wtoi(buf);
// check all the preview images still exist
for (int i = 1; i <= slideCount; i++)
{
swprintf_s(info, MAX_PATH, L"%s%i.bmp", pptView[id].previewPath, i);
if (GetFileAttributes(info) == INVALID_FILE_ATTRIBUTES)
{
DEBUG(L"GetPPTInfo: bmp not found\n");
return FALSE;
}
}
fclose(pFile);
pptView[id].slideCount = slideCount;
pptView[id].firstSlideSteps = firstSlideSteps;
DEBUG(L"GetPPTInfo: exit ok\n");
return TRUE;
}
BOOL SavePPTInfo(int id)
{
struct _stat fileStats;
wchar_t info[MAX_PATH];
FILE* pFile;
DEBUG(L"SavePPTInfo: start\n");
if (_wstat(pptView[id].filename, &fileStats) != 0)
{
DEBUG(L"SavePPTInfo: stat of %s failed\n", pptView[id].filename);
return FALSE;
}
swprintf_s(info, MAX_PATH, L"%sinfo.txt", pptView[id].previewPath);
int err = _wfopen_s(&pFile, info, L"w");
if (err != 0)
{
DEBUG(L"SavePPTInfo: fopen of %s failed%i\n", info, err);
return FALSE;
}
fprintf(pFile, "1\n");
fprintf(pFile, "%u\n", fileStats.st_mtime);
fprintf(pFile, "%u\n", fileStats.st_size);
fprintf(pFile, "%u\n", pptView[id].slideCount);
fprintf(pFile, "%u\n", pptView[id].firstSlideSteps);
fclose(pFile);
DEBUG(L"SavePPTInfo: exit ok\n");
return TRUE;
}
// Get the path of the PowerPoint viewer from the registry
BOOL GetPPTViewerPath(wchar_t *pptViewerPath, int stringSize)
{
wchar_t cwd[MAX_PATH];
DEBUG(L"GetPPTViewerPath: start\n");
if(GetPPTViewerPathFromReg(pptViewerPath, stringSize))
{
if(_waccess(pptViewerPath, 0) != -1)
{
DEBUG(L"GetPPTViewerPath: exit registry\n");
return TRUE;
}
}
// This is where it gets ugly. PPT2007 it seems no longer stores its
// location in the registry. So we have to use the defaults which will
// upset those who like to put things somewhere else
// Viewer 2007 in 64bit Windows:
if(_waccess(L"C:\\Program Files (x86)\\Microsoft Office\\Office12\\PPTVIEW.EXE",
0) != -1)
{
wcscpy_s(
L"C:\\Program Files (x86)\\Microsoft Office\\Office12\\PPTVIEW.EXE",
stringSize, pptViewerPath);
DEBUG(L"GetPPTViewerPath: exit 64bit 2007\n");
return TRUE;
}
// Viewer 2007 in 32bit Windows:
if(_waccess(L"C:\\Program Files\\Microsoft Office\\Office12\\PPTVIEW.EXE", 0)
!= -1)
{
wcscpy_s(L"C:\\Program Files\\Microsoft Office\\Office12\\PPTVIEW.EXE",
stringSize, pptViewerPath);
DEBUG(L"GetPPTViewerPath: exit 32bit 2007\n");
return TRUE;
}
// Give them the opportunity to place it in the same folder as the app
_wgetcwd(cwd, MAX_PATH);
wcscat_s(cwd, MAX_PATH, L"\\PPTVIEW.EXE");
if(_waccess(cwd, 0) != -1)
{
wcscpy_s(pptViewerPath, stringSize, cwd);
DEBUG(L"GetPPTViewerPath: exit local\n");
return TRUE;
}
DEBUG(L"GetPPTViewerPath: exit fail\n");
return FALSE;
}
BOOL GetPPTViewerPathFromReg(wchar_t *pptViewerPath, int stringSize)
{
HKEY hKey;
DWORD dwType, dwSize;
LRESULT lResult;
// The following registry settings are for, respectively, (I think)
// PPT Viewer 2007 (older versions. Latest not in registry) & PPT Viewer 2010
// PPT Viewer 2003 (recent versions)
// PPT Viewer 2003 (older versions)
// PPT Viewer 97
if ((RegOpenKeyExW(HKEY_CLASSES_ROOT,
L"PowerPointViewer.Show.12\\shell\\Show\\command", 0, KEY_READ, &hKey)
!= ERROR_SUCCESS)
&& (RegOpenKeyExW(HKEY_CLASSES_ROOT,
L"PowerPointViewer.Show.11\\shell\\Show\\command", 0, KEY_READ, &hKey)
!= ERROR_SUCCESS)
&& (RegOpenKeyExW(HKEY_CLASSES_ROOT,
L"Applications\\PPTVIEW.EXE\\shell\\open\\command", 0, KEY_READ, &hKey)
!= ERROR_SUCCESS)
&& (RegOpenKeyExW(HKEY_CLASSES_ROOT,
L"Applications\\PPTVIEW.EXE\\shell\\Show\\command", 0, KEY_READ, &hKey)
!= ERROR_SUCCESS))
{
return FALSE;
}
dwType = REG_SZ;
dwSize = (DWORD)stringSize;
lResult = RegQueryValueEx(hKey, NULL, NULL, &dwType, (LPBYTE)pptViewerPath,
&dwSize);
RegCloseKey(hKey);
if (lResult != ERROR_SUCCESS)
{
return FALSE;
}
// remove "%1" from end of key value
pptViewerPath[wcslen(pptViewerPath) - 4] = '\0';
return TRUE;
}
// Unhook the Windows hook
void Unhook(int id)
{
DEBUG(L"Unhook: start %d\n", id);
if (pptView[id].hook != NULL)
{
UnhookWindowsHookEx(pptView[id].hook);
}
if (pptView[id].msgHook != NULL)
{
UnhookWindowsHookEx(pptView[id].msgHook);
}
pptView[id].hook = NULL;
pptView[id].msgHook = NULL;
DEBUG(L"Unhook: exit ok\n");
}
// Close the PowerPoint viewer, release resources
DllExport void ClosePPT(int id)
{
DEBUG(L"ClosePPT: start%d\n", id);
pptView[id].state = PPT_CLOSED;
Unhook(id);
if (pptView[id].hWnd == 0)
{
TerminateThread(pptView[id].hThread, 0);
}
else
{
PostMessage(pptView[id].hWnd, WM_CLOSE, 0, 0);
}
CloseHandle(pptView[id].hThread);
CloseHandle(pptView[id].hProcess);
memset(&pptView[id], 0, sizeof(PPTVIEW));
DEBUG(L"ClosePPT: exit ok\n");
return;
}
// Moves the show back onto the display
DllExport void Resume(int id)
{
DEBUG(L"Resume: %d\n", id);
MoveWindow(pptView[id].hWnd, pptView[id].rect.left,
pptView[id].rect.top,
pptView[id].rect.right - pptView[id].rect.left,
pptView[id].rect.bottom - pptView[id].rect.top, TRUE);
Unblank(id);
}
// Moves the show off the screen so it can't be seen
DllExport void Stop(int id)
{
DEBUG(L"Stop:%d\n", id);
MoveWindow(pptView[id].hWnd, -32000, -32000,
pptView[id].rect.right - pptView[id].rect.left,
pptView[id].rect.bottom - pptView[id].rect.top, TRUE);
}
// Return the total number of slides
DllExport int GetSlideCount(int id)
{
DEBUG(L"GetSlideCount:%d\n", id);
if (pptView[id].state == 0)
{
return -1;
}
else
{
return pptView[id].slideCount;
}
}
// Return the number of the slide currently viewing
DllExport int GetCurrentSlide(int id)
{
DEBUG(L"GetCurrentSlide:%d\n", id);
if (pptView[id].state == 0)
{
return -1;
}
else
{
return pptView[id].currentSlide;
}
}
// Take a step forwards through the show
DllExport void NextStep(int id)
{
DEBUG(L"NextStep:%d (%d)\n", id, pptView[id].currentSlide);
if (pptView[id].currentSlide > pptView[id].slideCount) return;
if (pptView[id].currentSlide < pptView[id].slideCount)
{
pptView[id].guess = pptView[id].currentSlide + 1;
}
PostMessage(pptView[id].hWnd2, WM_MOUSEWHEEL, MAKEWPARAM(0, -WHEEL_DELTA),
0);
}
// Take a step backwards through the show
DllExport void PrevStep(int id)
{
DEBUG(L"PrevStep:%d (%d)\n", id, pptView[id].currentSlide);
if (pptView[id].currentSlide > 1)
{
pptView[id].guess = pptView[id].currentSlide - 1;
}
PostMessage(pptView[id].hWnd2, WM_MOUSEWHEEL, MAKEWPARAM(0, WHEEL_DELTA),
0);
}
// Blank the show (black screen)
DllExport void Blank(int id)
{
// B just toggles blank on/off. However pressing any key unblanks.
// So send random unmapped letter first (say 'A'), then we can
// better guarantee B will blank instead of trying to guess
// whether it was already blank or not.
DEBUG(L"Blank:%d\n", id);
HWND h1 = GetForegroundWindow();
HWND h2 = GetFocus();
SetForegroundWindow(pptView[id].hWnd);
SetFocus(pptView[id].hWnd);
// slight pause, otherwise event triggering this call may grab focus back!
Sleep(50);
keybd_event((int)'A', 0, 0, 0);
keybd_event((int)'A', 0, KEYEVENTF_KEYUP, 0);
keybd_event((int)'B', 0, 0, 0);
keybd_event((int)'B', 0, KEYEVENTF_KEYUP, 0);
SetForegroundWindow(h1);
SetFocus(h2);
}
// Unblank the show
DllExport void Unblank(int id)
{
DEBUG(L"Unblank:%d\n", id);
// Pressing any key resumes.
// For some reason SendMessage works for unblanking, but not blanking.
SendMessage(pptView[id].hWnd2, WM_CHAR, 'A', 0);
}
// Go directly to a slide
DllExport void GotoSlide(int id, int slideNo)
{
DEBUG(L"GotoSlide %i %i:\n", id, slideNo);
// Did try WM_KEYDOWN/WM_CHAR/WM_KEYUP with SendMessage but didn't work
// perhaps I was sending to the wrong window? No idea.
// Anyway fall back to keybd_event, which is OK as long we makesure
// the slideshow has focus first
char ch[10];
if (slideNo < 0) return;
pptView[id].guess = slideNo;
_itoa_s(slideNo, ch, 10, 10);
HWND h1 = GetForegroundWindow();
HWND h2 = GetFocus();
SetForegroundWindow(pptView[id].hWnd);
SetFocus(pptView[id].hWnd);
// slight pause, otherwise event triggering this call may grab focus back!
Sleep(50);
for (int i=0; i<10; i++)
{
if (ch[i] == '\0') break;
keybd_event((BYTE)ch[i], 0, 0, 0);
keybd_event((BYTE)ch[i], 0, KEYEVENTF_KEYUP, 0);
}
keybd_event(VK_RETURN, 0, 0, 0);
keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);
SetForegroundWindow(h1);
SetFocus(h2);
}
// Restart the show from the beginning
DllExport void RestartShow(int id)
{
// If we just go direct to slide one, then it remembers that all other
// slides have been animated, so ends up just showing the completed slides
// of those slides that have been animated next time we advance.
// Only way I've found to get around this is to step backwards all the way
// through. Lets move the window out of the way first so the audience
// doesn't see this.
DEBUG(L"RestartShow:%d\n", id);
Stop(id);
GotoSlide(id, pptView[id].slideCount);
for (int i=0; i <= pptView[id].steps - pptView[id].lastSlideSteps; i++)
{
PrevStep(id);
Sleep(10);
}
int i = 0;
while ((pptView[id].currentSlide > 1) && (i++ < 30000))
{
Sleep(10);
}
Resume(id);
}
// This hook is started with the PPTVIEW.EXE process and waits for the
// WM_CREATEWND message. At this point (and only this point) can the
// window be resized to the correct size.
// Release the hook as soon as we're complete to free up resources
LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam)
{
HHOOK hook = globalHook;
if (nCode == HCBT_CREATEWND)
{
wchar_t csClassName[32];
HWND hCurrWnd = (HWND)wParam;
DWORD retProcId = NULL;
GetClassName(hCurrWnd, csClassName, sizeof(csClassName));
if ((wcscmp(csClassName, L"paneClassDC") == 0)
||(wcscmp(csClassName, L"screenClass") == 0))
{
int id = -1;
DWORD windowThread = GetWindowThreadProcessId(hCurrWnd, NULL);
for (int i=0; i < MAX_PPTS; i++)
{
if (pptView[i].dwThreadId == windowThread)
{
id = i;
break;
}
}
if (id >= 0)
{
if (wcscmp(csClassName, L"paneClassDC") == 0)
{
pptView[id].hWnd2 = hCurrWnd;
}
else
{
pptView[id].hWnd = hCurrWnd;
CBT_CREATEWND* cw = (CBT_CREATEWND*)lParam;
if (pptView[id].hParentWnd != NULL)
{
cw->lpcs->hwndParent = pptView[id].hParentWnd;
}
cw->lpcs->cy = pptView[id].rect.bottom
- pptView[id].rect.top;
cw->lpcs->cx = pptView[id].rect.right
- pptView[id].rect.left;
cw->lpcs->y = -32000;
cw->lpcs->x = -32000;
}
if ((pptView[id].hWnd != NULL) && (pptView[id].hWnd2 != NULL))
{
UnhookWindowsHookEx(globalHook);
globalHook = NULL;
pptView[id].hook = SetWindowsHookEx(WH_CALLWNDPROC,
CwpProc, hInstance, pptView[id].dwThreadId);
pptView[id].msgHook = SetWindowsHookEx(WH_GETMESSAGE,
GetMsgProc, hInstance, pptView[id].dwThreadId);
Sleep(10);
pptView[id].state = PPT_OPENED;
}
}
}
}
return CallNextHookEx(hook, nCode, wParam, lParam);
}
// This hook exists whilst the slideshow is loading but only listens on the
// slideshows thread. It listens out for mousewheel events
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
HHOOK hook = NULL;
MSG *pMSG = (MSG *)lParam;
DWORD windowThread = GetWindowThreadProcessId(pMSG->hwnd, NULL);
int id = -1;
for (int i = 0; i < MAX_PPTS; i++)
{
if (pptView[i].dwThreadId == windowThread)
{
id = i;
hook = pptView[id].msgHook;
break;
}
}
if (id >= 0 && nCode == HC_ACTION && wParam == PM_REMOVE
&& pMSG->message == WM_MOUSEWHEEL)
{
if (pptView[id].state != PPT_LOADED)
{
if (pptView[id].currentSlide == 1)
{
pptView[id].firstSlideSteps++;
}
pptView[id].steps++;
pptView[id].lastSlideSteps++;
}
}
return CallNextHookEx(hook, nCode, wParam, lParam);
}
// This hook exists whilst the slideshow is running but only listens on the
// slideshows thread. It listens out for slide changes, message WM_USER+22.
LRESULT CALLBACK CwpProc(int nCode, WPARAM wParam, LPARAM lParam){
CWPSTRUCT *cwp;
cwp = (CWPSTRUCT *)lParam;
HHOOK hook = NULL;
wchar_t filename[MAX_PATH];
DWORD windowThread = GetWindowThreadProcessId(cwp->hwnd, NULL);
int id = -1;
for (int i = 0; i < MAX_PPTS; i++)
{
if (pptView[i].dwThreadId == windowThread)
{
id = i;
hook = pptView[id].hook;
break;
}
}
if ((id >= 0) && (nCode == HC_ACTION))
{
if (cwp->message == WM_USER + 22)
{
if (pptView[id].state != PPT_LOADED)
{
if ((pptView[id].currentSlide > 0)
&& (pptView[id].previewPath != NULL
&& wcslen(pptView[id].previewPath) > 0))
{
swprintf_s(filename, MAX_PATH, L"%s%i.bmp",
pptView[id].previewPath,
pptView[id].currentSlide);
CaptureAndSaveWindow(cwp->hwnd, filename);
}
if (((cwp->wParam == 0)
|| (pptView[id].slideNos[1] == cwp->wParam))
&& (pptView[id].currentSlide > 0))
{
pptView[id].state = PPT_LOADED;
pptView[id].currentSlide = pptView[id].slideCount + 1;
}
else
{
if (cwp->wParam > 0)
{
pptView[id].currentSlide = pptView[id].currentSlide + 1;
pptView[id].slideNos[pptView[id].currentSlide]
= cwp->wParam;
pptView[id].slideCount = pptView[id].currentSlide;
pptView[id].lastSlideSteps = 0;
}
}
}
else
{
if (cwp->wParam > 0)
{
if(pptView[id].guess > 0
&& pptView[id].slideNos[pptView[id].guess] == 0)
{
pptView[id].currentSlide = 0;
}
for(int i = 1; i <= pptView[id].slideCount; i++)
{
if(pptView[id].slideNos[i] == cwp->wParam)
{
pptView[id].currentSlide = i;
break;
}
}
if(pptView[id].currentSlide == 0)
{
pptView[id].slideNos[pptView[id].guess] = cwp->wParam;
pptView[id].currentSlide = pptView[id].guess;
}
pptView[id].guess = 0;
}
}
}
if ((pptView[id].state != PPT_CLOSED)
&&(cwp->message == WM_CLOSE || cwp->message == WM_QUIT))
{
pptView[id].state = PPT_CLOSING;
}
}
return CallNextHookEx(hook, nCode, wParam, lParam);
}
VOID CaptureAndSaveWindow(HWND hWnd, wchar_t* filename)
{
HBITMAP hBmp;
if ((hBmp = CaptureWindow(hWnd)) == NULL)
{
return;
}
RECT client;
GetClientRect(hWnd, &client);
UINT uiBytesPerRow = 3 * client.right; // RGB takes 24 bits
UINT uiRemainderForPadding;
if ((uiRemainderForPadding = uiBytesPerRow % sizeof(DWORD)) > 0)
uiBytesPerRow += (sizeof(DWORD) - uiRemainderForPadding);
UINT uiBytesPerAllRows = uiBytesPerRow * client.bottom;
PBYTE pDataBits;
if ((pDataBits = new BYTE[uiBytesPerAllRows]) != NULL)
{
BITMAPINFOHEADER bmi = {0};
BITMAPFILEHEADER bmf = {0};
// Prepare to get the data out of HBITMAP:
bmi.biSize = sizeof(bmi);
bmi.biPlanes = 1;
bmi.biBitCount = 24;
bmi.biHeight = client.bottom;
bmi.biWidth = client.right;
// Get it:
HDC hDC = GetDC(hWnd);
GetDIBits(hDC, hBmp, 0, client.bottom, pDataBits, (BITMAPINFO*) &bmi,
DIB_RGB_COLORS);
ReleaseDC(hWnd, hDC);
// Fill the file header:
bmf.bfOffBits = sizeof(bmf) + sizeof(bmi);
bmf.bfSize = bmf.bfOffBits + uiBytesPerAllRows;
bmf.bfType = 0x4D42;
// Writing:
FILE* pFile;
int err = _wfopen_s(&pFile, filename, L"wb");
if (err == 0)
{
fwrite(&bmf, sizeof(bmf), 1, pFile);
fwrite(&bmi, sizeof(bmi), 1, pFile);
fwrite(pDataBits, sizeof(BYTE), uiBytesPerAllRows, pFile);
fclose(pFile);
}
delete [] pDataBits;
}
DeleteObject(hBmp);
}
HBITMAP CaptureWindow(HWND hWnd)
{
HDC hDC;
BOOL bOk = FALSE;
HBITMAP hImage = NULL;
hDC = GetDC(hWnd);
RECT rcClient;
GetClientRect(hWnd, &rcClient);
if ((hImage = CreateCompatibleBitmap(hDC, rcClient.right, rcClient.bottom))
!= NULL)
{
HDC hMemDC;
HBITMAP hDCBmp;
if ((hMemDC = CreateCompatibleDC(hDC)) != NULL)
{
hDCBmp = (HBITMAP)SelectObject(hMemDC, hImage);
HMODULE hLib = LoadLibrary(L"User32");
// PrintWindow works for windows outside displayable area
// but was only introduced in WinXP. BitBlt requires the window to
// be topmost and within the viewable area of the display
if (GetProcAddress(hLib, "PrintWindow") == NULL)
{
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE);
BitBlt(hMemDC, 0, 0, rcClient.right, rcClient.bottom, hDC, 0,
0, SRCCOPY);
SetWindowPos(hWnd, HWND_NOTOPMOST, -32000, -32000, 0, 0,
SWP_NOSIZE);
}
else
{
PrintWindow(hWnd, hMemDC, 0);
}
SelectObject(hMemDC, hDCBmp);
DeleteDC(hMemDC);
bOk = TRUE;
}
}
ReleaseDC(hWnd, hDC);
if (!bOk)
{
if (hImage)
{
DeleteObject(hImage);
hImage = NULL;
}
}
return hImage;
}

View File

@ -1,80 +0,0 @@
/******************************************************************************
* OpenLP - Open Source Lyrics Projection *
* --------------------------------------------------------------------------- *
* Copyright (c) 2008-2018 OpenLP Developers *
* --------------------------------------------------------------------------- *
* This program is free software; you can redistribute it and/or modify it *
* under the terms of the GNU General Public License as published by the Free *
* Software Foundation; version 2 of the License. *
* *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
* more details. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., 59 *
* Temple Place, Suite 330, Boston, MA 02111-1307 USA *
******************************************************************************/
#define DllExport extern "C" __declspec( dllexport )
#define DEBUG(...) if (debug) wprintf(__VA_ARGS__)
enum PPTVIEWSTATE {PPT_CLOSED, PPT_STARTED, PPT_OPENED, PPT_LOADED,
PPT_CLOSING};
DllExport int OpenPPT(wchar_t *filename, HWND hParentWnd, RECT rect,
wchar_t *previewPath);
DllExport BOOL CheckInstalled();
DllExport void ClosePPT(int id);
DllExport int GetCurrentSlide(int id);
DllExport int GetSlideCount(int id);
DllExport void NextStep(int id);
DllExport void PrevStep(int id);
DllExport void GotoSlide(int id, int slide_no);
DllExport void RestartShow(int id);
DllExport void Blank(int id);
DllExport void Unblank(int id);
DllExport void Stop(int id);
DllExport void Resume(int id);
DllExport void SetDebug(BOOL onOff);
LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK CwpProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
BOOL GetPPTViewerPath(wchar_t *pptViewerPath, int stringSize);
BOOL GetPPTViewerPathFromReg(wchar_t *pptViewerPath, int stringSize);
HBITMAP CaptureWindow(HWND hWnd);
VOID SaveBitmap(wchar_t* filename, HBITMAP hBmp) ;
VOID CaptureAndSaveWindow(HWND hWnd, wchar_t* filename);
BOOL GetPPTInfo(int id);
BOOL SavePPTInfo(int id);
void Unhook(int id);
#define MAX_PPTS 16
#define MAX_SLIDES 256
struct PPTVIEW
{
HHOOK hook;
HHOOK msgHook;
HWND hWnd;
HWND hWnd2;
HWND hParentWnd;
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
RECT rect;
int slideCount;
int currentSlide;
int firstSlideSteps;
int lastSlideSteps;
int steps;
int guess;
wchar_t filename[MAX_PATH];
wchar_t previewPath[MAX_PATH];
int slideNos[MAX_SLIDES];
PPTVIEWSTATE state;
};

View File

@ -1,202 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="pptviewlib"
ProjectGUID="{04CC20D1-DC5A-4189-8181-4011E3C21DCF}"
RootNamespace="pptviewlib"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;PPTVIEWLIB_EXPORTS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
ModuleDefinitionFile=""
GenerateDebugInformation="true"
SubSystem="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;PPTVIEWLIB_EXPORTS"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\pptviewlib.cpp"
>
</File>
<File
RelativePath=".\README.TXT"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\pptviewlib.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -44,7 +44,6 @@ __default_settings__ = {'presentations/override app': QtCore.Qt.Unchecked,
'presentations/pdf_program': None,
'presentations/Impress': QtCore.Qt.Checked,
'presentations/Powerpoint': QtCore.Qt.Checked,
'presentations/Powerpoint Viewer': QtCore.Qt.Checked,
'presentations/Pdf': QtCore.Qt.Checked,
'presentations/presentations files': [],
'presentations/thumbnail_scheme': '',
@ -57,7 +56,7 @@ __default_settings__ = {'presentations/override app': QtCore.Qt.Unchecked,
class PresentationPlugin(Plugin):
"""
This plugin allowed a Presentation to be opened, controlled and displayed on the output display. The plugin controls
third party applications such as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer.
third party applications such as OpenOffice.org Impress, and Microsoft PowerPoint.
"""
log = logging.getLogger('PresentationPlugin')

View File

@ -1,226 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
This module contains tests for the pptviewcontroller module of the Presentations plugin.
"""
import shutil
from tempfile import mkdtemp
from unittest import TestCase, skipIf
from unittest.mock import MagicMock, patch
from openlp.core.common import is_win
from openlp.core.common.path import Path
from openlp.plugins.presentations.lib.pptviewcontroller import PptviewDocument, PptviewController
from tests.helpers.testmixin import TestMixin
from tests.utils.constants import RESOURCE_PATH
class TestPptviewController(TestCase, TestMixin):
"""
Test the PptviewController Class
"""
def setUp(self):
"""
Set up the patches and mocks need for all tests.
"""
self.setup_application()
self.build_settings()
self.mock_plugin = MagicMock()
self.temp_folder = mkdtemp()
self.mock_plugin.settings_section = self.temp_folder
def tearDown(self):
"""
Stop the patches
"""
self.destroy_settings()
shutil.rmtree(self.temp_folder)
def test_constructor(self):
"""
Test the Constructor from the PptViewController
"""
# GIVEN: No presentation controller
controller = None
# WHEN: The presentation controller object is created
controller = PptviewController(plugin=self.mock_plugin)
# THEN: The name of the presentation controller should be correct
assert 'Powerpoint Viewer' == controller.name, 'The name of the presentation controller should be correct'
def test_check_available(self):
"""
Test check_available / check_installed
"""
# GIVEN: A mocked dll loader and a controller
with patch('ctypes.cdll.LoadLibrary') as mocked_load_library:
mocked_process = MagicMock()
mocked_process.CheckInstalled.return_value = True
mocked_load_library.return_value = mocked_process
controller = PptviewController(plugin=self.mock_plugin)
# WHEN: check_available is called
available = controller.check_available()
# THEN: On windows it should return True, on other platforms False
if is_win():
assert available is True, 'check_available should return True on windows.'
else:
assert available is False, 'check_available should return False when not on windows.'
class TestPptviewDocument(TestCase):
"""
Test the PptviewDocument Class
"""
def setUp(self):
"""
Set up the patches and mocks need for all tests.
"""
self.pptview_document_create_thumbnails_patcher = patch(
'openlp.plugins.presentations.lib.pptviewcontroller.PptviewDocument.create_thumbnails')
self.pptview_document_stop_presentation_patcher = patch(
'openlp.plugins.presentations.lib.pptviewcontroller.PptviewDocument.stop_presentation')
self.presentation_document_get_temp_folder_patcher = patch(
'openlp.plugins.presentations.lib.pptviewcontroller.PresentationDocument.get_temp_folder')
self.presentation_document_setup_patcher = patch(
'openlp.plugins.presentations.lib.pptviewcontroller.PresentationDocument._setup')
self.screen_list_patcher = patch('openlp.plugins.presentations.lib.pptviewcontroller.ScreenList')
self.rect_patcher = MagicMock()
self.mock_pptview_document_create_thumbnails = self.pptview_document_create_thumbnails_patcher.start()
self.mock_pptview_document_stop_presentation = self.pptview_document_stop_presentation_patcher.start()
self.mock_presentation_document_get_temp_folder = self.presentation_document_get_temp_folder_patcher.start()
self.mock_presentation_document_setup = self.presentation_document_setup_patcher.start()
self.mock_rect = self.rect_patcher.start()
self.mock_screen_list = self.screen_list_patcher.start()
self.mock_controller = MagicMock()
self.mock_presentation = MagicMock()
self.temp_folder = mkdtemp()
self.mock_presentation_document_get_temp_folder.return_value = self.temp_folder
def tearDown(self):
"""
Stop the patches
"""
self.pptview_document_create_thumbnails_patcher.stop()
self.pptview_document_stop_presentation_patcher.stop()
self.presentation_document_get_temp_folder_patcher.stop()
self.presentation_document_setup_patcher.stop()
self.rect_patcher.stop()
self.screen_list_patcher.stop()
shutil.rmtree(self.temp_folder)
@skipIf(not is_win(), 'Not Windows')
def test_load_presentation_succesful(self):
"""
Test the PptviewDocument.load_presentation() method when the PPT is successfully opened
"""
# GIVEN: A reset mocked_os
self.mock_controller.process.OpenPPT.return_value = 0
instance = PptviewDocument(self.mock_controller, self.mock_presentation)
instance.file_path = 'test\path.ppt'
# WHEN: The temporary directory exists and OpenPPT returns successfully (not -1)
result = instance.load_presentation()
# THEN: PptviewDocument.load_presentation should return True
assert result is True
@skipIf(not is_win(), 'Not Windows')
def test_load_presentation_un_succesful(self):
"""
Test the PptviewDocument.load_presentation() method when the temporary directory does not exist and the PPT is
not successfully opened
"""
# GIVEN: A reset mock_os_isdir
self.mock_controller.process.OpenPPT.return_value = -1
instance = PptviewDocument(self.mock_controller, self.mock_presentation)
instance.file_path = 'test\path.ppt'
# WHEN: The temporary directory does not exist and OpenPPT returns unsuccessfully (-1)
with patch.object(instance, 'get_temp_folder') as mocked_get_folder:
mocked_get_folder.return_value = MagicMock(spec=Path)
result = instance.load_presentation()
# THEN: The temp folder should be created and PptviewDocument.load_presentation should return False
assert result is False
def test_create_titles_and_notes(self):
"""
Test PowerpointController.create_titles_and_notes
"""
# GIVEN: mocked PresentationController.save_titles_and_notes and a pptx file
doc = PptviewDocument(self.mock_controller, self.mock_presentation)
doc.file_path = RESOURCE_PATH / 'presentations' / 'test.pptx'
doc.save_titles_and_notes = MagicMock()
# WHEN reading the titles and notes
doc.create_titles_and_notes()
# THEN save_titles_and_notes should have been called once with empty arrays
doc.save_titles_and_notes.assert_called_once_with(['Test 1\n', '\n', 'Test 2\n', 'Test 4\n', 'Test 3\n'],
['Notes for slide 1', 'Inserted', 'Notes for slide 2',
'Notes \nfor slide 4', 'Notes for slide 3'])
def test_create_titles_and_notes_nonexistent_file(self):
"""
Test PowerpointController.create_titles_and_notes with nonexistent file
"""
# GIVEN: mocked PresentationController.save_titles_and_notes and an nonexistent file
with patch('builtins.open') as mocked_open, \
patch.object(Path, 'exists') as mocked_path_exists, \
patch('openlp.plugins.presentations.lib.presentationcontroller.create_paths') as \
mocked_dir_exists:
mocked_path_exists.return_value = False
mocked_dir_exists.return_value = False
doc = PptviewDocument(self.mock_controller, self.mock_presentation)
doc.file_path = Path('Idontexist.pptx')
doc.save_titles_and_notes = MagicMock()
# WHEN: Reading the titles and notes
doc.create_titles_and_notes()
# THEN: File existens should have been checked, and not have been opened.
doc.save_titles_and_notes.assert_called_once_with(None, None)
mocked_path_exists.assert_called_with()
assert mocked_open.call_count == 0, 'There should be no calls to open a file.'
def test_create_titles_and_notes_invalid_file(self):
"""
Test PowerpointController.create_titles_and_notes with invalid file
"""
# GIVEN: mocked PresentationController.save_titles_and_notes and an invalid file
with patch('builtins.open') as mocked_open, \
patch('openlp.plugins.presentations.lib.pptviewcontroller.zipfile.is_zipfile') as mocked_is_zf:
mocked_is_zf.return_value = False
mocked_open.filesize = 10
doc = PptviewDocument(self.mock_controller, self.mock_presentation)
doc.file_path = RESOURCE_PATH / 'presentations' / 'test.ppt'
doc.save_titles_and_notes = MagicMock()
# WHEN: reading the titles and notes
doc.create_titles_and_notes()
# THEN:
doc.save_titles_and_notes.assert_called_once_with(None, None)
assert mocked_is_zf.call_count == 1, 'is_zipfile should have been called once'