Artistic Style Developer Information

Calling Artistic Style from a C# Program

 

Artistic Style can be compiled as a shared library (DLL) and called from a C# program.

A C# program compiled with "Any CPU" can be run on any platform. Artistic Style, however, is a C++ program and must be compiled for the platform on which it will run. Once this is done Artistic Style can be called from a C# program.

Compile Options

To compile Artistic Style for use with a C# program the compile option ASTYLE_LIB must be defined. Then it will accept the files and options as parameters from a function call instead of the command line. It is the responsibility of the calling program to read the source files and accept the options from the user via a graphical interface or other method. These are then passed via the function call described below. After the source files are formatted they will be returned to the calling program, which must then save the source file and take other appropriate action.

AStyleMainUtf16 Method

This method is called to format the source code. Wide strings ARE used here.

Syntax

    /// AStyleMainUtf16 DllImport.
    /// Cannot use string as a return value because Mono runtime will attempt to
    /// free the returned pointer resulting in a runtime crash.
    /// NOTE: CharSet.Unicode and wide strings ARE used here.
    [DllImport(dllName, CharSet = CharSet.Unicode)]
    private static extern IntPtr AStyleMainUtf16(
        [MarshalAs(UnmanagedType.LPWStr)] string textIn,
        [MarshalAs(UnmanagedType.LPWStr)] string options,
        AStyleErrorDelgate AStyleError,
        AStyleMemAllocDelgate AStyleMemAlloc
    );

Parameters

textIn
A wide-string pointer to the source file to be formatted.

options
A wide-string pointer to the formatting options. They should be in the same format as in the default options file. The options may be set apart by new-lines, commas, tabs or spaces. The long options do not need the "--" prefix. Comments may be used but they must be terminated by a new-line "\n" character.

If the file is not a C/C++ file, the file mode option "mode=java" or "mode=cs" must be included. Otherwise the default mode of C/C++ is used.

AStyleError
A delegate containing the error handling function. If there are errors in the parameters textIn or options, this function is called. See the following section on Callback Methods for more information.

AStyleMemAlloc
A delegate to the memory allocation function. The calling program must allocate memory for the output source file. See the following section on Callback Methods for more information.

The calling program is responsible for freeing the allocated memory when it is no longer needed.

Return Value

If the function succeeds, the return value is an IntPtr to the formatted source code. This function returns a 16-bit Unicode string. The IntPtr should be converted to a C# string using Marshal.PtrToStringUni.

If the function fails, the return value is a pointer to an empty string. Before the pointer is returned, an error message will be sent to the error handling function.

This function typically fails for one of the following reasons:

The function will NOT fail for an invalid option in the formatting options. In this case an error message is sent to the error handling function and the formatted source code is returned without using the invalid option.

Remarks

The calling program is responsible for freeing the memory allocated by memAllocFunc when it is no longer needed.

The call to AStyleMainUtf16 should be placed in a try/catch block that contains a DllNotFoundException to handle a missing shared library and an EntryPointNotFoundException to handle not finding the entry point. A BadImageFormatException will be thrown if the shared object (dll) does not use the same bit configuration (32 or 64) as the calling program.

Callback Methods

Callback methods must be delegated for the call to AStyleMainUtf16. Wide strings are NOT used here.

Syntax

    /// AStyleMainUtf16 callbacks.
    /// NOTE: Wide strings are NOT used here.
    private delegate void AStyleErrorDelgate(
        int errorNum,
        [MarshalAs(UnmanagedType.LPStr)] string error
    );
    private delegate IntPtr AStyleMemAllocDelgate(int size);

Remarks

The delegates AStyleErrorDelegate and AStyleMemAllocDelegate delegate the error handling and memory allocation methods. These methods process any errors and allocate the required memory returned from AStyleMainUtf16. The delegates are similar to C++ function pointers but are type safe.

AStyleErrorDelgate
AStyleError is called if there are errors in the string parameters in the call to AStyleMainUtf16. It should display an error message and then either abort or continue the program depending on the error. The first parameter is a number identifying the error. The second parameter is a pointer to a standard error message. The error message is Not Unicode. The message is deleted when the error handler returns.

Error messages numbered 100-199 are errors that prevent the file from being formatted. A NULL pointer is returned to the calling program. Error messages numbered 200-299 are errors that do NOT prevent the file from being formatted. A valid string containing a formatted file is returned. This will occur if an invalid option is sent to AStyleMainUtf16. The calling program has the option of accepting or rejecting the formatted file.

AStyleMemAllocDelgate
AStyleMemAlloc is the memory allocation method. The program must allocate memory for the output source file. This function will be called when the memory is needed. The parameter is the amount of memory that should be allocated in number of char's.

Memory should be allocated using Marshal.AllocHGlobal. With this method, if there is an allocation exception, a NULL pointer will be sent to AStyleMainUtf16. Artistic Style will generate an error message and return an empty string from the AStyleMainUtf16 function. The calling program can handle the exception at this point rather than in the memory allocation function. See the following example program for the procedure.

The calling program is responsible for freeing the memory allocated by memAllocFunc when it is no longer needed.

