private static bool ValidateCalculationRequestMessage(CalculationRequestMessage crm) { if (String.IsNullOrEmpty(crm.RequestId)) { throw new Exception("RequestId cannot be null or empty"); } if (String.IsNullOrEmpty(crm.FunctionName)) { throw new Exception("Function Name cannot be null or empty"); } if (String.IsNullOrEmpty(crm.DllName)) { throw new Exception("Dll Name cannot be null or empty"); } if (String.IsNullOrEmpty(crm.ContainerName)) { throw new Exception("Container Name cannot be null or empty"); } return(true); }
private static async Task ProcessCalculationRequest(CalculationRequestMessage crm) { var calculationResponseMessage = new CalculationResponseMessage(crm); Console.WriteLine($"ProcessingCalculationRequest at {DateTime.Now.ToShortTimeString()}"); Console.WriteLine($"Request: {crm}"); Console.WriteLine($"Current Directory: {Directory.GetCurrentDirectory()}"); if (ValidateCalculationRequestMessage(crm)) { calculationResponseMessage.CalculationRequestStatus = CalculationRequestStatus.InProgress; } ConsoleWriteInColour($"Downloading DLL...{crm.DllName} from container {crm.ContainerName}", ConsoleColor.Green); string dllFileName; var storageAccount = CloudStorageAccount.Parse(storageConnectionString); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer container = blobClient.GetContainerReference(crm.ContainerName); try { Console.WriteLine($"Current directory={Directory.GetCurrentDirectory()}"); var directoryToCreate = $"{Directory.GetCurrentDirectory()}\\dll"; Directory.CreateDirectory(directoryToCreate); Console.WriteLine($"directory {directoryToCreate} was created"); dllFileName = $"{directoryToCreate}\\{crm.DllName}"; var dllBlob = container.GetBlockBlobReference(crm.DllName); await dllBlob.FetchAttributesAsync(); Console.WriteLine($"Blob {crm.DllName} in container {crm.ContainerName} is {dllBlob.Properties.Length} bytes"); var memoryStream = new MemoryStream(); await dllBlob.DownloadToStreamAsync(memoryStream); using (memoryStream) { var fileStream = File.Create(dllFileName); memoryStream.Position = 0; memoryStream.CopyTo(fileStream); fileStream.Close(); } Console.WriteLine($"{crm.DllName} was downloaded to local file system {directoryToCreate}"); Console.WriteLine($"Directrory Listing:"); var files = Directory.GetFiles(directoryToCreate); foreach (var fileName in files) { var fileInfo = new FileInfo($"{fileName}"); Console.WriteLine($"Name={fileInfo.Name}, length={fileInfo.Length}"); } } catch (Exception e) { throw new Exception($"Error downloading DLL {crm.DllName} from container {crm.ContainerName}. Message:{e.Message}", e); } IntPtr hModule = IntPtr.Zero; try { ConsoleWriteInColour($"DLL Load path is {dllFileName}", ConsoleColor.Green); // call a function in the DLL hModule = LoadLibrary(dllFileName); if (hModule == IntPtr.Zero) { int errorCode = Marshal.GetLastWin32Error(); throw new Exception($"Failed to load library {dllFileName} (ErrorCode: {errorCode})"); } ConsoleWriteInColour($"{DateTime.Now.ToString()} library {dllFileName} was loaded sucessfully. hModule={hModule}", ConsoleColor.Yellow); IntPtr funcaddr = GetProcAddress(hModule, crm.FunctionName); if (funcaddr == IntPtr.Zero) { int errorCode = Marshal.GetLastWin32Error(); throw new Exception($"Failed to find function {crm.FunctionName} (ErrorCode: {errorCode})"); } ConsoleWriteInColour($"{DateTime.Now.ToString()} function {crm.FunctionName} found in library {dllFileName} address={funcaddr}", ConsoleColor.Yellow); if (crm.FunctionType == FunctionType.StringFunction) { NicksStringFunction stringFunction = Marshal.GetDelegateForFunctionPointer <NicksStringFunction>(funcaddr) as NicksStringFunction; IntPtr stringResultPtr = stringFunction(); string stringResult = Marshal.PtrToStringBSTR(stringResultPtr); ConsoleWriteInColour($"{DateTime.Now.ToString()} function {crm.FunctionName} returned \"{stringResult}\"", ConsoleColor.Cyan); calculationResponseMessage.CalculationRequestStatus = CalculationRequestStatus.Succeeded; calculationResponseMessage.Result = stringResult; } if (crm.FunctionType == FunctionType.IntFunction) { if (Int32.TryParse(crm.Parameters[0], out Int32 number)) { IntPtr numPointer = new IntPtr(number); NicksIntFunction intFunction = Marshal.GetDelegateForFunctionPointer <NicksIntFunction>(funcaddr) as NicksIntFunction; IntPtr intResultPtr = intFunction(numPointer); Int32 intResult = intResultPtr.ToInt32(); ConsoleWriteInColour($"{DateTime.Now.ToString()} function {crm.FunctionName} returned \"{intResult}\"", ConsoleColor.Cyan); calculationResponseMessage.CalculationRequestStatus = CalculationRequestStatus.Succeeded; calculationResponseMessage.Result = intResult.ToString(); } else { ConsoleWriteInColour($"{DateTime.Now.ToString()} function {crm.FunctionName} no parameters supplied for function", ConsoleColor.Red); } } Console.WriteLine($"{DateTime.Now.ToString()} request {crm.RequestId} was processed successfully."); } catch (Exception e) { throw new Exception($"Error loading and executing function {crm.FunctionName} from DLL {crm.DllName}. Message:{e.Message}", e); } finally { if (hModule != IntPtr.Zero) { FreeLibrary(hModule); ConsoleWriteInColour($"{DateTime.Now.ToString()} library {dllFileName} was unloaded", ConsoleColor.Yellow); } ; } // Now serialize the result object to blob storage try { var resultsBlob = container.GetBlockBlobReference($"{crm.RequestId}.json"); var results = JsonConvert.SerializeObject(calculationResponseMessage); await resultsBlob.UploadTextAsync(results); } catch (Exception e) { throw new Exception($"Error uploading results to blob storage. Function {crm.FunctionName} in DLL {crm.DllName} was called sucessfully. Message:{e.Message}", e); } }
public static async Task <int> Main(string[] args) { var configBuilder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true) .AddJsonFile("settings.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables(); IConfiguration configuration = configBuilder.Build(); var rootCommand = new RootCommand { new Option <string>( "--messageType", getDefaultValue: () => "CalculateMessage", description: "The type of message to send. Valid values are QuitMessage and CalculateMessage. If not supplied this will defualt to CalculateMessage"), new Option <string>( "--dllName", description: "The name of the DLL from which you wish to execute a function (DLL will be downloaded from blob storage)"), new Option <string>( "--functionName", description: "the name of the function your wish to execute"), new Option <string>( "--functionType", description: "The type of function you wish to execute. Valid values are IntFunction or StringFunction. If you specify IntFunction you will need to supply a parameter which will be parsed as an integer"), new Option <string>( "--parameters", getDefaultValue: () => null, description: "A comma separated string of Int parameters. Must be supplied for IntFunction calls, ignored for StringFunctions. "), }; rootCommand.Description = "This command will generate a single calculation request based on the supplied input parameters, and will generate a calculation request message. " + "This will be placed on the configured service bus queue, which will then be picked up by the DLLStuff service and the requested calculation performed. " + "Finally, the results will be written to the blob storage container specified in configuration. You can see the results in a blob called <requestid>.json. " + "Request Id is written to the console on completion of the command."; try { rootCommand.Handler = CommandHandler.Create <string, string, string, string, string>(async(messageType, dllName, functionName, functionType, parameters) => { try { if (string.IsNullOrEmpty(messageType)) { throw new Exception($"--messageType {messageType} must be provided"); } if ((messageType.ToLower() != "quitmessage") && (messageType.ToLower() != "calculatemessage")) { throw new Exception($"--messageType {messageType} is invalid. Valid values are QuitMessage and CalculateMessage"); } Console.WriteLine($"The value for --messageType is: {messageType}"); if (string.IsNullOrEmpty(dllName)) { throw new Exception($"--dllName {dllName} must be provided"); } Console.WriteLine($"The value for --dllName is: {dllName}"); if (string.IsNullOrEmpty(functionName)) { throw new Exception($"--functionName {functionName} must be provided"); } Console.WriteLine($"The value for --functionName is: {functionName}"); if (string.IsNullOrEmpty(functionType)) { throw new Exception($"--functionType {functionType} must be provided"); } if ((functionType.ToLower() != "intfunction") && (functionType.ToLower() != "stringfunction")) { throw new Exception($"--messageType {messageType} is invalid. Valid values are IntFunction and StringFunction"); } Console.WriteLine($"The value for --functionType is: {functionType}"); if (functionType.ToLower() == "intfunction") { if (string.IsNullOrEmpty(parameters)) { throw new Exception($"--parameters {parameters} must be provided when --functionType is {functionType}"); } } Console.WriteLine($"The value for --parameters is: {parameters}"); string serviceBusConnectionString = ""; string serviceBusQueueName = ""; string storageConnectionString = ""; string containerName = ""; try { storageConnectionString = configuration["StorageConnectionString"]; serviceBusConnectionString = configuration["ServiceBusConnectionString"]; serviceBusQueueName = configuration["ServiceBusQueueName"]; containerName = configuration["ContainerName"]; } catch (Exception e) { Console.WriteLine($"Exception caught while accessing configuration: {e.Message}"); return; } queueClient = new QueueClient(serviceBusConnectionString, serviceBusQueueName); var crm = new CalculationRequestMessage(); crm.RequestId = Guid.NewGuid().ToString(); crm.ContainerName = containerName; if (messageType.ToLower() == "quitmessage") { crm.MessageType = MessageType.QuitMessage; } else { crm.MessageType = MessageType.CalculateMessage; crm.DllName = dllName; crm.FunctionName = functionName; if (functionType.ToLower() == "stringfunction") { crm.FunctionType = FunctionType.StringFunction; } else { crm.FunctionType = FunctionType.IntFunction; crm.Parameters = parameters.Split(','); } } var body = JsonConvert.SerializeObject(crm).ToString(); var message = new Message(Encoding.UTF8.GetBytes(body)); Console.WriteLine($"Sending message: {body}"); await queueClient.SendAsync(message); await queueClient.CloseAsync(); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"Done. Your request id was {crm.RequestId}"); } catch (Exception e) { Console.WriteLine(e.Message); } } ); } catch (Exception e) { Console.WriteLine(e.Message); return(-1); } return(rootCommand.InvokeAsync(args).Result); }