Inter Process Communication using vbscript - process

I need to send data form one process to another.
Constraints :
Sender process is very expensive call.
It needs to be done using vbscipt.
For Sender process,this data transferring is an additional work.It shouldn't get affected much by this feature. There are around 1000 threads in a sender process in 4-5 mins.
Faster IPC is important.If it can be done asynchronoulsy it will be better.
I read about the named pipe.Is it possible to open a named pipe using vbscript .Also is there any other possible way considering the above constraints.

Using a named pipe is probably your only option from native VBScript. You could access any of the other IPC methods by writing a COM object in some other language.
A named pipe can be written to just like a file so you can use the FileSystemObject to open and read/write from a named pipe. The format for opening a named pipe is to use the format \\\\.\pipe\PipeName (Replace PipeName with the pipe's actual name).
So to write to a named pipe in VBScript:
Set fs = CreateObject("Scripting.FileSystemObject")
Set a = fs.CreateTextFile("\\.\pipe\PipeName", True)
a.WriteLine("This is a test.")
a.Close

Option Explicit
Dim g_receivedCallback
If WScript.Arguments.Named.Exists("NEW") Then
RunSecondInstance
Else
RunFirstInstance
End If
Sub RunSecondInstance()
Dim oSa, oWindow, oData, oCallback
' Search for the window
Set oSa = CreateObject("Shell.Application")
For Each oWindow In oSa.Windows
If TypeName(oWindow.Document) = "HTMLDocument" Then
If InStr(oWindow.Document.Title, "IPC Window") > 0 Then
' Get the data object, set a property and callback a method
Set oData = oWindow.GetProperty ("IPCData")
Set oCallback = oData.Callback
oData.Value = "Success!"
Call oCallback
End If
End If
Next
End Sub
Sub RunFirstInstance()
Dim oData, oIe, oWs
' Create a object to pass to a other script
Set oData = New IPCData
' Set a property to a callback method
Set oData.Callback = GetRef("MyCallback")
' Create a window and store the data in the window
Set oIe = CreateObject("InternetExplorer.Application")
oIe.Navigate "about:blank"
Do Until oIe.ReadyState = 4 : WScript.Sleep 5 : Loop
oIe.Document.Title = "IPC Window"
oIe.PutProperty "IPCData", oData
' Run second script instance
Set oWs = CreateObject("WScript.Shell")
oWs.Run "WSCRIPT.EXE """ & WScript.ScriptFullName & """ /NEW"
' Wait for callback from second script
Do Until g_receivedCallback = True : WScript.Sleep 5 : Loop
' Display received data
MsgBox oData.Value
' Close ie
oIe.Quit
End Sub
Sub MyCallback()
g_receivedCallback = True
End Sub
Class IPCData
Private m_callback
Public Property Get Callback()
Set Callback = m_callback
End Property
Public Property Set Callback(ByVal v)
Set m_callback = v
End Property
Private m_value
Public Property Get Value()
If IsObject(m_value) Then
Set Value = m_value
Else
Value = m_value
End If
End Property
Public Property Let Value(ByVal v)
m_value = v
End Property
Public Property Set Value(ByVal v)
Set m_value = v
End Property
End Class

Related

Microsoft Access VBA invalid use of property error

So, I have this class where I call a few classes where data is checked. They give back an error class, named Failcase. Now I get an error when I first set the error to true.
The error states:
Invalid use of Property.
Private Sub btnImport_Click()
Dim fail As Failcase
Set fail.Success = True '<---- This is where the error occures
Set fail = ImportCheckSpec(Me.txtImportSpec)
If fail.Success Then
MsgBox "Error " + CStr(fail.Code) + ": " + fail.Message, vbCritical, "Error"
Exit Sub
End If
Set fail = ImportCheckDate(Me.txtDateTime)
If fail.Success Then
MsgBox "Error " + CStr(fail.Code) + ": " + fail.Message, vbCritical, "Error"
Exit Sub
Else
MsgBox "Success"
End If
End Sub
The Failcase class looks like this:
Option Compare Database
Option Explicit
Public Success As Boolean
Public Code As Integer
Public Message As String
I use:
Microsoft Access 2013
VBA
You are using OOP without creating a new object of class Failcase. Try this in the module:
Option Explicit
Public Sub TestMe()
Dim fail As New failcase
fail.Success = True
Debug.Print fail.Success
End Sub
In the class:
Option Explicit
Private m_bSuccess As Boolean
Public Property Get Success() As Boolean
Success = m_bSuccess
End Property
Public Property Let Success(ByVal bNewValue As Boolean)
m_bSuccess = bNewValue
End Property
Thus, you would achieve encapsulation. With it, you may set a bit more rules for accessing your property - https://www.google.com/search?q=encapsulation+oop&oq=encapsulation+oop&aqs=chrome..69i57j0l5.3599j0j7&sourceid=chrome&ie=UTF-8
The code above was an example of early binding. This is another one, example of late binding, doing the same:
Public Sub TestLateBinding()
Dim fail As Object
Set fail = New failcase
fail.Success = True
Debug.Print fail.Success
End Sub
Early and late binding have different pros and cons.
It works like this.
Dim fail As New Failcase
fail.Success = True

Check existence of a given ItemProperty for a generic Object

I have a Sub that takes as argument a generic Object olObj. I want access a given property of the object, only if it exists.
I wrote the Function below to check for this.
I conceive also using error handling for this.
Is there any other way? (e.g., something like HasItemProperty which does not need to sweep through all ItemProperties and check one by one).
Function HasItemProperty(ByRef olObj As Object, ByVal ipname As String) As Boolean
HasItemProperty = False
Dim ips As ItemProperties
Set ips = olObj.ItemProperties
Dim iip As Integer
For iip = 0 To ips.Count - 1
Dim ip As ItemProperty
Set ip = ips.Item(iip)
Dim ipn As String
ipn = ip.Name
If (ipn = ipname) Then
HasItemProperty = True
Exit Function
End If
Next iip
End Function
So essentially you want to check if your object exposes a particular property, like olObj.SomeProperty?
Not in VBA. On the low level (C++, Delphi, etc.), you can call IDispatch::GetIDsOfNames. But why not simply access the property (olObj.SomeProperty) and handle the exception (on Error Resume Next, Err.Number / Err.Clear)?
You can try this, it's a bit inelegant that's for sure. If the property fetch fails then it sets the flag to false and goes about it's merry way.
You can toggle the comment for testProperty
Sub marine()
Dim testObj As Object
Dim propertyValid As Boolean
Dim testProperty As String
On Error GoTo PROPERR
Set testObj = Sheets(1)
testProperty = testObj.Name
'testProperty = testObj.ThisDoesNotExist
propertyValid = True
PROPRETURN:
Exit Sub
PROPERR:
propertyValid = False
GoTo PROPRETURN
End Sub
The Outlook object model doesn't provide any other way. You need to iterate over all properties in the collection to find the one you need.

PowerPoint Add-In Loss of RibbonUI

I have been struggling to identify the cause of an error in a PPT Add-in that is distributed across about 40 end users.
Problem: loss of the ribbon state/loss of the ribbonUI object.
For some users, eventually the Rib object becomes Nothing.
Users assure me they are not getting any run-time errors nor script errors (from COM object that we also invoke through this add-in). An unhandled error, if user hits End would expectedly cause the state loss.
None of the users have been able to reliably reproduce the scenario which causes the observed failure. This is what makes it very difficult to troubleshoot. I am hoping against hope that there is something obvious that I'm missing, or that I didn't anticipate.
How I currently handle loss or RibbonUI
In attempt to combat this, I store the object pointer to the ribbon in THREE places, this seems like overkill to me but it is still apparently not sufficient:
A class object called cbRibbon has a property .RibbonUI which is assigned; Set cbRibbon.RibbonUI = Rib during the ribbon's onLoad callback procedure. So we have a byRef copy of the object itself. If the ribbon is nothing, theoretically I can Set rib = cbRibbon.RibbonUI and this works unless cbRibbon object is also out of scope.
The cbRibbon object has property .Pointer which is assigned: cbRibbon.Pointer = ObjPtr(Rib).
A CustomDocumentProperty called "RibbonPointer" is also used to store a reference to the object pointer. (Note: This persists even beyond state loss)
So you can see I've given some thought to this in attempt to replicate the way of storing this pointer the way one might store it in a hidden worksheet/range in Excel.
Additional information
I can see from robust client-side logging that this the error appears to happen usually but not always during the procedure below, which is used to refresh/invalidate the ribbon and its controls.
This procedure is called any time I need to dynamically refresh the ribbon or part of its controls:
Call RefreshRibbon(id)
The error appears to (sometimes, I can't stress this enough: the error cannot be replicated on-demand) happen during a full refresh, which is called like:
Call RefreshRibbon("")
This is the procedure that does the invalidation:
Sub RefreshRibbon(id As String)
If Rib Is Nothing Then
If RibbonError(id) Then GoTo ErrorExit
End If
Select Case id
Case vbNullString, "", "RibbonUI"
Call Logger.LogEvent("RefreshRibbon: Rib.Invalidate", Array("RibbonUI", _
"Ribbon:" & CStr(Not Rib Is Nothing), _
"Pointer:" & ObjPtr(Rib)))
Rib.Invalidate
Case Else
Call Logger.LogEvent("RefreshRibbon: Rib.InvalidateControl", Array(id, _
"Ribbon:" & CStr(Not Rib Is Nothing), _
"Pointer:" & ObjPtr(Rib)))
Rib.InvalidateControl id
End Select
Exit Sub
ErrorExit:
End Sub
As you can see, the very first thing I do in this procedure is test the Rib object for Nothing-ness. If this evaluates to True, then the RibbonUI object has somehow been lost.
The error function then attempts to re-instantiate the ribbon: first from cbRibbon.RibbonUI, then from the cbRibbon.Pointer and if both of those fails, then from the CustomDocumentProperties("RibbonPointer") value. If neither of these succeeds, then we display a fatal error and the user is prompted to close the PowerPoint application. If any one of these succeeds, then the ribbon is reloaded programmatically and everything continues to work.
Here is the code for that procedure. Note that it calls several other procedures which I have not included code for. These are helper functions or logger functions. The .GetPointer method actually invokes the WinAPI CopyMemory function to reload the object from its pointer value.
Function RibbonError(id As String) As Boolean
'Checks for state loss of the ribbon
Dim ret As Boolean
If id = vbNullString Then id = "RibbonUI"
Call Logger.LogEvent("RibbonError", Array("Checking for Error with Ribbon" & vbCrLf & _
"id: " & id, _
"Pointer: " & ObjPtr(Rib), _
"cbPointer: " & cbRibbon.Pointer))
If Not Rib Is Nothing Then
GoTo EarlyExit
End If
On Error Resume Next
'Attempt to restore from class object:
Set Rib = cbRibbon.ribbonUI
'Attempt to restore from Pointer reference if that fails:
If Rib Is Nothing Then
'Call Logger.LogEvent("Attempt to Restore from cbRibbon", Array(cbRibbon.Pointer))
If Not CLng(cbRibbon.Pointer) = 0 Then
Set Rib = cbRibbon.GetRibbon(cbRibbon.Pointer)
End If
End If
'Attempt to restore from CDP
If Rib Is Nothing Then
'Call Logger.LogEvent("Attempt to Restore from CDP", Array(MyDoc.CustomDocumentProperties("RibbonPointer")))
If HasCustomProperty("RibbonPointer") Then
cbRibbon.Pointer = CLng(MyDoc.CustomDocumentProperties("RibbonPointer"))
Set Rib = cbRibbon.GetRibbon(cbRibbon.Pointer)
End If
End If
On Error GoTo 0
If Rib Is Nothing Then
Debug.Print "Pointer value was: " & cbRibbon.Pointer
'Since we can't restore from an invalid pointer, erase this in the CDP
' a value of "0" will set Rib = Nothing, anything else will crash the appliation
Call SetCustomProperty("RibbonPointer", "0")
Else
'Reload the restored ribbon:
Call RibbonOnLoad(Rib)
Call SetCustomProperty("RibbonPointer", ObjPtr(Rib))
cbRibbon.Pointer = ObjPtr(Rib)
End If
'Make sure the ribbon exists or was able to be restored
ret = (Rib Is Nothing)
If ret Then
'Inform the user
MsgBox "A fatal error has been encountered. Please save & restart the presentation", vbCritical, Application.Name
'Log the event to file
Call Logger.LogEvent("RibbonError", Array("FATAL ERROR"))
Call ReleaseTrap
End If
EarlyExit:
RibbonError = ret
End Function
All of this works perfectly well in theory and in fact I can straight-up kill run-time (by invoking the End statement or otherwise) and these procedures reset the ribbon as expected.
So, what am I missing?
OK I forgot about this... while I still have not pinpointed the error I have some ideas that users are simply not reporting unhandled runtime errors and instead they're hitting "End" when prompted by PowerPoint.
I'm reasonably certain that is the cause and I have confirmation that in many cases, that sort of error precedes the "crash", so I'm updating to resolve that soon.
Otherwise, here is the method I ultimately have been using for several months, with success.
Create a procedure that writes the Pointer value of the ribbon on the user's machine. I didn't want to do this, but ultimately had to:
Sub LogRibbon(pointer As Long)
'Writes the ribbon pointer to a text file
Dim filename As String
Dim FF As Integer
filename = "C:\users\" & Environ("username") & "\AppData\Roaming\Microsoft\AddIns\pointer.txt"
FF = FreeFile
Open filename For Output As FF
Print #FF, pointer
Close FF
End Sub
In the ribbon's _OnLoad event handler, I call the LogRibbon procedure:
Public Rib As IRibbonUI
Public cbRibbon As New cRibbonProperties
Sub RibbonOnLoad(ribbon As IRibbonUI)
'Callback for customUI.onLoad
Set Rib = ribbon
Call LogRibbon(ObjPtr(Rib))
'Store the properties so we can easily access them later
cbRibbon.ribbonUI = Rib
End Sub
I created a class object to store some information about the ribbon to avoid repeated and slow calls to an external API, but for this purpose you can create a class that stores just the pointer value. That is referenced above in the cbRibbon.ribbonUI = Rib. This GetRibbon method of this class uses the CopyMemory function from WinAPI to restore the object from it's pointer.
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32" Alias _
"RtlMoveMemory" (destination As Any, source As Any, _
ByVal length As Long)
'example ported from Excel:
'http://www.excelguru.ca/blog/2006/11/29/modifying-the-ribbon-part-6/
Private pControls As Object
Private pRibbonUI As IRibbonUI
Private pPointer As Long
Sub Class_Initialize()
'Elsewhere I add some controls to this dictionary so taht I can invoke their event procedures programmatically:
Set pControls = CreateObject("Scripting.Dictionary")
Set pRibbonUI = Rib
Call SaveRibbonPointer(Rib)
pConnected = False
End Sub
'#############################################################
'hold a reference to the ribbon itself
Public Property Let ribbonUI(iRib As IRibbonUI)
'Set RibbonUI to property for later use
Set pRibbonUI = iRib
End Property
Public Property Get ribbonUI() As IRibbonUI
'Retrieve RibbonUI from property for use
Set ribbonUI = pRibbonUI
End Property
'http://www.mrexcel.com/forum/excel-questions/518629-how-preserve-regain-id-my-custom-ribbon-ui.html
Public Sub SaveRibbonPointer(ribbon As IRibbonUI)
Dim lngRibPtr As Long
' Store the custom ribbon UI Id in a static variable.
' This is done once during load of UI.
lngRibPtr = ObjPtr(ribbon)
cbRibbon.pointer = lngRibPtr
End Sub
Function GetRibbon(lngRibPtr As Long) As Object
'Uses CopyMemory function to re-load a ribbon that
' has been inadvertently lost due to run-time error/etc.
Dim filename As String
Dim ret As Long
Dim objRibbon As Object
filename = "C:\users\" & Environ("username") & "\AppData\Roaming\Microsoft\AddIns\pointer.txt"
On Error Resume Next
With CreateObject("Scripting.FileSystemObject").GetFile(filename)
ret = .OpenAsTextStream.ReadLine
End With
On Error GoTo 0
If lngRibPtr = 0 Then
lngRibPtr = ret
End If
CopyMemory objRibbon, lngRibPtr, 4
Set GetRibbon = objRibbon
' clean up invalid object
CopyMemory objRibbon, 0&, 4
Set objRibbon = Nothing
End Function
'##############################################################
' Store the pointer reference to the RibbonUI
Public Property Let pointer(p As Long)
pPointer = p
End Property
Public Property Get pointer() As Long
pointer = pPointer
End Property
'#############################################################
'Dictionary of control properties for Dropdowns/ComboBox
Public Property Let properties(p As Object)
Set pProperties = p
End Property
Public Property Get properties() As Object
Set properties = pProperties
End Property
Then, I have a function which checks for loss of ribbon, and restores from the pointer value. This one actually invokes the OnLoad procedure, which we can do since we have an object variable (or class object property) representing the Ribbon object).
Function RibbonError(id As String) As Boolean
'Checks for state loss of the ribbon
Dim ret As Boolean
Dim ptr As Long
Dim src As String
On Error Resume Next
If Not Rib Is Nothing Then
GoTo EarlyExit
End If
If Rib is Nothing then
ptr = GetPointerFile
cbRibbon.pointer = ptr
Set Rib = cbRibbon.GetRibbon(ptr)
End If
On Error GoTo 0
'make sure the ribbon has been restored or exists:
ret = (Rib is Nothing)
If Not ret then
'Reload the restored ribbon by invoking the OnLoad procedure
' we can only do this because we have a handle on the Ribbon object now
Call RibbonOnLoad(Rib)
cbRibbon.pointer = ObjPtr(Rib) 'store the new pointer
Else
MsgBox "A fatal error has been encountered.", vbCritical
End If
EarlyExit:
RibbonError = ret
End Function
Call on the RibbonError function any time you are going to refresh the ribbon through either Invalidate or InvalidateControl methods.
The code above may not 100% compile -- I had to modify it and trim some stuff out, so let me know if you have any problems trying to implement it!
Found the real solution: Credit
Public Declare Sub CopyMemory Lib "kernel32" Alias _
"RtlMoveMemory" (destination As Any, source As Any, _
ByVal length As Long)
Public Sub ribbon L o a ded(ribbon As IRibbonUI)
' Store pointer to IRibbonUI
Dim lngRibPtr As Long
' Store the custom ribbon UI Id in a static variable.
' This is done once during load of UI. I.e. during workbook open.
Set guiRibbon = ribbon
lngRibPtr = ObjPtr(ribbon)
' Write pointer to worksheet for safe keeping
Tabelle2.Range("A1").Value = lngRibPtr
End Sub
Function GetRibbon(lngRibPtr as Long) As Object
Dim objRibbon As Object
CopyMemory objRibbon, lngRibPtr, 4
Set GetRibbon = objRibbon
' clean up invalid object
CopyMemory objRibbon, 0&, 4
Set objRibbon = Nothing
End Function
Then
Public Sub DoButton(ByVal control As IRibbonControl)
' The onAction callback for btn1 and btn2
' Toggle state
Toggle12 = Not Toggle12
' Invalidate the ribbon UI so that the enabled-states get reloaded
If Not (guiRibbon Is Nothing) Then
' Invalidate will force the UI to reload and thereby ask for their enabled-states
guiRibbon.Invalidate 'Control ("tabCustom") InvalidateControl does not work reliably
Else
Set guiRibbon = GetRibbon(CLng(Tabelle2.Range("A1").Value))
guiRibbon.Invalidate
' The static guiRibbon-variable was meanwhile lost
' MsgBox "Due to a design flaw in the architecture of the MS ribbon UI you have to close " & _
' "and reopen this workbook." & vbNewLine & vbNewLine & _
' "Very sorry about that.", vbExclamation + vbOKOnly
MsgBox "Hopefully this is sorted now?"
' Note: In the help we can find
' guiRibbon.Refresh
' but unfortunately this is not implemented.
' It is exactly what we should have instead of that brute force reload mechanism.
End If
End Sub

VBA: Sub to Write to a Log File

I have a set of macros defined in my workbook, and I'd like to offer the user the option to log events related to those macros in a log file.
I initiate the log by creating the following in ThisWorkbook:
Public writeLog as Boolean
Public logWrite as Object
Public log as Object
Private Sub Worksheet_Open()
Dim prompt as Integer
prompt = MsgBox("Would you like to log events for this session?", vbYesNo, "Log Events?")
If prompt Then
writeLog = True
Set logWrite = CreateObject("Scripting.FileSystemObject")
Set log = logWrite.CreateTextFile("C:/TEST.txt", False)
Else
writeLog = False
End If
End Sub
I then created a procedure that I can use to write an argument to this object, which I've stored in its own module:
Public Sub PrintLog(obj as Object, argument as String)
If writeLog = True Then
obj.WriteLine argument
End If
End Sub
Unfortunately, this doesn't work, and I'm not sure why: even if I don't include obj as an argument to the function (since log and logWrite were created as global variables), I'm not able to Call WriteLog("String here.") or Call WriteLog(log, "String here.") without an error (Compile Error: Argument Not Optional.)
Is it possible to get such a Sub() to work, so that I can call it from anywhere in the workbook (after a button is pressed in a userform, for example) without having to define a new Scripting.FileSystemObject in every module?
I think that you can solve your problem by making some minor changes to your code. I tried the following setup:
logger module:
Option Explicit
Private log As Object
Public Sub initLog()
Dim prompt As VbMsgBoxResult
Dim fso As Object
prompt = MsgBox("Would you like to log events for this session?", vbYesNo, "Log Events?")
If prompt = vbYes Then
Set fso = CreateObject("Scripting.FileSystemObject")
Set log = fso.CreateTextFile("C:/TEST.txt", False)
End If
End Sub
Public Sub PrintLog(argument As String)
If Not log Is Nothing Then
log.WriteLine argument
End If
End Sub
Public Sub yadda()
'test
PrintLog "yadda"
End Sub
ThisWorkbook:
Private Sub Workbook_Open()
initLog
End Sub
This is my no-frills drop in replacement for Debug.Print(), that logs to "Log.txt" at your Workbook path.
To install : Just search and replace "Debug.Print" with "Log", and optionally call LogClear() at the start of your program.
Public Function Log(ByRef a_stringLogThis As String)
' send to TTY
Debug.Print (a_stringLogThis)
' append (not write) to disk
Open ThisWorkbook.path & "\Log.txt" For Append As #1
Print #1, a_stringLogThis
Close #1
End Function
OPTIONAL : And here's a helper you COULD call at the beginning of your to clear out the previous logs.
Public Function LogClear()
Debug.Print ("Erasing the previous logs.")
Open ThisWorkbook.path & "\Log.txt" For Output As #1
Print #1, ""
Close #1
End Function
OPTIONAL : Finally, if can't live without date and time in your logging, use this Log statement instead:
Public Function Log(ByRef a_stringLogThis As String)
' prepare date
l_stringDateTimeNow = Now
l_stringToday = Format(l_stringDateTimeNow, "YYYY-MM-DD hh:mm:ss")
' concatenate date and what the user wants logged
l_stringLogStatement = l_stringToday & " " & a_stringLogThis
' send to TTY
Debug.Print (l_stringLogStatement)
' append (not write) to disk
Open ThisWorkbook.path & "\Log.txt" For Append As #1
Print #1, l_stringLogStatement
Close #1
End Function
I believe you're having issues as writeLog already exists as a boolean. Error should be popping up "Ambiguous name detected"
Try the following,
Public bLog as Boolean
Public logWrite as Object
Public log as Object
Private Sub Worksheet_Open()
Dim prompt as Integer
prompt = MsgBox("Would you like to log events for this session?", vbYesNo, "Log Events?")
If prompt Then
bLog = True
Set logWrite = CreateObject("Scripting.FileSystemObject")
Set log = logWrite.CreateTextFile("C:/TEST.txt", False)
Else
bLog = False
End If
End Sub
Public Sub WriteLog(Optional obj as Object, Optional argument as String)
If bLog = True Then
obj.WriteLine argument
End If
End Sub
Edit: made parameters optional in WriteLog (or PrintLog) for further testing
' Write to a log file using Separator and Array of variant Parameters
' Auto generate the file
' USE EndLog to close
'use:
' PrintLog vbtab, "one", 2, 3
' PrintLog vbtab, "Apple","Windows","Linux","Android","Commodore","Amiga","Spectrum"
' EndLog
' Generate a csv file:
' PrintLog ";", rst!ID, rst!Name
Private FileLog As Object
Private fso As Object
Const DEBUG_LOG_FILE = "C:\log.txt"
Public Sub PrintLog(ByVal Separator As String, ParamArray Arguments() As Variant)
Dim ele As Variant
Dim line As String
If FileLog Is Nothing Then
Set fso = CreateObject("Scripting.FileSystemObject")
Set FileLog = fso.CreateTextFile(DEBUG_LOG_FILE, True, True)
End If
line = CStr(Now()) ' Print Timestamp
For Each ele In Arguments
If line > "" Then line = line & Separator
line = line & CStr(ele)
Next
If line > "" Then FileLog.WriteLine line
End Sub
Public Sub EndLog()
On Error Resume Next
FileLog.Close
Set FileLog = Nothing
Set fso = Nothing
On Error GoTo 0
End Sub

Weird call of Class_Terminate

I really don't know why this is happening:
This hier is a little presentation wrapper:
' Class PPTGenPresentation
Private m_Presentation As Presentation
Public Sub Class_Initialize()
Set m_Presentation = Nothing
End Sub
Public Sub Class_Terminate()
If Not m_Presentation Is Nothing Then
m_Presentation.Close
End If
End Sub
Public Sub Initialize(ByVal presentationPath As String)
On Error GoTo Error
Set m_Presentation = Presentations.Open(presentationPath, , , msoFalse)
Exit Sub
Error:
MsgBox ("Could not open " & presentationPath)
End Sub
Public Property Get Instance() As Presentation
' After this line Class_Terminate() gets called somehow ..
Instance = m_Presentation
End Property
After I opened the ppt I want to access the actual presentation by accessing the property:
For Each filePath In filePaths
Set safePresentation = New PPTGenPresentation
safePresentation.Initialize (filePath)
Dim tmp As Presentation
Set tmp = savePresentation.Instance
For Each oSlide In tmp.Slides
Set oShape = oSlide.Shapes(1)
If oShape.HasTextFrame Then
If oShape.TextFrame.HasText Then
MsgBox oShape.TextFrame
End If
End If
Next
Next
But after accessing the property Instance, somehow Class_terminate gets called.
I have no idea why ths is happening. Could somebody explain to me what the problem is?
I have added comments to your code.
Basically, when you use set=new to overwrite an object (as happens in each subsequent iteration through your For Each loop) the previous object has either one of two situations from a theoretical standpoint:
The reference is lost but the object exists and now creates a memory leak
The object is automatically cleaned up and destroyed when the reference is gone
VBA automatically causes the second to be true. When you use "New" again, the first presentation no longer will have any way to refer to it, and so it is cleaned up and destroyed. This calls Class_Terminate
Just a note, in other languages without this sort of code you have now would start causing memory leaks (such as C++).
For Each filePath In filePaths
'Each subsequent iteration the following basically happens:
'when you set the presentation to a new one, you are effectively
'ending the previous version. So for example, the following *basically* happens:
' if not safePresentation is nothing then set safePresentation=nothing
Set safePresentation = New PPTGenPresentation
safePresentation.Initialize (filePath)
Dim tmp As Presentation
Set tmp = savePresentation.Instance
For Each oSlide In tmp.Slides
Set oShape = oSlide.Shapes(1)
Next
Next
To solve this, move Set safePresentation = New PPTGenPresentation above your For Each loop.
It gets called as you have a syntax error in your code and I suspect that you have On Error Resume Next in the calling code.
Public Property Get Instance() As Presentation
' After this line Class_Terminate() gets called somehow ..
Instance = m_Presentation
End Property
Generates an error, which is suppressed by the resume next, try:
Public Property Get Instance() As Presentation
' After this line Class_Terminate() gets called somehow ..
Set Instance = m_Presentation
End Property
You'd be much better off not suppressing errors, and certainly not when testing