Controlling Design-Time Resizing

I needed a very simple extension of the standard combobox so I setup my own UserControl. As comboboxes have a fixes height I wanted to prevent the user from being able to resize vertically.

I remembered from the past that this was a trivial task, but could not remember how it was done. It took me one hour of googling before I found the solution, so I decided to spare everyone else from the trouble and post it here.

To control the selection rules of your control you need to first setup a custom designer:

    public class ComboboxControlDesigner : ControlDesigner
    {
        public override SelectionRules SelectionRules
        {
            get
            {
                return SelectionRules.LeftSizeable |
                    SelectionRules.RightSizeable |
                    SelectionRules.Visible |
                    SelectionRules.Moveable;
            }
        }
    }

Use the override to return any selection options you want to support.

Then finally attach the designer to your UserControl using the Designer attribute:

[Designer(typeof(ComboboxControlDesigner))]

To control resizing during run-time and design-time you can also just try overriding the SetBounds method of the control:

        protected override void SetBoundsCore(int x, int y, 
            int width, int height, BoundsSpecified specified)
        {
            if (this.DesignMode)
                base.SetBoundsCore(x, y, 100, 21, specified);
            else
                base.SetBoundsCore(x, y, width, height, specified);
        }

Uninstalling by Force

Sometimes an application fails to uninstall and you are stuck in a situation where you can’t repair, remove or update it. Fortunately Microsoft provides a tool called msizap that removes all the necessary MSI metadata enabling you to again reinstall the application.

Msizap is distributed as a part of the Windows SDK available at the Microsoft download center.

To remove a installation do the following:

  1. Start the SDK command line (CMD) through the Windows SDK menu in your start menu
  2. Type in the command “msizap TP <path to the msi package>”

Even if the msizap fails to remove some data, the Windows installer should again allow you to reinstall. Note that msizap will only remove the MSI metadata, leaving all files and registry keys installed.

For more information check out MSDN.

WCF: Windows Authentication and External Users

Using Windows authentication with WCF enables developers to support centralized access management for enterprise services. As default the framework uses the computer login credentials of the Windows user accessing the service.

This default behavior works for all the cases where the computer belongs to the same domain as the server, e.g. when using a company computer. But a when external users needs to access a service that uses Windows authentication, the client needs to send credentials the server can identify. For this you need to define the credentials prior to creating the channel.

factory.Credentials.Windows.ClientCredential =
new System.Net.NetworkCredential(name, password, domain);

 
Make sure not to mix the factory.Credentials.Windows.ClientCredential and factory.ClientCredential properties.

There is also a good resource about debugging Windows authentication errors at MSDN.

WCF Streams and Timeouts

I’ve been working on a WCF service for distributing file updates. Recently I did some testing to see how well the system performs through slow connections and discovered some issues I’m glad I run into before the release.

My first discovery was that WCF (using a streaming net.tcp binding) does not perform very well with high ping round-trip-times. In this case my first test setup was quite extreme, as I connected to the service using VPN through a WLAN router connected to the internet by 3G. The ping was something around 750ms.

My second discovery was that the default SendTimeout for a service is 1 minute. This is not nearly enough for a service returning files! I believe the default timeout will cause problems even with fast connections as it only depends on the size of the largest file. If the operation is still returning data after 1 minute, the server closes the stream without warning and returning just some timeout or IO exception.

Fortunately fixing the issue was easy, I just needed to increase the SendTimeout of the service:

binding.SendTimeout = new TimeSpan(1, 0, 0); // 1 hour

If you change the SendTimeout, you should also consider increasing the default 10 minute ReceiveTimeout of the client binding.

There is of course a small security risk in incresing the timeouts, but in my case it’s ok as my service will only be used in a internal network. I wonder if there is a way to define an operation specific SendTimeout?

AutoCAD and .NET

Since AutoCAD 2008 Autodesk ObjectARX® has provided managed wrapper classes that enable you to use .NET to write commands and extensions for AutoCAD. These managed classes enable you to throw away the old COM type libraries and adapt a new way of interacting with AutoCAD in a way much similiar to working with a database. Unfortunately there are quite little examples available to help get started.

The assemblies you need are acdbmgd.dll and acmgd.dll located under the AutoCAD installation directory and also included with the Autodesk ObjectARX SDK. When you reference to these, make sure you change the value of the “Copy local” property of the reference to false as the libraries are already loaded when AutoCAD calls your classes.

