Attribute VB_Name = "Analyse" | |
'************************************************************************* | |
' | |
' Licensed to the Apache Software Foundation (ASF) under one | |
' or more contributor license agreements. See the NOTICE file | |
' distributed with this work for additional information | |
' regarding copyright ownership. The ASF licenses this file | |
' to you under the Apache License, Version 2.0 (the | |
' "License"); you may not use this file except in compliance | |
' with the License. You may obtain a copy of the License at | |
' | |
' http://www.apache.org/licenses/LICENSE-2.0 | |
' | |
' Unless required by applicable law or agreed to in writing, | |
' software distributed under the License is distributed on an | |
' "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
' KIND, either express or implied. See the License for the | |
' specific language governing permissions and limitations | |
' under the License. | |
' | |
'************************************************************************* | |
Option Explicit | |
Private Const C_STAT_NOT_STARTED As Integer = 1 | |
Private Const C_STAT_RETRY As Integer = 2 | |
Private Const C_STAT_ERROR As Integer = 3 | |
Private Const C_STAT_DONE As Integer = 4 | |
Private Const C_STAT_ABORTED As Integer = 5 | |
Private Const C_MAX_RETRIES As Integer = 5 | |
Private Const C_ABORT_TIMEOUT As Integer = 30 | |
Private Const MAX_WAIT_TIME As Long = 600 | |
Private Const C_STAT_FINISHED As String = "finished" | |
Private Const C_STAT_ANALYSED As String = "analysed=" | |
Private Const C_STAT_ANALYSING As String = "analysing=" | |
Private Const CSINGLE_FILE As String = "singlefile" | |
Private Const CFILE_LIST As String = "filelist" | |
Private Const CSTAT_FILE As String = "statfilename" | |
Private Const CLAST_CHECKPOINT As String = "LastCheckpoint" | |
Private Const CNEXT_FILE As String = "NextFile" | |
Private Const C_ABORT_ANALYSIS As String = "AbortAnalysis" | |
Private Const CAPPNAME_WORD As String = "word" | |
Private Const CAPPNAME_EXCEL As String = "excel" | |
Private Const CAPPNAME_POWERPOINT As String = "powerpoint" | |
Private Const C_EXENAME_WORD As String = "winword.exe" | |
Private Const C_EXENAME_EXCEL As String = "excel.exe" | |
Private Const C_EXENAME_POWERPOINT As String = "powerpnt.exe" | |
Const CNEW_RESULTS_FILE = "newresultsfile" | |
Const C_LAUNCH_DRIVER = ".\resources\LaunchDrivers.exe" | |
'from http://support.microsoft.com/kb/q129796 | |
Private Type STARTUPINFO | |
cb As Long | |
lpReserved As String | |
lpDesktop As String | |
lpTitle As String | |
dwX As Long | |
dwY As Long | |
dwXSize As Long | |
dwYSize As Long | |
dwXCountChars As Long | |
dwYCountChars As Long | |
dwFillAttribute As Long | |
dwFlags As Long | |
wShowWindow As Integer | |
cbReserved2 As Integer | |
lpReserved2 As Long | |
hStdInput As Long | |
hStdOutput As Long | |
hStdError As Long | |
End Type | |
Private Type PROCESS_INFORMATION | |
hProcess As Long | |
hThread As Long | |
dwProcessID As Long | |
dwThreadID As Long | |
End Type | |
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal _ | |
hHandle As Long, ByVal dwMilliseconds As Long) As Long | |
Private Declare Function CreateProcessA Lib "kernel32" (ByVal _ | |
lpApplicationName As String, ByVal lpCommandLine As String, ByVal _ | |
lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, _ | |
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _ | |
ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As String, _ | |
lpStartupInfo As STARTUPINFO, lpProcessInformation As _ | |
PROCESS_INFORMATION) As Long | |
Private Declare Function CloseHandle Lib "kernel32" _ | |
(ByVal hObject As Long) As Long | |
Private Declare Function GetExitCodeProcess Lib "kernel32" _ | |
(ByVal hProcess As Long, lpExitCode As Long) As Long | |
Private Declare Function TerminateProcess Lib "kernel32" (ByVal hProcess As Long, _ | |
ByVal uExitCode As Long) As Long | |
Private Const NORMAL_PRIORITY_CLASS = &H20& | |
Private Const WAIT_TIMEOUT As Long = &H102 | |
Private Const ABORTED As Long = -2 | |
' from http://vbnet.mvps.org/index.html?code/system/toolhelpprocesses.htm | |
Public Const TH32CS_SNAPPROCESS As Long = 2& | |
Public Const MAX_PATH As Long = 260 | |
Public Type PROCESSENTRY32 | |
dwSize As Long | |
cntUsage As Long | |
th32ProcessID As Long | |
th32DefaultHeapID As Long | |
th32ModuleID As Long | |
cntThreads As Long | |
th32ParentProcessID As Long | |
pcPriClassBase As Long | |
dwFlags As Long | |
szExeFile As String * MAX_PATH | |
End Type | |
Public Declare Function CreateToolhelp32Snapshot Lib "kernel32" _ | |
(ByVal lFlags As Long, ByVal lProcessID As Long) As Long | |
Public Declare Function ProcessFirst Lib "kernel32" _ | |
Alias "Process32First" _ | |
(ByVal hSnapShot As Long, uProcess As PROCESSENTRY32) As Long | |
Public Declare Function ProcessNext Lib "kernel32" _ | |
Alias "Process32Next" _ | |
(ByVal hSnapShot As Long, uProcess As PROCESSENTRY32) As Long | |
Public Function IsOfficeAppRunning(curApplication As String) As Boolean | |
'DV: we need some error handling here | |
Dim hSnapShot As Long | |
Dim uProcess As PROCESSENTRY32 | |
Dim success As Long | |
Dim bRet As Boolean | |
Dim bAppFound As Boolean | |
Dim exeName As String | |
Dim curExeName As String | |
bRet = True | |
On Error GoTo FinalExit | |
curExeName = LCase$(curApplication) | |
If (curExeName = CAPPNAME_WORD) Then | |
exeName = C_EXENAME_WORD | |
ElseIf (curExeName = CAPPNAME_EXCEL) Then | |
exeName = C_EXENAME_EXCEL | |
ElseIf (curExeName = CAPPNAME_POWERPOINT) Then | |
exeName = C_EXENAME_POWERPOINT | |
Else | |
GoTo FinalExit | |
End If | |
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0&) | |
If hSnapShot = -1 Then GoTo FinalExit | |
uProcess.dwSize = Len(uProcess) | |
success = ProcessFirst(hSnapShot, uProcess) | |
bAppFound = False | |
While ((success = 1) And Not bAppFound) | |
Dim i As Long | |
i = InStr(1, uProcess.szExeFile, Chr(0)) | |
curExeName = LCase$(Left$(uProcess.szExeFile, i - 1)) | |
If (curExeName = exeName) Then | |
bAppFound = True | |
Else | |
success = ProcessNext(hSnapShot, uProcess) | |
End If | |
Wend | |
bRet = bAppFound | |
Call CloseHandle(hSnapShot) | |
FinalExit: | |
IsOfficeAppRunning = bRet | |
End Function | |
Private Sub CalculateProgress(statusFileName As String, fso As FileSystemObject, _ | |
lastIndex As Long, docOffset As Long, _ | |
myDocList As Collection) | |
On Error GoTo FinalExit | |
Dim curFile As String | |
Dim fileCont As TextStream | |
Dim myFile As file | |
If (fso.FileExists(statusFileName)) Then | |
Dim statLine As String | |
Set fileCont = fso.OpenTextFile(statusFileName, ForReading, False, TristateTrue) | |
statLine = fileCont.ReadLine | |
If (Left(statLine, Len(C_STAT_ANALYSED)) = C_STAT_ANALYSED) Then | |
curFile = Mid(statLine, Len(C_STAT_ANALYSED) + 1) | |
ElseIf (Left(statLine, Len(C_STAT_ANALYSING)) = C_STAT_ANALYSING) Then | |
curFile = Mid(statLine, Len(C_STAT_ANALYSING) + 1) | |
End If | |
End If | |
' when we don't have a file, we will show the name of the last used file in | |
' the progress window | |
If (curFile = "") Then curFile = myDocList.item(lastIndex) | |
If (GetDocumentIndex(curFile, myDocList, lastIndex)) Then | |
Set myFile = fso.GetFile(curFile) | |
Call ShowProgress.SP_UpdateProgress(myFile.Name, myFile.ParentFolder.path, lastIndex + docOffset) | |
End If | |
FinalExit: | |
If Not (fileCont Is Nothing) Then fileCont.Close | |
Set fileCont = Nothing | |
Set myFile = Nothing | |
End Sub | |
Function CheckAliveStatus(statFileName As String, _ | |
curApplication As String, _ | |
lastDate As Date, _ | |
fso As FileSystemObject) As Boolean | |
Dim isAlive As Boolean | |
Dim currDate As Date | |
Dim statFile As file | |
Dim testing As Long | |
isAlive = False | |
If Not fso.FileExists(statFileName) Then | |
currDate = Now() | |
If (val(DateDiff("s", lastDate, currDate)) > MAX_WAIT_TIME) Then | |
isAlive = False | |
Else | |
isAlive = True | |
End If | |
Else | |
Set statFile = fso.GetFile(statFileName) | |
currDate = statFile.DateLastModified | |
If (currDate > lastDate) Then | |
lastDate = currDate | |
isAlive = True | |
Else | |
currDate = Now() | |
If (lastDate >= currDate) Then ' There might be some inaccuracies in file and system dates | |
isAlive = True | |
ElseIf (val(DateDiff("s", lastDate, currDate)) > MAX_WAIT_TIME) Then | |
isAlive = False | |
Else | |
isAlive = IsOfficeAppRunning(curApplication) | |
End If | |
End If | |
End If | |
CheckAliveStatus = isAlive | |
End Function | |
Sub TerminateOfficeApps(fso As FileSystemObject, aParameter As String) | |
Dim msoKillFileName As String | |
msoKillFileName = fso.GetAbsolutePathName(".\resources\msokill.exe") | |
If fso.FileExists(msoKillFileName) Then | |
Shell msoKillFileName & aParameter | |
Else | |
End If | |
End Sub | |
Public Function launchDriver(statFileName As String, cmdLine As String, _ | |
curApplication As String, fso As FileSystemObject, _ | |
myDocList As Collection, myOffset As Long, _ | |
myIniFilePath As String) As Long | |
Dim proc As PROCESS_INFORMATION | |
Dim start As STARTUPINFO | |
Dim ret As Long | |
Dim currDate As Date | |
Dim lastIndex As Long | |
currDate = Now() | |
lastIndex = 1 | |
' Initialize the STARTUPINFO structure: | |
start.cb = Len(start) | |
' Start the shelled application: | |
ret = CreateProcessA(vbNullString, cmdLine$, 0&, 0&, 1&, _ | |
NORMAL_PRIORITY_CLASS, 0&, vbNullString, start, proc) | |
' Wait for the shelled application to finish: | |
Do | |
ret = WaitForSingleObject(proc.hProcess, 100) | |
If ret <> WAIT_TIMEOUT Then | |
Exit Do | |
End If | |
If Not CheckAliveStatus(statFileName, curApplication, currDate, fso) Then | |
' Try to close open office dialogs and then wait a little bit | |
TerminateOfficeApps fso, " --close" | |
ret = WaitForSingleObject(proc.hProcess, 1000) | |
' next try to kill all office programs and then wait a little bit | |
TerminateOfficeApps fso, " --kill" | |
ret = WaitForSingleObject(proc.hProcess, 1000) | |
ret = TerminateProcess(proc.hProcess, "0") | |
ret = WAIT_TIMEOUT | |
Exit Do | |
End If | |
If (ShowProgress.g_SP_Abort) Then | |
WriteToLog C_ABORT_ANALYSIS, True, myIniFilePath | |
Call HandleAbort(proc.hProcess, curApplication) | |
ret = ABORTED | |
Exit Do | |
End If | |
Call CalculateProgress(statFileName, fso, lastIndex, myOffset, myDocList) | |
DoEvents 'allow other processes | |
Loop While True | |
If (ret <> WAIT_TIMEOUT) And (ret <> ABORTED) Then | |
Call GetExitCodeProcess(proc.hProcess, ret&) | |
End If | |
Call CloseHandle(proc.hThread) | |
Call CloseHandle(proc.hProcess) | |
launchDriver = ret | |
End Function | |
Function CheckAnalyseStatus(statusFileName As String, _ | |
lastFile As String, _ | |
fso As FileSystemObject) As Integer | |
Dim currStatus As Integer | |
Dim fileCont As TextStream | |
If Not fso.FileExists(statusFileName) Then | |
currStatus = C_STAT_NOT_STARTED | |
Else | |
Dim statText As String | |
Set fileCont = fso.OpenTextFile(statusFileName, ForReading, False, TristateTrue) | |
statText = fileCont.ReadLine | |
If (statText = C_STAT_FINISHED) Then | |
currStatus = C_STAT_DONE | |
ElseIf (Left(statText, Len(C_STAT_ANALYSED)) = C_STAT_ANALYSED) Then | |
currStatus = C_STAT_RETRY | |
lastFile = Mid(statText, Len(C_STAT_ANALYSED) + 1) | |
ElseIf (Left(statText, Len(C_STAT_ANALYSING)) = C_STAT_ANALYSING) Then | |
currStatus = C_STAT_RETRY | |
lastFile = Mid(statText, Len(C_STAT_ANALYSING) + 1) | |
Else | |
currStatus = C_STAT_ERROR | |
End If | |
fileCont.Close | |
End If | |
CheckAnalyseStatus = currStatus | |
End Function | |
Function WriteDocsToAnalyze(myDocList As Collection, myApp As String, _ | |
fso As FileSystemObject) As String | |
On Error GoTo HandleErrors | |
Dim currentFunctionName As String | |
currentFunctionName = "WriteDocsToAnalyze" | |
Dim TempPath As String | |
Dim fileName As String | |
Dim fileContent As TextStream | |
fileName = "" | |
TempPath = fso.GetSpecialFolder(TemporaryFolder).path | |
If (TempPath = "") Then | |
TempPath = "." | |
End If | |
Dim vFileName As Variant | |
Dim Index As Long | |
Dim limit As Long | |
limit = myDocList.count | |
If (limit > 0) Then | |
fileName = fso.GetAbsolutePathName(TempPath & "\FileList" & myApp & ".txt") | |
Set fileContent = fso.OpenTextFile(fileName, ForWriting, True, TristateTrue) | |
For Index = 1 To limit | |
vFileName = myDocList(Index) | |
fileContent.WriteLine (vFileName) | |
Next | |
fileContent.Close | |
End If | |
FinalExit: | |
Set fileContent = Nothing | |
WriteDocsToAnalyze = fileName | |
Exit Function | |
HandleErrors: | |
WriteDebug currentFunctionName & " : " & Err.Number & " " & Err.Description & " " & Err.Source | |
Resume FinalExit | |
End Function | |
' This function looks for the given document name in the document collection | |
' and returns TRUE and the position of the document in that collection if found, | |
' FALSE otherwise | |
Function GetDocumentIndex(myDocument As String, _ | |
myDocList As Collection, _ | |
lastIndex As Long) As Boolean | |
Dim currentFunctionName As String | |
currentFunctionName = "GetDocumentIndex" | |
On Error GoTo HandleErrors | |
Dim lastEntry As Long | |
Dim curIndex As Long | |
Dim curEntry As String | |
Dim entryFound As Boolean | |
entryFound = False | |
lastEntry = myDocList.count | |
curIndex = lastIndex | |
' We start the search at the position of the last found | |
' document | |
While Not entryFound And curIndex <= lastEntry | |
curEntry = myDocList.item(curIndex) | |
If (curEntry = myDocument) Then | |
lastIndex = curIndex | |
entryFound = True | |
Else | |
curIndex = curIndex + 1 | |
End If | |
Wend | |
' When we could not find the document, we start the search | |
' from the beginning of the list | |
If Not entryFound Then | |
curIndex = 1 | |
While Not entryFound And curIndex <= lastIndex | |
curEntry = myDocList.item(curIndex) | |
If (curEntry = myDocument) Then | |
lastIndex = curIndex | |
entryFound = True | |
Else | |
curIndex = curIndex + 1 | |
End If | |
Wend | |
End If | |
FinalExit: | |
GetDocumentIndex = entryFound | |
Exit Function | |
HandleErrors: | |
WriteDebug currentFunctionName & " : " & Err.Number & " " & Err.Description & " " & Err.Source | |
Resume FinalExit | |
End Function | |
Function AnalyseList(myDocList As Collection, _ | |
myApp As String, _ | |
myIniFilePath As String, _ | |
myOffset As Long, _ | |
analysisAborted As Boolean) As Boolean | |
On Error GoTo HandleErrors | |
Dim currentFunctionName As String | |
currentFunctionName = "AnalyseList" | |
Dim cmdLine As String | |
Dim filelist As String | |
Dim statFileName As String | |
Dim finished As Boolean | |
Dim analyseStatus As Integer | |
Dim nRetries As Integer | |
Dim lastFile As String | |
Dim lastHandledFile As String | |
Dim launchStatus As Long | |
Dim fso As New FileSystemObject | |
Dim progressTitle As String | |
filelist = WriteDocsToAnalyze(myDocList, myApp, fso) | |
cmdLine = fso.GetAbsolutePathName(C_LAUNCH_DRIVER) & " " & myApp | |
finished = False | |
Dim TempPath As String | |
TempPath = fso.GetSpecialFolder(TemporaryFolder).path | |
If (TempPath = "") Then TempPath = "." | |
statFileName = fso.GetAbsolutePathName(TempPath & "\StatFile" & myApp & ".txt") | |
If (fso.FileExists(statFileName)) Then fso.DeleteFile (statFileName) | |
WriteToLog CFILE_LIST, filelist, myIniFilePath | |
WriteToLog CSTAT_FILE, statFileName, myIniFilePath | |
WriteToLog CLAST_CHECKPOINT, "", myIniFilePath | |
WriteToLog CNEXT_FILE, "", myIniFilePath | |
WriteToLog C_ABORT_ANALYSIS, "", myIniFilePath | |
' In this loop we will restart the driver until we have finished the analysis | |
nRetries = 0 | |
While Not finished And nRetries < C_MAX_RETRIES | |
launchStatus = launchDriver(statFileName, cmdLine, myApp, fso, _ | |
myDocList, myOffset, myIniFilePath) | |
If (launchStatus = ABORTED) Then | |
finished = True | |
analyseStatus = C_STAT_ABORTED | |
analysisAborted = True | |
Else | |
analyseStatus = CheckAnalyseStatus(statFileName, lastHandledFile, fso) | |
End If | |
If (analyseStatus = C_STAT_DONE) Then | |
finished = True | |
ElseIf (analyseStatus = C_STAT_RETRY) Then | |
If (lastHandledFile = lastFile) Then | |
nRetries = nRetries + 1 | |
Else | |
lastFile = lastHandledFile | |
nRetries = 1 | |
End If | |
Else | |
nRetries = nRetries + 1 | |
End If | |
Wend | |
If (analyseStatus = C_STAT_DONE) Then | |
AnalyseList = True | |
Else | |
AnalyseList = False | |
End If | |
'The next driver should not overwrite this result file | |
WriteToLog CNEW_RESULTS_FILE, "False", myIniFilePath | |
FinalExit: | |
Set fso = Nothing | |
Exit Function | |
HandleErrors: | |
AnalyseList = False | |
WriteDebug currentFunctionName & " : " & Err.Number & " " & Err.Description & " " & Err.Source | |
Resume FinalExit | |
End Function | |
Sub HandleAbort(hProcess As Long, curApplication As String) | |
On Error Resume Next | |
Dim ret As Long | |
Dim curDate As Date | |
Dim stillWaiting As Boolean | |
Dim killApplication As Boolean | |
Dim waitTime As Long | |
curDate = Now() | |
stillWaiting = True | |
killApplication = False | |
While stillWaiting | |
stillWaiting = IsOfficeAppRunning(curApplication) | |
If (stillWaiting) Then | |
waitTime = val(DateDiff("s", curDate, Now())) | |
If (waitTime > C_ABORT_TIMEOUT) Then | |
stillWaiting = False | |
killApplication = True | |
End If | |
End If | |
Wend | |
If (killApplication) Then | |
ShowProgress.g_SP_AllowOtherDLG = True | |
TerminateMSO.Show vbModal, ShowProgress | |
End If | |
ret = TerminateProcess(hProcess, "0") | |
End Sub |