FTPS client for xamarin based apps

Hi Community,

Today’s post is a quick recipe to enabling FTPS capabilities to our Xamarin applications. Please refer to code below to see it fully.

Angel

ObjectInfo Class

using System;


namespace WebUtils {

    /// <summary>

    /// 

    /// </summary>

    public class ObjectInfo {

            /// <summary>

            /// Gets or sets the name.

            /// </summary>

            /// <value>

            /// The name.

            /// </value>

            public string Name {

                get; set;

            }

            /// <summary>

            /// Gets or sets the size.

            /// </summary>

            /// <value>

            /// The size.

            /// </value>

            public long Size {

                get; set;

            }


            /// <summary>

            /// Gets or sets the type.

            /// </summary>

            /// <value>

            /// The type.

            /// </value>

            public ObjectType Type {

                get; set;

            }


            /// <summary>

            /// Gets or sets the created.

            /// </summary>

            /// <value>

            /// The created.

            /// </value>

            public DateTime Created {

                get; set;

            }

        }

}

ExecutionResult Class

using System;


namespace WebUtils  {

    /// <summary>

    ///  Class to be used as HRESULT

    /// </summary>

    public class ExecutionResult {

        /// <summary>

        /// Gets or sets a value indicating whether this <see cref="ExecutionResult"/> is succeeded.

        /// </summary>

        /// <value>

        ///   <c>true</c> if succeeded; otherwise, <c>false</c>.

        /// </value>

        public bool Succeeded {

            get; set;

        }


        /// <summary>

        /// Gets or sets the caught exception.

        /// </summary>

        /// <value>

        /// The caught exception.

        /// </value>

        public Exception CaughtException {

            get; set;

        }


        /// <summary>

        /// Gets or sets the tag.

        /// </summary>

        /// <value>

        /// The tag.

        /// </value>

        public object Tag {

            get; set;

        }


        /// <summary>

        /// The empty

        /// </summary>

        public static ExecutionResult Empty = new ExecutionResult() { Succeeded = false };


    }

}

IFtpsClient Interface

using System;

using System.Collections.Generic;

using System.Net;


namespace WebUtils {

    /// <summary>

    /// 

    /// </summary>

    public enum ObjectType {

        File,

        Folder

    }


    /// <summary>

    /// 

    /// </summary>

    /// <typeparam name="T"></typeparam>

    /// <typeparam name="TV">The type of the v.</typeparam>

    /// <typeparam name="TW">The type of the w.</typeparam>

    /// <param name="t">The t.</param>

    /// <param name="tv">The tv.</param>

    /// <param name="tw">The tw.</param>

    public delegate void GenericHandler<in T, in TV, in TW>(T t, TV tv, TW tw);


    /// <summary>

    /// 

    /// </summary>

    public struct FtpResult {

        /// <summary>

        /// Gets or sets the code.

        /// </summary>

        /// <value>

        /// The code.

        /// </value>

        public FtpStatusCode Code {

            get; set;

        }

        /// <summary>

        /// Gets or sets the message.

        /// </summary>

        /// <value>

        /// The message.

        /// </value>

        public string Message {

            get; set;

        }

    }


    /// <summary>

    /// 

    /// </summary>

    public interface IFtpsClient {

        /// <summary>

        /// Occurs when [on exception caught].

        /// </summary>

        event GenericHandler<object, Exception, object> OnExceptionCaught;


        /// <summary>

        /// Occurs when [on FTP request completed].

        /// </summary>

        event GenericHandler<object, FtpResult, object> OnFtpRequestCompleted;


        /// <summary>

        /// Occurs when [on upload progress].

        /// </summary>

        event GenericHandler<object, long, TimeSpan> OnUploadProgress;


        /// <summary>

        /// Downloads the specified downloaded file path.

        /// </summary>

        /// <param name="downloadedFilePath">The downloaded file path.</param>

        /// <param name="fileName">Name of the file.</param>

        /// <returns></returns>

        ExecutionResult Download(string downloadedFilePath, string fileName);


        /// <summary>

        /// Deletes the specified file name.

        /// </summary>

        /// <param name="fileName">Name of the file.</param>

        /// <returns></returns>

        ExecutionResult Delete(string fileName);


        /// <summary>

        /// Uploads the specified file name.

        /// </summary>

        /// <param name="fileName">Name of the file.</param>

        /// <param name="remoteFileName">Name of the remote file.</param>

        /// <param name="remoteFolder">The remote folder.</param>

        /// <returns></returns>

        ExecutionResult Upload(string fileName, string remoteFileName, string remoteFolder = null);


        /// <summary>

        /// Renames the file.

        /// </summary>

        /// <param name="fileName">Name of the file.</param>

        /// <param name="remoteFileName">Name of the remote file.</param>

        /// <param name="remoteFolder">The remote folder.</param>

        /// <returns></returns>

        ExecutionResult RenameFile(string fileName, string remoteFileName, string remoteFolder = null);


        /// <summary>

        /// Makes the directory.

        /// </summary>

        /// <param name="remoteDirName">Name of the remote dir.</param>

        /// <returns></returns>

        ExecutionResult MakeDirectory(string remoteDirName);


        /// <summary>

        /// Gets the directory listing.

        /// </summary>

        /// <param name="remoteFolder">The remote folder.</param>

        /// <returns></returns>

        List<ObjectInfo> GetDirectoryListing(string remoteFolder = null);

    }

}

FtpsClient Class

using System;

using System.Collections.Generic;

using System.Globalization;

using System.IO;

using System.Net;

using System.Text;




namespace WebUtils {

    /// <summary>

    /// 

    /// </summary>

    public class FtpsClient : IFtpsClient {

        #region "Constants"


        /// <summary>

        /// The buffer size

        /// </summary>

        private const int BufferSize = 2048; // 2KB buffer size for chunks


        #endregion


        #region "Events"

        /// <summary>

        /// Occurs when [on exception caught].

        /// </summary>

        public event GenericHandler<object, Exception, object> OnExceptionCaught;

        /// <summary>

        /// Occurs when [on FTP request completed].

        /// </summary>

        public event GenericHandler<object, FtpResult, object> OnFtpRequestCompleted;

        /// <summary>

        /// Occurs when [on upload progress].

        /// </summary>

        public event GenericHandler<object, long, TimeSpan> OnUploadProgress;


        #endregion


        #region "Properties"


        /// <summary>

        /// Gets or sets the credentials.

        /// </summary>

        /// <value>

        /// The credentials.

        /// </value>

        protected NetworkCredential Credentials {

            get; set;

        }


        /// <summary>

        /// Gets or sets the name of the host.

        /// </summary>

        /// <value>

        /// The name of the host.

        /// </value>

        protected string HostName {

            get; set;

        }


        #endregion


        #region "Ctor"


        /// <summary>

        /// Initializes a new instance of the <see cref="FtpsClient" /> class.

        /// </summary>

        /// <param name="hostName">Name of the host.</param>

        /// <param name="credential">The credential.</param>

        public FtpsClient(string hostName, NetworkCredential credential) {

            if (string.IsNullOrEmpty(hostName) || credential == null)

                throw new ArgumentException("Unable to construct FtpsClient due to arguments mismatch.");


            HostName = hostName;

            Credentials = credential;

        }


        #endregion


        #region "Helper methods"


        /// <summary>

        /// Gets the request.

        /// </summary>

        /// <param name="requestUri">The request URI.</param>

        /// <returns></returns>

        private FtpWebRequest GetRequest(string requestUri) {

            FtpWebRequest retval = null;


            try {

                ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;

                retval = (FtpWebRequest)WebRequest.Create(new Uri($"ftp://{HostName}/{ requestUri ?? string.Empty}"));

                retval.EnableSsl = retval.UseBinary = true;

                retval.Credentials = Credentials;

            } catch (Exception ex) {

                OnExceptionCaught?.Invoke(this, ex, requestUri);

            }


            return retval;

        }



        /// <summary>

        /// Parses the FTP list item.

        /// </summary>

        /// <param name="listItem">The list item.</param>

        /// <returns></returns>

        private ObjectInfo ParseFtpListItem(string listItem) {

            string[] parts;

            ObjectInfo retval = null;


            if (!string.IsNullOrEmpty(listItem) && (parts = listItem.Split(' ')).Length > 0) {

                long tryParse = 0;

                DateTime formattedDate;

                retval = new ObjectInfo { Name = parts[parts.Length - 1] };


                // Let's extract date and time

                if (parts.Length >= 2 &&

                    DateTime.TryParseExact(parts[0], "MM-dd-yyyy", CultureInfo.InvariantCulture,

                    DateTimeStyles.None, out formattedDate)) {

                    var t = formattedDate.ToShortDateString() + " " + parts[2];

                    formattedDate = DateTime.Parse(t);

                    retval.Created = formattedDate;

                }


                // Let's extract the file size (if applicable)

                if (long.TryParse(parts[parts.Length - 2], out tryParse)) {

                    retval.Size = tryParse;

                    retval.Type = ObjectType.File;

                } else

                    retval.Type = ObjectType.Folder;

            }


            return retval;

        }


        #endregion


        #region "Main Functionality"


        /// <summary>

        /// Downloads the specified downloaded file path.

        /// </summary>

        /// <param name="downloadedFilePath">The downloaded file path.</param>

        /// <param name="fileName">Name of the file.</param>

        /// <returns></returns>

        public ExecutionResult Download(string downloadedFilePath, string fileName) {

            var retval = ExecutionResult.Empty;


            if (!string.IsNullOrEmpty(downloadedFilePath) && !string.IsNullOrEmpty(fileName)) {

                try {

                    var readCount = 0;

                    var serializedCount = 0;

                    var startTime = DateTime.Now;

                    var request = GetRequest(fileName);

                    request.Method = WebRequestMethods.Ftp.DownloadFile;

                    var targetFile = Path.Combine(downloadedFilePath, fileName);


                    if (File.Exists(targetFile))

                        File.Delete(targetFile);


                    using (var fs = new FileStream(targetFile, FileMode.Create)) {

                        using (var response = (FtpWebResponse)request.GetResponse()) {

                            using (var stream = response.GetResponseStream()) {

                                var buffer = new byte[BufferSize];

                                var responseLength = response.ContentLength;

                                serializedCount = readCount = stream.Read(buffer, 0, BufferSize);


                                while (readCount > 0) {

                                    stream.Write(buffer, 0, BufferSize);

                                    OnUploadProgress?.Invoke($"Downloading {fileName}...", serializedCount, DateTime.Now - startTime);

                                    readCount = stream.Read(buffer, 0, BufferSize);

                                    serializedCount += readCount;

                                }


                                var ftpResult = new FtpResult { Code = FtpStatusCode.CommandOK, Message = "Download completed." };

                                OnFtpRequestCompleted?.Invoke(this, ftpResult, $"Download took {DateTime.Now - startTime}");

                                retval = new ExecutionResult() { Succeeded = true, Tag = ftpResult };

                                stream.Close();

                                response.Close();

                            }

                        }

                    }

                } catch (Exception ex) {

                    var msg = $"Unable to download file - {fileName}";

                    retval = new ExecutionResult() { CaughtException = ex, Succeeded = false, Tag = msg };

                    OnExceptionCaught?.Invoke(this, ex, msg);

                }

            }


            return retval;

        }


        /// <summary>

        /// Deletes the specified file name.

        /// </summary>

        /// <param name="fileName">Name of the file.</param>

        /// <returns></returns>

        public ExecutionResult Delete(string fileName) {

            var retval = ExecutionResult.Empty;


            if (!string.IsNullOrEmpty(fileName)) {

                var result = string.Empty;

                var request = GetRequest(fileName);

                request.KeepAlive = false;

                request.Method = WebRequestMethods.Ftp.DeleteFile;


                try {

                    using (var response = (FtpWebResponse)request.GetResponse()) {

                        var responseLength = response.ContentLength;

                        using (var stream = response.GetResponseStream()) {

                            using (var sr = new StreamReader(stream)) {

                                result = sr.ReadToEnd();

                                sr.Close();

                            }

                            stream.Close();

                            var ftpResult = new FtpResult { Code = response.StatusCode, Message = response.StatusDescription };

                            retval = new ExecutionResult { Succeeded = true, Tag = ftpResult };

                            OnFtpRequestCompleted?.Invoke(this, ftpResult, new {

                                Result = result, Size = responseLength

                            });

                        }

                        response.Close();

                    }

                } catch (Exception ex) {

                    var msg = $"Unable to delete file - {fileName}";

                    retval = new ExecutionResult() { CaughtException = ex, Succeeded = false, Tag = msg };

                    OnExceptionCaught?.Invoke(this, ex, msg);

                }

            }


            return retval;

        }


        /// <summary>

        /// Uploads the specified file name.

        /// </summary>

        /// <param name="fileName">Name of the file.</param>

        /// <param name="remoteFileName">Name of the remote file.</param>

