switch_bool_t embedded;\r
\r
MonoMethod *loadMethod;\r
- MonoMethod *unloadMethod;\r
#endif\r
};\r
typedef struct mod_managed_globals mod_managed_globals;\r
public:\r
static Assembly^ mod_dotnet_managed;\r
static MethodInfo^ loadMethod;\r
- static MethodInfo^ unloadMethod;\r
};\r
\r
#endif\r
{\r
protected static bool Load() { return true; }\r
\r
- protected static void Unload() { }\r
-\r
public abstract void ExecuteBackground(string args);\r
\r
public abstract void Execute(FreeSWITCH.Native.Stream stream, FreeSWITCH.Native.Event evt, string args);\r
{\r
protected static bool Load() { return true; }\r
\r
- protected static void Unload() { }\r
-\r
protected Native.ManagedSession Session { get; private set; }\r
\r
protected string Arguments { get; private set; }\r
// General Information about an assembly is controlled through the following \r
// set of attributes. Change these attribute values to modify the information\r
// associated with an assembly.\r
-[assembly: AssemblyTitle("mod_mono_managed")]\r
+[assembly: AssemblyTitle("mod_managed_lib")]\r
[assembly: AssemblyDescription("")]\r
[assembly: AssemblyConfiguration("")]\r
[assembly: AssemblyCompany("")]\r
-[assembly: AssemblyProduct("mod_mono_managed")]\r
+[assembly: AssemblyProduct("mod_managed_lib")]\r
[assembly: AssemblyCopyright("Copyright © 2008")]\r
[assembly: AssemblyTrademark("")]\r
[assembly: AssemblyCulture("")]\r
// You can specify all the values or you can default the Build and Revision Numbers \r
// by using the '*' as shown below:\r
// [assembly: AssemblyVersion("1.0.*")]\r
-[assembly: AssemblyVersion("1.0.0.0")]\r
-[assembly: AssemblyFileVersion("1.0.0.0")]\r
+[assembly: AssemblyVersion("1.0.2.0")]\r
+[assembly: AssemblyFileVersion("1.0.2.0")]\r
return true;\r
}\r
\r
- new protected static void Unload()\r
- {\r
- Log.WriteLine(LogLevel.Info, "Inside AppDemo::Unload.");\r
- }\r
-\r
protected override void Run()\r
{\r
Session.Answer();\r
return true;\r
}\r
\r
- new protected static void Unload()\r
- {\r
- Log.WriteLine(LogLevel.Debug, "Inside ApiDemo::Unload.");\r
- }\r
-\r
public override void ExecuteBackground(string args)\r
{\r
Log.WriteLine(LogLevel.Debug, "ApiDemo on a background thread #({0}), with args '{1}'.",\r
internal static class Loader\r
{\r
// Stores a list of the loaded function types so we can instantiate them as needed\r
- static readonly Dictionary<string, Type> functions = new Dictionary<string, Type>(StringComparer.InvariantCultureIgnoreCase);\r
+ static Dictionary<string, Type> functions = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);\r
// Only class name. Last in wins.\r
- static readonly Dictionary<string, Type> shortFunctions = new Dictionary<string, Type>(StringComparer.InvariantCultureIgnoreCase);\r
+ static Dictionary<string, Type> shortFunctions = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);\r
\r
#region Load/Unload\r
\r
+ static string managedDir;\r
+\r
public static bool Load()\r
{\r
- string managedDir = Path.Combine(Native.freeswitch.SWITCH_GLOBAL_dirs.mod_dir, "managed");\r
+ managedDir = Path.Combine(Native.freeswitch.SWITCH_GLOBAL_dirs.mod_dir, "managed");\r
Log.WriteLine(LogLevel.Debug, "mod_managed_lib loader is starting with directory '{0}'.", managedDir);\r
if (!Directory.Exists(managedDir)) {\r
Log.WriteLine(LogLevel.Error, "Managed directory not found: {0}", managedDir);\r
return File.Exists(path) ? Assembly.LoadFile(path) : null;\r
};\r
\r
- InitManagedDelegates(_run, _execute, _executeBackground);\r
+ InitManagedDelegates(_run, _execute, _executeBackground, _loadAssembly);\r
\r
// This is a simple one-time loader to get things in memory\r
// Some day we should allow reloading of modules or something\r
- loadAssemblies(managedDir)\r
- .SelectMany(a => a.GetExportedTypes())\r
- .Where(t => !t.IsAbstract)\r
- .Where(t => t.IsSubclassOf(typeof(AppFunction)) || t.IsSubclassOf(typeof(ApiFunction)))\r
- .ToList()\r
- .loadFunctions();\r
+ var allTypes = loadAssemblies(managedDir).SelectMany(a => a.GetExportedTypes());\r
+ loadFunctions(allTypes);\r
\r
return true;\r
}\r
\r
+ public static bool LoadAssembly(string filename) {\r
+ try {\r
+ string path = Path.Combine(managedDir, filename);\r
+ if (!File.Exists(path)) {\r
+ Log.WriteLine(LogLevel.Error, "File not found: '{0}'.", path);\r
+ return false;\r
+ }\r
+ var asm = Assembly.LoadFile(path);\r
+ loadFunctions(asm.GetExportedTypes());\r
+ return true;\r
+ } catch (Exception ex) {\r
+ Log.WriteLine(LogLevel.Error, "Exception in LoadAssembly('{0}'): {1}", filename, ex.ToString());\r
+ return false;\r
+ }\r
+ }\r
+\r
delegate bool ExecuteDelegate(string cmd, IntPtr streamH, IntPtr eventH);\r
delegate bool ExecuteBackgroundDelegate(string cmd);\r
delegate bool RunDelegate(string cmd, IntPtr session);\r
+ delegate bool LoadAssemblyDelegate(string filename);\r
static readonly ExecuteDelegate _execute = Execute;\r
static readonly ExecuteBackgroundDelegate _executeBackground = ExecuteBackground;\r
static readonly RunDelegate _run = Run;\r
- //SWITCH_MOD_DECLARE(void) InitManagedDelegates(runFunction run, executeFunction execute, executeBackgroundFunction executeBackground) \r
- [DllImport("mod_managed")]\r
- static extern void InitManagedDelegates(RunDelegate run, ExecuteDelegate execute, ExecuteBackgroundDelegate executeBackground);\r
+ static readonly LoadAssemblyDelegate _loadAssembly = LoadAssembly;\r
+ //SWITCH_MOD_DECLARE(void) InitManagedDelegates(runFunction run, executeFunction execute, executeBackgroundFunction executeBackground, loadAssemblyFunction loadAssembly) \r
+ [DllImport("mod_managed", CharSet = CharSet.Ansi)]\r
+ static extern void InitManagedDelegates(RunDelegate run, ExecuteDelegate execute, ExecuteBackgroundDelegate executeBackground, LoadAssemblyDelegate loadAssembly);\r
\r
// Be rather lenient in finding the Load and Unload methods\r
static readonly BindingFlags methodBindingFlags =\r
BindingFlags.IgnoreCase | // Some case insensitive languages?\r
BindingFlags.FlattenHierarchy; // Allow inherited methods for hierarchies\r
\r
- static void loadFunctions(this List<Type> allTypes)\r
+ static void loadFunctions(IEnumerable<Type> allTypes)\r
{\r
- foreach (var t in allTypes) {\r
+ var functions = new Dictionary<string, Type>(Loader.functions, StringComparer.OrdinalIgnoreCase);\r
+ var shortFunctions = new Dictionary<string, Type>(Loader.shortFunctions, StringComparer.OrdinalIgnoreCase);\r
+ var filteredTypes = allTypes\r
+ .Where(t => !t.IsAbstract)\r
+ .Where(t => t.IsSubclassOf(typeof(AppFunction)) || t.IsSubclassOf(typeof(ApiFunction)));\r
+ foreach (var t in filteredTypes) {\r
try {\r
if (functions.ContainsKey(t.FullName)) {\r
- Log.WriteLine(LogLevel.Error, "Duplicate function {0}. Skipping.", t.FullName);\r
- continue;\r
+ functions.Remove(t.FullName);\r
+ Log.WriteLine(LogLevel.Warning, "Replacing function {0}.", t.FullName);\r
}\r
var loadMethod = t.GetMethod("Load", methodBindingFlags, null, Type.EmptyTypes, null);\r
var shouldLoad = Convert.ToBoolean(loadMethod.Invoke(null, null)); // We don't require the Load method to return a bool exactly\r
logException("Load", t.FullName, ex);\r
}\r
} \r
+ Loader.functions = functions;\r
+ Loader.shortFunctions = shortFunctions;\r
}\r
\r
static Assembly[] loadAssemblies(string managedDir)\r
/// <summary>Runs an application function.</summary>\r
public static bool Run(string command, IntPtr sessionHandle)\r
{\r
- Log.WriteLine(LogLevel.Debug, "mod_mono attempting to run application '{0}'.", command);\r
+ Log.WriteLine(LogLevel.Debug, "mod_managed_lib: attempting to run application '{0}'.", command);\r
System.Diagnostics.Debug.Assert(sessionHandle != IntPtr.Zero, "sessionHandle is null.");\r
var parsed = parseCommand(command);\r
if (parsed == null) return false;\r
SWITCH_BEGIN_EXTERN_C \r
\r
SWITCH_MODULE_LOAD_FUNCTION(mod_managed_load);\r
-SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_managed_shutdown);\r
-SWITCH_MODULE_DEFINITION(mod_managed, mod_managed_load, mod_managed_shutdown, NULL);\r
+SWITCH_MODULE_DEFINITION(mod_managed, mod_managed_load, NULL, NULL);\r
\r
SWITCH_STANDARD_API(managedrun_api_function); /* ExecuteBackground */\r
SWITCH_STANDARD_API(managed_api_function); /* Execute */\r
SWITCH_STANDARD_APP(managed_app_function); /* Run */\r
+SWITCH_STANDARD_API(managed_loadassembly); /* Load assembly */\r
\r
#define MOD_MANAGED_ASM_NAME "mod_managed_lib"\r
#define MOD_MANAGED_ASM_V1 1\r
#define MOD_MANAGED_ASM_V2 0\r
-#define MOD_MANAGED_ASM_V3 0\r
+#define MOD_MANAGED_ASM_V3 2\r
#define MOD_MANAGED_ASM_V4 0\r
#define MOD_MANAGED_DLL MOD_MANAGED_ASM_NAME ".dll"\r
\r
typedef int (*runFunction)(const char *data, void *sessionPtr);\r
typedef int (*executeFunction)(const char *cmd, void *stream, void *Event);\r
typedef int (*executeBackgroundFunction)(const char* cmd);\r
+typedef int (*loadAssemblyFunction)(const char* filename);\r
static runFunction runDelegate;\r
static executeFunction executeDelegate;\r
static executeBackgroundFunction executeBackgroundDelegate;\r
+static loadAssemblyFunction loadAssemblyDelegate;\r
\r
-SWITCH_MOD_DECLARE(void) InitManagedDelegates(runFunction run, executeFunction execute, executeBackgroundFunction executeBackground) \r
+SWITCH_MOD_DECLARE(void) InitManagedDelegates(runFunction run, executeFunction execute, executeBackgroundFunction executeBackground, loadAssemblyFunction loadAssembly) \r
{\r
runDelegate = run;\r
executeDelegate = execute;\r
executeBackgroundDelegate = executeBackground;\r
+ loadAssemblyDelegate = loadAssembly;\r
}\r
\r
// Sets up delegates (and anything else needed) on the ManagedSession object\r
return SWITCH_STATUS_FALSE;\r
}\r
\r
- if (!(globals.unloadMethod = getMethod("FreeSWITCH.Loader:Unload()", loaderClass))) {\r
- return SWITCH_STATUS_FALSE;\r
- }\r
-\r
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Found all loader functions.\n");\r
return SWITCH_STATUS_SUCCESS;\r
}\r
{\r
try {\r
FreeSwitchManaged::loadMethod = FreeSwitchManaged::mod_dotnet_managed->GetType("FreeSWITCH.Loader")->GetMethod("Load");\r
- FreeSwitchManaged::unloadMethod = FreeSwitchManaged::mod_dotnet_managed->GetType("FreeSWITCH.Loader")->GetMethod("Unload");\r
} catch(Exception^ ex) {\r
IntPtr msg = Marshal::StringToHGlobalAnsi(ex->ToString());\r
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not load FreeSWITCH.Loader class: %s\n", static_cast<const char*>(msg.ToPointer()));\r
SWITCH_ADD_API(api_interface, "managedrun", "Run a module (ExecuteBackground)", managedrun_api_function, "<module> [<args>]");\r
SWITCH_ADD_API(api_interface, "managed", "Run a module as an API function (Execute)", managed_api_function, "<module> [<args>]");\r
SWITCH_ADD_APP(app_interface, "managed", "Run CLI App", "Run an App on a channel", managed_app_function, "<modulename> [<args>]", SAF_NONE);\r
+ SWITCH_ADD_API(api_interface, "managedload", "Load assembly", managed_loadassembly, "<filename>");\r
return SWITCH_STATUS_SUCCESS;\r
}\r
\r
}\r
success = runDelegate(data, session);\r
if (!success) {\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Application run failed for %s (unknown module?).\n", data);\r
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Application run failed for %s (unknown module or exception).\n", data);\r
}\r
}\r
\r
-\r
-\r
-SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_managed_shutdown) \r
+SWITCH_STANDARD_API(managed_loadassembly)\r
{\r
-#ifdef _MANAGED\r
- Object ^objResult;\r
- try {\r
- objResult = FreeSwitchManaged::unloadMethod->Invoke(nullptr, nullptr);\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "UnloadMethod completed successfully.\n");\r
- }\r
- catch(Exception^ ex) {\r
- IntPtr msg = Marshal::StringToHGlobalAnsi(ex->ToString());\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Exception occurred in Loader::Unload: %s\n", static_cast<const char*>(msg.ToPointer()));\r
- Marshal::FreeHGlobal(msg);\r
- return SWITCH_STATUS_FALSE;;\r
- }\r
-#else\r
- MonoObject * ex;\r
-\r
- mono_thread_attach(globals.domain);\r
- mono_runtime_invoke(globals.unloadMethod, NULL, NULL, &ex);\r
-\r
- if (ex) {\r
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Exception occurred in Loader::Unload.\n");\r
- mono_print_unhandled_exception(ex);\r
+ int success;\r
+ if (switch_strlen_zero(cmd)) {\r
+ stream->write_function(stream, "-ERR no args specified!\n"); \r
+ return SWITCH_STATUS_SUCCESS;\r
}\r
-\r
- if (!globals.embedded) {\r
- mono_jit_cleanup(globals.domain);\r
+ success = loadAssemblyDelegate(cmd);\r
+ if (success) {\r
+ stream->write_function(stream, "+OK\n");\r
+ } else { \r
+ stream->write_function(stream, "-ERR LoadAssembly returned false (invalid file or exception).\n");\r
}\r
-#endif\r
return SWITCH_STATUS_SUCCESS;\r
}\r
\r