How to set instance variable by code?

Hi,

After creating a WFI, some tasks and scripts are executed.
In one of the scripts a procedure inside the mainform is executed that will open a specific form where a document is created.
This document is stored in some table with RecordID = 187.

This RecordID I need to store in a WF-variable called "RecordIDCreated" (so I can use it later on to locate this record again.
I've tried to accomplish this by using following code, but the variable isn't stored.

CODE:
      wfi  := Form1.WorkflowStudio1.WorkflowManager.FindWorkflowInstanceByKey(IntToStr(vwfWFIID));
      wvar := wfi.Diagram.Variables.FindByName('RecordIDCreated');
      if Assigned(wvar) then
        wvar.Value := NewID;  // in this case it will be 187

I've checked that "vwfWFIID" and NewID were properly initialized and contain the correct values.
The code is completely executed (including wvar.Value := NewID;).

In the next script in the same WFI the stored variable is assigned to a global variable, so I can used the global variable to retrieve the record with ID = 187.
SCRIPT:
vwfRecordIDCreated := RecordIDCreated;
As stated before, variable "RecordIDCreated" is still 0 in stead of 187.

Can you please explain how a WFI-variable can be assigned/updated properly during execution of the WFI?

If you are running that code outside an workflow instance execution, then you must call SaveWorkflowInstance to persist changes to the workflow.

But if the workflow is already running and that code is called from script, then you should not open another instance of TWorkflowInstance object. There is already one instantiated and running! You should use script variable _Workflow (which is actually a TWorkflowDiagram) and pass it to your Delphi code so that code uses that workflow instance instead of finding a new one with the manager.
The workflowinstance is created with this code.

function TForm1.RunSomeWorkflow(AWorkflowDefinitionName: string): string;
var wdf : TWorkflowDefinition;
    wfi : TWorkflowInstance;
    wvar: TWorkflowVariable;
begin
  wdf := Form1.WorkflowStudio1.WorkflowManager.FindWorkflowDefinitionByName(AWorkflowDefinitionName);
  wfi := Form1.WorkflowStudio1.WorkflowManager.CreateWorkflowInstance(wdf);
  result := wfi.Key;
  wvar := wfi.Diagram.Variables.FindByName('WFIID');
  if Assigned(wvar) then
    wvar.Value := wfi.Key;
  Form1.WorkflowStudio1.WorkflowEngine.RunWorkflow(wfi);
end;

Here the ID of the created Workflow instance is saved in the instance itself.

This first task within the WFI is started (Status = Started). This triggers a script that will first save the WFI-variable "WFIID" into global variable "vwfWFIID" and than it will initiate creation of a document. When saving the document, the ID of that documentrecord needs to be saved inside the running workflowinstance for which I use following code is inside a procedure of the form (so not inside a script).

Procedure TForm1.SaveCreatedRecordIDinWFI;
begin
      wfi  := Form1.WorkflowStudio1.WorkflowManager.FindWorkflowInstanceByKey(IntToStr(vwfWFIID));
      wvar := wfi.Diagram.Variables.FindByName('RecordIDCreated');
      if Assigned(wvar) then
        wvar.Value := NewID;
      Form1.WorkflowStudio1.WorkflowManager.SaveWorkflowInstance(wfi);
end;
This code isn't saving the ID into WFI-variable "RecordIDCreated", so it probably must be setup differently.

Can you give me a sample on how to get this working?

As I said, if the form was executed from the workflow, there is already an instance running. The code in SaveCreatedRecordIDinWFI create ANOTHER instance, saves it, but if the execution flow goes back to the original workflow instance, that is then saved, and overwrites all changes you made in SaveCreatedRecordIDinWFI method.

So if you want to change data in a workflow instance that IS running, you should do that in that TWorkflowInstance object.

Hello Wagner,


Can you please supply me with an example which shows hoe to use TWorkflowInstance object to update the running WFI?

I don't know how your workflow works exactly, not sure how your form is being called, how is your script. But as I said, you just need to pass the workflow instance from script to your form/delphi code.

The workflow instance is available from script using this:

WFI := _Workflow.WorkflowInstance;

then you can pass that instance to your form or whatever you use from your script:

CallSomeDelphiCode(WFI);

from your Delphi code/form, after you received your workflow instance, you can use it to set the variable value like you did in your original code:

wvar := wfi.Diagram.Variables.FindByName('RecordIDCreated');
      if Assigned(wvar) then
        wvar.Value := NewID;  // in this case it will be 187

and that's it, when execution goes back to the workflow engine, the workflow will be saved with the updated variable value.
Hi,
This is harder than I thought :-).

How to set this up correclty?    "WFI := _Workflow.WorkflowInstance;"

// ****************************
// WORKFLOW definition
// ****************************
Added Workflow variable: 
CurrentWFI