        /// <param name="remoteFolder">The remote folder.</param>

        /// <returns></returns>

        public ExecutionResult Upload(string fileName, string remoteFileName, string remoteFolder = null) {

            var retval = ExecutionResult.Empty;


            if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName) && !string.IsNullOrEmpty(remoteFileName)) {

                long uploaded = 0;

                var contentLength = 0;

                var fi = new FileInfo(fileName);

                var requestUri = $"{remoteFolder}/{remoteFileName}";

                var startTime = DateTime.Now;

                var request = GetRequest(requestUri);

                request.KeepAlive = false;

                request.Method = WebRequestMethods.Ftp.UploadFile;

                request.ContentLength = fi.Length;

                var buffer = new byte[BufferSize];


                try {

                    using (var fs = fi.OpenRead()) {

                        using (var stream = request.GetRequestStream()) {

                            uploaded = contentLength = fs.Read(buffer, 0, BufferSize);

                            while (contentLength != 0) {

                                stream.Write(buffer, 0, contentLength);

                                OnUploadProgress?.Invoke($"Uploading {fileName}...", uploaded, DateTime.Now - startTime);

                                contentLength = fs.Read(buffer, 0, BufferSize);

                                uploaded += contentLength;

                            }

                            var ftpResult = new FtpResult { Code = FtpStatusCode.CommandOK, Message = "Upload completed." };

                            OnFtpRequestCompleted?.Invoke(this, ftpResult, $"Upload took {DateTime.Now - startTime}");

                            retval = new ExecutionResult() { Succeeded = true, Tag = ftpResult };

                            stream.Close();

                            fs.Close();

                        }

                    }

                } catch (Exception ex) {

                    var msg = $"Unable to upload file - {fileName}";

                    retval = new ExecutionResult() { CaughtException = ex, Succeeded = false, Tag = msg };

                    OnExceptionCaught?.Invoke(this, ex, msg);

                }

            }

            return retval;

        }


        /// <summary>

        /// Renames the file.

        /// </summary>

        /// <param name="fileName">Name of the file.</param>

        /// <param name="remoteFileName">Name of the remote file.</param>

        /// <param name="remoteFolder">The remote folder.</param>

        /// <returns></returns>

        public ExecutionResult RenameFile(string fileName, string remoteFileName, string remoteFolder = null) {

            var retval = ExecutionResult.Empty;


            if (!string.IsNullOrEmpty(fileName) && !string.IsNullOrEmpty(remoteFileName)) {

                var requestUri = $"{remoteFolder}/{remoteFileName}";

                var request = GetRequest(requestUri);

                request.KeepAlive = false;

                request.Method = WebRequestMethods.Ftp.Rename;

                request.RenameTo = fileName;


                try {

                    using (var response = (FtpWebResponse)request.GetResponse()) {

                        var ftpResult = new FtpResult { Code = response.StatusCode, Message = response.StatusDescription };

                        retval = new ExecutionResult { Succeeded = true, Tag = ftpResult };

                        OnFtpRequestCompleted?.Invoke(this, ftpResult, retval);

                        response.Close();

                    }

                } catch (Exception ex) {

                    var msg = $"Unable to rename file - {requestUri} to {fileName}";

                    retval = new ExecutionResult() { CaughtException = ex, Succeeded = false, Tag = msg };

                    OnExceptionCaught?.Invoke(this, ex, msg);

                }

            }


            return retval;

        }


        /// <summary>

        /// Makes the directory.

        /// </summary>

        /// <param name="remoteDirName">Name of the remote dir.</param>

        /// <returns></returns>

        public ExecutionResult MakeDirectory(string remoteDirName) {

            var retval = ExecutionResult.Empty;


            if (!string.IsNullOrEmpty(remoteDirName)) {

                try {

                    var request = GetRequest(remoteDirName);

                    request.Method = WebRequestMethods.Ftp.MakeDirectory;


                    using (var response = (FtpWebResponse)request.GetResponse()) {

                        using (var stream = response.GetResponseStream()) {

                            var ftpResult = new FtpResult { Code = response.StatusCode, Message = response.StatusDescription };

                            retval = new ExecutionResult { Succeeded = true, Tag = ftpResult };

                            OnFtpRequestCompleted?.Invoke(this, ftpResult, retval);

                            stream?.Close();

                        }

                        response.Close();

                    }

                } catch (Exception ex) {

                    var msg = $"Unable to Create Directory - {remoteDirName}";

                    retval = new ExecutionResult() { CaughtException = ex, Succeeded = false, Tag = msg };

                    OnExceptionCaught?.Invoke(this, ex, msg);

                }

            }


            return retval;

        }


        /// <summary>

        /// Gets the directory list.

        /// </summary>

        /// <param name="remoteFolder">The remote folder.</param>

        /// <returns></returns>

        public List<ObjectInfo> GetDirectoryListing(string remoteFolder = null) {

            string[] items = null;

            List<ObjectInfo> retval = null;


            try {

                var buffer = new StringBuilder();

                var request = GetRequest(remoteFolder);

                request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;


                using (var response = (FtpWebResponse)request.GetResponse()) {

                    using (var reader = new StreamReader(response.GetResponseStream())) {

                        var line = reader.ReadLine();

                        while (!string.IsNullOrEmpty(line)) {

                            buffer.AppendLine(line);

                            buffer.Append("\n");

                            line = reader.ReadLine();

                        }


                        // Did we find anything?

                        if (buffer.Length > 0) {

                            retval = new List<ObjectInfo>();

                            buffer.Remove(buffer.ToString().LastIndexOf('\n'), 1);

                            items = buffer.ToString().Split('\n');


                            Array.ForEach(items, _ => {

                                var item = ParseFtpListItem(_);

                                if (item != null)

                                    retval.Add(item);

                            });

                        }


                        var itemCount = items != null ? items.Length : 0;


                        OnFtpRequestCompleted?.Invoke(this, new FtpResult { Code = response.StatusCode, Message = response.StatusDescription },

                                                      $"GetDirectoryListing returned {itemCount} items.");

                        reader.Close();

                        response.Close();

                    }

                }

            } catch (Exception ex) {

                retval = null;

                OnExceptionCaught?.Invoke(this, ex, $"Unable to get directory listing on {remoteFolder}");

            }


            return retval;

        }


        #endregion

    }

}

Easy and convenient way to serialize SQLite tables with xamarin

Hi Community,

As a follow up to my previous post on Xamarin development, Today is about a common task which is serialization of SQLite tables as a CSV file (It could be any file actually, but in this specific case is a CSV file). At the same time, I’d like to also share with you a convenient way to pass across relevant information about Android.Content.Context to a PCL library that does not have nor support it.

Let’s get started by describing the way we serialize our SQLite tables as CSV files.  Firstly, we define an interface that will be implemented by a helper class in the Xamarin Android project. Please note that its only method “SerializeTableAsCsv” is a generic method, therefore we will pass the model class (Table).

public interface ITableSerializer {

        /// <summary>

        /// Serializes the table as CSV.

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <param name="conn">The connection.</param>

        /// <param name="outputFilePath">The output file path.</param>

        /// <returns></returns>

        ExecutionResult SerializeTableAsCsv<T>(SQLite.Net.SQLiteConnection conn, string outputFilePath) where T: class, new();

    }

public class TableSerializer : ITableSerializer {

    /// <summary>

    /// The reflection binding flags

    /// </summary>

    private const BindingFlags ReflectionBindingFlags = BindingFlags.DeclaredOnly | BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public;

 

    /// <summary>

    /// Serializes the table as CSV.

    /// </summary>

    /// <typeparam name="T"></typeparam>

    /// <param name="conn">The connection.</param>

    /// <param name="outputFilePath">The output file path.</param>

    /// <returns></returns>

    /// <exception cref="NotImplementedException"></exception>

    public ExecutionResult SerializeTableAsCsv<T>(SQLiteConnection conn, string outputFilePath) where T : class, new() {

        List<TableMapping> mappings = null;

        var retval = ExecutionResult.Empty;

        var context = TinyIoC.TinyIoCContainer.Current.Resolve<ILightAndroidContext>();

 

        if (conn != null && (mappings = conn.TableMappings?.ToList()).Count > 0 && context != null) {

            var table = conn.TableMappings.FirstOrDefault(_ => typeof(T).ToString().Contains(_.TableName));

 

            if (table != null) {

                var workFolder = $"{context.Cache.Parent}/files/DB/Export";

                var targetFile = outputFilePath ?? $"{workFolder}/{table.TableName}.csv";

 

                try {

                    // If file exists, we'll delete it

                    if (File.Exists(targetFile))

                        File.Delete(targetFile);

 

                    // If Export directory doesn't exist, we'll create it

                    if (!Directory.Exists(workFolder))

                        Directory.CreateDirectory(workFolder);

 

                    SerializeHelper<T>(conn, targetFile);

 

                    retval = new ExecutionResult() { Succeeded = true, Tag = targetFile };

                } catch (Exception ex) {

                    retval = new ExecutionResult() { Tag = targetFile, CaughtException = ex, Succeeded = false };

                }

            }

        } else

            throw new ArgumentException(Constants.UnableToSerializeTableWithMissingInformation);

 

        return retval;

    }

 

 

    /// <summary>

    /// Lines the builder.

    /// </summary>

    /// <typeparam name="T"></typeparam>

    /// <param name="t">The t.</param>

    /// <param name="tv">The tv.</param>

    /// <returns></returns>

    private static string LineBuilder<T>(Type t, T tv) {

        var retval = string.Empty;

        var buffer = new StringBuilder();

        var formatter = new Func<object, string>(p => p?.ToString() ?? string.Empty);

        var props = t.GetProperties(ReflectionBindingFlags).ToList();

        props?.ForEach(x => buffer.Append($"\"{formatter(x.GetValue(tv))}\","));

        retval = buffer.ToString();

 

        return retval.Substring(0, retval.Length - 1);

    }

 

    /// <summary>

    /// Serializes the helper.

    /// </summary>

    /// <typeparam name="T"></typeparam>

    /// <param name="conn">The connection.</param>

    /// <param name="targetFile">The target file.</param>

    private static void SerializeHelper<T>(SQLiteConnection conn, string targetFile) where T : class, new() {

        var newLine = string.Empty;

        var rows = conn.Table<T>().ToList();

 

        if (rows?.Count > 0) {

            using (var csvFile = File.CreateText(targetFile)) {

                rows.ForEach(x => {

                    if (!string.IsNullOrEmpty(newLine = LineBuilder(x.GetType(), x)))

                        csvFile.WriteLine(newLine);

                });

                csvFile.Flush();

                csvFile.Close();

            }

        }

    }

}

The only question now is how to invoke this method from the PCL library project?  The answer is simple, we use a delegate (or Func) that points to the above implementation.

/*****************************/

/*     Interface             */

/*****************************/  

 

  public interface IDataService {

  /// <summary>

  /// Serializes the table as CSV.

  /// </summary>

  /// <typeparam name="T"></typeparam>

  /// <param name="exportFunc">The export function.</param>

  /// <returns></returns>

  ExecutionResult SerializeTableAsCsv<T>(Func<SQLite.Net.SQLiteConnection, string, ExecutionResult> exportFunc);

}

 

/*****************************/

/*     Implementation        */

/*****************************/  

 

public class DataService: IDataService {

    /// <summary>

    /// Serializes the table as CSV.

    /// </summary>

    /// <typeparam name="T"></typeparam>

    /// <param name="exportFunc">The export function.</param>

    /// <returns></returns>

    public ExecutionResult SerializeTableAsCsv<T>(Func<SQLite.Net.SQLiteConnection, string, ExecutionResult> exportFunc) {

        return exportFunc?.Invoke(Connection, null);

    }

}

 

 

/*****************************/

/*     Usage                 */

/*****************************/  

 

private void ExportTablesForDebugging() {

    //For debugging purposes only 

    #if DEBUG

        var dataSvc = TinyIoC.TinyIoCContainer.Current.Resolve<IDataService>();

        var debugUtil = TinyIoC.TinyIoCContainer.Current.Resolve<IDebugUtil>();

        var csvExporter = TinyIoC.TinyIoCContainer.Current.Resolve<ITableSerializer>();

 

        dataSvc?.SerializeTableAsCsv<Log>(csvExporter.SerializeTableAsCsv<Log>);

    #endif

}

Up until now, we are able to serialize our SQLite table, but in case you didn’t notice I’m able to pull information about the Android Context which is non-existent in PCL, in order to make it available I have come up with my ILightAndroidContext implementation that allows me to get information about the application.

#region "Required structs"

 

/// <summary>

/// 

/// </summary>

public struct CacheDir {

    /// <summary>

    /// Gets or sets the absolute path.

    /// </summary>

    /// <value>

    /// The absolute path.

    /// </value>

    public string AbsolutePath {

        get; set;

    }

 

    /// <summary>

    /// Gets or sets the canonical path.