AStyleGetVersion Method

This method is called to get the Artistic Style version number.

Syntax

[DllImport(dllName)]
private static extern IntPtr AStyleGetVersion();

Return Value

The return value is an IntPtr to the Artistic Style version number. This function does NOT return Unicode. The IntPtr should be converted to a C# string using Marshal.PtrToStringAnsi.

Remarks

The call to AStyleGetVersion should be placed in a try/catch block that contains a DllNotFoundException to handle a missing shared library and an EntryPointNotFoundException to handle not finding the entry point. A BadImageFormatException will be thrown if the shared object (dll) does not use the same bit configuration (32 or 64) as the calling program.

Example

The following example formats source files by calling the Artistic Style formatter. It is a console application, but the procedure for a GUI is the same. The two classes can be copied and pasted into source files. Or they can be downloaded with test data from the "Developer Information" page.

The Artistic Style source code must be compiled as a shared library (DLL) using the option ASTYLE_LIB. The shared library must be copied to the directory that contains the C# executable (it is loaded from the current directory). The directory of the files to be formatted is an absolute path in the function getProjectDirectory() in Example.cs. This will need to be changed to reflect your directory structure. When the program is run be sure the current working directory is the directory with the executable and shared library.

The Example class is identical in both C# examples. The differences are in the AStyleInterface class.

 

 
// Example.cs

using System;
using System.IO;
using System.Text;

/// Example opens the source files, calls the AStyleInterface methods
/// to format the files, and saves the reformatted source. The files
/// are in a test-data directory. The option mode=cs must be included
/// for C# files.
public class Example
{   /// Main function for this example.
    public static void Main(string[] args)
    {   // files to pass to AStyle
        string[] fileName =  { "ASBeautifier.cpp",
                               "ASFormatter.cpp",
                               "astyle.h"
                             };

        // options to pass to AStyle
        // mode=cs is required for C# files
        string options = "-A2tOP";

        // create an object
        AStyleInterface AStyle = new AStyleInterface();

        // get Artistic Style version
        // does not need to terminate on an error
        string version = AStyle.GetVersion();
        if (version != String.Empty)
            Console.WriteLine("Example C# - AStyle " + version);

        // process the files
        for (int i = 0; i < fileName.Length; i++)
        {   // get the text to format
            string filePath = GetTestDirectoryPath() + fileName[i];
            string textIn = GetText(filePath);

            // call the Artistic Style formatting function
            // does not need to terminate on an error
            string textOut = AStyle.FormatSource(textIn, options);
            // does not need to terminate on an error
            // an error message has been displayed by the error handler
            if (textOut == String.Empty)
            {   Console.WriteLine("Cannot format "  + filePath);
                continue;
            }

            // return the formatted text
            Console.WriteLine("Formatted " + fileName[i]);
            SetText(textOut, filePath);
        }

        return;
    }

    ///  Error message function for this example.
    private static void Error(string message)
    {   Console.WriteLine(message);
        Console.WriteLine("The program has terminated!");
        Environment.Exit(1);
    }

    /// Find the test directory path from the application program path.
    /// This may need to be changed for your directory structure.
    private static string GetTestDirectoryPath()
    {   string topDirectory = "astyledev";
        string appDirectory = System.AppDomain.CurrentDomain.BaseDirectory;
        if (String.IsNullOrEmpty(appDirectory))
            Error("Cannot find application directory!");
        int indexTop = appDirectory.ToLower().IndexOf(topDirectory);
        if (indexTop == -1)
            Error("Cannot find top level folder!");
        string testPath = appDirectory.Substring(
                              0, indexTop + topDirectory.Length) + "/test-data/";
        return testPath;
    }

    ///  Get the text to be formatted.
    ///  Usually the text would be obtained from an edit control.
    private static string GetText(string filePath)
    {   // create input buffers
        const int readSize = 131072;     // 128 KB
        StringBuilder bufferIn = new StringBuilder(readSize);
        char[] fileIn = new char[readSize];

        // read file data
        try
        {   FileStream file = new FileStream(filePath, FileMode.Open);
            StreamReader streamIn = new StreamReader(file);
            // use ReadBlock to preserve the current line endings
            int charsIn = streamIn.ReadBlock(fileIn, 0, readSize);
            while (charsIn != 0)
            {   bufferIn.Append(fileIn, 0, charsIn);
                charsIn = streamIn.ReadBlock(fileIn, 0, readSize);
            }
            streamIn.Close();
        }
        catch (DirectoryNotFoundException e)
        {   Console.WriteLine(e.ToString());
            Error("Cannot find directory " + filePath);
        }
        catch (FileNotFoundException e)
        {   Console.WriteLine(e.ToString());
            Error("Cannot find file " + filePath);
        }
        catch (Exception e)
        {   Console.WriteLine(e.ToString());
            Error("Error reading file " + filePath);
        }

        return bufferIn.ToString();
    }

