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 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 either
the instance gone (462, RPC call failed, RPC server unavailable), 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
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
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.
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) As String
Parameters:
errNumber: Long. The error number.errSource: String. The error source. Empty takes this class.errDesc: String. The description.
Returns: String. The stopped outcome for the instance gone, the plain error outcome otherwise.
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 (8 file(s))
- EventsDesignerAdvanced.bas
- EventsDesignerCore.bas
- EventsDesignerMulti.bas
- SetupRibbon.bas
- TestLLImportTiming.bas
- TestDesignerMulti.bas
- TestGenerationHost.bas
- TestHelpersLite.bas