    /// </summary>

    /// <value>

    /// The canonical path.

    /// </value>

    public string CanonicalPath {

        get; set;

    }

 

    /// <summary>

    /// Gets or sets the parent.

    /// </summary>

    /// <value>

    /// The parent.

    /// </value>

    public string Parent {

        get; set;

    }

 

    /// <summary>

    /// Gets or sets the path.

    /// </summary>

    /// <value>

    /// The path.

    /// </value>

    public string Path {

        get; set;

    }

 

    /// <summary>

    /// Gets or sets the total space.

    /// </summary>

    /// <value>

    /// The total space.

    /// </value>

    public long TotalSpace {       

        get; set;

    }

 

    /// <summary>

    /// Gets or sets the usable space.

    /// </summary>

    /// <value>

    /// The usable space.

    /// </value>

    public long UsableSpace {

        get; set;

    }

}

 

/// <summary>

/// 

/// </summary>

public struct AppContext {

    /// <summary>

    /// The package code path

    /// </summary>

    public string PackageCodePath {

        get; set;

    }

    /// <summary>

    /// The package name

    /// </summary>

    public string PackageName {

        get; set;

    }

}

 

#endregion

 

/**********************************/

/*  Interface                     */

/**********************************/     

 

/// <summary>

/// 

/// </summary>

public interface ILightAndroidContext {

    /// <summary>

    /// Gets or sets the raw context.

    /// </summary>

    /// <value>

    /// The raw context.

    /// </value>

    object RawContext {

        get;

    }

 

    /// <summary>

    /// Gets or sets the application context.

    /// </summary>

    /// <value>

    /// The application context.

    /// </value>

    AppContext ApplicationContext {

        get; set;

    }

 

    /// <summary>

    /// Gets or sets the cache.

    /// </summary>

    /// <value>

    /// The cache.

    /// </value>

    CacheDir Cache {

        get; set;

    }

 

    /// <summary>

    /// Gets or sets the name of the context.

    /// </summary>

    /// <value>

    /// The name of the context.

    /// </value>

    string ContextName {

        get; set;

    } 

}

 

 

/**********************************/

/*  Implementation                */

/**********************************/   

 

public class LightAndroidContext : ILightAndroidContext {

        /// <summary>

        /// The raw context

        /// </summary>

        private static object _rawContext;

 

        /// <summary>

        /// The has been initialized

        /// </summary>

        private bool _hasBeenInitialized = false;

 

        /// <summary>

        /// The context name

        /// </summary>

        private string _contextName;

 

        /// <summary>

        /// The application context

        /// </summary>

        private AppContext _applicationContext = new AppContext();

 

        /// <summary>

        /// The cache

        /// </summary>

        private CacheDir _cache = new CacheDir();

 

        /// <summary>

        /// Gets or sets the application context.

        /// </summary>

        /// <value>

        /// The application context.

        /// </value>

        public AppContext ApplicationContext {

            get {

                return _applicationContext;

            }

            set {

                throw new Exception(Constants.UnableToWriteReadOnlyPropertyException);

 

            }

        }

 

        /// <summary>

        /// Gets or sets the cache.

        /// </summary>

        /// <value>

        /// The cache.

        /// </value>

        public CacheDir Cache {

            get {

                return _cache;

            }

 

            set {

                throw new Exception(Constants.UnableToWriteReadOnlyPropertyException);

            }

        }

 

        /// <summary>

        /// Gets or sets the name of the context.

        /// </summary>

        /// <value>

        /// The name of the context.

        /// </value>

        public string ContextName {

            get {

                return _contextName;

            }

 

            set {

                throw new Exception(Constants.UnableToWriteReadOnlyPropertyException);

            }

        }

 

        /// <summary>

        /// Gets or sets the raw context.

        /// </summary>

        /// <value>

        /// The raw context.

        /// </value>

        public object RawContext {

            get {

                return _rawContext;

            }

            private set {

                lock (new object()) {

                    if (_rawContext == null)

                        _rawContext = value;

                }

 

            }

        }

 

        /// <summary>

        /// Initializes a new instance of the <see cref="LightAndroidContext"/> class.

        /// </summary>

        /// <param name="context">The context.</param>

        /// <exception cref="ArgumentException"></exception>

        public LightAndroidContext(object context) {

            if (!_hasBeenInitialized)

                if (!IsAvalidAndroidContext(context))

                    throw new ArgumentException(Constants.UnableToConstructLightContextWithoutProperContext);

        }

 

        /// <summary>

        /// Determines whether [is avalid android context] [the specified context].

        /// </summary>

        /// <param name="context">The context.</param>

        /// <returns>

        ///   <c>true</c> if [is avalid android context] [the specified context]; otherwise, <c>false</c>.

        /// </returns>

        private bool IsAvalidAndroidContext(object context) {

            var retval = false;

            Type targetType = null;

            PropertyInfo appInfo, cacheDir;

 

            if (context != null && (targetType = context.GetType()) != null) {

                cacheDir = targetType.GetRuntimeProperties().FirstOrDefault(_ => _.Name.Equals("CacheDir"));

                appInfo = targetType.GetRuntimeProperties().FirstOrDefault(_ => _.Name.Equals("ApplicationContext"));

 

                if (appInfo != null && cacheDir != null) {

                    try {

                        var appInfoValue = appInfo.GetValue(context);

                        var packageName = appInfoValue.GetType().GetRuntimeProperties().FirstOrDefault(_ => _.Name.Equals("PackageName"));

                        _applicationContext.PackageName = packageName.GetValue(context).ToString();

                        var packageCodePath = appInfoValue.GetType().GetRuntimeProperties().FirstOrDefault(_ => _.Name.Equals("PackageCodePath"));

                        _applicationContext.PackageCodePath = packageCodePath.GetValue(context).ToString();

                        var cache = cacheDir.GetValue(context);

                        var absolutePath = cache.GetType().GetRuntimeProperties().FirstOrDefault(_ => _.Name.Equals("AbsolutePath"));

                        _cache.AbsolutePath = absolutePath.GetValue(cache).ToString();

                        var canonicalPath = cache.GetType().GetRuntimeProperties().FirstOrDefault(_ => _.Name.Equals("CanonicalPath"));

                        _cache.CanonicalPath = canonicalPath.GetValue(cache).ToString();

                        var parent = cache.GetType().GetRuntimeProperties().FirstOrDefault(_ => _.Name.Equals("Parent"));

                        _cache.Parent = parent.GetValue(cache).ToString();

                        var path = cache.GetType().GetRuntimeProperties().FirstOrDefault(_ => _.Name.Equals("Path"));

                        _cache.Path = path.GetValue(cache).ToString();

                        var totalSpace = cache.GetType().GetRuntimeProperties().FirstOrDefault(_ => _.Name.Equals("TotalSpace"));

                        _cache.TotalSpace = long.Parse(totalSpace.GetValue(cache).ToString());

                        var usableSpace = cache.GetType().GetRuntimeProperties().FirstOrDefault(_ => _.Name.Equals("UsableSpace"));

                        _cache.UsableSpace = long.Parse(usableSpace.GetValue(cache).ToString());

                        RawContext = context;

                        _contextName = context.ToString();

                        retval = true;

                    } catch {

                        // Safe to ignore exception. Reflection related

                    }

                }

            }

 

            return retval;

        }

    }

Regards,

Angel

Overcoming lack of AppDomain class in PCL library with Xamarin

Hi Community,

If you usually wear two hats (architect and  developer) like I do, you might find this blog post useful. My current engagement requires me to have the precision of a surgeon, because anything misplaced might break the functionality of this Xamarin Android application. My customer’s requirement was “simple” being able to extend existing functionality and stabilize the solution as it stands now. I do not know who architected or built it in the first place, but that is irrelevant at this moment in time. The solution has one Xamarin Android (Xamarin Forms) and a common library that is PCL, and even when the application has a service locator and dependency injection the way it was originally built does not allow me to pass certain types to the PCL side of things. Let’s remember, PCL does not support all the features in .NET because it is agnostic of the platform, so it can run and behave the same on iOS, Android, Windows Phone, etc. Since I had to consume some functionality in .NET project (Xamarin) in PCL it was very likely that functionality was not present.  For those scenarios, I rely on the AppDomain to store code that must be available to the overall solution, in the AppDomain (in .NET) I can easily store an object or just a IntPtr and use Marshal class to consume it from somewhere else, this was not the case however. In PCL land, AppDomain is not serializable that’s why I had to come up with this approach.

PCL does not support AppDomain nor has a Marshal class, so from that standpoint I was limited. In order to workaround it this is what I had to do.

1-. In the Xamarin Android code that has access to AppDomain, we store the object that is dependent by PCL project. We implement and expose our required object as a singleton.

///////////////////////////////////////////////

// Code that stores information in AppDomain //

////////////////////////////////////////////// 


// Due to lack of AppDomain in PCL, pinning ITransactionalDataService instance to then retrieve it from PCl library

if (AppDomain.CurrentDomain.GetData(Constants.TransactionalDataService) == null) {

    var tranDb = TinyIoC.TinyIoCContainer.Current.Resolve<ITransactionalDataService>();

    AppDomain.CurrentDomain.SetData(Constants.TransactionalDataService, tranDb);

    IOC.AppDomainWrapper.PinObjectInAppDomain(AppDomain.CurrentDomain.GetData(Constants.TransactionalDataService));

}



///////////////////////////

//AppDomainWrapper class //

//////////////////////////


using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;


namespace MyNamespace {

    /// <summary>

    /// 

    /// </summary>

    public class AppDomainWrapper {

        /// <summary>

        /// The _ application domain pinned object

        /// </summary>

        private static object _appDomainPinnedObject;


        /// <summary>

        /// The _lock object

        /// </summary>

        private static readonly object _lockObj = new object();



        /// <summary>

        /// Pins the object in application domain.

        /// </summary>

        /// <param name="objToPin">The object to pin.</param>

        public static void PinObjectInAppDomain(object objToPin) {

            lock (_lockObj) {

                if (_appDomainPinnedObject == null)

                    _appDomainPinnedObject = objToPin;

            }

        }


        /// <summary>

        /// Gets the pinned object from application domain.

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <returns></returns>

        public static T GetPinnedObjectFromAppDomain<T>() where T : class {

            var retval = default(T);


            if (_appDomainPinnedObject != null)

                retval = _appDomainPinnedObject as T;


            return retval;

        }


    }

}

 

2-. In the PCL project we then retrieve for use

private static void SafeExecuteWrapper(MetroLog.ILogger logger, string message, bool storeInDb = true, Exception ex = null, LoggerMethod method = LoggerMethod.Info, params object[] ps) {

    if (!string.IsNullOrEmpty(message) && logger != null) {

        try {

            if (storeInDb) {

                var formattedMsg = message;

                var appDomain = AppDomainWrapper.GetPinnedObjectFromAppDomain<ITransactionalDataService>();


                if (ps != null)

                    formattedMsg = string.Format(message, ps);


                appDomain.Insert(new Log() { Message = formattedMsg, SerializedException = ex?.SerializeAsString() });

            } else {

                if (method == LoggerMethod.Info)

                    logger.Info(message, ex);

                else if (method == LoggerMethod.Error)

                    logger.Error(message, ex);

            }

        } catch {

            // Safe to swallow exception    

        }

    }

}

 

Now, the object that was required in PCL library can be retrieved from AppDomain. I would also like to mention that we should not store too much stuff in AppDomain. In this case, I had a requirement and was the only option available. If code would have been written better, none of all this would make sense.

 

Regards,

 

Angel

Visual C++ for Linux Development

Hi Community,

It’s been  almost 3 months since Microsoft announced their “Visual C++ for Linux Development” extension. There is a very concise and clear article here. This extension allows developers to target and deploy to *Nix systems (even to a Raspberry Pi) using Visual C++. Developers can even set breakpoints and debug remote code.  In my personal case, I have always Qt Creator or CLion for C++ development in Linux but with this extension I can do it from my favorite IDEVisual Studio.

Visual Studio interacts with *Nix system via SSH (provided by SSHD), and it hosts and allows debugging the application through GDBServer, below a screenshot of my gnome-system-monitor depicting this (also take note of the PID 10468 in the remote Ubuntu system, it’s the same one shown inside Visual Studio debugger)

 

gdbserver

The debugging experience in Visual Studio is natural and we can even see the information about threads and loaded modules

VS Debug Experience

At the same time and as a reminder, it’s worth mentioning that WSL (Windows Subsystem for Linux) will be released soon (next month, as far as I know with “Windows Anniversary Update”).  A bit of background of WSL is that it implements Pico processes originally found in project Drawbridge, and it’s got a few similarities to WINE, hence Windows will be capable of running ELF images natively.

 

Regards,

Angel

Instrumenting *nix Systems via C#, C++, Mono and Qt

Hi Community,

