In some of my recent posts, I’ve covered the topic of accessing and parsing an INI file for configuration data in a C# Application.
Some may wonder why. After all; the “norm” for C# and .NET applications is to use XML files for configuration information, isn’t it? Well, yes. But to be honest, XML files are a fucking pain in the ass. They aren’t human readable to your average person the same way an INI file is, and getting/setting values is tedious. Primarily, the reason I use INI files is that they are:
Mostly, I feel that XML, and in many ways other configuration options, are more or less driven by fad. Another option for configuration settings on Windows is the Registry, which is in fact often the recommended method; but this is anything but accessible to the user. Would you rather guide a user to edit a INI file or to fiddle with registry settings?
With that said, INI Files do have their own issues. For example, their data is typically typeless; or, more precisely, the Values are all strings. Whereas using a .NET XML Serializer, for example, you could easily(relatively speaking) serialize and deserialize a special configuration class to and from an XML file and preserve it’s format, with my INI file class there will typically be some work to parse the values.
It was with the idea of turning my string-only INIFile configuration settings into something that can be used for nearly any type that I created the INItemValueExtensions class, which is nothing more than a static class that provides some extension methods for the INIDataItem class. I covered this in my previous post.
The prototypes for the two static functions are:
How would one use these extension methods? Well, here’s an Example:
Woah, hold the phone! What’s going on here? We’re loading DateTime values directly from the INI File? How does that work?
All the “magic” happens in the getValue
If it does implement a TryParse() routine, (like, for example, DateTime) it doesn’t try quite as hard. It takes the string from the INI file and hands it to the Type’s TryParse() routine, and then returns what that gives back. Naturally, the inverse function (setValue) does something somewhat opposite; it checks the Base64 logic, and if so it sets the value of the item to the Base64 encoded value of the serialized object. Otherwise, it just uses toString().
This typically works, particularly with DateTime, because usually ToString() is the inverse of TryParse(). In the case of DateTime, this has a few edge cases with regards to locale, but usually it works quite well. And more importantly, the introduction of allowing any object that implements ISerializable to simply be thrown as an INI value via a Base64 encoded string is useful too, although with large objects it’s probably not a good idea for obvious reasons.
Of Course, an INIFile is only one of any number of ways to store/retrieve configuration settings. And while they don’t typically lend themselves to the same syntax provided by the INIFile class, it would be useful to have some sort of common denominator that can handle it all. That was the original intent of the relatively unassuming ISettingsStorage interface:
This uses a concept known as a “category” which is pretty much the same idea as an INI File section. What makes it different is that, for implementors that use other storage mechanisms, it could have additional meaning; for example, a fictitious XML implementation of ISettingsStorage could use the “Category” string as an XPath to an element; and the Value could be stored/retrieved as a Attribute. a Registry implementation might use it as a Registry path, and so on.
The problem is, even though the INIFile class implements this interface, it’s too basic, and doesn’t provide nearly the syntactic cleanliness that just using the INIFile does. Stemming from that, and because I wanted to try to get a way to store settings directly in a DB, I introduced two events to the INIFile class; one that fires when a Value is retrieved, and one when a value is saved. This way, the event could be hooked and the value saved elsewhere, If desired. Now, to be fair, this is mostly a shortcoming of my interface definition; as you can see above, there is no way to, for example, inspect category or Value names. I toyed with the idea of adding a “psuedo” category/value combination that would return a delimited string of category names, but that felt extremely silly. The creation of a generic interface- or abstract class- that provides all the conveniences I currently enjoy using my INIFile class but allowing me to also use XML, Registry, or nearly any other persistent storage for settings will be a long term goal. For now, I’m content with accessing INI files and having a unclean event to hack in my own behaviour.
My first test of the above feature- whereby it allows values to be TryParse’d and ToString’d back and forth from a given type on the fly- was the creation of a FormPositionSaver class.
The proper way to save and restore a window’s position on Windows is using the GetWindowPlacement() and SetWindowPlacement() API Functions. These use a structure, named, quite aptly, “WINDOWPLACEMENT” to retrieve and set the window position and various attributes. Therefore, our first task is to create the proper P/Invoke’s for these functions:
I also include OffsetRect(), but I’ll get to that in a bit. Now the “big one” is the definition of the WINDOWPLACEMENT structure and it’s various aggregate structures. Why? well, in the interest of leveraging the INIFile’s static extensions, Why not define a static TryParse() and a toString() method on the structure that can set and retrieve the member values:
WHEW! that’s quite a bit of code for a structure definition, but we’ll make up for it with the brevity of the actual FormPositionSaver class itself. First, my design goal with this class was to make it basically do all the heavy lifting; it hooks both the Load and Unload event, and saves to and from a given INIFile Object in those events. Since the application I was working on at the time didn’t actually get a Valid INI object until during it’s main form’s Load event, and since there is no way to say “Invoke this event first no matter what” I also added a way for it to be told that hooking the load event would be pointless since it already occured, at which point it will not hook the event and instead set the form position immediately. Values are stored
Alright, so maybe I lied a bit. It's not super short. Although a lot of it is comments. Some might note that I only sporadically add doc comments, even though I ought to be adding them everywhere. Well, sue me. I just add them when I feel like it. When I'm concentrating on function, I'm not one to give creedence to form.
This is where I explain OffsetRect(). Basically, if your application is run twice, and you load the form position twice, the second form will open over the first one, and the screen will look pretty much the same. So we detect previous instances and offset by an amount to make it's position different from any previous instances as necessary. That's pretty much the only purpose of OffsetRect.
I have packaged the current versions of cINIFile.cs and the new FormPositionSaver.cs in a zip file, it can be downloaded from here .
HAHA! How’s that for a clever title?
Oh… well… ahem… nevermind.
As a avid user of my own INIFile class, which I first write about- at least it’s C# implementation- in my parsing INI files posting , I am always looking for ways to improve it’s usage make it more “accessible”.
Recently, I have been tasked (by way of my new title of “freelance consultant”) with creating several LOB (Line of Business) Type applications. Applications, naturally, have a tendency to lend their implementations to the creation and reading of settings. Being something of a fan of the simplicity of INI Files, I chose to use my INIFile class in the application. It works well, however, I have noticed that I have a lot of duplicate code. More specifically, I typically have to implement a “wrapper” class, which manages configuration information and reads/writes values to and from the INIFile as its own properties are accessed. For example:
Nothing too dreadful, but imagine having nearly the exact same thing repeated a number of times! The code is repeated and as Larry Wall says, one of the traits of a good programmer is sloth. I don’t like having to write this same code over and over again! The INIFile is supposed to make it easy!
The trouble here stems from the fact that the INIFile values are only strings; and typically, many settings are represented in the application itself as integers and booleans, dates, and so forth. My first attempt to mitigate the clutter was a static method, which I called xParse:
relatively straightforward- basically it’s a shell of what I had repeated over and over again. This mitigated the issue somewhat, so my properties in the wrapper looked like this:
much more managable, but still, could we not make this more concise? My first thought, was that perhaps I could eliminate the necessity of having the wrapper at all; I recalled two interfaces from my old COM programming days, specifically, IDispatch and IDispatchEx. Surely, I could do something similar?
Unfortunately, the interfaces are for COM, and C# doesn’t have dynamics until Version 4.
So, I fired up Visual Studio 2010 express to see if I couldn’t add the dynamic language constructs to the INIFile class; additionally, since I still need to work with .NET 3.5, I’ll add the new code as a conditional compilation.
The first step was deciding exactly what I wanted to happen. Imagine code like this:
The holy grail of the INIFile simplicity! Naturally, the .NET framework does provide the facility with which to add this functionality, as part of the System.Dynamic namespace.
The first step was deciding on the method by which to conditional compile. Since projects copy the source of a file to your project folder when you add them, it seemed reasonable to simply add it as a #define right inside the INIFile class itself.
#define CS4
And now, I just need to enclose all my new happy stuff in a conditional directive, and I’ll get the best of both worlds- C# 4.0 consumers who keep the #define will be able to use the suave new feature, and older consumers will still be able to work without ripping apart the classes. The code to add this was surprisingly simple; as it stands now the longest method (An implementation of TryDeleteMember, which is never called from C#/VB.NET consumers, so is excessive for my usage). First, obviously we enclose the import statement in the conditional compile; the class headers are conditionally compiled as well, only deriving from DynamicObject with CS4 set.
The core of the new functionality is in the overrides to the Dynamic Object’s TryGetMember.
For the INISection:
And for the INIFile…
Exactly the same, in fact. This works because of the indexer I added; the indexer will add the item if it doesn’t exist and return the new value, so even if the member name doesn’t exist, the INIFile will simply have that section added.
That’s for the retrieval of erements; to allow the assignment to them in the same fashion, we need to override TrySetMember(). In my case, this was a bit more involved, for flexibility purposes.
For example, code like INIFile.MainSection=”hello” should work, and change the name of the section. And why allow things like assignments from a Dictionary<String, String>, or maybe even a list (assigning a numbered id to set values)? And of course allow setting the Value directly, which will likely use the indexer much as I did for the TryGet… Implementations.
setting the Value should be equally flexible; since we can, why not?
for example, why not make the following “legal”?
The first example sets the Value to a string, the second sets it to a DateTime that is silently casted to a String (using toString(), and the last two use the new C# 4.0 tuples, to set both the name of the value and the value simultaneously.
A more elegant solution would be to add this code to the Indexer, and merely call the indexer with the name and the value and return true if no exception occurs and false otherwise. However, I’m reluctant to go that route since some of the types are C# 4 types (Tuples).
So Now, I’ve got an INI File implementation that supports Dynamic invocation. Well, that’s great… except that the application I first found it clumsy in is using .NET 3.5, so I can’t use the dynamic features. Back at square one.
In C# 2008/3, we might not be able to leverage the power of dynamics, but we do have generics and Extension methods at our disposal. a feasible alternative could be to add a extension method to the INIDataItem class that has a generic type parameter that it will attempt to convert it’s string Value into. First, using ChangeType, second, it can try to invoke a static TryParse on the given Type to parse the “value” string. And if none of that works, it can return a passed in default. This is still more verbose than the dynamic solution, but it has two distinct advantages- first, it’s type-safe, so you get all the intellisense goodness, and second, it’s still shorter than the alternative.
Here is the code, which can be found in the cINIFile.cs file attached to this posting as well.
And there you have it, a bunch of awesome additions. INI files are often thought of as deprecated, but that’s only the INIFile functions. This class was designed because working with the registry makes it difficult to test properly, and because JSON,YAML, and many other formats are excessively complicated. when you just need a few basic settings, all you need is the clean, simple format of a INI file. And now, with these additions, code for reading from those INI files is clean and simple as well!
As I posted previously here , Sorting a Listview can be something of a pain in the butt.
In that article, I covered some basics on providing a class that would essentially give you sorting capabilities for free, without all the messy code that would normally be required. A lot of the code required for sorting is mostly boilerplate with a few modifications for sorting various types. As a result, the generic implementation works rather well.
However, as with any class, adding features never hurts. In this case, I got to thinking- why not have right-clicking the ColumnHeaders show a menu for sorting against that Column? Seems simple enough. I quickly learned that apparent simplicity often is misattributed.
I faced several issues. The first thought was that I could hook a Mouse event for Right-Clicking a column header. Unfortunately, I soon discovered two facts about the .NET ListView control. First, was that there was no event for right-clicking a header control. Second, no even was fired at all by the ListView control when you right-clicked a header.
This left me stymied. How the heck do I implement this feature? I discovered something of a “hack” however, in that when the ListView’s ContextMenuStrip property is set, that ContextMenu Strip will be shown regardless of the location the ListView is clicked. This at least gave me something to work with. Since a ContextMenuStrip’s “Opening” event can be easily hooked, we can use that as an entry point and perform needed calculations to determine if we are indeed on a columnheader.
Which brings me to the next problem, which is determining when a columnheader was in fact the item that was clicked. This requires determining the rectangle the Header control occupies, first. The Header Control is a child control of the ListView; as such, a platform Invoke using the EnumChildWindows() API was required, something like this:
private Rectangle _HeaderRect;
private delegate bool EnumWindowsCallBack(IntPtr hwnd,IntPtr lparam);
[DllImport("user32.dll")]
private static extern int EnumChildWindows(IntPtr hwndParent,EnumWindowCallBack callbackFunction,IntPtr lParam);
[DllImport("user32.dll"]
private static extern bool GetWindowRect(IntPtr hWnd,out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
private bool EnumWindowCallback(IntPtr hwnd, IntPtr lParam)
{
RECT rct;
if(!GetWindowRect(hwnd,out rct))
{
//first child of the listview should be the header control
_HeaderRect=Rectangle.Empty; //likely the listview is not in Details mode, so there is no header control.
}
else
{
_HeaderRect = new Rectangle(rct.Left,rct.Top,rct.Right-rct.Left,rct.Bottom-rct.Top);
}
return false; //cancel enumeration.
}
private static ColumnHeader [] GetOrderedHeaders(ListView lvw)
{
ColumnHeader [] returnarray = new ColumnHeader [lvw.Columns.Count] ;
foreach(ColumnHeader loopheader in lvw.Columns)
{
returnarray [loopheader.DisplayIndex] = loopheader;
}
return returnarray;
}
Quite a bit of boilerplate to add in. Basically, the idea is that we will hook the contextMenu Opening event of the Listview, (and we add a context menu to hook if the listview in fact doesn’t have one) in our constructor; and then when we receive the event we need to determine if the click occured within the area of the header control of the listview, if so, we cancel the event (which stops the default context menu from appearing) and show our own menu for the columnheader, which we can acquire using a bit of math and the static “GetOrderedHeaders” function, which retrieves the array of columnheaders of a ListView in order of appearance Left to Right (since the user could rearrange the Columns).
So First, we need to add code to the GenericListViewSorter’s Constructor. We also have a few private variables that are added; in this case, we need a ContextMenuStrip variable called “_ghostStrip” which we will use if we need to create a context menu for the control, since we don’t want that to appear in the default case. Of course we create our own ContextMenuStrip which we will show in the event instead of the default when appropriate. so we add this beneath the existing code in the constructor:
if(handleListView.ContextMenuStrip==null)
{
handleListView.ContextMenuStrip = new ContextMenuStrip();
handleListView.ContextMenuStrip.Items.Add("GHOST"); //add a ghost item so we get the Opening Event
_ghoststrip = handleListView.ContextMenuStrip;
}
//create OUR context menu
_headerContextMenuStrip = new ContextMenuStrip();
//add a ghost item to make sure Opening will fire.
_HeaderContextMenuStrip.Items.Add("ghost");
handleListView.ContextMenuStrip.Opening += ContextMenuStrip_Opening;
handleListView.ContextMenuStripChanged += handleListView_ContextMenuStripChanged;
Of course we need to add the two referenced event handlers, too. The ContextMenuStripChanged being a rather simple implementation designed to keep changes in the contextmenu of the listview from causing us to balls up and stop showing ours (since we are now hooking a orphaned context menu not being shown by the listview).
void handleListView_ContextMenuStripChanged(object sender,EventArgs e)
{
OurListView.ContextMenuStrip.Opening+=ContextMenuStrip_Opening;
}
Now the meat of the code is in the ContextMenuStrip_Opening() routine. This will need to determine wether its applicable to show the Column menu, or the already present menu (which it doesn’t show either if it happens to be the _ghoststrip). This is accomplished by use of the GetCursorPos() API routine paired with the already present GetWindowRect() implementation, which we update by calling EnumWindows.
void ContextMenuStrip_Opening(object sender,System.ComponentModel.CancelEventArgs e)
{
//first, get screen coordinates of Cursor.
POINTAPI gapi;
GetCursorPos(out gapi);
Point gotposition = new Point(gapi.X,gapi.Y);
//acquire the HeaderRect of the control...
EnumChildWindows(OurListView.Handle,new EnumWindowCallBack(EnumWindowCallback),IntPtr.Zero);
//if the mouse position is within the retrieved rectangle, cancel the display of the normal menu and create and show ours.
if(_HeaderRect.Contains(gotposition))
{
e.Cancel=true;
int xoffset = gotposition.X - _HeaderRect.Left;
ColumnHeader clickedheader = HeaderAtOffset(OurListView,xoffset);
if(clickedheader != null)
{
//create the context menu as needed.
_HeaderContextMenuStrip = new ContextMenuStrip();
_HeaderContextMenuStrip.Tag = clickedheader;
//two items, one for ascending order, one for descending order.
ToolStripMenuItem AscendingHeaderItem = new ToolStripMenuItem(String.Format("Sort Column \"{0}\" Ascending",clickedheader.Text));
ToolStripMenuItem DescendingHeaderItem = new ToolStripMenuItem(String.Format("Sort Column \"{1}\" Descending",clickedheader.Text));
//if the current sort column is the header, check it off and disable it.
if(CurrentSortColumn == clickedheader)
{
if(OurListView.Sorting ==SortOrder.Ascending)
{
AscendingHeaderItem.Checked=true;
AscendingHeaderItem.Enabled=false;
}
else if (OurListView.Sorting==SortOrder.Descending)
{
DescendingHeaderItem.Checked=true;
DescendingHeaderItem.Enabled=false;
}
}
AscendingHeaderItem.Tag = ClickedHeader;
DescendingHeaderItem.Tag = ClickedHeader;
//set event handlers for the two items.
AscendingHeaderItem.Click+= AscendingHeaderItem_Click;
DescendingHeaderItem.Click+= DescendingHeaderItem_Click;
//add them to the context menu strip.
_HeaderContextMenuStrip.Items.Add(AscendingHeaderItem);
_HeaderContextMenuStrip.Items.Add(DescendingHeaderItem);
//display the menu.
_HeaderContextMenuStrip.Show(gotposition);
}
}
else
{
//show the default menu, but only if it isn't the ghoststrip.
if(OurListView.ContextMenuStrip == _ghoststrip)
e.Cancel=true;
}
}
The events for the two buttons basically sort based on the columnheader in their tag, nothing particularly special there. the actual details can be seen in the source file itself, really.
It actually works quite well, I’m using it in a production application, and it’s working quite well.
Some obvious enhancements, of course, include making it possible to customize the shown menu, to present other options; perhaps a delegate or event that can be hooked that is given the Strip and the clicked column, and any number of other parameters? This would essentially give the equivalent of a ColumnHeaderRightClicked type event, too.
Anybody who has used windows is probably familiar with the ListView control. It is used in Windows Explorer; it is even used for the desktop. Heck, the ListView control even has implementations on Linux and Mac, and in the latter case it was there first.
The ListView itself can display in several modes. Normally, it shows things as Icons. But it can also be set to show Small Icons, a List, in some Operating Systems, there is a ‘Tile’ option, or even options like Large,Medium, and other sizes of Icons. My Personal favourite is the details mode.
Because I mostly see and use Listviews in Details mode, I also force people who use my software to deal with Details mode. Mostly because the reason I am displaying a ListView is to show some data in a somewhat tabular format and not just give them a few icons to drag around with minimal actual information, but I digress. Anyway, I think a good question at this point might be to look at what different parts this particular ListView has. First, the gray “buttons” at the top, which serve to title each column, are referred to affectionately as ColumnHeaders. Under each ColumnHeader there is data for a given “subitem” of each item. For example, the “Size” entry here is a Subitem for each drive. An interesting feature of columnheaders that is nearly universal is that you can click on one, and it will sort by that column.
Another interesting thing, is that in many programming environments, the ListView control doesn’t actually provide this feature for you, and you have to code it yourself. It is rather frustrating. In particular, Visual Basic 6 allows you to sort, but you can’t really customize what you sort by; it always treats it as text. In one of my VB6 applications, BCSearch (which is available for download from my Downloads page) I managed to use a Custom control, available from VBAccelerator.com, which exposes additional functionality of the ListView Control on top of that provided in either of the MS provided libraries for use within Visual Basic. One of these features is that it has better support for sorting. I still had to add my own “arrow” to show the sort direction, though.
The VBAccelerator control exposes a number of events and properties for controlling sorting, which I use to properly sort the various subitems, so that various entries like date or size aren’t sorted as text.
Curiously, the .NET Windows Forms ListView control, while having more functionality, still leaves a lot of effort to the programmer for what ideally ought to be a free feature supported by the OS. In fact it IS a free feature supported by the OS. Thankfully, the .NET control does in fact provide a feature for customizing sort functionality, And all you need is a class to implement IComparer. the IComparer will be used to compare the listitems as the Listview sorts. But if you have, say, Date and Time fields and size fields or other fields that can’t just be sorted as text, you are going to need to implement your own special comparer for each. This amounts to quite a bit of glue code; on top of that, you will need to handle the ColumnClick events on the ColumnHeader, change the sort mode, and sort it, and so forth.
To combat this bloating code, I wrote a relatively small class designed to encapsulate sorting. The idea being that you create a instance of this class for each listview, pass in the ListView to it’s constructor, and the class handles all the details. It worked quite well. There was a minor issue that amounted to a gigantic pain in the ass but at the same time made the result a lot better.
As you can see, it it relatively small (overall). the API code at the top might be a bit confusing, but it is a result of what can only be described as an oversight on Microsoft’s part; see, originally, I was changing the sort arrow header by simply changing the columnheader image. This worked, sorta of, but there was no way to remove the image and it had this weird effect where it would basically move the text and make it aligned sorta weird. Turns out that the way the ListView would “normally” show sort order icons was a built in feature of the Listview since Common Controls 6 (XP). After some SDK digging I was able to use the Platform Invoke feature of C# to call all the appropriate API functions and “force” the Listview to show the sort order in the header appropriately.
The class also exposes a custom delegate which can be implemented and passed in to the constructor, which will allow for “custom” sorts. This is useful if columns contain data like dates, or numbers that you don’t want to be sorted using the normal “text” comparison.
All in all, It’s a class I’ve added to my “toolbox”, alongside my INIFile class for accessing INI Files. did I write about that one? I forget.
Most Computer users are familiar with the Sounds that Windows emits when you plug and unplug a USB thumb drive. It’s a useful form of auditory feedback that the drive was in fact detected. However, I’ve found linux to be oddly tacit in this regard. So I set to work writing a python script that uses DBUS to monitor for new USB devices and will play a sound whenever a new Volume is attached.
As can be seen, it’s a tad messy, and even rather hackish. For one thing, it uses DBUS, which to my understanding is deprecated. Unfortunately, the replacement I couldn’t really get a clear answer on. From what I can gather, the proper method for now is libnotify and pynotify, but I couldn’t get libnotify to compile and thus was not able to properly use pynotify, and I didn’t want to have to force people to go through that sort of hell when they tried to use my script, so I stuck to DBUS.
The only limitation I discovered is that on device removal, you can’t really inspect what device was removed. At first I just figured, Just play the sound everytime and let the user figure it out, but for some reason that just assaulted me with constant device removal sounds. So I ended up commenting (and I think removing) that particular segment of code.
Playing Sounds is unnecessarily difficult in Python, or more specifically, Linux. It’s ridiculous. First I found a build in module for python, ossdevsound (or something to that effect), but attempts to use that failed because apparently it uses OSS, which apparently was replaced by ALSA for whatever reason. So I tried pygame, which errored out that I had no mixer device when I tried to initialize the mixer. So I decided to hell with it and just spawned a mplayer process, and redirected it’s stdout to NULL to avoid the nasty business where it barfs all over the console. And amazingly, that seems to work fine for device insertions, which I decided I was content with.
By default I use the Windows insertion and removal sound files. The removal sound isn’t actually used but I kept it in the g-zipped tar because I wanted to. Personally I usually just launch this in a terminal and then tuck it away on another desktop. No doubt one can execute it as a daemon or something instead and get the functionality without the console window baggage to keep around, though.
No, Not the kissing disease, Infectious mononucleosis, the open-source .NET CLR interpreter and class library.
.NET; I might have ranted about this before, if not on my blog, elsewhere. most of my arguments were against it, being a VB6 using ignorant buffoon. In any case, I’ve found C# to be an awesome language. One of the obvious downsides is that for the most part programs written in C# using Windows forms cannot be run on other systems.
The concept of Mono is to change that; remember, C# isn’t a Microsoft standard, it’s an ECMA specification, and Microsoft holds no copyright over it or anything like that. Mono is a relatively huge undertaking; the creation of something on the scale of a Virtual machine as well as a class library is gargantuan, and of course there is very little hope for 100% success.
On the whole, Mono performs it’s primary goals admirably; there is a GTK# framework that can be used to develop windowed applications… of course then the installation on a windows PC would require the install of the Mono GTK# framework, but I digress. In any case, applications can be developed and the C# code interpreter (for on the fly syntax highlighting) as well as the compiler are top-notch and seem to work well.
My only beefs can of course be in the class library. Of course, re-implementing the class library provided by MS on non-windows systems is not something that can be done in a single afternoon; this stuff takes time. My attempts to create a Mono-compatible project have been stifled, however, by seemingly innocuous issues. AlLlow me to explain.
As many reader may be aware, I have been working on a “upgrade” of sorts to my now ancient “Poing” game, which can be found on my downloads page; the original game was written in Visual Basic 6, and, in it’s earlier incantations didn’t have a single Class; it was all based on User-Defined Types (structures for those privvy to C parlance) and functions; (short explanation: it was made before I understood classes). Later, after I had learned about classes, I refactored everything into classes. This is all rather redundant; in any case, I have since created a new project in C#, in an attempt to learn about GDI+ as well as what appeared to be a different (in some ways) painting model. As one can see by the youtube videos I have posted on it, development has gone well.
The idea occured to me, after Tux2 (of http://www.jrtechsupport.com/) managed to create a Mono-workable, if soundless, version of my earlier C# game, BCDodger that worked in Linux. Sound was unavailable as a result of my choice of sound library, the IRRKLANG library, while truly usable in Linux, doesn’t have a wrapper that works in Linux (or something, I don’t know… it uses DSound or something, I forget). In any case, I have since added the ability for various sound engines to be used, and have added working implementations for Open-Source and Linux-available Sound systems, such as NBass (as well as a broken fmod implementation, let’s ignore that though).
Much obliged, I had decided to try to get it working via Mono as well; this was facilitated by my recent reformatting of my laptop to run a dual boot Windows 7 and Mint 10 system. Installing MonoDevelop and all that, etc.
So, I of course open the project in Monodevelop, quite ready for errors relating to porting.
The version I am using is MonoDevelop (and I assume also Mono) version 2.4, for those following along.
My first hurdle was getting my Appdata files for the program in the right location; on windows systems they are placed in the application data folder; so too they would need to be on a Linux system. Thankfully, a quick C# program run on the Linux machine cured this issue:
using System;
namespace specialfolders
{
class MainClass
{
public static void Main(String [] args)
{
foreach(Environment.SpecialFolder sfolder in
Enum.GetValues(typeof(Environment.SpecialFolder)))
Console.WriteLine(sfolder.ToString() + "=" +
Environment.GetFolderPath(sfolder));
}
}
}
Which gave me, on a Windows system:
Desktop=C:\Users\BC_Programming\Desktop Programs=C:\Users\BC_Programming\AppData\Roaming\Microsoft\Windows\Start Menu\Programs Personal=C:\Users\BC_Programming\Documents Personal=C:\Users\BC_Programming\Documents Favorites=C:\Users\BC_Programming\Favorites Startup=C:\Users\BC_Programming\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup Recent=C:\Users\BC_Programming\AppData\Roaming\Microsoft\Windows\Recent SendTo=C:\Users\BC_Programming\AppData\Roaming\Microsoft\Windows\SendTo StartMenu=C:\Users\BC_Programming\AppData\Roaming\Microsoft\Windows\Start Menu MyMusic=C:\Users\BC_Programming\Music DesktopDirectory=C:\Users\BC_Programming\Desktop MyComputer= Templates=C:\Users\BC_Programming\AppData\Roaming\Microsoft\Windows\Templates ApplicationData=C:\Users\BC_Programming\AppData\Roaming LocalApplicationData=C:\Users\BC_Programming\AppData\Local InternetCache=C:\Users\BC_Programming\AppData\Local\Microsoft\Windows\Temporary Internet Files Cookies=C:\Users\BC_Programming\AppData\Roaming\Microsoft\Windows\Cookies History=C:\Users\BC_Programming\AppData\Local\Microsoft\Windows\History CommonApplicationData=C:\ProgramData System=C:\Windows\system32 ProgramFiles=C:\Program Files MyPictures=C:\Users\BC_Programming\Pictures CommonProgramFiles=C:\Program Files\Common Files
Nothing particularly out of the ordinary there. So, running the program on a Linux machine:
Programs= Personal=/home/bc_programming Personal=/home/bc_programming Favorites= Startup= Recent= SendTo= StartMenu= MyMusic=/home/bc_programming/Music DesktopDirectory=/home/bc_programming/Desktop MyComputer= Templates= ApplicationData=/home/bc_programming/.config LocalApplicationData=/home/bc_programming/.local/share InternetCache= Cookies= History= CommonApplicationData=/usr/share System= ProgramFiles= MyPictures=/home/bc_programming/Pictures CommonProgramFiles=
which gave me what I needed: the appdata folder “BASeBlocks” needed to be copied to /home/bc_programming/.config.
Doing so was easy enough; the file manager on Mint 10 (dolphin) is different from windows explorer but hardly paradigm-breaking.
That copied, I simply threw the source folder as it was on the Visual Studio 2008 projects folder into the MonoDevelop projects folder (well, not really, it was /home/bc_programming/projects/ which I suppose means that any program that uses a projects folder will use it, oh well.)
psyched as I was I ripped into it with MonoDevelop, ready for anything! well, nearly anything.
My first error occured on line 107 of “Block.cs”:
mPowerupChanceSum = Powerupchance.Sum();
Java programmers may be thinking “ew uppercase characters” to them I say be quiet you. Anyways, the problem here was that there was no “Sum()” Extension method defined. Which I found odd. Oh well, though, I simply created my own:
#if MONO
public static class monoextensions
{
public static float Sum(this float [] floats)
{
float accumulator=0;
foreach(float loopfloat in floats)
accumulator+=loopfloat;
return accumulator;
}
}
#endif
this solved the immediate issue of the “Sum()” extension method. (For more info on C# extension methods, see here )
My next hurdle was on line 540 of cBall.cs:
if (wholeref.Blocks.Count((q)=>q.MustDestroy()) == 0)
The error was something to the effect of not being able to pass a delegate or method group to a lambda expression.
oddly enough, the fix was the change the “q” to an x… and after it successfully built I was able to comment out the “new” line (with the x rather then a q) and uncomment the original. very odd.
In either case, now I was confronted with what I knew to be the biggest issue; the fact that I had to now discover how to set it up so that I was able to use the same nBASS library, but so that the nBASS library was “silently” made to use the BASS.so linux library, rather then bass.dll which wouldn’t load either way.
The first step in this process was in finding bass.so. Hoping for the best, I pulled up good ol’ synaptic package manager.
No luck there
so, I went into the depths of the internet….
the BASS for Linux (note the lack of the n prefix; it’s the core BASS library that the nBASS dll is wrapping) can be found here
this topic; or, the original post, to be precise, gives the download location for the Linux source files for BASS 2.4, which can be found at http://www.un4seen.com/stuff/bass24-linux.zip
So, I downloaded the zip file, extracted it to a folder, and am about to attempt to compile it. (there is a libbass.so that I could probably try, but I think I’ll compile it myself instead).
Before I continue, however, I would like to mention something that has most impressed me about Mint 10; the multiple desktops. Now, this is hardly a new feature; programs can be downloaded that do this on windows, and windows itself has built in API support for multiple desktops; however, what impresses me most is that when you activate a window on another screen, it switches to it; what I mean is, for example, in this instance my FF download window was on another desktop (no idea why) and when I went to open the downloads window via Tools->Downloads, it did the fancy compiz box animation (as I have selected in preferences) and switched to it. very cool. But enough if my gushing over the unexpected presense of sanity in the UI design of a Linux desktop environment! back to my attempts to get BaseBlock working on Linux.
my attempts to make the bass project, however, failed:
bc_programming@Satellite ~/Projects/bass $ sudo make make -C 3dtest make [1] : Entering directory `/home/bc_programming/Projects/bass/3dtest' cc -Os -I/home/bc_programming/Projects/bass -L/home/bc_programming/Projects/bass -lbass -Wl,-rpath,/home/bc_programming/Projects/bass `pkg-config gtk+-2.0 --cflags --libs` `pkg-config libglade-2.0 --cflags --libs` -export-dynamic -D'GLADE_PATH="/home/bc_programming/Projects/bass/3dtest/"' 3dtest.c -o 3dtest Package libglade-2.0 was not found in the pkg-config search path. Perhaps you should add the directory containing `libglade-2.0.pc' to the PKG_CONFIG_PATH environment variable No package 'libglade-2.0' found 3dtest.c:7: fatal error: glade/glade.h: No such file or directory compilation terminated. make [1] : *** [3dtest] Error 1 make [1] : Leaving directory `/home/bc_programming/Projects/bass/3dtest' make: *** [3dtest] Error 2 bc_programming@Satellite ~/Projects/bass $
Oh the humanity! now I had to learn about this package nonsense. I first suspected perhaps, as evidence by the fact that it wasn’t found, said package wasn’t installed; so I did a quick sudo apt-get install libglade-2.0 … it reported it was installed already.
after a bit of effort, and installing a few dev packages, I gave up. I was able to resolve the missing dependencies but then it complained about a missing .h file (which was indeed missing and clearly should have been present) so I gave up on that, and am at the time currently trying to simply use the .so file included. An additionally problem that arises here is the obvious fact that I am using a x64 Linux system, working with C# code written on a x64 windows system but targeted towards a x86 system, problem being that to my understanding Linux is not as lenient when it comes to architecture differences; but I suppose I’ll figure that out for myself if it’s the case.
In any case, I am now attempting to get the nBASS library to wrapp around libbass.so rather then bass.dll; from my research if the imports are the same I should have no problem creating a dllmap entry in the appropriate config file. From the documentation it would seem that discovering where that config file goes is left as an exercise for the reader.
Actions:
pasted libbass.so in the “Debug” folder (alongside the dll).
Copied the BASeBlock.config file from the project root to the debug folder; renamed to Bass.Net.config.
opened preceding file in gedit; it now looks like this:
< ?xml version="1.0" encoding="utf-8" ?>
And now, I attempt to run BASeBlock… recall that it now compiles on Linux, so it’s just a matter of making it work. I could, in a worst case scenario, construct a “nullsound” driver that simply stubs out the sound playing interfaces. But that seems like a bit of a cop-out. IN any case, attempts to run it resulted in the same error; clearly either the documentation I was reading was incorrect or I was not interpreting it properly.
I did a few more googles and came upon this page, which, despite being about something completely different addressed the same problem; that is, translating the dllimport(whatever.dll) into imports of functions from Linux .so libraries. namely, it told me where the Mono parameter file was- /etc/mono/config. I quickly opened the feller using gedit:
I tossed in the line:
and crossed my fingers…
It still didn’t work. I guessed maybe mono only loaded that stuff up when it was started? So I restarted monodevelop.
excellent! a little progress. Now I was still getting the error, but it was complaining about libbass.so missing. At least we’re getting somewhere.
It still should have found the file; however, I decided that instead of just throwing it in the app directory (because god knows where Mono is truly looking for it; even putting ./libbass.so in the dllmap failed, so Mono clearly thinks something else is the “current” directory. Instead, I decided to simply cp it somewhere globally accessible;
It still didn’t work.
Anyway, I messed around with the config file and the dllmap attribute and no matter what I put it it refused to find the file; clearly I’m missing something obvious (maybe capitalization? a missing slash? who knows). In either case I decided to defer that work to later on; I’m sure there was more to be fixed afterwards.
So, I created the aforementioned “Null Sound” driver; it worked fine. compiled alright. Encountered a bunch of issues during startup relating to my use of the \ as a path separator, whereas Linux uses /, fixed this with a simple Replace to change all slashes to the slashes of the OS (Environment.PathSeparatorChar).
It still refuses to start; Some gdi plus error. I have no idea how to workaround this, since it seems to be related to the windows forms, and has nothing to do with my own code, but rather with some configuration option. I recall Tux2 working around a similar error in BCDodger but I wasn’t paying very close attention and forget what the fix was, or even if he mentioned it. Either way, at least now BASeBlock compiles on a linux system, and most of the code-related oversights have been resolved.
EDIT:
I’ve managed to solve both issues; not at once of course.
First, the loading issue was fixed; pretty troublesome. In my attempts to resolve the problem I created a number of projects that used the same Image.FromFile() method to load pictures, and they worked fine.
Clearly, the problem then was not in what was not visible; the difference between the two. I realized that the difference was pretty clear: in my test projects, I was loading the images from within the form’s Load event; in BASeBlock, they were being loaded in a static constructor. So I decided to try to load the images elsewhere; I converted the static constructor to a static function, and called that function in the form load; there were a few other changes that I needed to make, mostly in the form of initializers attempting to use values that wouldn’t have been initialized.
The game started and ran fine with the NullSound driver.
Now, to fix the Sound; I opted to try to get nBASS working.
the solution was actually quite simple; merely a dllmap for bass.dll to libbass.so, and placing the x64 libbass.so in usr/lib was enough, and sound worked fine.
Unfortunately, it’s still slow as hell but at least I think that’s Mono’s fault.
When first encountering a x64 based system running windows, one of the first questions posed is often, “what is C:\program files (x86)?” The simple response is that it is the equivalent of Program files on 32-bit systems; that is, 32-bit program files are installed there. This is true; but this is far from the only difference between the two systems and their file systems.
There is, however, further redirection within both other folders as well as The registry.
For example, the “real” %systemroot%\system32 folder actually stores the x64 system files; the 32-bit files are stored in %systemroot%\syswow64. However, when a 32-bit application access this system32, Windows automatically redirects all requests to the syswow64 folder, so accessing, say, “C:\windows\system32\file.txt” is actually accessing C:\windows\syswow64\file.txt. This includes any file operation at all. creation, editing, etc. there are a few folders exempt from this redirection- that is, they are not redirected. these folders include %windir%\system32\catroot,windir%\system32\catroot2,%windir%\system32\driversstore (this folder is redirected on all pre-windows 7 systems),%windir%\system32\drivers\etc,%windir%\system32\logfiles,%windir%\system32\spool. There are a few other features, for example, running “C:\windows\regedit.exe” from a 32-bit program will in fact run the program from C:\windows\syswow64\regedit.exe.
An additional feature is that a 32-bit program can access the 64-bit folder, Vista adds a “sysnative” alias folder in the windows folder, by accessing C:\windows\sysnative instead of C:\windows\system32, one gains access to the real C:\windows\system32 folder rather then being redirected to C:\windows\syswow64. The caveat of this feature is that most programs have validations before they accept a filename; since sysnative is not an actual folder but rather an alias used by the redirector, many validations (including the standard Open/Save dialog) can fail, meaning that one cannot “force” the access to a specific file. the main purpose is for use by the application for various reasons, not so users can access files within the folder from 32-bit applications.
The registry posesses redirection as well; the HKEY_LOCAL_MACHINE hive on x64 systems contains a key called “Wow6432node”, when a 32-bit program accesses, for example, “HKEY_LOCAL_MACHINE\Software\company\app”, they are in fact accessing “HKEY_LOCAL_MACHINE\Wow6432node\company\app”- basically, it separates 32-bit machine-specific data from 64-bit data. The only caveat is that, unlike the file system redirections, registry accesses are “mirrored” across the two; for example, COM classes are stored in HKEY_LOCAL_MACHINE\software\classes. when either HKEY_LOCAL_MACHINE\software\classes or HKEY_LOCAL_MACHINE\wow6432node\classes are have keys within changed, the change is reflected to the other location.
Additionally, certain 32-bit compiled applications can be given special treatment; if the image file (exe,dll, ocx) has the IMAGE_FILE_LARGE_ADDRESS_AWARE flag set, wow64 (the windows 32 on windows 64 emulation layer) gives it a 4GB user-mode address space, whereas with a 32-bit system it would be given a 2GB user mode address space. the flag is required, rather then being the default behaviour, because such large addresses may not have been expected when the program was written; therefore, by adding the compiler flag, you are telling windows “yes, I understand and am able to deal with the larger address space in my program”. It doesn’t actually do anything to the program itself, just changes how windows deals it memory.
another special-case is with regard to program installers for some older 32-bit programs. Many such programs used a stub 16-bit windows 3.1 program to determine the windows version, and then, launch the 32-bit installer if possible. Since 64-bit windows cannot run 16-bit applications, Microsoft decided to hack about a little fix; the followint 16-bit installer technologies have 64-bit equivalents that will be launched instead:
the list of such redirections can be found in HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\NtVdm64, the 64-bit equivalents are found in Syswow64.
For the most part, these changes make using a 64-bit operating system nearly indistiguishable from using it’s 32-bit equivalent; windows itself bears the brunt of the change, and the application developers pick up a little of the tail-end of it.
In my previous entry, I discussed and provided code that would allow for the detection of Triple-Clicks on a window. By extending the provided architecture, it is relatively trivial to allow any number of consecutive clicks; for example, detecting pentuple clicks.
Visual Basic 6; and, well- nearly any other relatively modern language; provides a way to detect mouse events. These usually include mouse down, mouse up, mouseclicks, and double clicks. Absent are any higher “N-clicks”; I speak, for the most part, of the triple click. More »

Categories
Tag Cloud
Blog RSS
Comments RSS
Last 50 Posts
Back
Back
Void « Default
Life
Earth
Wind
Water
Fire
Light 