Automating publishing of FLA files; calling Process.Start multiple times

北城以北 提交于 2019-12-01 07:17:10

I solved this problem with the following project: http://www.codeproject.com/Articles/17606/NET-Interprocess-Communication

It offers a simple, zero-configuration implementation of IPC as a class library.

I added it to my automation program, and allowed the executable to run with arguments indicating it should signal the main instance and shut down. The main logic just checks: Environment.GetCommandLineArgs() for flags indicating it should send an IPC message and close instead of actually displaying the main form.

Here is the full implementation of the main program's signaling system:

static class Program
{
    private static readonly string MUTEX_AND_CHANNEL_NAME = "FlashPublishingAutomation";
    private static bool acquired_app_lock = false;
    private static Mutex app_lock;

    private static XDListener listener;
    public static ManualResetEvent publishCompleteSignal = new ManualResetEvent( true );

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        string[] args = Environment.GetCommandLineArgs();
        if ((args.Length > 1) && (args[1] == "-publishcomplete"))
        {
            XDBroadcast.SendToChannel( MUTEX_AND_CHANNEL_NAME, "publishcomplete" );
            Application.Exit();
            return;
        }
        else
        {
            bool createdNew = false;
            MutexSecurity security = new MutexSecurity();
            MutexAccessRule rule = new MutexAccessRule( "Users", MutexRights.Synchronize | MutexRights.Modify | MutexRights.ReadPermissions, AccessControlType.Allow );
            security.AddAccessRule( rule );
            app_lock = new Mutex( false, "Global\\" + MUTEX_AND_CHANNEL_NAME, out createdNew, security ); //Name must start with "Global\\" in order to be a system-wide mutex for all logged on usesr.
            acquired_app_lock = app_lock.WaitOne( TimeSpan.Zero, true );

            if (!acquired_app_lock)
            {
                MessageBox.Show( "An instance of FlashPublishingAutomation is already running.\r\nOnly one instance is allowed." );
            }
            else
            {
                listener = new XDListener();
                listener.RegisterChannel( MUTEX_AND_CHANNEL_NAME );
                listener.MessageReceived += listener_MessageReceived;
                Application.ApplicationExit += Application_ApplicationExit;
                Application.Run(new Form1());
            }

            if (acquired_app_lock)
                app_lock.ReleaseMutex();
            app_lock.Close();
        }
    }

    static void listener_MessageReceived(object sender, XDMessageEventArgs e)
    {
        switch (e.DataGram.Message)
        {
            case "publishcomplete":
                publishCompleteSignal.Set();
                break;
        }
    }

    static void Application_ApplicationExit(object sender, EventArgs e)
    {
        listener.MessageReceived -= listener_MessageReceived;
        listener.UnRegisterChannel( MUTEX_AND_CHANNEL_NAME );
    }
}

And the "publish" methods that are called when a button is clicked for a project (along with a "fillTemplate" method:

private static readonly string FLASH_PATH = @"C:\Program Files (x86)\Adobe\Adobe Flash CS6\Flash.exe";
public void publish( string fla_directory, string fla_filename, string jsfl_filename )
{
    Program.publishCompleteSignal.Reset();
    string template = fillTemplate( fla_directory, fla_filename );
    string curdir = Environment.CurrentDirectory;
    string tempJSFLfilepath = Path.Combine( curdir, jsfl_filename );
    File.WriteAllText( tempJSFLfilepath, template );
    Process p = Process.Start( FLASH_PATH, tempJSFLfilepath );
    Program.publishCompleteSignal.WaitOne( 30000 ); //wait for signal from JSFL runnCommandLine; timeout after 30 seconds; may want to increase this value if Flash needs time to startup or files take a long time to publish
}

private string fillTemplate( string fla_directory, string fla_filename )
{
    string fileuri = "file:///" + Path.Combine( fla_directory, fla_filename ).Replace( '\\','/' ); //convert path to file URI
    return EmbeddedResources.OpenAndPublishJSFLTemplate
        .Replace( "FLAFILEPATH", HttpUtility.JavaScriptStringEncode( fileuri ) )
        .Replace("FLAFILENAME", HttpUtility.JavaScriptStringEncode( fla_filename ) )
        .Replace("COMPLETECOMMAND", HttpUtility.JavaScriptStringEncode( "\"" + Application.ExecutablePath + "\"" + " -publishcomplete" ));
}

Also, here is the JSFL template that the automation program fills in before executing it in Flash. It is embedded as a string under EmbeddedResources.OpenAndPublishJSFLTemplate`. The C# app replaces the FLAFILENAME, FLAFILEPATH, and COMPLETECOMMAND strings with the target FLA filename, FLA uri (of the form file:///path_to_FLA), and finally the path to the C# app itself as implemented above (plus the "-publishcomplete" switch). The C# app obtains its own path via System.Windows.Forms.Application.ExecutablePath. Once this template is filled, it is written to disk as a JSFL file and passed as an argument to Flash Professional (flash.exe) via Process.Start. Once the JSFL file publishes the FLA, it executes a new instance of the automation program with the "-publishcomplete" flag, which signals the main instance of the automation program to trigger a manual reset event.

In summary, the automation program resets an event before calling Flash, then waits for the signal once Flash finishes publishing, before trying to publish the next file.

var myDocument = null;
var wasOpen = false;
var isOpen = false;
var openDocs = fl.documents;
var filename = "FLAFILENAME"; //template parameter: the filename (name only, without the path) of the FLA file to publish
var filepath = "FLAFILEPATH"; //template parameter: the URI (beginning with "file:///") of the FLA file to publish
for(var i=0;i < openDocs.length; i++)
{
    myDocument = openDocs[i];
    if (myDocument.name.toLowerCase() == filename.toLowerCase())
    {
        wasOpen = true;
        isOpen = true;
        break;
    }
}
if (!wasOpen)
{
    myDocument = null;
    fl.openDocument( filepath );
    openDocs = fl.documents;
    for(var i=0;i < openDocs.length; i++)
    {
        myDocument = openDocs[i];
        if (myDocument.name.toLowerCase() == filename.toLowerCase())
        {
            isOpen = true;
            break;
        }
    }
}
if (isOpen && (myDocument != null))
{
    //Publish the document
    myDocument.publish(); //this method is synchronous, so it won't return until the publish operation has fully completed

    //Signal the automation program that publishing has completed (COMPLETECOMMAND should be 
    FLfile.runCommandLine("COMPLETECOMMAND"); //tempate parameter: the automation program's executable path plus the "-publishcomplete" argument
}
else
    alert( "Publishing of " + filename + " failed.  File was not open and failed to open." );

I'm actually very impressed with what I've created here. It achieves end-to-end publication (versioning, compilation, backup, and deployment to web server) of two very large (tens of thousands of lines; hundreds of classes) FLA projects with a single click of a button, and it all completes in under 10 seconds.

This could probably run even faster if the JSFL template was simplified to just call the silent FLA publishing method which doesn't even open the file, and allows you to specify the publish profile to use: fl.publishDocument( flaURI [, publishProfile] ).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!