Skip to main content

Message Actions

SAL On SAM_... sections become cases in a PPJ action handler. That handler preserves SAL message behavior alongside the events exposed by the underlying UI framework. A native Click event and SAM_Click may differ in ordering, validation, and inherited behavior, so replacing one with the other requires a behavioral test.

Newer PPJ releases use WindowActions. MessageActions is the older event model, deprecated starting with PPJ 3.5. Check the event and argument type used by the generated project before copying an example from an older application.

Adding new MessageActions / WindowActions​

Use the control's event list in the designer to locate or create its WindowActions handler. A typical handler has this shape; RunCommand represents application code:

private void pbRun_WindowActions(object sender, WindowActionsEventArgs e)
{
switch (e.ActionType)
{
case Sys.SAM_Click:
e.Handled = true;
RunCommand();
break;
}
}

The namespace for these arguments is PPJ.Runtime.Windows. Mark the action handled before a conditional business branch. For example, even if RunCommand decides there is nothing to save, the handler still recognizes this action.

Legacy MessageActions uses a SalMessage passed by reference and switches on message.Code. The corresponding properties are message.Handled and message.Return. Passing the struct by reference allows the handler to modify the message supplied by the caller; it does not imply that every struct is allocated on the stack.

Message handling​

Two decisions must remain separate:

  • Handled: this handler recognizes the action and participates in processing it.
  • Return value: the SAL message handler supplies a value to its caller, with the meaning defined by that specific message.

In the reviewed PPJ 5.0 implementation, assigning e.Return also marks the action handled and sets HasReturnValue. Assigning e.Handled = true alone does not supply a return value. For example, assigning zero is an explicit return; it is different from leaving the return unset. A C# return; only exits the C# handler and does not assign e.Return.

SAL action arrives
│
▼
Newest applicable action handler
│
├─ Handled or explicit Return ─► stop automatic handler-chain traversal
│
└─ Neither set ────────────────► try earlier handler
│
no handler claims action
│
▼
may cache as unhandled

The desktop dispatcher can remember an unhandled action code for a control and skip later dispatches of that code. This is why an action may appear to work only on its first occurrence if Handled is omitted. This cache concerns that control's action dispatch; it does not mean an action code is globally disabled for the whole application.

For desktop messages that reach the native window procedure, whether PPJ invokes default processing also depends on whether a return value was supplied. Do not assign a return merely to silence a warning: some messages use it to approve, reject, or modify an operation. Preserve the original SAL message's return contract.

SalSendClassMessage​

The action chain is not an ordinary multicast event that always invokes every subscriber. In the reviewed desktop and web 5.0 runtime, dispatch begins with the newest registered handler and walks backward until a handler marks the action handled or supplies a return. Therefore the older description “only the last attached handler is invoked” is incomplete: an unclaimed action can reach earlier handlers.

Sal.SendClassMessage is the explicit mechanism used by generated code when a handler needs inherited class handling as well as its own work. Preserve its position relative to the derived handler's work and return assignment. Calling the same handler again as an ordinary method can bypass the dispatch context or produce duplicate processing. Older releases and specialized controls should be checked against their own implementation.

Sending and Posting messages​

On Desktop, Sal.SendMsg is synchronous: nested processing can occur before it returns. Sal.PostMsg queues work: the posted action has not necessarily executed when the call returns. Both differ from calling a business method directly.

Send: caller → action handler → result → caller continues
Post: caller → queue → caller continues
└─ later: action handler

A posted action must still have a valid destination when processed. Do not post work to a form that is being disposed, and do not use a successful post as confirmation that a database save or validation completed. Synchronous sends can be reentrant, so keep intermediate state consistent before sending.

PPJ Web messaging is part of the server-side UI/session model. A browser control is not a Windows HWND; do not transfer native message assumptions across targets.

Windows messages​

On Desktop, PPJ can relay native Windows messages into the action system. Keep OS-specific handling separate from application-level SAM actions. Prefer supported control properties over native style manipulation when an equivalent property expresses the intent.

Choosing an Event Model​

Add a new business operation in one deliberate place. Wiring it to both WindowActions and a native event may execute it twice. Test mouse and keyboard activation, validation, derived/base handlers, repeated actions, and synchronous reentry.

PPJ's action dispatch establishes SAL context. If a new native event calls context-dependent SAL code outside that path, establish the necessary SalContext. Retain generated scopes rather than assuming this always represents the current SAL form or item.