    ///  Return the formatted text.
    ///  Usually the text would be returned to an edit control.
    private static void SetText(string textOut, string filePath)
    {   // create a backup file
        string origfilePath = filePath + ".orig";
        File.Delete(origfilePath);                  // remove a pre-existing file
        FileInfo outFile = new FileInfo(filePath);
        outFile.MoveTo(origfilePath);

        // write the output file - same name as input
        try
        {   char[] bufferOut = textOut.ToCharArray();
            FileStream file = new FileStream(filePath, FileMode.Create);
            StreamWriter streamOut = new StreamWriter(file);
            streamOut.Write(bufferOut, 0, bufferOut.Length);
            streamOut.Close();
        }
        catch (Exception e)
        {   Console.WriteLine(e.ToString());
            Error("Error writing file " + filePath);
        }

        return;
    }

}   // class Example

 

Beginning with version 2.04 of Artistic Style, UTF-16 Unicode can be used by calling the function AStyleMainUtf16. With C#, UTF-16 Unicode is used in both Windows and Linux.

 

        
// AStyleInterface.cs

using System;
using System.Runtime.InteropServices;

/// AStyleInterface contains methods to call the Artistic Style formatter.
public class AStyleInterface
{   // Dll name
#if (WINDOWS)
// Windows will NOT attach an extension to a filename containing dots (.).
#if (DEBUG)
    private const string dllName = "astyle31d.dll";
#else
    private const string dllName = "astyle31.dll";
#endif
#else
    private const string SOLIBVER = "3.1.0";
#if (DEBUG)
    private const string dllName = "libastyled.so." + SOLIBVER;
#else
    private const string dllName = "libastyle.so." + SOLIBVER;
#endif
#endif
    /// AStyleGetVersion DllImport.
    /// Cannot use string as a return value because Mono runtime will attempt to
    /// free the returned pointer resulting in a runtime crash.
    /// NOTE: CharSet.Unicode is NOT used here.
    [DllImport(dllName)]
    private static extern IntPtr AStyleGetVersion();

    /// AStyleMainUtf16 DllImport.
    /// Cannot use string as a return value because Mono runtime will attempt to
    /// free the returned pointer resulting in a runtime crash.
    /// NOTE: CharSet.Unicode and wide strings ARE used here.
    [DllImport(dllName, CharSet = CharSet.Unicode)]
    private static extern IntPtr AStyleMainUtf16(
        [MarshalAs(UnmanagedType.LPWStr)] string textIn,
        [MarshalAs(UnmanagedType.LPWStr)] string options,
        AStyleErrorDelgate AStyleError,
        AStyleMemAllocDelgate AStyleMemAlloc
    );

    /// AStyleMainUtf16 callbacks.
    /// NOTE: Wide strings are NOT used here.
    private delegate void AStyleErrorDelgate(
        int errorNum,
        [MarshalAs(UnmanagedType.LPStr)] string error
    );
    private delegate IntPtr AStyleMemAllocDelgate(int size);

    /// Error handler to abort the program.
    private void Error(string message)
    {   Console.WriteLine(message);
        Console.WriteLine("The program has terminated!");
        Environment.Exit(1);
    }

    /// Call the AStyleMainUtf16 function in Artistic Style.
    /// An empty string is returned on error.
    public string FormatSource(string textIn, string options)
    {   // Return the allocated string
        // Memory space is allocated by AStyleMemAlloc, a callback function
        string sTextOut = String.Empty;
        try
        {   IntPtr pText = AStyleMainUtf16(textIn, options,
                                           AStyleError, AStyleMemAlloc);
            if (pText != IntPtr.Zero)
            {   sTextOut = Marshal.PtrToStringUni(pText);
                Marshal.FreeHGlobal(pText);
            }
        }
        catch (BadImageFormatException e)
        {   Console.WriteLine(e.ToString());
            Error("You may be mixing 32 and 64 bit code!");
        }
        catch (DllNotFoundException)
        {   //Console.WriteLine(e.ToString());
            Error("Cannot load native library: " + dllName);
        }
        catch (Exception e)
        {   Error(e.ToString());
        }
        return sTextOut;
    }

    /// Get the Artistic Style version number.
    public string GetVersion()
    {   string sVersion = String.Empty;
        try
        {   IntPtr pVersion = AStyleGetVersion();
            if (pVersion != IntPtr.Zero)
            {   sVersion = Marshal.PtrToStringAnsi(pVersion);
            }
        }
        catch (BadImageFormatException e)
        {   Console.WriteLine(e.ToString());
            Error("You may be mixing 32 and 64 bit code!");
        }
        catch (DllNotFoundException)
        {   //Console.WriteLine(e.ToString());
            Error("Cannot load native library: " + dllName);
        }
        catch (Exception e)
        {   Error(e.ToString());
        }
        return sVersion;
    }

    /// AStyleMainUtf16 callback to allocate memory for the return string.
    private IntPtr AStyleMemAlloc(int size)
    {   return Marshal.AllocHGlobal(size);
    }

    /// AStyleMainUtf16 callback to display errors from Artistic Style.
    private void AStyleError(int errorNumber, string error)
    {   Console.WriteLine("AStyle error " + errorNumber + "\n" + error);
    }

}   // class AStyleInterface