Sending Data to Océ Printer Using .NET

I was asked to turn a old Inventor printing macro into a .NET add-in. This was a fairly simple task and only translating the SendKeys and Shell commands to code required some thinking.

To print using a Océ 9600 large format printer the macro called the following shell command to send the custom settings along with the plot data (generated using Inventors PrintManager.PrintToFile function) to the printer:

"cmd /c copy settings.txt /a + drawing.plt /b "

This told me I needed to first read the custom settings from a file and turn it into ASCII, then combine that with the data in the binary plot file and finally send it to the printer.

I did not know how to send the data to the printer, so I just tried opening a file stream using the printer’s address (without expecting it to work). This gave me the following exceptionally informative error message:

"FileStream was asked to open a device that was not a file. For support for devices like 'com1:' or 'lpt1:', call CreateFile, then use the FileStream constructors that take an OS handle as an IntPtr."

The message told me exactly what I needed to do. I needed to use the CreateFile Windows API call to get a handle I could pass the the FileStream class. As this was the final piece of the puzzle I quite quickly produced the following code:

public void WriteToPrinter(string pltFile, string settings, string printer)
{
    IntPtr handle = IntPtr.Zero;
 
    try
    {
        handle = CreateFile(printer,
            (uint)EFileAccess.GenericWrite,
            (uint)(EFileShare.Read | EFileShare.Write),
            (uint)0,
            (uint)(ECreationDisposition.OpenExisting),
            (uint)(EFileAttributes.NoBuffering),
            (int)IntPtr.Zero);
 
        if (handle.ToInt32() == -1)
        {
            int hresult = Marshal.GetHRForLastWin32Error();
            Marshal.ThrowExceptionForHR(hresult);
        }
 
        using (FileStream stream = new FileStream(handle, FileAccess.Write))
        {
            using (BinaryWriter writer = new BinaryWriter(stream))
            {
                byte[] asciiArray = Encoding.Convert(Encoding.UTF8, 
                    Encoding.ASCII, File.ReadAllBytes(settings));
 
                writer.Write(asciiArray);
 
                byte[] binaryArray = File.ReadAllBytes(pltFile);
                writer.Write(binaryArray);
 
                writer.Close();
            }
        }
    }
    finally
    {
        if (handle != IntPtr.Zero && handle.ToInt32() != -1)
            CloseHandle(handle);
    }
}
 
#region Windows API calls
 
[DllImport("kernel32", SetLastError = true)]
static extern unsafe IntPtr CreateFile(
    string FileName, 
    uint DesiredAccess, 
    uint ShareMode, 
    uint SecurityAttributes, 
    uint CreationDisposition, 
    uint FlagsAndAttributes, 
    int hTemplateFile);
 
[DllImport("kernel32", SetLastError = true)]
static extern unsafe bool CloseHandle(
    IntPtr hObject);  
 
[Flags]
public enum EFileAccess : uint
{
    GenericWrite = 0x40000000
}
 
[Flags]
public enum EFileShare : uint
{
    Read = 0x00000001,
    Write = 0x00000002
}
 
public enum ECreationDisposition : uint
{
    OpenExisting = 3
}
 
[Flags]
public enum EFileAttributes : uint
{
    NoBuffering = 0x20000000
}
 
#endregion
Advertisements

WCF and Domain Trust Failure

Lately users have been getting the following error when accessing a WCF services that uses Windows authentication.

"The trust relationship between the primary domain and the trusted domain failed."

Not all users are getting this error despite that they all belong to the same domain.

Trying to find a solution I came up with three options:

  • Remove any inactive trusted domains from Active Directory
  • In the local security policy of the server change the cache value of the “Interactive Logon: Number of previous logons to cache” to 0 (zero)
  • Change the unhandled exception policy back to the default behavior of previous .NET Framework versions.

As we needed a quick fix, the tech guys picked the last option and added the following lines to the Aspnet.config in the %WINDIR%\Microsoft.NET\Framework\v2.0.50727 directory:

<configuration>
<
runtime>

<legacyUnhandledExceptionPolicy enabled="true" />

</runtime>
</
configuration>

After restarting the server the users could once again access the service.

I believe the second option of disabling the logons cache would be a better solution, but that one it yet to be tested.

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);
        }