Thursday, 23 April 2009

Windows forms application using command line arguments

This is one of those (many) little things I do infrequently enough to have to 're-learn' it every time as I just can't quite remember the syntax. But every time I google it I seem to find lots of console application examples like this:

Public Sub Main(ByVal args() As String)
'do stuff
End Sub

Well, for the (or rather my) record, here's how you do it in a winforms app:

Dim args() As String = Environment.GetCommandLineArgs()

Wednesday, 15 April 2009

Switching to SQL Compact for Revit 64 bit

I recently posted that with the advent of Revit 64 some of us might be faced with having to update old systems and code for compatibility. Specifically for me this meant having to leave behind the old but reasonably faithful OLE DB provider for Microsoft Jet (no 64-bit version was ever provided for this) and adopt something new.

Microsoft's recommended database solution for single-user desktop (and mobile) applications is the free to use and distribute SQL Server Compact Edition (CE). With an approximately 5 MB memory footprint and a less than 2 MB disk footprint, SQL Server Compact Edition can run concurrently with other applications, making it effectively invisible to the user of the application. The app I'm working with extracts data from a Revit model, stores it in a local database, and compares it with data in another local database. For this, and I'd imagine lots of Revit plugins, SQL Server CE is ideal.

Of course, if you were starting a new project, or your app was small, you might considering using SQL CE along with newer technology like LINQ, which means you don't have to get your hands dirty with SQL stuff. But in the Real World some of us have big projects and little time, and need to adapt the code we have rather than re-write.

These are some of the things I had to consider when adapting our applications, which have plenty of different coding techniques from the hands of different developers:

Converting MS Access (.mdb) files to SQL Server CE (.sdf)

I used Primeworks's Data Port Wizard. It's intuitive and easy to use, and it did the job without a hitch.

When this is done, to open and manage your sdf files you can use Microsoft SQL Server Management Studio (SSMS) 2008 (but not 2005). To get this just download the full free trial of SQL Server 2008 and in the install process just go for the management tools only.

Sorting out SQL syntax and data types

Some of your SQL queries that did work won't work any more. Here's an example:

Select count(*) from TableName where myfield='hello'

Needs to be smartened up a little:

Select count(*) from [TableName] where [myfield]='hello'

And you may be using data types that aren't recognised in SQL CE. 'text' is one of them, for which I chose to substitute 'nvarchar'. But I'm no database expert - there may have been better options.

Changing connection strings

Typically I was changing my connection strings from something like this:

Private MasterFileConnection As OleDb.OleDbConnection

Dim connstring = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=myDatabase.mdb;Jet OLEDB:Database Password=letmein;"

MasterFileConnection = New OleDbConnection(connstring)

to something like this:

Private MasterFileConnection As SqlCeConnection

MasterFileConnection = New SqlCeConnection

MasterFileConnection.ConnectionString = "Data Source=myDatabase.sdf;Persist Security Info=False;Password=letmein;"


Handling data

In this example we were using an ADODB recordset to append new rows to a table:

Dim rsRecord As ADODB.Recordset
rsRecord = New ADODB.Recordset

rsRecord.Open(TableName, DB, ADODB.CursorTypeEnum.adOpenKeyset, ADODB.LockTypeEnum.adLockOptimistic, ADODB.CommandTypeEnum.adCmdTable)

rsRecord.Fields("Fieldname").Value = newValue


and it needed to be changed to this:

Dim rsRecord As SqlCeResultSet
Dim cmd As SqlCeCommand = DB.CreateCommand()

cmd.CommandText = "select * from [TableName]"
rsRecord = cmd.ExecuteResultSet(ResultSetOptions.Updatable Or ResultSetOptions.Scrollable)

newRow = rsRecord.CreateRecord

newRow.SetValue(rsRecord.GetOrdinal("Fieldname"), newValue)


and elsewhere using an OLEDB datareader we had to move from this:

Private command As OleDbCommand
Private drawingOptions As OleDbDataReader

command = New OleDbCommand("SELECT * FROM Tablename", MasterFileConnection)

