GenerationHost
Carries the whole platform difference of a linelist build in one object. A build runs as the steps of BuildSteps, and this class owns the Excel instance those steps run in. On the instance path (Windows, the chkBuildInPlace box unchecked) Acquire starts a hidden Excel, OpenDesignerCopy writes a copy of the designer and opens it there, Run reaches a step through Application.Run across the two processes, and ReleaseInstance closes the copy and quits the instance this object started. On the in-place path (Mac always, Windows when the box is checked) the instance is the designer's own, the copy is the designer itself, Run is an in-process Application.Run and ReleaseInstance is a no-op.
GenerationHost.cls without interface: single implementation, no test fake, no compile cycle.
THE OUTPUT WORKBOOK IS PAINTED ONCE, FROM THIS SIDE
A window that was never painted refuses Window.FreezePanes with 1004, and with ScreenUpdating off for the whole build the window of the output workbook is never painted: every data entry sheet came out unfrozen. Nothing a macro of the instance does paints it. Switching ScreenUpdating on inside the step, with the sheet activated, the zoom written, a scroll, a cell rewritten, Application.Wait or DoEvents after it, was refused every time (tried 2026-10-01). What paints it is ScreenUpdating switched on from THIS side while the instance sits idle between two steps, and off again right after. PaintInstance does that. The driver calls it once the output workbook exists, after the linelist step, and every freeze of the build is accepted from then on: Info and the 511 variable Linelist sheet of the ebola setup both froze, the shared graphics memory of the instance stayed at 18 MB, and the sheet took the same time with and without it.
A CALL THIS EXCEL STOPPED WAITING FOR
A step that runs long makes the visible Excel raise its box "waiting for another application to complete an OLE action", and the pending call can end cancelled: Run then sees -2147418110 while the step carries on in the instance and finishes. Its answer is still there. BuildSteps keeps what its last step answered, and after a dropped call Run asks for it through LastAnswer, waiting for the instance to come free. The step named by LastAnswerStep is the one asked; RecoveredCalls counts the answers read back that way, for the driver to file in its report.
THE COPY OPENS READ-WRITE
The copy is a private temp file named __designer_copy.xlsb that is never saved, so it opens read-write. A read-only open of the same file costs 9 to 20 seconds against 0.6; that difference is what makes the hidden build run at the in-place speed. A user file never carries the copy's name.
A RAISE INSIDE A STEP IS A HANG
Application.Run opens a fresh call stack, so an untrapped raise inside a
step reaches no handler here, on either path. In place it shows the
runtime error box; across the processes the hidden instance shows the VBE
box, un-hides itself, and the visible side blocks. Every step therefore
keeps its own handler and answers a string, and Run passes that string
through. A raise Run sees on the visible side is one of three things. The
instance gone (462, RPC call failed, RPC server unavailable) is answered
as "the build instance stopped answering after
RELEASE RUNS FROM EVERY EXIT
ReleaseInstance closes the copy without saving, quits the instance this object owns and deletes the copy file. It runs from Class_Terminate too, under On Error Resume Next, so a driver that raises on its way out still frees the instance. A Quit that fails leaves the instance and answers its window handle, taken at Acquire, for the driver to log. The instance is never found by process name.
Depends on: ProjectError, DesignerPreparation, BetterArray
Version: 1.0 (2026-08-28)
Factory
Create #
create
Build a host over a designer workbook and pick its path
Signature:
Public Function Create(Optional ByVal designerBook As Workbook = Nothing, _
Optional ByVal pathChoice As Byte = HostPathFromFlag) As GenerationHost
The path is settled here and never changes for the life of the object: in place on Mac whatever the choice, and on Windows the chkBuildInPlace flag of the designer unless the choice forces a path. Nothing is started until Acquire.
Parameters:
designerBook: Optional Workbook. The designer the build reads. Nothing takes ThisWorkbook.pathChoice: Optional Byte. A GenerationHostPath member. Defaults to HostPathFromFlag.
Returns: GenerationHost. Ready for Acquire.
Introspection
InPlace #
in-place
Whether the build runs in the designer's own instance
Signature:
Public Property Get InPlace() As Boolean
Returns: Boolean. True on Mac, and on Windows when the flag or the choice says so.
IsAcquired #
is-acquired
Whether Acquire has run and ReleaseInstance has not
Signature:
Public Property Get IsAcquired() As Boolean
Returns: Boolean.
InstanceStopped #
instance-stopped
Whether a Run found the instance gone
Signature:
Public Property Get InstanceStopped() As Boolean
Returns: Boolean. True once a Run answered "stopped answering".
Hwnd #
hwnd
The window handle of the owned instance, taken at Acquire
Signature:
Public Property Get Hwnd() As Long
Returns: Long. 0 in place, and before Acquire.
HostApplication #
host-application
The instance the build runs in
Signature:
Public Property Get HostApplication() As Application
Returns: Application. The owned instance, or the designer's own in place. Nothing before Acquire.
DesignerCopy #
designer-copy
The workbook the steps run inside
Signature:
Public Property Get DesignerCopy() As Workbook
Returns: Workbook. The copy in the instance, or the designer in place. Nothing before OpenDesignerCopy.
CopyPath #
copy-path
The path of the copy file
Signature:
Public Property Get CopyPath() As String
Returns: String. Empty in place, and before OpenDesignerCopy.
LastStep #
last-step
The step the last Run was asked for
Signature:
Public Property Get LastStep() As String
Returns: String. Empty before the first Run.
ReleaseOutcome #
release-outcome
What the last ReleaseInstance answered
Signature:
Public Property Get ReleaseOutcome() As String
Returns: String. "OK", or "ERROR
LastAnswerStep #
last-answer-step
The step that answers what the last step answered
Signature:
Public Property Get LastAnswerStep() As String
A public function of the workbook the steps run inside, answering
"
Returns: String. The step name, with its module.
LastAnswerStep #
last-answer-step-let
Name the step that answers what the last step answered
Signature:
Public Property Let LastAnswerStep(ByVal stepName As String)
Parameters:
stepName: String. A public function of the copy. Empty puts the default back.
RecoveredCalls #
recovered-calls
How many answers were read back after a dropped call
Signature:
Public Property Get RecoveredCalls() As Long
Returns: Long. Zero when every Run came back on its own call.
The instance
Acquire #
acquire
Start the instance the build runs in
Signature:
Public Sub Acquire()
In place, the instance is the designer's own Application and nothing changes on it. On the instance path a new Excel is started hidden, with alerts, events and link prompts off and automation security Low, and its window handle is recorded. Calculation waits for OpenDesignerCopy.
Throws:
- ProjectError.ErrorUnexpectedState When Acquire already ran, or the instance would not start.
OpenDesignerCopy #
open-designer-copy
Put the designer into the instance
Signature:
Public Function OpenDesignerCopy(Optional ByVal copyFolder As String = vbNullString) As Workbook
In place the designer itself is the workbook the steps run inside, and the folder is ignored. On the instance path the designer is written as __designer_copy.xlsb in the folder through SaveCopyAs, which takes the unsaved entries, formatter, passwords and translations with it and leaves the open designer alone, then the copy is opened read-write in the instance with links left alone, and Calculation goes manual there.
Parameters:
copyFolder: String. The folder the copy is written in, on the instance path. Must exist.
Returns: Workbook. The workbook the steps run inside.
Throws:
- ProjectError.ErrorUnexpectedState When Acquire has not run, or the copy would not open.
- ProjectError.InvalidArgument When the folder is empty or missing, on the instance path.
The file names
CheckOpenNames #
check-open-names
Apply the file-name rules to the files a build opens
Signature:
Public Function CheckOpenNames(ByVal paths As BetterArray, _
Optional ByVal target As Application = Nothing) As String
Excel refuses to open a file whose name matches one already open in the same instance, and to save onto a file open anywhere. Two rules: two files of the list sharing a name is a refusal naming both paths, and a name already open in the target instance is a refusal naming the open file. Names compare without regard to case; empty entries are skipped. The target is the instance the build runs in once Acquire ran, or the designer's own before, so on Mac the designer itself is one more open name and on Windows the copy is. A driver checks the visible side by passing its own Application.
Parameters:
paths: BetterArray. Full paths, or bare file names. Nothing checks nothing.target: Optional Application. The instance to check against. Nothing takes the host's.
Returns: String. Empty when nothing clashes; otherwise one refusal per line, vbLf between them.
The steps
Run #
run
Run one build step inside the workbook the steps run in
Signature:
Public Function Run(ByVal stepName As String, ParamArray arguments() As Variant) As String
Application.Run over the step name, qualified with the workbook the
steps run inside: the copy in the instance, the designer in place. The
name may carry its module, as in "BuildSteps.BuildSheet". Up to three
arguments are forwarded; across the processes they have to be strings
or numbers, since a workbook object cannot cross. The step's answer
comes back as text. A raise on the visible side comes back as an
outcome string too: the instance gone is "the build instance stopped
answering after
A call this Excel stopped waiting for (cancelled, rejected, or told to come back later) is answered from the copy: the step carries on over there, so Run waits for the instance to come free and reads back what the step answered, through LastAnswer. The driver then sees the answer of the step itself. On the instance path ScreenUpdating is written off before the step, whatever the step before it left.
Parameters:
stepName: String. The public function to run.arguments: ParamArray. Up to three arguments for the step.
Returns: String. What the step answered, or an "ERROR ..." outcome.
Throws:
- ProjectError.ErrorUnexpectedState When OpenDesignerCopy has not run.
- ProjectError.InvalidArgument When more than three arguments are handed over.
PaintInstance #
paint-instance
Let the hidden instance paint its windows once
Signature:
Public Function PaintInstance() As String
ScreenUpdating on, then off, as two calls from this side with the instance idle between them. That one paint is what makes the window of the output workbook take its freezes for the rest of the build; see the note at the top of this class. In place nothing is written: the designer's own Excel paints on its own and its windows take the freeze. The second write runs even when the first one raised, so the screen of the instance is off again on every exit.
Returns: String. "OK", the stopped outcome when the instance is gone, or "ERROR
Throws:
- ProjectError.ErrorUnexpectedState When OpenDesignerCopy has not run.
LastAnswer #
last-answer
Read back what the last step of the copy answered
Signature:
Public Function LastAnswer(ByVal stepName As String, Optional ByVal argument As Variant) As String
Asks the step named by LastAnswerStep, which answers "
Parameters:
stepName: String. The step whose answer is wanted, as Run was given it.argument: Optional Variant. The one argument of that step, when it took one.
Returns: String. What the step answered, or an "ERROR ..." outcome when the answer could not be read back.
Throws:
- ProjectError.ErrorUnexpectedState When OpenDesignerCopy has not run.
ReleaseInstance
ReleaseInstance #
release
Close the copy, quit the owned instance, delete the copy file
Signature:
Public Function ReleaseInstance() As String
In place nothing is closed and nothing quits: the designer stays where it is. On the instance path the copy is closed without saving and the instance this object started is quit; an instance a Run found gone is left alone, since there is nothing left to quit. The whole body runs under On Error Resume Next, because it runs from the failure exits and from Class_Terminate. A Quit that fails answers the window handle for the driver to log; the answer is kept on ReleaseOutcome too. Calling ReleaseInstance twice is one release.
Returns: String. "OK", or "ERROR
Internal members (not exported)
Factory
Initialise #
initialise
Bind the designer and settle the path
Signature:
Friend Sub Initialise(ByVal designerBook As Workbook, ByVal pathChoice As Byte)
Parameters:
designerBook: Workbook. The designer, or Nothing for ThisWorkbook.pathChoice: Byte. A GenerationHostPath member.
ResolveInPlace #
resolve-in-place
Decide the path from the platform, the choice and the flag
Signature:
Private Function ResolveInPlace(ByVal pathChoice As Byte) As Boolean
Mac Excel is single-instance and has no COM, so the answer is True there whatever the choice. On Windows a forced choice wins; otherwise the chkBuildInPlace flag of the designer decides, False when it was never set.
Parameters:
pathChoice: Byte. A GenerationHostPath member.
Returns: Boolean. True for the in-place path.
The instance
StartHiddenInstance #
start-hidden-instance
Start a hidden Excel and set it up for a build nobody watches
Signature:
Private Sub StartHiddenInstance()
Windows only; the in-place path never reaches this. A start that fails leaves nothing behind.
Throws:
- ProjectError.ErrorUnexpectedState When the instance would not start or take a setting.
ResolveCopyPath #
resolve-copy-path
The path of the copy inside the folder
Signature:
Private Function ResolveCopyPath(ByVal copyFolder As String) As String
Parameters:
copyFolder: String. The folder, with or without its trailing separator.
Returns: String. The full path of the copy file.
Throws:
- ProjectError.InvalidArgument When the folder is empty or missing.
FolderExists #
folder-exists
Whether a path names an existing folder
Signature:
Private Function FolderExists(ByVal folderPath As String) As Boolean
Parameters:
folderPath: String. The path.
Returns: Boolean.
DeleteCopyFile #
delete-copy-file
Remove the copy file from disk, quietly
Signature:
Private Sub DeleteCopyFile()
A file the instance still holds open refuses the delete, and that is left as it is: the file is scratch and the next run overwrites it.
The file names
FileNameOf #
file-name-of
The file name at the end of a path
Signature:
Private Function FileNameOf(ByVal pathText As String) As String
Both separators are read, because a Mac path carries a slash and a Windows path may carry either.
Parameters:
pathText: String. A full path or a bare name.
Returns: String. The part after the last separator.
SameName #
same-name
Whether two file names match without regard to case
Signature:
Private Function SameName(ByVal first As String, ByVal second As String) As Boolean
Parameters:
first: String.second: String.
Returns: Boolean.
InstanceLabel #
instance-label
Name an instance for a refusal message
Signature:
Private Function InstanceLabel(ByVal checkedApp As Application) As String
Parameters:
checkedApp: Application. The instance.
Returns: String. "the build instance" for the owned one, "this Excel" otherwise.
AppendLine #
append-line
Add a line to a text, vbLf between the lines
Signature:
Private Sub AppendLine(ByRef text As String, ByVal lineText As String)
Parameters:
text: String. ByRef. The text so far.lineText: String. The line.
The steps
FailureOutcome #
failure-outcome
Shape the outcome of a Run that raised on the visible side
Signature:
Private Function FailureOutcome(ByVal errNumber As Long, _
ByVal errSource As String, _
ByVal errDesc As String, _
ByVal stepLabel As String) As String
Three cases. The instance gone answers the stopped outcome. A call dropped on this side while the instance was at work is answered with what the step answered over there, read back from the copy. Any other fault answers the plain error outcome.
Parameters:
errNumber: Long. The error number.errSource: String. The error source. Empty takes this class.errDesc: String. The description.stepLabel: String. The label the copy files the step under.
Returns: String. The stopped outcome, the answer read back, or the plain error outcome.
AnswerOfDroppedCall #
answer-of-dropped-call
Answer a Run whose call was dropped while the instance was at work
Signature:
Private Function AnswerOfDroppedCall(ByVal errNumber As Long, _
ByVal stepLabel As String) As String
The step is still running over there, or has finished. Its answer is read back once the instance comes free and counted on RecoveredCalls. An instance that went away meanwhile answers the stopped outcome. An answer that cannot be read back answers an error outcome carrying the number of the dropped call and the reason.
Parameters:
errNumber: Long. The number the dropped call raised.stepLabel: String. The label the copy files the step under.
Returns: String. What the step answered, or an "ERROR ..." outcome.
AwaitLastAnswer #
await-last-answer
Ask the copy for its last answer until the instance answers
Signature:
Private Function AwaitLastAnswer(ByVal stepLabel As String, ByRef answer As String) As Byte
One call on the step named by LastAnswerStep. While the instance is still inside the step, that call waits its turn and may be dropped like the first one, so it is asked again after a short pause, for thirty minutes at most. An instance gone ends the wait; so does any other fault, a copy that carries no such step being the common one.
Parameters:
stepLabel: String. The label the answer has to be filed under.answer: String. Answers what the step answered, or why it could not be read.
Returns: Byte. AWAIT_ANSWERED, AWAIT_INSTANCE_GONE or AWAIT_UNREADABLE.
ReadFiledAnswer #
read-filed-answer
Take the answer out of "
Signature:
Private Function ReadFiledAnswer(ByVal filed As Variant, _
ByVal stepLabel As String, _
ByRef answer As String) As Byte
Parameters:
filed: Variant. What the last-answer step answered.stepLabel: String. The label the answer has to be filed under.answer: String. Answers what the step answered, or why it could not be read.
Returns: Byte. AWAIT_ANSWERED or AWAIT_UNREADABLE.
StepLabelOf #
step-label-of
The label the copy files a step under
Signature:
Private Function StepLabelOf(ByVal stepName As String, Optional ByVal argument As Variant) As String
The name of the step with its module dropped, and the argument after a space when the step took one whole number: "BuildSheet 2".
Parameters:
stepName: String. The step, with or without its module.argument: Optional Variant. The one argument of the step, when it took one.
Returns: String. The label.
IsDroppedCallError #
is-dropped-call-error
Whether an error number says this side dropped the call
Signature:
Private Function IsDroppedCallError(ByVal errNumber As Long) As Boolean
Cancelled by the message filter of this Excel, which is what a long step ends on once the "waiting for another application" box has been up; rejected by the instance; or told to come back later. In all three the instance is alive.
Parameters:
errNumber: Long.
Returns: Boolean.
PauseThisExcel #
pause-this-excel
Hold the visible Excel for the pause between two tries
Signature:
Private Sub PauseThisExcel()
IsInstanceGoneError #
is-instance-gone-error
Whether an error number says the instance is gone
Signature:
Private Function IsInstanceGoneError(ByVal errNumber As Long) As Boolean
The three shapes the spike measured: 462 on every call after a quit or a kill, the RPC call failed on the call the kill interrupted, and the RPC server unavailable. Plus the one the suite met: the object disconnected from its clients, on the first call after a quit.
Parameters:
errNumber: Long.
Returns: Boolean.
StoppedOutcome #
stopped-outcome
The outcome of a Run whose instance is gone
Signature:
Private Function StoppedOutcome(ByVal errNumber As Long) As String
Parameters:
errNumber: Long. The number that said so.
Returns: String. "ERROR
ReleaseInstance
Class_Terminate #
class-terminate
ReleaseInstance the instance when the object is dropped
Signature:
Private Sub Class_Terminate()
A driver that raises on its way out drops this object without calling ReleaseInstance, and the hidden instance would live on with nobody to quit it.
Guards
RequireAcquired #
require-acquired
Raise when Acquire has not run
Signature:
Private Sub RequireAcquired(ByVal memberName As String)
Parameters:
memberName: String. The member asking.
RequireCopy #
require-copy
Raise when OpenDesignerCopy has not run
Signature:
Private Sub RequireCopy(ByVal memberName As String)
Parameters:
memberName: String. The member asking.
ThrowError #
throw-error
Raise a typed project error
Signature:
Private Sub ThrowError(ByVal errNumber As ProjectError, ByVal message As String)
Parameters:
errNumber: ProjectError. The error code.message: String. Descriptive error message.
Used in (9 file(s))
- BuildSteps.bas
- EventsDesignerAdvanced.bas
- EventsDesignerCore.bas
- EventsDesignerMulti.bas
- SetupRibbon.bas
- TestLLImportTiming.bas
- TestDesignerMulti.bas
- TestGenerationHost.bas
- TestHelpersLite.bas