The following article is about one of the subjects I’m most passionate about that’s integration of disparate systems and cross-platform computing. I think, it’s something I had to publish and it’s one of the core components of my SIF project (Service Integration Framework). This approach can be applied to a variety of scenarios, like exposing performance counters on a *nix system that is hosted in the cloud (good example, a NAT server hosted AWSLinux server without any UI elements but only allowing connection over HTTP/HTTPS/SSH). This also reminds me of a distributed CyberKiosk (Cyber Cafe) solution I built in Visual FoxPro like 20 years ago, unfortunately back then SOAP was pretty much unknown territory thus management and instrumentation of the networked computers was achievable through sockets only (and yes, I did try DCOM but back then it gave me more headaches than solutions).

Anyways, I’m done with the introduction so what’s this article about? Well,  in a nutshell it’s part of my SIF project but it’s also also a PoC (Proof of Concept) of a Qt application hosting the CLR implemented in mono which in turn exposes a WCF service that allows me to instrument the target *nix system through SOAP, sounds cool, right? *nix systems as their Windows counterparts provide a variety of different performance counters and profiling tools, a good example can be the perf stat utility

angel@Obi-wan:~$ sudo perf stat -a

^C

 Performance counter stats for 'system wide':

 

      62568.846961      task-clock (msec)         #    4.000 CPUs utilized            (100.00%)

           214,730      context-switches          #    0.003 M/sec                    (100.00%)

            11,889      cpu-migrations            #    0.190 K/sec                    (100.00%)

             2,019      page-faults               #    0.032 K/sec                  

    28,653,473,022      cycles                    #    0.458 GHz                      (100.00%)

    39,173,539,651      stalled-cycles-frontend   #  136.71% frontend cycles idle     (100.00%)

   <not supported>      stalled-cycles-backend   

     9,286,112,645      instructions              #    0.32  insns per cycle        

                                                  #    4.22  stalled cycles per insn  (100.00%)

     1,977,739,115      branches                  #   31.609 M/sec                    (100.00%)

        83,956,096      branch-misses             #    4.25% of all branches        

 

      15.640948007 seconds time elapsed

 

 

angel@Obi-wan:~$ 

 

 

In order to understand how this solution was architected is important to mention that our Qt application hosts the CLR (more information on embedding mono here), once the AppDomain is created we pass a native functor to the managed world in order to bubble up messages back to the Qt application. C++11 introduced the std::function class template that’s passed to the CLR as a pointer and marshalled by our C# code, but I think it’s worthy to start from the very beginning and then we’ll describe this later.

 

Our Qt application implements a wrapper class (monowrapper) to interact with mono

 

#include "monowrapper.h"

 

monowrapper::monowrapper() {

 

}

 

bool monowrapper::Initialized_get() const {

    return m_IsInitialized;

}

 

void monowrapper::UnloadAppDomain() {

    if (m_IsInitialized)

        mono_jit_cleanup(m_domain);

}

 

void monowrapper::CreateDomain(std::function<void(const char*)> logger) {

    if (m_IsInitialized)

        return;

 

    m_logger = logger;

 

    mono_set_dirs("/usr/lib/", "/etc/mono");

 

    mono_config_parse(nullptr);

 

    m_domain = mono_jit_init("./MonoDaemon.Service.dll");

 

    m_assembly = mono_domain_assembly_open (m_domain, "./MonoDaemon.ServiceHost.dll");

 

    if (m_assembly) {

        MonoObject* ex = nullptr;

        mono_domain_set_config(m_domain, "/etc/mono/4.5/", "machine.config");

        m_monoImage = mono_assembly_get_image(m_assembly);

        m_hostClass = mono_class_from_name(m_monoImage, "MonoDaemon", "ServiceHost");

        m_hostInstance  = mono_object_new(m_domain, m_hostClass);

        auto ctorMethod = mono_class_get_method_from_name(m_hostClass, ".ctor", 0);

        mono_runtime_invoke(ctorMethod, m_hostClass, nullptr, &ex);

        auto hostInitializeMethod = mono_class_get_method_from_name(m_hostClass, "InitializeHost", 1);

        void* args[1] = {&logger};

        mono_runtime_invoke(hostInitializeMethod, m_hostInstance, args, &ex);

        m_IsInitialized = true;

    } else {

        // Log & display error message here

 

    }

}

 

The mainwindow class has an instance of the monowrapper class

#include "mainwindow.h"

#include "ui_mainwindow.h"

 

static const MainWindow* m_selfReference;

 

MainWindow::MainWindow(QWidget *parent) :

    QMainWindow(parent),

    ui(new Ui::MainWindow)

{

    ui->setupUi(this);

    m_monoWrapper = std::unique_ptr<monowrapper>(new monowrapper);

    m_selfReference = this;

}

 

MainWindow::~MainWindow()

{

    delete ui;

}

 

void MainWindow::LogMessage(const char *msg) {

    auto msgAsStr = std::string(msg);

    auto lstMessages = m_selfReference->findChild<QListWidget*>("lstMessages");

    new QListWidgetItem(tr(msgAsStr.c_str()), lstMessages);

}

 

void MainWindow::on_btnStartHost_clicked()

{

    std::function<void(const char*)> func =  &MainWindow::LogMessage;

    m_monoWrapper->CreateDomain(func);

}

 

void MainWindow::on_btnExitHost_clicked()

{

    m_monoWrapper->UnloadAppDomain();

    close();

}

 

Please note that I’m using a few features available in modern C++ like smart-pointers and the new std::function (which is the equivalent of Action<T> in .NET). Once our application has loaded the CLR we can see it in the “Loaded Modules” in Qt Creator (shown highlighted “mscorlib”)

 

image

 

In the C#/CLR side of things (mono) we can see how we get the void* from Qt as an IntPtr, we then pin it with GCHandle.Alloc (this is to prevent the GC to move it around) and we pass it across to service implementation too, therefore we can bubble up or fire notifications back to the native world (Qt application). Below the implementation of InitializeHost and ShowMessage method, please note how we need to add the null-operator to the string we’re returning through the callback otherwise our strings in the Qt application might have some “funny” characters at the end.

/// <summary>

/// Initializes the host.

/// </summary>

/// <param name="callback">Callback.</param>

public void InitializeHost (IntPtr callback) {

    if (callback != IntPtr.Zero) {

        ExternalCallback = GCHandle.Alloc (Marshal.GetDelegateForFunctionPointer<Utilities.QtExternalCode> (callback));

        DaemonOperation.QtMessenger = new Action<string> (ShowMessage);

    }

 

    ShowMessage ("Creating binding...");

    var binding = new BasicHttpBinding ();

    binding.Security.Mode = BasicHttpSecurityMode.None; 

 

    ShowMessage ("Allocating endpoint addresses...");

    var address = new Uri ("http://localhost:8888/monodaemon");

    var addressMex = new Uri ("http://localhost:8888/monodaemon/mex");

 

    ShowMessage ("Creating host...");

    m_host = new System.ServiceModel.ServiceHost (typeof(DaemonOperation),

        new Uri[] { new Uri ("http://localhost:8888/monodaemon") }); 

 

    ShowMessage ("Adding behaviors...");

    m_host.Description.Behaviors.Remove<System.ServiceModel.Description.ServiceMetadataBehavior> ();

    m_host.Description.Behaviors.Add (new System.ServiceModel.Description.ServiceMetadataBehavior {

        HttpGetEnabled = true, HttpsGetEnabled = false

    }); 

 

    ShowMessage ("Adding endpoints...");

    m_host.AddServiceEndpoint (typeof(IDaemonOperation), binding, address);

    m_host.AddServiceEndpoint (System.ServiceModel.Description.ServiceMetadataBehavior.MexContractName, 

        System.ServiceModel.Description.MetadataExchangeBindings.CreateMexHttpBinding (), addressMex); 

 

    ShowMessage ("Starting host...");

    m_host.Open ();

}

 

/// <summary>

/// Shows the message.

/// </summary>

/// <param name="message">Message.</param>

protected void ShowMessage (string message)  {

    if (!string.IsNullOrEmpty (message)) {

        Console.WriteLine (message);

        var bytes = Encoding.Default.GetBytes (message);

        var buffer = new byte[bytes.Length + 1];

        Array.Copy (bytes, buffer, bytes.Length);

        buffer [buffer.Length - 1] = (byte) '\0';

        ((Utilities.QtExternalCode)ExternalCallback.Target).Invoke (buffer);

    }

}

 

 

 

As you can see, our native callback will be the interface between managed and native world. The ShowMessage method will output the notifications to the “Application Output” window in Qt Creator (System console)

 

image

 

but also to the ListBox (QListWidget) in the main window

 

image

 

The client application or the one driving the operations is a Windows Form application (depicted below)

image

One of the methods in our MonoDaemon (that’s the Qt application acting as WCF host, effectively) is StartRemoteProcess that can easily call a bash script or in this case we’re starting “gnome-calculator”. The image below shows my Ubuntu desktop along with “gnome-system-monitor” (highlighting qtMonoHost and mono is also visible there) and the application we started remotely (gnome-calculator)

 

image

 

The requests/responses are HTTP/SOAP so unlike my situation a few years back I had to do it with sockets. The WireShark image below shows the SOAP envelope returned to the client after having called the “GetRunningProcesses” method.

 

image

 

And that’s pretty much it, folks. Source code can be found here.

 

I would like to thank and dedicate this article to the people I love the most… God and his son Jesus, my wife and two young girls (I call them “3M”). Thanks for being patient and bearing with me when I sit in front of the PC for hours or go to bed late at night.

 

Regards,

 

Angel

Checking for library dependencies in Windows and Linux

Hi Community,

Today’s post is about identifying and learning about the dependencies our applications might have with shared libraries. One of the beautiful things about software is that it’s universal similar to music, therefore patterns and principles are agnostic of any platform, but they’re also applicable to specific platforms.  In order to get started, we need to understand the concept of executable format and its many flavors. Linux like any other *nix system uses ELFWindows in the other hand uses PE. They both describe code that can run on their corresponding target system. At the same time, it’s fair to say that I can run (and I do) Windows programs on Linux via Wine (which is a compatibility layer that implements a virtual machine or sandbox environment  where “Windows” code is safely executed).

When it comes to shared libraries and dependencies, headaches and issues always arise regardless of the platform. Some of these issues went away with the introduction of .NET back in 2002 that helped alleviate the “DLL Hell”, which it might not “occur”, but different architectures (x86, x64, ARM and Itanium to mention a few) also bring their own set of problems, like referencing a library (e.g.: DLL) from a project that targets a different architecture, or how to differentiate a Win32 library from a .NET assembly is another clear example. I wrote an utility “.NET Library Explorer” (in Spanish) back in 2002 which helps developers to identify and understand libraries a bit better.

Now that we have a better understanding of the subject, let’s start describing and talking about the tools that might help us identify and recognize libraries and their dependencies. 

Let’s start with Linux, shall we? One of the things I love about Linux is that besides being open it also provides a variety of toolchains. There are two utilities that allow me to see at the ELF information of any given executable, so for example LDD (Print shared object dependencies). In this case, I’m printing out all the dependencies required this new Qt project I started working on recently. For a lot of people that are new to Linux, they might get confused when they find out that “Shared Libraries” in Linux can either be .a (static) or .so (dynamically linked). In other words, .a is the equivalent of a .LIB in Visual C++ and .so is a DLL that can be dynamically loaded at runtime.

 

angel@Obi-wan:~/Code/Ubuntu/QtProjects/qtMonoHost/build-qtMonoHost-Desktop_Qt_5_6_0_GCC_64bit-Debug$ ldd qtMonoHost

    linux-vdso.so.1 =>  (0x00007ffe136b7000)

    libmonoboehm-2.0.so.1 => /usr/lib/libmonoboehm-2.0.so.1 (0x00007f6bf3b4a000)

    libQt5Widgets.so.5 => /usr/local/Qt/5.6/gcc_64/lib/libQt5Widgets.so.5 (0x00007f6bf32d7000)

    libQt5Core.so.5 => /usr/local/Qt/5.6/gcc_64/lib/libQt5Core.so.5 (0x00007f6bf2bc5000)

    libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f6bf2843000)

    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f6bf262c000)

    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6bf2263000)

    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f6bf1f5a000)

    librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f6bf1d51000)

    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f6bf1b4d000)

    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f6bf1930000)

    /lib64/ld-linux-x86-64.so.2 (0x000055bcbfaf9000)

    libQt5Gui.so.5 => /usr/local/Qt/5.6/gcc_64/lib/libQt5Gui.so.5 (0x00007f6bf1138000)

    libgobject-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 (0x00007f6bf0ee5000)

    libgthread-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libgthread-2.0.so.0 (0x00007f6bf0ce3000)

    libglib-2.0.so.0 => /lib/x86_64-linux-gnu/libglib-2.0.so.0 (0x00007f6bf09d1000)

    libXext.so.6 => /usr/lib/x86_64-linux-gnu/libXext.so.6 (0x00007f6bf07bf000)

    libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007f6bf0485000)

    libGL.so.1 => /usr/lib/nvidia-361/libGL.so.1 (0x00007f6bf01f5000)

    libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f6beffdb000)

    libicui18n.so.56 => /usr/local/Qt/5.6/gcc_64/lib/libicui18n.so.56 (0x00007f6befb41000)

    libicuuc.so.56 => /usr/local/Qt/5.6/gcc_64/lib/libicuuc.so.56 (0x00007f6bef788000)

    libicudata.so.56 => /usr/local/Qt/5.6/gcc_64/lib/libicudata.so.56 (0x00007f6bedda5000)

    libffi.so.6 => /usr/lib/x86_64-linux-gnu/libffi.so.6 (0x00007f6bedb9c000)

    libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f6bed92c000)

    libxcb.so.1 => /usr/lib/x86_64-linux-gnu/libxcb.so.1 (0x00007f6bed70a000)

    libGLX.so.0 => /usr/lib/nvidia-361/libGLX.so.0 (0x00007f6bed4d7000)

    libGLdispatch.so.0 => /usr/lib/nvidia-361/libGLdispatch.so.0 (0x00007f6bed1ef000)

    libXau.so.6 => /usr/lib/x86_64-linux-gnu/libXau.so.6 (0x00007f6becfea000)

    libXdmcp.so.6 => /usr/lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007f6becde4000)

 