If command.Connection.State = 0 Then command.Connection.Open()
drawingOptions = command.ExecuteReader(CommandBehavior.CloseConnection)

Do While drawingOptions.Read

'do something


to a SQL CE datareader, like this:

Private command As SqlCeCommand
Private drawingOptions As SqlCeDataReader

command = New SqlCeCommand("SELECT * FROM [Tablename]", MasterFileConnection)

If command.Connection.State = 0 Then command.Connection.Open()
drawingOptions = command.ExecuteReader()

Do While drawingOptions.Read

'do something



To do the coding shown above you already need to have referenced in the System.Data.SQLServerCE.dll to your project.

To get things to work on the end-user's machine you can either add SQL CE as a pre-requisite to your setup file, which will identify if a machine has it installed or not and send the user to download it from Microsoft accordingly.

Alternatively you can create a 'private installation', which by carrying all the necessary files (sqlceer35EN.dll, sqlcese35.dll, sqlceme35.dll, sqlceqp35.dll, System.Data.SqlServerCe.dll) in your package avoids the need for bootstrapping and a possibly contentious separate download. A lot neater.

Friday, 3 April 2009

Troubleshooting my Revit installation

In a recent post I described a problem I was having with my home desktop. Revit was failing to load and the following error would display:

Unfortunately it still does this, and having exhausted seemingly all other avenues with the helpful people at Autodesk I'm now preparing to wipe my machine and start again. There were however a few useful tips I picked up along the way that are worth remembering if you have problems with Revit.

Inspect your journal file

Your journal files (C:\Program Files\Revit SomethingOrOther 20xx\Journals\***.txt) amongst many other things may give you more of an idea of what might be going wrong. If you have a recurring but unpredictable error you could compare journal files to see under what circumstances it occurs, and thereby replicate it. Failing this, take the relevant information from the journal file and google it - you might not be the only one experiencing this. Unfortunately in my case the journal file wasn't much use:

The installer log file

If like me your problem happened as soon as you installed then you may want to look at the installer log file. This can be found in your %temp% folder ("Start" -> "Run." -> type "%temp%" -> "OK"), and will be called something like "Autodesk Revit Architecture 2010 - Preview Beta Install.log". Mine's 16MB of records from the installation process. If something went wrong with install, I assume you'd see it here.

Use Dependency Walker

Dependency Walker is a brilliant tool for all developers.

It is a free utility that scans any 32-bit or 64-bit Windows module (exe, dll, ocx, sys, etc.) and builds a hierarchical tree diagram of all dependent modules. For each module found, it lists all the functions that are exported by that module, and which of those functions are actually being called by other modules. Another view displays the minimum set of required files, along with detailed information about each file including a full path to the file, base address, version numbers, machine type, debug information, and more.

Dependency Walker is also very useful for troubleshooting system errors related to loading and executing modules. It detects many common application problems such as missing modules, invalid modules, import/export mismatches, circular dependency errors, mismatched machine types of modules, and module initialization failures.

Google "Dependency Walker" and you'll end up on the free download site.

The output from DW did help a little, and seemingly narrowed my problem down to the initialisation of one of the AutoCAD UI components. In response to this, I was advised to do this:

Uninstall all Autodesk products and remove Autodesk from path variable

This is quite extreme, but all else had failed so far so I was advised to uninstall all Autodesk products. When uninstalled, I edited the system path variable by going to System Properties > Advanced > Environment Variables:

The entry highlighted in the image above shows the path you need to edit. Remove the Autodesk entry - it should appear something like this: "C:\Program Files\Common Files\Autodesk Shared".

When done, reboot, and re-install.

This didn't work for me, but it might do for you. So after hours of tinkering, I'm none the wiser. Today I'm going to compare my install and DW logs with a known-working install to see if I can spot anything obvious. Failing that, I think it's time to re-install windows :(

UPDATE: May 7th 2009. In the last week Google analytics tells me I've had nearly 40 visits from users with Revitmfc problems, so I know I'm not alone. My problem is still unresolved, I bought a new machine instead as I couldn't face a Windows re-install! If anyone does resolve this please let me know. As far as Autodesk are concerned they think I'm unique. My stats prove otherwise...