liorafar
Level 6

How to reuse DTF Custom Action while passing parameters?

Hi all,
I have a big problem I always come across and I think if someone can answer this, it will help to the people who use C# DTF Custom Actions.

I've created a MSI Dll custom action which does something on a file.
what I wanna do is reusing this function several time in the sequence , however, each time pass to the function a different file path.

The way a know how to pass a parameter with DTF is with CustomActionData.
This is My Code:
[CODE]
[CustomAction]
public static ActionResult PassParametersToExecuteFile(Session session)
{
CustomActionData customActionData = new CustomActionData();
string filePath = session["FilePath"];
customActionData.Add("FilePathData", filePath);

session["ExecuteFile"] = customActionData.ToString();

return ActionResult.Success;
}

[CustomAction]
public static ActionResult ExecuteFile(Session session)
{
CustomActionData customActionData = session.CustomActionData;
string filePath = customActionData["FilePathData"];

if (File.Exists(filePath))
{
// Do My Code here....
}

return ActionResult.Success;
}
[/CODE]


The property FilePath that I pass here is the same property I passed from my MSI Project(ism file):



Therefore, The msi passes the value of the property(FilePath) to "PassParametersToExecuteFile" function and then this CA passes this property to the deferred CA function "ExecuteFile" with the customActionData

PassParametersToExecuteFile is an immidiate CA
And
ExecuteFile is a deferred CA
The explenation for this is because only immidiate CA can go stricly to the session database using session["PROPNAME"](this is the same reason that deferred CA cant get session["PROPNAME"] since it doesnt have access to the database)

However,
since the immidiate custom action initiate before the "InstallInitialize" sequence , The propery FilePath is getting the value its first time (In PassParametersToExecuteFile1 CA) and then it gets its value the second time (in PassParametersToExecuteFile2) before even "ExecuteFile1" CA Started! this way I always get to all my CA the last value I entered to this property.

I know that i can do a property for each CA but than again i can write the same time 3 different CA in C#

But how exactly can I reuse a C# function with different parameter values if I cant even pass different values for the same parameter?

Please I'll appriciate any help since it stucks me and my company on our new project

Thanks alot,
Lior
Labels (1)
0 Kudos
6 Replies
liorafar
Level 6

Please why no body answers i really need help.
This is the only place i can find answers
0 Kudos
MichaelU
Level 12 Flexeran
Level 12 Flexeran

If I'm reading your code correctly, you're setting up a property which will specify CustomActionData for a custom action called "ExecuteFile", but your custom actions are called "ExecuteFile1" and "ExecuteFile2". It should be easy to confirm this in a verbose log file, which is the first step I'd recommend in debugging this sort of issue.

Depending on what you're really trying to do here, it would probably be better to rearchitect this as a table-driven action set. The single immediate action would read each row in the table, perhaps turning it into data passed through CustomActionData to the single deferred custom action. The single deferred custom action would then act on the accumulated data. It's a little more leg work to set this up, but much more scalable.
0 Kudos
liorafar
Level 6

First of all thanks for your answer.
Second, If My description was not clear I'll explain it better:
*FilePath1 and FilePath2 both keep the property "FilePath" this will be the parameter that is passed to ExecuteFile function eventually.
*PassParametersToExcuteFile1 and PassParametersToExcuteFile2 both call to PassParametersToExcuteFile function and both immidiate CA
* ExecuteFile1 and ExecuteFile2 both call to ExecuteFile function and both deffered.
What I want to do is to resemble this situation for resuing function
for example:
There is a function:

public void Foo(string message)
{
Console.Writeline(message)
}


then the reusing will be as followed:

public void DoFoo1()
{
string message1 = "Doing Foo1"
Foo(message)
}

public void DoFoo2()
{
string message2 = "Doing Foo2"
Foo(message)
}


In my project:
FilePath property resemble to message parameter
FilePath1 and FilePath2 resemble to message1 and message2
DoFoo resemble to ExcuteFile function
DoFoo1 and DoFoo2 resemble to ExcuteFile1 and ExcuteFile2 Cutom Actions

PassParametersToExecuteFile1 and PassParametersToExecuteFile2 CA only uses PassParametersToExecuteFile function in order to pass a property to a deferred custom action.