At the same time, I could have also used objdump (Display information from object files) to find out the needed files, as shown below in the “Dynamic Section” labeled as NEEDED

 

angel@Obi-wan:~/Code/Ubuntu/QtProjects/qtMonoHost/build-qtMonoHost-Desktop_Qt_5_6_0_GCC_64bit-Debug$ objdump -x qtMonoHost

 

qtMonoHost:     file format elf64-x86-64

qtMonoHost

architecture: i386:x86-64, flags 0x00000112:

EXEC_P, HAS_SYMS, D_PAGED

start address 0x00000000004028b0

 

Program Header:

    PHDR off    0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3

         filesz 0x00000000000001f8 memsz 0x00000000000001f8 flags r-x

  INTERP off    0x0000000000000238 vaddr 0x0000000000400238 paddr 0x0000000000400238 align 2**0

         filesz 0x000000000000001c memsz 0x000000000000001c flags r--

    LOAD off    0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21

         filesz 0x00000000000048ba memsz 0x00000000000048ba flags r-x

    LOAD off    0x0000000000004b48 vaddr 0x0000000000604b48 paddr 0x0000000000604b48 align 2**21

         filesz 0x0000000000000610 memsz 0x0000000000000618 flags rw-

 DYNAMIC off    0x0000000000004db0 vaddr 0x0000000000604db0 paddr 0x0000000000604db0 align 2**3

         filesz 0x0000000000000240 memsz 0x0000000000000240 flags rw-

    NOTE off    0x0000000000000254 vaddr 0x0000000000400254 paddr 0x0000000000400254 align 2**2

         filesz 0x0000000000000044 memsz 0x0000000000000044 flags r--

EH_FRAME off    0x0000000000003f58 vaddr 0x0000000000403f58 paddr 0x0000000000403f58 align 2**2

         filesz 0x00000000000001ac memsz 0x00000000000001ac flags r--

   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4

         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-

   RELRO off    0x0000000000004b48 vaddr 0x0000000000604b48 paddr 0x0000000000604b48 align 2**0

         filesz 0x00000000000004b8 memsz 0x00000000000004b8 flags r--

 

Dynamic Section:

  NEEDED               libmonoboehm-2.0.so.1

  NEEDED               libQt5Widgets.so.5

  NEEDED               libQt5Core.so.5

  NEEDED               libstdc++.so.6

  NEEDED               libgcc_s.so.1

  NEEDED               libc.so.6

  RPATH                $ORIGIN:/usr/local/Qt/5.6/gcc_64/lib

  INIT                 0x0000000000402618

  FINI                 0x0000000000403ba4

  INIT_ARRAY           0x0000000000604b48

  INIT_ARRAYSZ         0x0000000000000008

  FINI_ARRAY           0x0000000000604b50

  FINI_ARRAYSZ         0x0000000000000008

  GNU_HASH             0x0000000000400298

  STRTAB               0x0000000000400cf8

  SYMTAB               0x00000000004002d8

  STRSZ                0x0000000000000e5e

  SYMENT               0x0000000000000018

  DEBUG                0x0000000000000000

  PLTGOT               0x0000000000605000

  PLTRELSZ             0x0000000000000378

  PLTREL               0x0000000000000007

  JMPREL               0x00000000004022a0

  RELA                 0x0000000000401d00

  RELASZ               0x00000000000005a0

  RELAENT              0x0000000000000018

  FLAGS_1              0x0000000000000080

  VERNEED              0x0000000000401c30

  VERNEEDNUM           0x0000000000000005

  VERSYM               0x0000000000401b56

 

Version References:

  required from libgcc_s.so.1:

    0x0b792650 0x00 07 GCC_3.0

  required from libstdc++.so.6:

    0x056bafd3 0x00 08 CXXABI_1.3

    0x08922974 0x00 06 GLIBCXX_3.4

  required from libc.so.6:

    0x09691a75 0x00 09 GLIBC_2.2.5

    0x0d696914 0x00 05 GLIBC_2.4

  required from libQt5Core.so.5:

    0x00058a25 0x00 04 Qt_5

    0x058a2816 0x00 03 Qt_5.6

  required from libQt5Widgets.so.5:

    0x00058a25 0x00 02 Qt_5

 

Sections:

Idx Name          Size      VMA               LMA               File off  Algn

  0 .interp       0000001c  0000000000400238  0000000000400238  00000238  2**0

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

  1 .note.ABI-tag 00000020  0000000000400254  0000000000400254  00000254  2**2

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

  2 .note.gnu.build-id 00000024  0000000000400274  0000000000400274  00000274  2**2

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

  3 .gnu.hash     00000040  0000000000400298  0000000000400298  00000298  2**3

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

  4 .dynsym       00000a20  00000000004002d8  00000000004002d8  000002d8  2**3

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

  5 .dynstr       00000e5e  0000000000400cf8  0000000000400cf8  00000cf8  2**0

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

  6 .gnu.version  000000d8  0000000000401b56  0000000000401b56  00001b56  2**1

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

  7 .gnu.version_r 000000d0  0000000000401c30  0000000000401c30  00001c30  2**3

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

  8 .rela.dyn     000005a0  0000000000401d00  0000000000401d00  00001d00  2**3

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

  9 .rela.plt     00000378  00000000004022a0  00000000004022a0  000022a0  2**3

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

 10 .init         0000001a  0000000000402618  0000000000402618  00002618  2**2

                  CONTENTS, ALLOC, LOAD, READONLY, CODE

 11 .plt          00000260  0000000000402640  0000000000402640  00002640  2**4

                  CONTENTS, ALLOC, LOAD, READONLY, CODE

 12 .plt.got      00000008  00000000004028a0  00000000004028a0  000028a0  2**3

                  CONTENTS, ALLOC, LOAD, READONLY, CODE

 13 .text         000012f2  00000000004028b0  00000000004028b0  000028b0  2**4

                  CONTENTS, ALLOC, LOAD, READONLY, CODE

 14 .fini         00000009  0000000000403ba4  0000000000403ba4  00003ba4  2**2

                  CONTENTS, ALLOC, LOAD, READONLY, CODE

 15 .rodata       00000385  0000000000403bc0  0000000000403bc0  00003bc0  2**5

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

 16 .qtversion    00000010  0000000000403f48  0000000000403f48  00003f48  2**3

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

 17 .eh_frame_hdr 000001ac  0000000000403f58  0000000000403f58  00003f58  2**2

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

 18 .eh_frame     00000704  0000000000404108  0000000000404108  00004108  2**3

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

 19 .gcc_except_table 000000ae  000000000040480c  000000000040480c  0000480c  2**0

                  CONTENTS, ALLOC, LOAD, READONLY, DATA

 20 .init_array   00000008  0000000000604b48  0000000000604b48  00004b48  2**3

                  CONTENTS, ALLOC, LOAD, DATA

 21 .fini_array   00000008  0000000000604b50  0000000000604b50  00004b50  2**3

                  CONTENTS, ALLOC, LOAD, DATA

 22 .jcr          00000008  0000000000604b58  0000000000604b58  00004b58  2**3

                  CONTENTS, ALLOC, LOAD, DATA

 23 .data.rel.ro  00000250  0000000000604b60  0000000000604b60  00004b60  2**5

                  CONTENTS, ALLOC, LOAD, DATA

 24 .dynamic      00000240  0000000000604db0  0000000000604db0  00004db0  2**3

                  CONTENTS, ALLOC, LOAD, DATA

 25 .got          00000010  0000000000604ff0  0000000000604ff0  00004ff0  2**3

                  CONTENTS, ALLOC, LOAD, DATA

 26 .got.plt      00000140  0000000000605000  0000000000605000  00005000  2**3

                  CONTENTS, ALLOC, LOAD, DATA

 27 .data         00000018  0000000000605140  0000000000605140  00005140  2**3

                  CONTENTS, ALLOC, LOAD, DATA

 28 .bss          00000008  0000000000605158  0000000000605158  00005158  2**0

                  ALLOC

 29 .comment      0000002f  0000000000000000  0000000000000000  00005158  2**0

                  CONTENTS, READONLY

 30 .debug_aranges 000002f0  0000000000000000  0000000000000000  00005187  2**0

                  CONTENTS, READONLY, DEBUGGING

 31 .debug_info   0008935d  0000000000000000  0000000000000000  00005477  2**0

                  CONTENTS, READONLY, DEBUGGING

 32 .debug_abbrev 0000249a  0000000000000000  0000000000000000  0008e7d4  2**0

                  CONTENTS, READONLY, DEBUGGING

 33 .debug_line   00001bf6  0000000000000000  0000000000000000  00090c6e  2**0

                  CONTENTS, READONLY, DEBUGGING

 34 .debug_str    0004406c  0000000000000000  0000000000000000  00092864  2**0

                  CONTENTS, READONLY, DEBUGGING

 35 .debug_ranges 00000300  0000000000000000  0000000000000000  000d68d0  2**0

                  CONTENTS, READONLY, DEBUGGING

SYMBOL TABLE:

0000000000400238 l    d  .interp    0000000000000000              .interp

0000000000400254 l    d  .note.ABI-tag    0000000000000000              .note.ABI-tag

0000000000400274 l    d  .note.gnu.build-id    0000000000000000              .note.gnu.build-id

0000000000400298 l    d  .gnu.hash    0000000000000000              .gnu.hash

00000000004002d8 l    d  .dynsym    0000000000000000              .dynsym

0000000000400cf8 l    d  .dynstr    0000000000000000              .dynstr

0000000000401b56 l    d  .gnu.version    0000000000000000              .gnu.version

0000000000401c30 l    d  .gnu.version_r    0000000000000000              .gnu.version_r

0000000000401d00 l    d  .rela.dyn    0000000000000000              .rela.dyn

00000000004022a0 l    d  .rela.plt    0000000000000000              .rela.plt

0000000000402618 l    d  .init    0000000000000000              .init

0000000000402640 l    d  .plt    0000000000000000              .plt

00000000004028a0 l    d  .plt.got    0000000000000000              .plt.got

00000000004028b0 l    d  .text    0000000000000000              .text

0000000000403ba4 l    d  .fini    0000000000000000              .fini

0000000000403bc0 l    d  .rodata    0000000000000000              .rodata

0000000000403f48 l    d  .qtversion    0000000000000000              .qtversion

0000000000403f58 l    d  .eh_frame_hdr    0000000000000000              .eh_frame_hdr

0000000000404108 l    d  .eh_frame    0000000000000000              .eh_frame

000000000040480c l    d  .gcc_except_table    0000000000000000              .gcc_except_table

0000000000604b48 l    d  .init_array    0000000000000000              .init_array

0000000000604b50 l    d  .fini_array    0000000000000000              .fini_array

0000000000604b58 l    d  .jcr    0000000000000000              .jcr

0000000000604b60 l    d  .data.rel.ro    0000000000000000              .data.rel.ro

0000000000604db0 l    d  .dynamic    0000000000000000              .dynamic

0000000000604ff0 l    d  .got    0000000000000000              .got

0000000000605000 l    d  .got.plt    0000000000000000              .got.plt

0000000000605140 l    d  .data    0000000000000000              .data

0000000000605158 l    d  .bss    0000000000000000              .bss

0000000000000000 l    d  .comment    0000000000000000              .comment

0000000000000000 l    d  .debug_aranges    0000000000000000              .debug_aranges

0000000000000000 l    d  .debug_info    0000000000000000              .debug_info

0000000000000000 l    d  .debug_abbrev    0000000000000000              .debug_abbrev