To create a new command or extension for AutoCAD you need to setup a class library project and create the necessary classes:

using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[assembly: CommandClass(typeof(AutoCadDemo.MyCommands))]
[assembly: ExtensionApplication(typeof(AutoCadDemo.MyExtensionApplication))]
 
namespace AutoCadDemo
{
    public class MyCommands
    {
        [CommandMethod("MYCOMMAND")]
        public void MyCommand()
        {
            // Command implemenation
        }
    }
 
    public class MyExtensionApplication : IExtensionApplication
    {
        public void Initialize()
        {
            // Extension implemenation
        }
 
        public void Terminate()
        {
            // Extension disposer
        }
    }
}

As you can see, assembly attributes are used to tell AutoCAD what classes it should load. You also need to use attributes to define each command. To load your commands and extensions in AutoCAD you need to use the netload command. There is also a way to use the registry to define what assemblies should be loaded when AutoCAD starts.

The following example shows how to start a transaction, get input from the user and create a new object:

private void DrawCircle()
{
    // Uses editor to prompt user for center point and radius
    Editor editor = Application.DocumentManager.MdiActiveDocument.Editor;
 
    PromptPointResult pointResult = editor.GetPoint("Select center point:");
    if (pointResult.Status != PromptStatus.OK)
        return;
 
    PromptDistanceOptions radiusOptions = new PromptDistanceOptions("Radius:");
    radiusOptions.BasePoint = pointResult.Value;
    radiusOptions.UseBasePoint = true;
 
    PromptDoubleResult radiusResult = editor.GetDistance(radiusOptions);
    if (radiusResult.Status != PromptStatus.OK)
        return;
 
    // Starts transaction to enable editing of drawing
    Database database = Application.DocumentManager.MdiActiveDocument.Database;
    Transaction transaction = database.TransactionManager.StartTransaction();
    {
        try
        {
            // Creates new object representing a circle
            Circle circle = new Circle(pointResult.Value, Vector3d.ZAxis, 
                radiusResult.Value);
            BlockTableRecord activeSpace = (BlockTableRecord)transaction.GetObject(
                database.CurrentSpaceId, OpenMode.ForWrite);
 
            // Adds object to active space
            activeSpace.AppendEntity(circle);
 
            // Adds newly created object to transaction
            transaction.AddNewlyCreatedDBObject(circle, true);
 
            // Commits changes
            transaction.Commit();
        }
        catch (System.Exception)
        {
            // Rollbacks changes on error
            transaction.Abort();
        }
    }
}

Things work a bit differently compared to the old COM interface used in the VBA macros and most Autodesk examples. Each object has a object id that is used to call the object. Database object has properties that provide the ids of most of the main collections e.g. blocks table.

As the drawing file can be seen as a database also access to it can be shared. This means you need to define the minimum access level (open mode) you need to complete your actions. The active transaction enables you to rollback any changes you have made in case something goes wrong.

The wrapper classes also enable you to store data into the drawing in such a way not accessable by the user through the AutoCAD user interface. For this you can either use the database specific Named Object Dictionary (NOD) or the Extenion dictionary of every object.

public void AddItemToNod(string key, string value)
{
    // Starts transaction
    Database database = Application.DocumentManager.MdiActiveDocument.Database;
    Transaction transaction = database.TransactionManager.StartTransaction();
 
    try
    {
        // Retrieves the NOD table and inserts a new record
        DBDictionary nod = (DBDictionary)transaction.GetObject(
            database.NamedObjectsDictionaryId, OpenMode.ForWrite);
 
        ResultBuffer buffer = new ResultBuffer();
        buffer.Add(new TypedValue((int)DxfCode.Text, value));
 
        Xrecord record = new Xrecord();
        record.Data = buffer;
 
        nod.SetAt(key, record);
 
        // Adds newly created record to transaction
        transaction.AddNewlyCreatedDBObject(record, true);
 
        // Commits changes
        transaction.Commit();
    }
    catch (System.Exception)
    {
        transaction.Abort();
    }
}

By using XML serialization you can basically store any object data into the drawing.

Also the classic way of storing data under the file properties (summary info) is possible:

public void AddItemToSummaryInfo(string key, string value)
{
    Database database = Application.DocumentManager.MdiActiveDocument.Database;
 
    // Uses builder to edit the summary info (file properties) of the drawing
    DatabaseSummaryInfoBuilder infoBuilder = 
        new DatabaseSummaryInfoBuilder(database.SummaryInfo);
 
    infoBuilder.CustomPropertyTable.Add(key, value);
 
    database.SummaryInfo = infoBuilder.ToDatabaseSummaryInfo();
}

You should check out Kean Walmsleys blog “Through the Interface” for good tips on developing extensions for AutoCAD using .NET.

Setup Project: Custom Actions

Sometimes you need to add some custom actions to your setup project to complete the installation by running tools like regasm, ngen or caspol. The last time I was forced to do this was when my Inventor Add-in (COM based) did not get registered properly. The problem was that the Register=vsdrpCOM setting of the project output did not get the assembly codebase set. I needed to get my setup to include the codebase in the assembly registration to get my add-in installed.

The first step was that I added a new class library project to my solution and added there a new class called RegasmInstaller.cs:

[RunInstaller(true)]
public partial class RegasmInstaller : Installer
{
    public RegasmInstaller()
    {
        InitializeComponent();
    }

    public override void Install(IDictionary stateSaver)
    {
        base.Install(stateSaver);

        Regasm(true);
    }

    public override void Rollback(IDictionary savedState)
    {
        base.Rollback(savedState);

        Regasm(false);
    }

    public override void Uninstall(IDictionary savedState)
    {
        base.Rollback(savedState);

        Regasm(false);
    }

    private void Regasm(bool register)
    {
        string file = base.Context.Parameters["Assembly"];
        if (string.IsNullOrEmpty(file))
            throw new InstallException("Assembly not defined");
        if (!File.Exists(file))
            return;

        RegistrationServices regsrv = new RegistrationServices();
        Assembly assembly = Assembly.LoadFrom(file);

        if (register)
        {
            regsrv.RegisterAssembly(assembly, AssemblyRegistrationFlags.SetCodeBase);
        }
        else
        {
            try
            {
                regsrv.UnregisterAssembly(assembly);
            }
            catch
            {
                //Exceptions are ignored: even if the unregistering failes
                //it should notprevent the user from uninstalling
            }
        }
    }
}

The basic idea in writing a installer class is to override the methods you need. Override the install method to perform whatever actions you need and override the rollback and uninstall methods to undo these changes. You usually only need to override the commit method if you need to cleanup any temporary files. Don’t forget to start every override by calling the base function first!

The CustomActionData property of each custom action can be used to pass parameters to the installer class. In the installer class these parameter values can be read from the Context.Parameters dictionary. In my installer I used the property to define the assembly I wanted to get registered.

To get my installer class to actually register an assembly I had two options:  to use the Process class to call regasm.exe or to use the RegistrationServices class. My first version used the regasm.exe, but I later changed it to use the service class, both options are fine.

The second step was to add the project output of the installer class library to the setup project and then add the actions I needed.  For each action I selected the project output containing my installer class.

Setup Actions

Finally I set the CustomActionData property of each action to define the path to the target assembly.

CustomActionData Property

Don’t use “Add Service Reference”!

The Visual Studio tools have always been crappy at generating code. The result is acceptable if you are a Microsoft representative selling new technology at some fancy seminar, but it is nothing you want to use in a release version of you application. The Visual Studio “Add Service Reference” (WCF) feature makes no exception.

The “Add Service Reference” feature might become handy when you need to connect to some none-.NET services, but in most other cases I sincerely recommend the manual approach. In this article I’m only going to tell you have, if you need more information check Miguel A. Castro’s article “WCF the Manual Way…the Right Way“.

Start by createing a basic class library project and reference the System.ServiceModel. This library is going to be referenced by both the client and the server. Don’t use the WCF templates as they will only make your life harder on the long run.

Add your service interface to the newly created project:

[ServiceContract(Name = "TestService", Namespace = "http://www.company.com/tests")]
public
interface ITestService
{
    
    [OperationContract]
    TestData GetData(TestData data);

}

[DataContract]
public
class TestData
{
   
public TestData(string message)
    {
        Message = message;
    }

    [DataMember]
    public
string Message { get; set; }

   
public
override string ToString()
    {
        return “Some shared override”;
    }
}

Now add a new project to your solution and implement the server the same way you normaly would:

public class TestService: ITestService
{

    #region ITestService Members

    public TestData GetData(TestData data)
    {
        return new TestData(“Hello world! + data.Message);
    }

    #endregion
}

