我通过以下项目解决了这个问题:http://www.codeproject.com/Articles/17606/NET-Interprocess-Communication
它提供了一个简单、零配置的 IPC 作为类库实现。
我将它添加到我的自动化程序中,并允许可执行文件运行,参数表明它应该向主实例发出信号并关闭。主要逻辑只是检查: Environment.GetCommandLineArgs() 是否有标志表明它应该发送 IPC 消息并关闭而不是实际显示主窗体。
下面是主程序信号系统的完整实现:
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 );
}
}
以及单击项目按钮时调用的“发布”方法(以及“填充模板”方法:
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" ));
}
此外,这是自动化程序在 Flash 中执行之前填写的 JSFL 模板。它作为字符串嵌入到 EmbeddedResources.OpenAndPublishJSFLTemplate` 下。 C# 应用程序将 FLAFILENAME、FLAFILEPATH 和 COMPLETECOMMAND 字符串替换为目标 FLA 文件名、FLA uri(格式为 file:///path_to_FLA),最后是上面实现的 C# 应用程序本身的路径(加上“-publishcomplete “ 转变)。 C# 应用程序通过 System.Windows.Forms.Application.ExecutablePath 获取自己的路径。填充此模板后,它将作为 JSFL 文件写入磁盘,并通过 Process.Start 作为参数传递给 Flash Professional (flash.exe)。一旦 JSFL 文件发布了 FLA,它就会执行带有“-publishcomplete”标志的自动化程序的新实例,该标志向自动化程序的主实例发出信号以触发手动重置事件。
总之,自动化程序会在调用 Flash 之前重置一个事件,然后在 Flash 完成发布后等待信号,然后再尝试发布下一个文件。
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." );
我实际上对我在这里创建的东西印象深刻。实现了两个超大(上万行,上百个类)FLA项目的端到端发布(版本控制、编译、备份、部署到web服务器),一键完成不到 10 秒。
如果将 JSFL 模板简化为仅调用甚至不打开文件的静默 FLA 发布方法,并允许您指定要使用的发布配置文件,这可能会运行得更快:fl.publishDocument( flaURI [, publishProfile] )。