If I log the values of FilePath before ExcuteFile1 initiate it will get the value of FilePath2 ("c:\filepath2") since the following explenation:
Immidiate custom action happen before InstallInitialize even if i put them after InstallInitialize.
Therefore, Since ExecuteFile1 is a deferred Custom Action then PassParametersToExecuteFile2 will initiate before it. Therefore the value that it will get is "c:\filepath2" instead of "c:\filepath1". Conclusion: I cant reuse the same function wile passing parameters since it will always get the last immidiate custom action that modified the passing parameter.
So, how do I resolve this common problem.

About your solution with the table, I didnt understand what you've meant.
If this is the only way to do it I assume that if you could give me an example it will be clear to me.

So then again, Thank you very much'
Waiting for further answers'
Lior
0 Kudos
liorafar
Level 6

Sorry for my mistake in my code its:

Foo(Message1)

and

Foo(Message2)

Instead of "Foo(Message)"
0 Kudos
MichaelU
Level 12 Flexeran
Level 12 Flexeran

I think you may have a misconception of where CustomActionData comes from. When Windows Installer processes a deferred action during the immediate phase of the execute sequence, it stores a copy of the property with the same name as the custom action. Then when it runs this custom action in the deferred phase, this copy becomes available as the property CustomActionData. There's some futher information on that available at Obtaining Context Information for Deferred Execution Custom Actions.

So going back to your original code, it looked like your immediate action set a property called ExecuteFile. Then you had two other actions with similar but not identical names. Thus the immediate phase portion would not find any property that matched the deferred action's name, and would not store a copy of anything. I suggest the log file because you can verify the property at the first mention of the action (immediate phase), and the second mention of the deferred action (deferred phase) will also tell you exactly what is made available as its CustomActionData.
0 Kudos
liorafar
Level 6

OK OK Now I get it. My Problem was that I thought that I need to save to CustomActionData values in session["name of the function"] while what I should have done is saving in session["name of the deferred custom Action"]

This way I can reuse the same function while accessing from different deferred cutom actions. However, before every deferred one I should do an immidiate custom Action that passes the customActionData to the corresponding deffered custom Action. In my xase instead of writing :
session["ExecuteFile"] I should have written session["ExecuteFile1"]
and session ["ExecuteFile2"].
Here is the new Code:
[CODE]
[CustomAction]
public static ActionResult PassParametersToExecuteFile1(Session session, string fileName)
{
CustomActionData customActionData = new CustomActionData();
string filePath = session["FilePath"];
Log.LogMessageToFile("Immidiate :: filePath = " + filePath);
customActionData.Add("FilePath", filePath);


session["ExecuteFile1"] = customActionData.ToString();
Log.LogMessageToFile("Immidiate :: Finish");
return ActionResult.Success;
}

[CustomAction]
public static ActionResult PassParametersToExecuteFile2(Session session)
{
CustomActionData customActionData = new CustomActionData();
string filePath = session["FilePath"];
Log.LogMessageToFile("Immidiate :: filePath = " + filePath);
customActionData.Add("FilePath", filePath);


session["ExecuteFile2"] = customActionData.ToString();
Log.LogMessageToFile("Immidiate :: Finish");
return ActionResult.Success;
}

[CustomAction]
public static ActionResult ExecuteFile(Session session)
{
Log.LogMessageToFile("Deferred :: Start");
CustomActionData customActionData = session.CustomActionData;
string filePath = customActionData["FilePath"];
Log.LogMessageToFile("Deferred :: filePath = " + filePath);

if (File.Exists(filePath))
{
// Do My Code here....
}
Log.LogMessageToFile("Deferred :: Finish");
return ActionResult.Success;
}
[/CODE]

and here is the log:
14/04/2011 11:41:31: Immidiate :: filePath = c:\filepath1.
14/04/2011 11:41:31: Immidiate :: Finish.
14/04/2011 11:41:33: Immidiate :: filePath = c:\filepath2.
14/04/2011 11:41:33: Immidiate :: Finish.
14/04/2011 11:41:34: Deferred :: Start.
14/04/2011 11:41:34: Deferred :: filePath = c:\filepath1.
14/04/2011 11:41:34: Deferred :: Finish.
14/04/2011 11:41:34: Deferred :: Start.
14/04/2011 11:41:34: Deferred :: filePath = c:\filepath2.
14/04/2011 11:41:34: Deferred :: Finish.

MichaelU,
Thanks for your faithful support and for your time,
Lior.
0 Kudos