Instead of messing around with the XML configuration file, just configure your host by code:

ServiceHost host = new ServiceHost(typeof(TestService), new Uri[] { new Uri(uri) });
NetTcpBinding
binding = new NetTcpBinding(SecurityMode.Transport);

binding.TransferMode = TransferMode.Streamed;
binding.MaxBufferSize = 65536;

binding.MaxReceivedMessageSize = 104857600;
host.AddServiceEndpoint(typeof(ITestService), binding, “service1″);

host.Open();

As you can see there is not much new to implementing the service, but now it’s time for the client!

Create a new project for the client and reference the class library you first created. Instead of going for the “Add Service Reference” option create your own proxy:

public class TestServiceClient: ITestService
{

    private ITestService service;
    
    public TestServiceClient(Uri uri)
    {
        
// Any channel setup code goes here
   
     EndpointAddress address = new EndpointAddress(uri);
        NetTcpBinding binding = new NetTcpBinding(SecurityMode.Transport);
        binding.TransferMode = TransferMode.Streamed;
        binding.MaxBufferSize = 65536;
        binding.MaxReceivedMessageSize = 104857600;

        ChannelFactory<ITestService> factory = new ChannelFactory<ITestService>(binding, address);
        service = factory.CreateChannel();
    }

    #region ITestService Members

    public TestData GetData(TestData data)
    {
        return service.GetData(data);
    }

    #endregion
}

You can now place useful functions in your data classes and utilize them both on the client and server side. If you are running with .NET 3.5 you can even leave out the Data and Member Contract attributes of the data classes (same as choosing “Reuse types in referenced assemblies” when using thee Add Service Reference feature).

The biggest advantages of building your proxy manually compared to using the Add Service Reference feature is that you can now share classes and have the same client class implement several service interfaces instead of only one.

Test Run Deployment Issue

I replaced one of my test files with an image from the internet. I was a bit surprised when I got the following error when trying to run a test that was working just minutes before:

Failed to queue test run ‘MYCOMPUTER 2008-12-08 15:11:35′: Test Run deployment issue: The location of the file or directory ‘Tests\Material\TestPic.jpg’ is not trusted.

 

I then googled around and found the following thread on the MSDN forums: http://social.msdn.microsoft.com/Forums/en-US/vststest/thread/1a9c13b3-e8b9-4619-9159-3edbfc67b8a9/

 

What you need to do is unblock the file. Go to your deployment files folder and open the file properties:

 

Blocked file

 

 

Notice the security note on the bottom. Click on the Unblock button and also uncheck the readonly checkbox.

 

After pressing OK Visual Studio will again accept the file.

 

 

Closing Returned Streams in WCF

Streams should always be closed after usage to free the resources behind them. WCF web service functions returning values likes streams are no exception. You should never rely on your client to do this but for some reason many WCF streaming examples overlook this.

To correctly dispose return values WCF provides you with two options:

  • Setting the OperationBehaviorAttribute.AutoDisposeParameters to true
  • Using the OperationCompleted event.

I like to use the event as it gives me more control.

Here is what I think is a correctly implemented GetFile method:

public Stream GetFile(string path) {
   Sream fileStream = null;    

   try   
   {
      fileStream = File.OpenRead(path);
   }
   catch(Exception)
   {
      return null;
   }

   OperationContext clientContext = OperationContext.Current;
clientContext.OperationCompleted += new EventHandler(delegate(object sender, EventArgs args)
   {
      if (fileStream != null)
         fileStream.Dispose();
   });

       return fileStream;
}

The dispose method should not throw an error even if the client has already correctly closed the stream.

More info: http://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontext.operationcompleted.aspx

Serializing DateTime to XML

The XmlConvert class has been obsolete since .NET 2.0. I found this out when I needed to manually serialize and deserialize DateTime values to XML and all the examples used the old class. After some researching I found out that the following call will return the date time alue in the same format as the XmlSerializer uses:

DateTime.Now.ToString("o");
Returns “2008-11-17T12:28:09.9862678+02:00″

The value is easily converted back using the DateTime.Parse() function.

If you happen to need the functions of the XmlConvert class, you can achive the same results by combining the XmlSerializer with the TextWriter/TextReader classes. The following deserializes a XML value back to the correct type.

XmlSerializer dateSerializer = new XmlSerializer(type);
StringReader reader = new StringReader(stringValue);
return dateSerializer.Deserialize(reader);