0000000000000000 l    d  .debug_line    0000000000000000              .debug_line

0000000000000000 l    d  .debug_str    0000000000000000              .debug_str

0000000000000000 l    d  .debug_ranges    0000000000000000              .debug_ranges

0000000000000000 l    df *ABS*    0000000000000000              crtstuff.c

0000000000604b58 l     O .jcr    0000000000000000              __JCR_LIST__

00000000004028e0 l     F .text    0000000000000000              deregister_tm_clones

0000000000402920 l     F .text    0000000000000000              register_tm_clones

0000000000402960 l     F .text    0000000000000000              __do_global_dtors_aux

0000000000605158 l     O .bss    0000000000000001              completed.7585

0000000000604b50 l     O .fini_array    0000000000000000              __do_global_dtors_aux_fini_array_entry

0000000000402980 l     F .text    0000000000000000              frame_dummy

0000000000604b48 l     O .init_array    0000000000000000              __frame_dummy_init_array_entry

0000000000000000 l    df *ABS*    0000000000000000              main.cpp

0000000000403bc4 l     O .rodata    0000000000000001              _ZStL19piecewise_construct

0000000000403bc8 l     O .rodata    0000000000000004              _ZN2QtL13UninitializedE

0000000000403bcc l     O .rodata    0000000000000001              _ZStL13allocator_arg

0000000000403bcd l     O .rodata    0000000000000001              _ZStL6ignore

0000000000403bd0 l     O .rodata    0000000000000004              _ZL8RGB_MASK

0000000000000000 l    df *ABS*    0000000000000000              mainwindow.cpp

0000000000403bd8 l     O .rodata    0000000000000001              _ZStL19piecewise_construct

0000000000403bdc l     O .rodata    0000000000000004              _ZN2QtL13UninitializedE

0000000000403be0 l     O .rodata    0000000000000001              _ZStL13allocator_arg

0000000000403be1 l     O .rodata    0000000000000001              _ZStL6ignore

0000000000403be4 l     O .rodata    0000000000000004              _ZL8RGB_MASK

0000000000000000 l    df *ABS*    0000000000000000              moc_mainwindow.cpp

0000000000403e00 l     O .rodata    0000000000000001              _ZStL19piecewise_construct

0000000000403e04 l     O .rodata    0000000000000004              _ZN2QtL13UninitializedE

0000000000403e08 l     O .rodata    0000000000000001              _ZStL13allocator_arg

0000000000403e09 l     O .rodata    0000000000000001              _ZStL6ignore

0000000000403e0c l     O .rodata    0000000000000004              _ZL8RGB_MASK

0000000000403e20 l     O .rodata    0000000000000070              _ZL29qt_meta_stringdata_MainWindow

0000000000403ea0 l     O .rodata    0000000000000054              _ZL23qt_meta_data_MainWindow

0000000000000000 l    df *ABS*    0000000000000000              crtstuff.c

0000000000404808 l     O .eh_frame    0000000000000000              __FRAME_END__

0000000000604b58 l     O .jcr    0000000000000000              __JCR_END__

0000000000000000 l    df *ABS*    0000000000000000              

0000000000605000 l     O .got.plt    0000000000000000              _GLOBAL_OFFSET_TABLE_

0000000000604db0 l     O .dynamic    0000000000000000              _DYNAMIC

0000000000604b50 l       .init_array    0000000000000000              __init_array_end

0000000000604b48 l       .init_array    0000000000000000              __init_array_start

0000000000403f58 l       .eh_frame_hdr    0000000000000000              __GNU_EH_FRAME_HDR

0000000000605150  w    O .data    0000000000000008              .hidden DW.ref.__gxx_personality_v0

0000000000403790  w    F .text    000000000000001a              _ZN19QBasicAtomicIntegerIiE5derefEv

00000000004029a6 g     F .text    00000000000000be              main

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget10leaveEventEP6QEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget4showEv@@Qt_5

0000000000000000       O *UND*    0000000000000000              qt_version_tag@@Qt_5.6

0000000000000000       F *UND*    0000000000000000              _ZN11QMainWindow16setCentralWidgetEP7QWidget@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget13keyPressEventEP9QKeyEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget10closeEventEP11QCloseEvent@@Qt_5

00000000004028b0 g     F .text    000000000000002a              _start

0000000000403ba4 g     F .fini    0000000000000000              _fini

0000000000402a64 g     F .text    00000000000000d4              _ZN10MainWindowC2EP7QWidget

0000000000000000       F *UND*    0000000000000000              _ZThn16_NK7QWidget6metricEN12QPaintDevice17PaintDeviceMetricE@@Qt_5

000000000040391a  w    F .text    0000000000000036              _ZN10QAtomicOpsIiE4loadIiEET_RKSt6atomicIS2_E

0000000000403674  w    F .text    0000000000000101              _ZN13Ui_MainWindow13retranslateUiEP11QMainWindow

0000000000000000       F *UND*    0000000000000000              _ZN7QObject16disconnectNotifyERK11QMetaMethod@@Qt_5

0000000000402f96  w    F .text    0000000000000046              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENUlvE1_4_FUNEv

0000000000000000       F *UND*    0000000000000000              _ZNK7QObject10objectNameEv@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget14setWindowTitleERK7QString@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZNK7QWidget17hasHeightForWidthEv@@Qt_5

0000000000403990 g     F .text    000000000000003d              .hidden _ZN10MainWindow18qt_static_metacallEP7QObjectN11QMetaObject4CallEiPPv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget13focusOutEventEP11QFocusEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              __stack_chk_fail@@GLIBC_2.4

0000000000402c30  w    F .text    0000000000000043              _ZN9QtPrivate8RefCount5derefEv

0000000000403aea  w    F .text    000000000000003e              _ZNK14QScopedPointerI11QObjectData21QScopedPointerDeleterIS0_EEptEv

0000000000402c94  w    F .text    000000000000001a              _ZN7QStringC2E14QStringDataPtr

0000000000402d70  w    F .text    0000000000000019              _ZN6QFlagsIN2Qt10WindowTypeEEC1EMNS2_7PrivateEi

0000000000605160 g       .bss    0000000000000000              _end

00000000004037aa  w    F .text    0000000000000025              _ZN15QTypedArrayDataItE10deallocateEP10QArrayData

00000000004030e2  w    F .text    0000000000000046              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENUlvE3_4_FUNEv

0000000000604d98  w    O .data.rel.ro    0000000000000018              _ZTI10MainWindow

0000000000000000       F *UND*    0000000000000000              _ZN10QArrayData10deallocateEPS_mm@@Qt_5

0000000000000000       F *UND*    0000000000000000              mono_jit_init

0000000000000000       F *UND*    0000000000000000              _ZN12QApplicationC1ERiPPci@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZdlPv@@GLIBCXX_3.4

0000000000000000       F *UND*    0000000000000000              _ZN11QMainWindow16contextMenuEventEP17QContextMenuEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget14dragEnterEventEP15QDragEnterEvent@@Qt_5

0000000000402bd2 g     F .text    0000000000000043              _ZN10MainWindow21on_pushButton_clickedEv

0000000000000000       F *UND*    0000000000000000              _ZNK7QWidget11paintEngineEv@@Qt_5

0000000000402ef0  w    F .text    0000000000000046              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENUlvE0_4_FUNEv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget15mousePressEventEP11QMouseEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZNK7QWidget13sharedPainterEv@@Qt_5

0000000000403896  w    F .text    0000000000000042              _ZNK17QStaticStringDataILi11EE8data_ptrEv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget11actionEventEP12QActionEvent@@Qt_5

0000000000403082  w    F .text    0000000000000060              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE3_clEv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget9hideEventEP10QHideEvent@@Qt_5

0000000000402d20  w    F .text    000000000000004f              _ZN5QRectC1Eiiii

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget9showEventEP10QShowEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _Unwind_Resume@@GCC_3.0

0000000000000000       F *UND*    0000000000000000              _ZThn16_NK7QWidget11initPainterEP8QPainter@@Qt_5

0000000000000000       O *UND*    0000000000000000              _ZTI11QMainWindow@@Qt_5

0000000000402cc8  w    F .text    0000000000000034              _ZN7QStringD1Ev

00000000004038d8  w    F .text    0000000000000042              _ZNK17QStaticStringDataILi9EE8data_ptrEv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget12focusInEventEP11QFocusEvent@@Qt_5

0000000000403d80 u     O .rodata    0000000000000030              _ZZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE3_clEvE15qstring_literal

0000000000000000       F *UND*    0000000000000000              _ZNK7QWidget15minimumSizeHintEv@@Qt_5

0000000000403b30 g     F .text    0000000000000065              __libc_csu_init

0000000000403776  w    F .text    000000000000001a              _ZNK19QBasicAtomicIntegerIiE4loadEv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget10paintEventEP11QPaintEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget13dragMoveEventEP14QDragMoveEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZNK7QWidget7devTypeEv@@Qt_5

0000000000402bcc g     F .text    0000000000000006              _ZThn16_N10MainWindowD0Ev

0000000000000000       F *UND*    0000000000000000              _ZN11QMetaObject18connectSlotsByNameEP7QObject@@Qt_5

0000000000402cae  w    F .text    0000000000000019              _ZNK7QString7isEmptyEv

0000000000403128  w    F .text    0000000000000060              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE4_clEv

00000000004037d0  w    F .text    0000000000000042              _ZNK17QStaticStringDataILi10EE8data_ptrEv

0000000000000000       F *UND*    0000000000000000              _ZNK7QWidget16inputMethodQueryEN2Qt16InputMethodQueryE@@Qt_5

0000000000605140  w      .data    0000000000000000              data_start

0000000000000000       F *UND*    0000000000000000              _ZN8QMenuBarC1EP7QWidget@@Qt_5

0000000000605148 g     O .data    0000000000000000              .hidden __dso_handle

0000000000000000       F *UND*    0000000000000000              _ZThn16_NK7QWidget7devTypeEv@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZNK11QObjectData17dynamicMetaObjectEv@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZNK7QWidget14heightForWidthEi@@Qt_5

0000000000402b38 g     F .text    0000000000000067              _ZN10MainWindowD2Ev

0000000000000000       F *UND*    0000000000000000              _ZN11QMainWindow15createPopupMenuEv@@Qt_5

0000000000000000  w      *UND*    0000000000000000              _ITM_registerTMCloneTable

0000000000000000       F *UND*    0000000000000000              _ZThn16_NK7QWidget10redirectedEP6QPoint@@Qt_5

0000000000402cfc  w    F .text    0000000000000024              _ZN5QSizeC1Eii

0000000000604b60 g     O .data.rel.ro    0000000000000030              _ZN10MainWindow16staticMetaObjectE

0000000000403bc0 g     O .rodata    0000000000000004              _IO_stdin_used

0000000000000000       F *UND*    0000000000000000              _ZN16QCoreApplication9translateEPKcS1_S1_i@@Qt_5

0000000000403cc0 u     O .rodata    0000000000000038              _ZZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE0_clEvE15qstring_literal

0000000000604b90  w    O .data.rel.ro    0000000000000208              _ZTV10MainWindow

0000000000000000       F *UND*    0000000000000000              _ZN11QMainWindow10addToolBarEN2Qt11ToolBarAreaEP8QToolBar@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZNK7QWidget6metricEN12QPaintDevice17PaintDeviceMetricE@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN11QMainWindow12setStatusBarEP10QStatusBar@@Qt_5

0000000000605158 g     O .data    0000000000000000              .hidden __TMC_END__

0000000000000000       F *UND*    0000000000000000              _ZN15QAbstractButton7setTextERK7QString@@Qt_5

000000000040303c  w    F .text    0000000000000046              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENUlvE2_4_FUNEv

0000000000403188  w    F .text    0000000000000046              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENUlvE4_4_FUNEv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget11tabletEventEP12QTabletEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _Znwm@@GLIBCXX_3.4

0000000000000000       F *UND*    0000000000000000              _ZThn16_NK7QWidget13sharedPainterEv@@Qt_5

0000000000000000  w      *UND*    0000000000000000              _ITM_deregisterTMCloneTable

0000000000403ba0 g     F .text    0000000000000002              __libc_csu_fini

0000000000000000       F *UND*    0000000000000000              _ZN7QObject10timerEventEP11QTimerEvent@@Qt_5

0000000000402b38 g     F .text    0000000000000067              _ZN10MainWindowD1Ev

0000000000403d00 u     O .rodata    0000000000000030              _ZZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE1_clEvE15qstring_literal

0000000000605140 g       .data    0000000000000000              __data_start

0000000000605158 g       .bss    0000000000000000              __bss_start

0000000000403c80 u     O .rodata    0000000000000030              _ZZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE_clEvE15qstring_literal

