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.
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.
This method is called to format the source code. Wide strings ARE used here.
/// 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 );
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.
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.
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 must be delegated for the call to AStyleMainUtf16. Wide strings are NOT used here.
/// 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);
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.
This method is called to get the Artistic Style version number.
[DllImport(dllName)] private static extern IntPtr AStyleGetVersion();
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.
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.
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