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 HIDDEN INSTANCE SHOWS NOTHING

The instance is started with Visible False, DisplayAlerts False, EnableEvents False, AskToUpdateLinks False and AutomationSecurity Low. Low is the level the steps need: ForceDisable turns off every macro of the opened copy and Application.Run answers 1004. The events stay off through EnableEvents False, so the copy's Workbook_Open and ribbon load stay quiet. ScreenUpdating stays True there: the output workbook refused its freeze under ScreenUpdating False in an invisible instance, and nothing is painted in an invisible instance anyway. Calculation is set to manual once the copy is open, because the write raises 1004 on an instance holding no workbook.

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 " with the instance marked stopped so ReleaseInstance quits nothing, or a plain fault such as a step name the copy does not carry, answered as "ERROR (): ".

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 (): " naming the window handle. Empty before ReleaseInstance.


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 " and marks the instance stopped; any other fault is the plain error outcome. Once the instance is marked stopped every later Run answers the stopped outcome without a call.

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 (GenerationHost.ReleaseInstance): ..." naming the window handle.


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:


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:

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:


ResolveCopyPath #

resolve-copy-path

The path of the copy inside the folder

Signature:

Private Function ResolveCopyPath(ByVal copyFolder As String) As String

Parameters:

Returns: String. The full path of the copy file.

Throws:


FolderExists #

folder-exists

Whether a path names an existing folder

Signature:

Private Function FolderExists(ByVal folderPath As String) As Boolean

Parameters:

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:

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:

Returns: Boolean.


InstanceLabel #

instance-label

Name an instance for a refusal message

Signature:

Private Function InstanceLabel(ByVal checkedApp As Application) As String

Parameters:

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:


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:

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:

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:

Returns: String. "ERROR (GenerationHost.Run): the build instance stopped answering after ".


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:


RequireCopy #

require-copy

Raise when OpenDesignerCopy has not run

Signature:

Private Sub RequireCopy(ByVal memberName As String)

Parameters:


ThrowError #

throw-error

Raise a typed project error

Signature:

Private Sub ThrowError(ByVal errNumber As ProjectError, ByVal message As String)

Parameters:


Used in (8 file(s))