0000000000000000       F *UND*    0000000000000000              _ZN11QMainWindow10setMenuBarEP8QMenuBar@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QObject10childEventEP11QChildEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget18focusNextPrevChildEb@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN11QPushButtonC1EP7QWidget@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget11resizeEventEP12QResizeEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget14dragLeaveEventEP15QDragLeaveEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN11QMainWindowC2EP7QWidget6QFlagsIN2Qt10WindowTypeEE@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN10QStatusBarC1EP7QWidget@@Qt_5

0000000000000000  w      *UND*    0000000000000000              __gmon_start__

0000000000403970  w    F .text    000000000000001f              _ZNSt13__atomic_baseIiEmmEv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidgetC1EPS_6QFlagsIN2Qt10WindowTypeEE@@Qt_5

0000000000402c94  w    F .text    000000000000001a              _ZN7QStringC1E14QStringDataPtr

0000000000000000       F *UND*    0000000000000000              _ZNK7QWidget10redirectedEP6QPoint@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZThn16_NK7QWidget11paintEngineEv@@Qt_5

0000000000000000       F *UND*    0000000000000000              mono_config_parse

0000000000402c1c  w    F .text    0000000000000014              _ZStanSt12memory_orderSt23__memory_order_modifier

0000000000402cfc  w    F .text    0000000000000024              _ZN5QSizeC2Eii

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget9moveEventEP10QMoveEvent@@Qt_5

0000000000403a66 g     F .text    0000000000000084              _ZN10MainWindow11qt_metacallEN11QMetaObject4CallEiPPv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget10enterEventEP6QEvent@@Qt_5

0000000000605158 g       .data    0000000000000000              _edata

0000000000402d8a  w    F .text    000000000000005f              _ZN7QWidget6resizeEii

0000000000000000       F *UND*    0000000000000000              _ZN7QObject11customEventEP6QEvent@@Qt_5

0000000000402fdc  w    F .text    0000000000000060              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE2_clEv

0000000000000000       F *UND*    0000000000000000              __gxx_personality_v0@@CXXABI_1.3

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget10wheelEventEP11QWheelEvent@@Qt_5

0000000000402c74  w    F .text    0000000000000020              _ZNK9QtPrivate8RefCount8isStaticEv

0000000000000000  w      *UND*    0000000000000000              _Jv_RegisterClasses

0000000000403f38  w    O .rodata    000000000000000d              _ZTS10MainWindow

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget16inputMethodEventEP17QInputMethodEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget15keyReleaseEventEP9QKeyEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QObject11eventFilterEPS_P6QEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget14mouseMoveEventEP11QMouseEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget9dropEventEP10QDropEvent@@Qt_5

00000000004031ce  w    F .text    00000000000004a6              _ZN13Ui_MainWindow7setupUiEP11QMainWindow

0000000000000000       F *UND*    0000000000000000              _ZN12QApplication4execEv@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QObject13setObjectNameERK7QString@@Qt_5

0000000000402c15  w    F .text    0000000000000007              _Z7qt_noopv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget17mouseReleaseEventEP11QMouseEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget11nativeEventERK10QByteArrayPvPl@@Qt_5

0000000000402dea  w    F .text    0000000000000060              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE_clEv

0000000000402618 g     F .init    0000000000000000              _init

0000000000000000       F *UND*    0000000000000000              _ZNK7QWidget11initPainterEP8QPainter@@Qt_5

0000000000402ba6 g     F .text    0000000000000026              _ZN10MainWindowD0Ev

0000000000000000       F *UND*    0000000000000000              strcmp@@GLIBC_2.2.5

0000000000402e90  w    F .text    0000000000000060              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE0_clEv

0000000000000000       F *UND*    0000000000000000              _Z9qt_assertPKcS0_i@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget6resizeERK5QSize@@Qt_5

0000000000402cc8  w    F .text    0000000000000034              _ZN7QStringD2Ev

0000000000402a64 g     F .text    00000000000000d4              _ZN10MainWindowC1EP7QWidget

0000000000000000       O *UND*    0000000000000000              _ZTVN10__cxxabiv120__si_class_type_infoE@@CXXABI_1.3

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget11changeEventEP6QEvent@@Qt_5

0000000000403812  w    F .text    0000000000000042              _ZNK17QStaticStringDataILi13EE8data_ptrEv

0000000000000000       F *UND*    0000000000000000              __libc_start_main@@GLIBC_2.2.5

0000000000402b9f g     F .text    0000000000000006              _ZThn16_N10MainWindowD1Ev

0000000000000000       F *UND*    0000000000000000              _ZN11QMainWindowD2Ev@@Qt_5

00000000004039ce g     F .text    0000000000000048              _ZNK10MainWindow10metaObjectEv

0000000000402e4a  w    F .text    0000000000000046              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENUlvE_4_FUNEv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget11setGeometryERK5QRect@@Qt_5

0000000000402f36  w    F .text    0000000000000060              _ZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE1_clEv

0000000000402d20  w    F .text    000000000000004f              _ZN5QRectC2Eiiii

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget21mouseDoubleClickEventEP11QMouseEvent@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN7QObject13connectNotifyERK11QMetaMethod@@Qt_5

0000000000403a16 g     F .text    0000000000000050              _ZN10MainWindow11qt_metacastEPKc

0000000000000000       F *UND*    0000000000000000              _ZN12QApplicationD1Ev@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN11QMainWindow5eventEP6QEvent@@Qt_5

0000000000403950  w    F .text    000000000000001f              _ZN10QAtomicOpsIiE5derefIiEEbRSt6atomicIT_E

0000000000403d40 u     O .rodata    0000000000000028              _ZZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE2_clEvE15qstring_literal

0000000000403dc0 u     O .rodata    0000000000000030              _ZZZN13Ui_MainWindow7setupUiEP11QMainWindowENKUlvE4_clEvE15qstring_literal

0000000000000000       F *UND*    0000000000000000              mono_set_dirs

0000000000000000       F *UND*    0000000000000000              _ZN11QMainWindow11qt_metacastEPKc@@Qt_5

0000000000000000       O *UND*    0000000000000000              _ZN11QMainWindow16staticMetaObjectE@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZNK7QWidget8sizeHintEv@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN8QToolBarC1EP7QWidget@@Qt_5

0000000000403854  w    F .text    0000000000000042              _ZNK17QStaticStringDataILi7EE8data_ptrEv

0000000000000000       F *UND*    0000000000000000              _ZN7QWidget10setVisibleEb@@Qt_5

0000000000000000       F *UND*    0000000000000000              _ZN11QMainWindow11qt_metacallEN11QMetaObject4CallEiPPv@@Qt_5

 

Now, let’s turn our attention to Windows. Visual Studio installs DumpBin that allows to examine COFF object files, executable (PE as mentioned earlier) and dynamic-link libraries (DLL). One of the many switches it’s got is “/dependents” that produces the  following output (in this case, we’re dumping Explorer.exe). if we wanted, we could have also dumped the exported symbols in a dynamic-library, for instance.

 

Microsoft (R) COFF/PE Dumper Version 14.00.23506.0

Copyright (C) Microsoft Corporation.  All rights reserved.

 

 

Dump of file explorer.exe

 

File Type: EXECUTABLE IMAGE

 

  Image has the following delay load dependencies:

 

    netutils.dll

    wkscli.dll

    api-ms-win-security-sddl-l1-1-0.dll

    CRYPTSP.dll

    ole32.dll

    CFGMGR32.dll

    WINTRUST.dll

    Bcp47Langs.dll

    WINSTA.dll

    OLEACC.dll

    DUser.dll

    DUI70.dll

    SndVolSSO.DLL

    WinLangdb.dll

    MFPlat.DLL

    MF.dll

    SETTINGSYNCPOLICY.dll

    wlanapi.dll

    AppXAllUserStore.dll

    api-ms-win-appmodel-state-l1-2-0.dll

    ext-ms-win-ntuser-draw-l1-1-1.dll

    ext-ms-win-ntuser-draw-l1-1-2.dll

    ext-ms-win-rtcore-ntuser-window-ext-l1-1-0.dll

    api-ms-win-core-winrt-propertysetprivate-l1-1-1.dll

    api-ms-win-core-biplmapi-l1-1-1.dll

    dsreg.dll

    ext-ms-onecore-appmodel-veventdispatcher-l1-1-0.dll

    SystemEventsBrokerClient.dll

    api-ms-win-service-management-l1-1-0.dll

    api-ms-win-service-winsvc-l1-2-0.dll

    WINMM.dll

    UIAutomationCore.DLL

    SLC.dll

    XmlLite.dll

    api-ms-win-security-provider-l1-1-0.dll

 

  Image has the following dependencies:

 

    msvcrt.dll

    api-ms-win-core-libraryloader-l1-2-0.dll

    OLEAUT32.dll

    api-ms-win-eventing-provider-l1-1-0.dll

    api-ms-win-core-processthreads-l1-1-2.dll

    api-ms-win-core-debug-l1-1-1.dll

    api-ms-win-core-localization-l1-2-1.dll

    api-ms-win-core-synch-l1-2-0.dll

    api-ms-win-core-threadpool-l1-2-0.dll

    api-ms-win-core-com-l1-1-1.dll

    api-ms-win-core-errorhandling-l1-1-1.dll

    api-ms-win-core-handle-l1-1-0.dll

    api-ms-win-core-sysinfo-l1-2-1.dll

    api-ms-win-core-synch-l1-2-1.dll

    api-ms-win-core-registry-l1-1-0.dll

    api-ms-win-core-heap-l2-1-0.dll

    api-ms-win-core-winrt-string-l1-1-0.dll

    api-ms-win-core-heap-l1-2-0.dll

    api-ms-win-core-string-l1-1-0.dll

    api-ms-win-eventing-classicprovider-l1-1-0.dll

    api-ms-win-core-processenvironment-l1-2-0.dll

    api-ms-win-security-base-l1-2-0.dll

    api-ms-win-power-base-l1-1-0.dll

    api-ms-win-core-libraryloader-l1-2-1.dll

    api-ms-win-core-string-l2-1-1.dll

    api-ms-win-core-path-l1-1-0.dll

    api-ms-win-core-timezone-l1-1-0.dll

    api-ms-win-core-file-l1-2-1.dll

    api-ms-win-core-winrt-l1-1-0.dll

    api-ms-win-core-datetime-l1-1-1.dll

    api-ms-win-core-psapi-l1-1-0.dll

    api-ms-win-core-util-l1-1-0.dll

    api-ms-win-core-memory-l1-1-2.dll

    api-ms-win-core-interlocked-l1-2-0.dll

    api-ms-win-core-profile-l1-1-0.dll

    ntdll.dll

    api-ms-win-core-job-l2-1-0.dll

    api-ms-win-core-kernel32-private-l1-1-1.dll

    api-ms-win-core-registryuserspecific-l1-1-0.dll

    api-ms-win-core-com-private-l1-1-0.dll

    api-ms-win-core-atoms-l1-1-0.dll

    api-ms-win-core-url-l1-1-0.dll

    KERNEL32.dll

    USER32.dll

    GDI32.dll

    SHCORE.dll

    SHLWAPI.dll

    SHELL32.dll

    PROPSYS.dll

    UxTheme.dll

    dwmapi.dll

    TWINAPI.dll

    combase.dll

    d3d11.dll

    dcomp.dll

    api-ms-win-core-biptcltapi-l1-1-1.dll

    api-ms-win-core-biptcltapi-l1-1-3.dll

    api-ms-win-core-string-l2-1-0.dll

    SspiCli.dll

    api-ms-win-security-lsalookup-l2-1-1.dll

    api-ms-win-core-winrt-error-l1-1-1.dll

    api-ms-win-core-registry-l1-1-1.dll

    api-ms-win-core-io-l1-1-1.dll

    api-ms-win-eventing-controller-l1-1-0.dll

    api-ms-win-core-errorhandling-l1-1-3.dll

    USERENV.dll

    api-ms-win-core-file-l2-1-2.dll

    api-ms-win-service-management-l2-1-0.dll

    CRYPT32.dll

    api-ms-win-core-delayload-l1-1-1.dll

    api-ms-win-core-sidebyside-l1-1-0.dll

    api-ms-win-security-lsalookup-l1-1-2.dll

    MrmCoreR.dll

    api-ms-win-core-apiquery-l1-1-0.dll

    RPCRT4.dll

    profapi.dll

    api-ms-win-security-lsalookup-l1-1-1.dll

 

  Summary

 

        3000 .data

        1000 .didat

        8000 .idata

        1000 .imrsiv

       1A000 .reloc

      1EA000 .rsrc

      1CD000 .text

 

There is also a “visual” dependency walker that can be downloaded from here.  It provides the same functionality as DumpBin but it’s got a much nicer and better user interface. It provides access to Exported symbols too. The image depicted below belongs to GDI32.DLL

 

image

 

Happy coding!

 

Angel

Building smart and self-configurable Windows Services

Hi Community,