SCRIPT:
CurrentWFI :=  _Workflow.WorkflowInstance;
Form1.UitvoerenWorkflow(CurrentWFI);


// ****************************
// MAIN FORM
// ****************************
// Added global variable:
varCurrentWFI : TWorkflowInstance;

procedure TForm1.PrepareScripter(Sender: Tobject);
begin
  With TatCustomScripter(Sender) do
  begin
    AddComponent(Form1);
    With DefineClass(TCustomForm) do
    begin

// Is this declared correctly?
     DefineMethod('UitvoerenWorkflow',0,tkNone,nil,UitvoerenWorkflowProc, true);
    end;
  end;
end;

procedure TForm1.UitvoerenWorkflowProc(AMachine: TatVirtualMachine);
begin
  With AMachine do
    UitvoerenWorkflow( ??????? );  // Here CurrentWFI must be passsed through.
end;

procedure TForm1.UitvoerenWorkflow(vCurrentWFI : TWorkflowInstance);
begin
  // Here I need to store the variable pointing to the running instance into a global variable for later usage.
  varCurrentWFI := vCurrentWFI;
  // etc.
end; 

Can you please correct my code?

Here are the small fixes in your code to make it work:


in PrepareScripter:

    // TCustomForm will work, but the method will be declared in any
    // TCustomForm descendant. It's more correct to declare in your
    // specific form class
    With DefineClass(TForm1) do
      DefineMethod('UitvoerenWorkflow',1,tkNone,nil,UitvoerenWorkflowProc);

In your form:

procedure TForm1.UitvoerenWorkflowProc(AMachine: TatVirtualMachine);
begin
  With AMachine do
    UitvoerenWorkflow(TWorkflowInstance(VarToObject(GetInputArg(0))));  
end;

I changed my code according to your instructions, but the variable "RecordIDCreated" is still not stored in the WFI.

wvar := varCurrentWFI.Diagram.Variables.FindByName('RecordIDCreated');
      if Assigned(wvar) then
        wvar.Value := NewID; 

Do you have any other suggestions?

Is your form being displayed as ShowModal?

Yes, the form that creates the document is shown as ShowModal.


procedure TFHoofdscherm.UitvoerenWorkflow(vCurrentWFI : TWorkflowInstance);
begin
  varCurrentWFI := vCurrentWFI;
  FCreateDoc := TFCreateDoc.Create(Self);
  FCreateDoc.Showmodal;
end;

FCreateDoc will call a procedure in Form1 to add a record for the created document.

In that procedure I add the ID of the created record to the running WFI.

Initial creation of the WFI is done via a different form.

function TFAanmakenWorkflow.RunSomeWorkflow(AWorkflowDefinitieNaam: string; vID, vNaam, vEntiteit, vProgrammaID, vProgrammaActieID, vRecordIDAangemaakt : Variant): string;
var wdf : TWorkflowDefinition;
    wfi : TWorkflowInstance;
    wvar: TWorkflowVariable;
begin
  wdf := Form1.WorkflowStudio1.WorkflowManager.FindWorkflowDefinitionByName(AWorkflowDefinitieNaam);
  wfi := Form1.WorkflowStudio1.WorkflowManager.CreateWorkflowInstance(wdf);
  result := wfi.Key;

  wvar := wfi.Diagram.Variables.FindByName('ID');
  if Assigned(wvar) then
    wvar.Value := vID;

  wvar := wfi.Diagram.Variables.FindByName('Naam');
  if Assigned(wvar) then
    wvar.Value := vNaam;

  wvar := wfi.Diagram.Variables.FindByName('Entiteit');
  if Assigned(wvar) then
    wvar.Value := vEntiteit;

  wvar := wfi.Diagram.Variables.FindByName('ProgrammaID');
  if Assigned(wvar) then
    wvar.Value := vProgrammaID;

  wvar := wfi.Diagram.Variables.FindByName('ProgrammaActieID');
  if Assigned(wvar) then
    wvar.Value := vProgrammaActieID;

  wvar := wfi.Diagram.Variables.FindByName('RecordIDAangemaakt');
  if Assigned(wvar) then
    wvar.Value := vRecordIDAangemaakt;

  Form1.WorkflowStudio1.WorkflowEngine.RunWorkflow(wfi);
end;

Forgot to translate the names for you, but you get the idea I think.
RecordIDAangemaakt = RecordIDCreated

RecordIDCreated is at creation of the WFI set to 0 (zero)

If it helps I can sent you a project by mail. 

Did you put a breakpoint in the wvar.Value assignment just to check if the variable is being found by the FindByName method?

Yes, you can send the project, but please make sure I can compile it here with no extra components needed besides the ones provided in Delphi itself, thanks.