This post is about a question that many of us as architects and developers tend to ask ourselves, how can we build reliable yet flexible software that has the ability to adapt itself on the fly based on configuration changes? There are a few approaches to this, but Today I’d like to share a recent implementation I had to architect and pretty much build for one of my clients. My customer’s requirements comprised a web front-end built on ASP.NET MVC in conjunction with JQuery and JavaScript, since my customer did not have any Web Services and due to the delivery nature of this solution as an MVP, the orchestration and integration with backend (Database and Tableau) was responsibility of a Windows Service (Daemon) that can operate in two modes: 1) File operations manager and 2) “message” broker. At the same time, the daemon must be able to identify changes made to its configuration, stop itself, apply changes and restart itself all of this without any user interaction, this sounds cool, right?

In order to understand, how we can accomplish this it’s important to describe the foundational aspects of it or what is going to drive this behaviour. In this case, the daemon’s configuration.

<?xml version="1.0" encoding="utf-8" ?>

<ExecutionDaemon FileDelimiter="~" xmlns="http://schemas.bonafideideas.com/2016/ExecutionDaemon">

    <BrokerRole LocalFolderPath="" />

    <FileManagerRole LocalFolderPath="" RemoteFolderPath="" FileExtensionToBeProcessed="*.csv;*.xml"/>

    <ExternalToolPostExecution ImagePath="" Arguments="" DeleteFileOnCompletion="true" />

</ExecutionDaemon>

To support this functionality it was required to create a few classes that inherit from CustomConfiguration and ConfigurationElement. Config files can be written to/read from at runtime, but I needed to have something in place to handled this event and so there’s FileSystemWatcher. The code responsible for initializing the daemon, reload and apply changes and reconfiguration are listed below

/// <summary>

/// Initializes a new instance of the <see cref="ExecutionDaemon" /> class.

/// </summary>

/// <param name="args">The arguments.</param>

public ExecutionDaemon(string[] args) {

    _startArguments = args;

    ServiceName = Constants.ServiceName;

    _components = new System.ComponentModel.Container();

    Bootstrapper.Run(InitializeTypeContainer); // Take care of IoC plumbing and similar

    _configurationReader = TypeContainer.Resolve<IConfigurationReader>();

    var svcHomeDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

    _configFileMonitor = new FileSystemWatcher(svcHomeDir) {

        EnableRaisingEvents = true, Filter = Constants.ConfigFileExtension,

        NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size

    };

    _configFileMonitor.Changed += (s, e) => ReloadAndApplyConfigChanges();

    Configure();

}


/// <summary>

/// Reloads the and apply configuration changes.

/// </summary>

private void ReloadAndApplyConfigChanges() {

    _logger.Log(Constants.PreReconfigureMessage);

    _configurationReader.Refresh();

    OnStop();

    Configure();

    OnStart(_startArguments);

    _logger.Log(Constants.PostReconfigureMessage);

}


/// <summary>

/// Configures this instance.

/// </summary>

private void Configure() {

    try {

        _mainWatcher?.Dispose();

        _isFileManagerMode = _startArguments?.Contains(Constants.FileManagerMode);

        var config = (CustomConfigSectionReader)_configurationReader.Configuration;

        _mainWatcher = new FileSystemWatcher(_isFileManagerMode.HasValue && _isFileManagerMode.Value ?

                                             config.FileManagerRole.LocalFolderPath :

                                             config.BrokerRole.LocalFolderPath) {

            EnableRaisingEvents = true,

            NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size

        };


        _mainWatcher.Changed += (s, e) => ProcessFileRequest(s, e);

    } catch (Exception ex) {

        _logger.Log(ex);

        _logger.Log(Constants.ConfigurationErrorStopServiceMessage);

        OnStop();

    }

}

it’s important to note the fact that I use NotifyFilter property, reason being is that FileSystemWatcher might “misfire” the Changed event, and in order to prevent this from happening we instruct the FileSystemWatcher to fire events only when (NotifyFilters.FileName | NotifyFilters.Size) have changed. If we make changes to the config file while the service is running, Eventvwr will inform of such changes (assuming Eventvwr had been cleared in advanced)

 

image

image

image image

 

As mentioned before, this Windows Service or Daemon has got dual execution mode, which means same executable but depending on the arguments it behaves and operates differently. The values entered in service property page (Start parameters) are not persisted, so I’ve got no idea why having that in the UI if it doesn’t do anything

 

image

 

but anyways we can set this parameters and make them “persistable” if we add them to the Registry in the following key – HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ExecutionDaemon

 

image

 

and voila! We have our dual operation Windows service deployed on two different servers and customers don’t have to deploy or start/stop anything but only make changes in the configuration file.

 

Regards,

 

Angel

LightIoC– Another lightweight IoC Library

Hi Community,

Today’s post is about an IoC library I wrote back in 2013. I called it “LightIoc” because it’s self-contained in a single assembly file. Over the years I have used a few IoC libraries some of them are easy to use, others a bit bulkier or with more dependant assemblies but for this one I added a few custom features as mentioned below:

  • Pre-jitting of assemblies to improve start-up performance. This is configuration driven
  • Discovery of assemblies at start-up
  • Configuration and registration of types via code or config files
  • Lifespan of objects, in other words, objects that are required to do something then are self-disposed
///////////////////////

// Sample config file//

///////////////////////

 

<configuration>

    <configSections>

        <section name="LightIoC" allowLocation="false" allowDefinition="Everywhere"

                         type="LightIoC.Configuration.ConfigReader, LightIoC, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>

    </configSections>

    <LightIoC>

        <Pre-Jitting enabled="true"/>

        <registrations>

            <register name="IDispatchMessageInspector" />

            <register name="ITestLib" type="WebAPI.Core.Interfaces.ITestLib" assemblyFQN="WebAPI.Core.Interfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23e52edaf0ea7bd4"  />

            <register name="IWebApiLogger" type="WebAPI.Core.Interfaces.IWebApiLogger" assemblyFQN="WebAPI.Core.Interfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23e52edaf0ea7bd4"  />

            <register name="IWebApiPerfCounter" type="WebAPI.Core.Interfaces.IWebApiLogger" assemblyFQN="WebAPI.Core.Interfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23e52edaf0ea7bd4"  />

        </registrations>

        <mappings>

            <map abstraction="IWebApiLogger" to="DefaultLogger" instanceRequired="true" />

            <map abstraction="IWebApiPerfCounter" to="WebApiPerfCounter" instanceRequired="true" />

            <map abstraction="IDispatchMessageInspector" to="MessageInspector" instanceRequired="true" />

            <map abstraction="ITestLib" to="ThisIsADestroyableClass" instanceRequired="true" lifeSpan="300" />

        </mappings>

    </LightIoC>

</configuration?

Alternatively I could have also registered my type container in code, as shown. Type registration can take a Type as such or also the name of the type as string, hence validation occurs at initialization of the container.

public void InitializeTypeContainer<T>() where T: System.ComponentModel.Component {

            if (!TypeContainerInitialized) {

                TypeContainer.Current.RegisterType<IUnifiedShell>("UnifiedShell");

                TypeContainer.Current.RegisterType<IShellConfig>("ShellConfig");

                TypeContainer.Current.RegisterType<ILogger>("Logger", true);

                TypeContainer.Current.RegisterType<IAgentService>("AgentService");

                TypeContainer.Current.RegisterType<IAutomationResult>("AutomationResult");

                TypeContainer.Current.RegisterType<IAutomationRequest>("AutomationRequest");

                TypeContainer.Current.RegisterType<IPerfCounters>("PerfCounters", true);

                TypeContainer.Current.RegisterType<IServiceOperation>("ServiceOperation");

                TypeContainer.Current.RegisterType<IServiceInformation>("CustomServiceBase", true);

                TypeContainer.Current.RegisterType<IBaseService<T>>(typeof(T).Name, true);

                TypeContainer.Current.RegisterType<IAutomationEngine>(TypeContainer.Current.Resolve<IBaseService<T>>());

            }

}

 

I hope you find this useful. Source code is available here

 

Regards,

 

Angel

Binary Palindrome check in C#

Hi Community,

This post is about how to check whether the binary representation of a byte is a palindrome. I’ve recently received an email by a fellow developer requesting some help with this, and after having done a bit of research I couldn’t find any good example written in C#, so I decided to do it and share it with you. Similar to any other programming task and faithful to the phrase “There’s more than a way to skin a cat”, there are at least a couple of ways to do this via:

 

 

For the sake of clarity and completeness, I have written a code snippet to demonstrate both approaches, and they were implemented as extension methods for byte type as shown below

 

public static class Binary {

    /// <summary>

    /// Gets the bits.

    /// </summary>

    /// <param name="a">a.</param>

    /// <returns></returns>

    public static string GetBits(this byte a) {

        var retval = string.Empty;

 

        if (a > 0) {

            var buffer = new StringBuilder();

            var bitArray = new BitArray(new[] { a });

 

            for (var index = bitArray.Count - 1; index >= 0; index--)

                buffer.Append(bitArray[index] ? "1" : "0");

 

            retval = buffer.ToString().Substring(buffer.ToString().IndexOf("1", StringComparison.OrdinalIgnoreCase));

 

        }

 

        return retval;

    }

 

 

    /// <summary>

    /// Determines whether [is palindrome with string op].

    /// </summary>

    /// <param name="a">a.</param>

    /// <returns></returns>

    public static bool IsPalindromeWithStringOp(this byte a) {

        var retval = false;

        var bitStr = GetBits(a);

 

        if (!string.IsNullOrEmpty(bitStr)) {

            var reversed = new string(bitStr.Reverse().ToArray());

            retval = reversed == bitStr;

        }

 

        return retval;

    }

 

    /// <summary>

    /// Determines whether [is palindrome with bit ops].

    /// </summary>

    /// <param name="a">a.</param>

    /// <returns></returns>

    public static bool IsPalindromeWithBitOps(this byte a) {

        var retval = false;

 

        if (a > 0) {

            var leftShift = 0;

            var tempValue = (int)a;

 

            while (tempValue != 0) {

                leftShift = leftShift << 1;

                var bitCheck = tempValue & 1;

                tempValue = tempValue >> 1;

                leftShift = leftShift | bitCheck;

            }

            retval = (leftShift ^ a) == 0;

        }

 

        return retval;

    }

 

Both methods are invoked

var values = new byte[] { 9, 98, 17 };

 

Array.ForEach(values, b => Console.WriteLine($"Number {b} has the following bits {b.GetBits()} - IsPalindromeWithStringOp:{b.IsPalindromeWithStringOp()} |IsPalindromeWithBitOps:{b.IsPalindromeWithBitOps()} "));

 

Console.ReadLine();

 

to check they return the same expected result

image

 

Regards,

 

Angel

Remove unwanted HTTP response headers and enable HSTS on IIS

Hi Community,

I don’t consider myself a security specialist (or specialist at anything for that matter but a generalist instead). I am currently architecting a Web solution for one of my clients, and they came to me with a requirement “review, assess and rectify security vulnerabilities” on an existing Web application. it’s not what I often do, and the new application will integrate into the existing, so I agreed on helping them with this requirement and in doing so this will help me understand the existing solution.  The vulnerabilities I had to address were:

  • Remove common IIS/ASP.NET headers
  • Enable HTTP Strict Transport Security (HSTS)

In order to get started, I needed to download the “URL Rewrite” module for IIS, then create a few outbound rules. The resulting web.config file were then checked in to TFS and ready to be used when deploying to a different environment (e.g.: UAT). Below the rules created in IIS, as well as the generated config file with these rules

 

image

 

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

  <system.webServer>

    <rewrite>

      <outboundRules>

        <rule name="Add Strict-Transport-Security when HTTPS" enabled="true">

          <match serverVariable="RESPONSE_Strict_Transport_Security" pattern=".*" />

          <conditions>

            <add input="{HTTPS}" pattern="on" ignoreCase="true" />

          </conditions>

          <action type="Rewrite" value="max-age=31536000" />

        </rule>

        <rule name="Remove Server">

          <match serverVariable="RESPONSE_SERVER" pattern=".*" />

          <action type="Rewrite" value="IIS" />

        </rule>

        <rule name="Remove X-Powered-By">

          <match serverVariable="RESPONSE_X-POWERED-BY" pattern=".*" />

          <action type="Rewrite" />

        </rule>

        <rule name="Remove ASPNET Version">

          <match serverVariable="RESPONSE_X-ASPNET-VERSION" pattern=".*" />

          <action type="Rewrite" />

        </rule>

        <rule name="Remove ASPNET MVC Version">

          <match serverVariable="RESPONSE_X-ASPNETMVC-VERSION" pattern=".*" />

          <action type="Rewrite" />

        </rule>

      </outboundRules>

    </rewrite>

  </system.webServer>

</configuration>

 

An interesting article on the subject can be found here on MSDN

 

Regards,

 

Angel

My ramblings on computers and architecture