Archive for the ‘code’ Category
wireframe
What’s the “minimum” amount of C# code you need to create a spinning wireframe quad in 3D?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
namespace Wireframe1
{
public partial class Form1 : Form
{
List<Vertex> vertices = new List<Vertex>();
Point prev = new Point();
Point curr = new Point();
Point first = new Point();
float angle = 0.0f;
public Form1()
{
InitializeComponent();
vertices.Add(new Vertex(-1.0f, -1.0f, 0.0f));
vertices.Add(new Vertex(-1.0f, 1.0f, 0.0f));
vertices.Add(new Vertex( 1.0f, 1.0f, 0.0f));
vertices.Add(new Vertex( 1.0f, -1.0f, 0.0f));
}
private void OnTick(object sender, EventArgs e)
{
this.Invalidate();
}
private void OnPaint(object sender, PaintEventArgs e)
{
// rotate quad a little
int rotationTime = 8000;
int time = System.Environment.TickCount % rotationTime;
angle = time * (2.0f * (float)Math.PI) / rotationTime;
for (int i = 0; i < 4; ++i)
{
// we don't want to alter the original vert
Vertex v = new Vertex(vertices[i].x,
vertices[i].y,
vertices[i].z);
float oldX = vertices[i].x;
float oldY = vertices[i].y;
float oldZ = vertices[i].z;
// y-axis rotation
v.x = oldX * (float) Math.Cos(angle) +
oldZ * (float)Math.Sin(angle);
v.z = oldX * (float)-Math.Sin(angle) +
oldZ * (float)Math.Cos(angle);
// view transform - translate inverse cam
v.z -= -2.4f; // move camera back slightly
// - simple perspective divide (not the best looking)
// - note: building a perspective matrix with an
// arbitrary field of view is more involved
v.x /= v.z;
v.y /= v.z;
// screen transform
v.x = v.x * 640 / 2 + 640 / 2;
v.y = -v.y * 480 / 2 + 480 / 2;
curr.X = (int)v.x;
curr.Y = (int)v.y;
if (i == 0)
{
first = curr;
prev = curr;
continue;
}
e.Graphics.DrawLine(Pens.Black, prev, curr);
prev = curr;
} // end for
e.Graphics.DrawLine(Pens.Black, curr, first);
} // end OnPaint
} // end Form
public struct Vertex
{
public float x;
public float y;
public float z;
public Vertex(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
} // end Vertex
} // end namespace
signing C++/CLI assemblies
Stubbornness is a form of persistence and I’m continually amazed at my inability to give something up and move on – at least when it comes to programming. Getting a signed C++/CLI DLL to work with a C# project had me stumped for a while. Here’s the process I went through.
First, create a new solution:
Now add, a C++/CLI class library project:
I’ve called the project ClassLibrary1 and added some trivial code:
namespace ClassLibrary1
{
public ref class Class1
{
public: void Foo()
{
Console::WriteLine("Bar");
}
};
}
The first step to making this a strong-named DLL is to generate a cryptographic key pair using sn.exe. This command line utility is located under the “Microsoft Visual Studio 8SDKv2.0Bin” directory path. To execute sn.exe you’ll need a Run prompt. To bring up the Run prompt hit the Windows+R key combo. Now type CMD and hit Enter:
You have to navigate to the Bin directory mentioned earlier. The DOS command to do this is called CD (change directory). For example:
Call sn.exe with the -k option to generate a new key pair to the specified file. In this example, a new key pair is generated and written to the file ClassLibrary1.snk:
Grab the newly generated file from the Bin folder and cut ‘n paste it into your project folder:
Inside of AssemblyInfo.cpp, add the following line of code:
[assembly:AssemblyKeyFile("ClassLibrary1.snk")];
Compile the project and mt.exe (manifest tool) returns the following:
general warning 810100b3: ..debugClassLibrary1.dll is a strong-name
signed assembly and embedding a manifest invalidates the signature.
You will need to re-sign this file to make it a valid assembly.
Okay…So let’s get rid of the embedded manifest:
A rebuild proves successful. Now add a CLR console app:
Right-click the project and select References to bring up the following dialog:
Under the Projects tab select ClassLibrary1 and click OK:
You should see the newly added ClassLibrary1 in the References section. None of the Build Properties need to be altered.
I’ve added the following code to test things out:
using namespace System;
using namespace System::Reflection;
int main(array<System::String ^>^ args)
{
ClassLibrary1::Class1 c1;
c1.Foo();
for each(Assembly^ a in AppDomain::CurrentDomain->GetAssemblies())
{
Console::WriteLine(a->FullName);
}
Console::ReadLine();
return 0;
}
Set ConsoleApplication1 as the startup project and test it app. Output example:
The output shows the full name of ClassLibrary1 with PublicKeyToken and hash value. Now, you may think everything is working fine but let’s see what happens when a C# project tries to use ClassLibrary1.dll. Add a new C# console project and a reference to ClassLibrary1:
As soon as you do so, the following warning pops up:
And sure enough, if you compile it, the build fails with a ResolveNativeReference task failure. So, delete the reference to ClassLibrary1. Add the reference again but this time, do so from the Browse tab:
Recompile and it builds successfully. Now, just because it compiles doesn’t mean it works, so let’s test it out.
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
ClassLibrary1.Class1 c1 = new ClassLibrary1.Class1();
c1.Foo();
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
Console.WriteLine(a.FullName);
}
Console.ReadLine();
}
}
}
Hit F5 to launch a debug session and:
The specified module could not be found. Great…but which module? After messing with this failure for a long time it turns out I should have signed the assembly in a different manner (not using the AssemblyKeyFile attribute). Have you ever signed a C# DLL? If you open the AssemblyInfo.cs file (it’s inside the Properties folder) and add the following:
[assembly: AssemblyKeyFile("SomeCSharpLibrary.snk")]
It returns with a warning not to use it.
Use command line option '/keyfile' or appropriate project settings instead of
'AssemblyKeyFile'
Visual Studio prefers you go under project properties to set the stong name.
Okay. Perhaps we can do something similar with the C++/CLI DLL? In the ClassLibrary1 project, remove the AssemblyKeyFile attribute from the AssemblyInfo.cpp file. Under the project properties, restore Embed Manifest to Yes. Now go to the Advanced section and set:
A rebuild proves successful. Run it and it works! Finally…
In the future, don’t forget to add the library via the Browse tab or you’ll get a ResolveNativeReference error.
Hello Kondo!
I’m sure the marketing department will tell you that the Kondo KHR-1 is a programmable humanoid robot capable of amazing feats.
It looks humanoid. It’s programmable – via the RCB-1 board mounted on its back. It’s certainly a robot. However, in its current state it’s nothing more than a bunch of servos held together by aluminum brackets. Only through programmability can we bring it to life.
Set aside your dreams of artificial intelligence for the moment. The first thing we need to accomplish is basic movement. The equivalent of a “Hello World!” app for the KHR-1 is the following:
Move the slider, move the head. Once you learn to move one servo, you can move many.
Communication between the PC and the RCB-1 happens through the serial port. If your computer (ie. laptop) doesn’t have a serial port, you can purchase a USB to serial port adapter.
Thankfully, the release of .NET 2.0 saw the introduction of a SerialPort class (found under the System.IO.Ports namespace). You can drag ‘n drop an instance of the SerialPort control from the Toolbox onto your Form. Here”s the Form constructor:
public Form1()
{
InitializeComponent();
this.serialPort1 = new System.IO.Ports.SerialPort();
this.serialPort1.BaudRate = 115200;
// change to whatever port # you're using
this.serialPort1.PortName = "COM4";
this.serialPort1.ReadTimeout = 30; // ms timeout
try
{
serialPort1.Open();
}
catch(Exception e)
{
MessageBox.Show(e.Message + "nnBe sure to change the port # in code");
}
}
If the serial port is successfully opened, trackbar events will be routed to the RotateHead method:
private void OnTrackbarScroll(object sender, EventArgs e)
{
if (serialPort1.IsOpen)
this.RotateHead((byte)this.trackBar1.Value);
}
To communicate with the RCB-1, fill an array of bytes and send them out the port. How do you know what bytes to send? A command reference PDF is available for download, however it’s not written in English. Fortunately, it has been translated. Here is page six from the RCB1stdraft.doc included with the download:
This page details the command used to send positional data to one or more servos. TX stands for transmission. An array of 16 bytes needs to be filled with the following information:
- Byte 1 – The command. 0xFD to flag a servo position change.
- Byte 2 – The board ID. The KHR-1 has two RCB-1 boards. One board controls servos 1-12, the other board controls servos 13-24.
- Byte 3 – The servo speed. Eight speeds are supported with 0 being fastest and 7 the slowest.
- Bytes 4-15 – The servo degree. There are 12 channels (corresponding to servos on either board) specifying the degree of rotation.
- Byte 16 – A checksum. This is a value computed from the contents of the packet. The RCB-1 will calculate the checksum (given the contents) to determine if the data was sent correctly.
public void RotateHead(byte degree)
{
byte[] by = new byte[16];
byte command = 0xFD;
byte boardId = 0;
byte speed = 0; // 0 = fastest, 7 = slowest
by[0] = command;
by[1] = boardId;
by[2] = speed;
// you can rotate the servos 0 to 180
// so set unused channels to a value beyond this range
by[3] = 255; // ch 1
by[4] = 255; // ch 2
by[5] = 255; // ch 3
by[6] = 255; // ch 4
by[7] = 255; // ch 5
by[8] = degree; // ch 6 - the head on my kondo
by[9] = 255; // ch 7
by[10] = 255; // ch 8
by[11] = 255; // ch 9
by[12] = 255; // ch 10
by[13] = 255; // ch 11
by[14] = 255; // ch 12
by[15] = (byte)(command + boardId + speed + (255 * 11 + degree) & 0x7F);
Trace.WriteLine("sending...");
this.serialPort1.Write(by, 0, by.Length);
}
You can download the source here.
powermate
The Griffin PowerMate is a USB input device. In addition to the hardware, it comes with a driver and a control panel. The PowerMate control panel allows you to map a “User Action” to a “Computer Action”. For example, when the user rotates the knob right, the page in Internet Explorer will scroll down:
For the majority of end users, this is all the customization they need. However, wouldn’t it be nice if we could program directly against the PowerMate? That’s the focus of today’s post and here’s a screenshot of the resulting app:
The first requirement to programming the PowerMate is a copy of the Windows Driver Development Kit (DDK). The Windows Server 2003 SP1 DDK is free. However, you will have to pay for shipping and handling of the CD to your door. The option to download is available only to MSDN subscribers. After installation, the directory paths will need to be set. Add the DDK directory path to the start of the search path (bottom of the list) to avoid compilation errors.
The Solution distributed with this post has two projects:
- C++/CLI class library for the PowerMate code
- C# application to test the PowerMate.dll
Let’s begin by listing the types and data members of the PowerMate class. First, simple enums to represent button state and rotational direction:
public: enum class ButtonState
{
Up,
Down
};
public: enum class RotationalDirection
{
Left,
Right
};
Next, we have a private enum representing human interface device (HID) attributes for the PowerMate hardware. These values will be compared against HID attributes for each device returned during the enumeration process.
private: enum class HidAttributes
{
VendorID = 0x077d,
ProductID = 0x0410,
VersionNumber = 0x0311
};
A kernel object handle to the HID. We’ll be using this handle for read/write operations later on.
private: HANDLE handleToDevice;
A managed thread handle. The thread created will be used for event notification and only terminates when the exit event is triggered.
private: Thread^ inputThread;
private: ManualResetEvent^ exitEvent
Next, we have storage for a rotational value that is clamped between the upper and lower bounds. By default, the bounds are between -100 and 100. If a user continues rotating the knob beyond the specified range the event will be ignored and the delegate will not fire. Of course, you’re free to alter this feature.
private: int rotationValue;
private: int rotationLowerBound;
private: int rotationUpperBound;
Finally, delegates for button and rotation events:
public: delegate void RotationHandler(RotationalDirection direction, int value);
public: delegate void ButtonHandler(ButtonState bs);
private: RotationHandler^ rotationDelegate;
private: ButtonHandler^ buttonDelegate;
Clients can subscribe to events using the following event accessors:
public: event RotationHandler^ RotateEvent
{
void add(RotationHandler^ value)
{
rotationDelegate += value;
}
void remove(RotationHandler^ value)
{
rotationDelegate -= value;
}
}
public: event ButtonHandler^ ButtonEvent
{
void add(ButtonHandler^ value)
{
buttonDelegate += value;
}
void remove(ButtonHandler^ value)
{
buttonDelegate -= value;
}
}
Before we examine more properties of the PowerMate class, let’s first have a look at its two-stage construction. The constructor is trivial so we can safely skip it. After construction, the Initialize method needs to be called. Initialize will enumerate available HID devices and open the first PowerMate device it encounters. Once you’ve successfully programmed one PowerMate device, you can easily modify the code to suport many – perhaps by creating a PowerMateManager class to maintain a list of every PowerMate connected to the system.
The Initialize method begins by finding and shutting down the PowerMate Control Panel. Why? The PowerMate Control Panel intercepts input and executes the appropriate action stored in its settings. An example of one such setting is ”Increase volume” under Global settings. As our app responds to the user performing a right rotation, the PowerMate Control Panel also responds to this action and increases the system volume. To obtain exclusive access to the hardware, we need to close the PowerMate Control Panel (PowerMate.exe).
Please note that all of the code related to the Initialize method has been stripped clean of of error handling and conditionals for presentation purposes.
array^ powerMateProcess = Process::GetProcessesByName("PowerMate");
HANDLE hp = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE,
FALSE,
powerMateProcess[0]->Id);
HWND hWnd = FindWindow(NULL, L"PowerMateWnd");
PostMessage(hWnd, WM_CLOSE, 0, 0);
WaitForSingleObject(hp, 5000);
Now it’s time to put that DDK to work. This section of the Initialize method involves the following steps:
- Get the HID GUID.
- Obtain a handle to a device information set.
- Enumerate device interfaces.
- Open a device.
- Check for matching HID attributes.
Step 1: Get the HID GUID.
This GUID represents a device interface that the driver exposes. Device interfaces with the same functionality are grouped together in interface classes. For example, all joysticks on the system could be members of the joystick device interface class.
GUID hidClass;
HidD_GetHidGuid(&hidClass);
Step 2: Obtain a handle to a device information set.
A device information set consists of device information elements. A device information element contains a list of all device interfaces associated with a particular device. The SetupDiGetClassDevs function retrieves a set of devices for the specified class. In other words, a list of HID-compliant devices.
HDEVINFO hDevInfoSet = SetupDiGetClassDevs(&hidClass,
NULL,
NULL,
DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
Step 3: Enumerate device interfaces.
To communicate with the device, we need to open the device. To open the device, we need a device path. The device path is retrieved during the enumeration process and involves the following steps:
- For each device class in the device information set, call SetupDiEnumDeviceInterfaces.
- Each call to the above function returns an SP_DEVICE_INTERFACE_DATA structure.
- We pass the above structure to SetupDiGetDeviceInterfaceDetail.
- The above method returns an SP_DEVICE_INTERFACE_DETAIL_DATA structure.
- The SP_DEVICE_INTERFACE_DETAIL_DATA structure contains the device path.
SP_INTERFACE_DEVICE_DATA interfaceData;
interfaceData.cbSize = sizeof(interfaceData);
for (int i = 0;
SetupDiEnumDeviceInterfaces(hDevInfoSet,
NULL,
&hidClass,
i,
&interfaceData);
++i)
{
// determine buffer size first
DWORD bufferLength;
SetupDiGetDeviceInterfaceDetail(hDevInfoSet,
&interfaceData,
NULL,
0,
&bufferLength,
NULL);
// then allocate
PSP_INTERFACE_DEVICE_DETAIL_DATA interfaceDetail =
(PSP_INTERFACE_DEVICE_DETAIL_DATA)new char[bufferLength];
interfaceDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
SetupDiGetDeviceInterfaceDetail(hDevInfoSet,
&interfaceData,
interfaceDetail,
bufferLength,
NULL,
NULL);
Step 4: Open a device.
With a successful call to SetupDiGetDeviceInterfaceDetail above we now have a device path to pass into CreateFile. Upon success, the CreateFile function will open the device and return a handle to it.
HANDLE hDevice = CreateFile(interfaceDetail->DevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
Step 5: Check for matching HID attributes.
We check the attributes of the newly opened device to verify it’s a PowerMate input device.
HIDD_ATTRIBUTES hidAttr;
BOOLEAN result = HidD_GetAttributes(hDevice, &hidAttr);
if ((int)HidAttributes::ProductID == hidAttr.ProductID &&
(int)HidAttributes::VendorID == hidAttr.VendorID)
{
// ...
inputThread = gcnew Thread(gcnew ThreadStart(this, &PowerMate::InputThread));
inputThread->Priority = ThreadPriority::Normal;
inputThread->Start();
return true;
}
When a PowerMate input device is found, an input thread is started for event notification. We”ll have a look at the input thread shortly, but first two-stage destruction:
public: void Shutdown()
{
if (nullptr != exitEvent)
{
exitEvent->Set();
inputThread->Join();
}
if (NULL != handleToDevice)
CloseHandle(handleToDevice);
Process^ p = Process::Start(
"C:Program FilesGriffin TechnologyPowerMatePowerMate.exe");
HWND hWndTaskBar = FindWindowEx(NULL, NULL, L"Shell_TrayWnd", NULL);
HWND hWndTrayNotify= FindWindowEx(hWndTaskBar, NULL, L"TrayNotifyWnd", NULL);
HWND hWndSysPager = FindWindowEx(hWndTrayNotify, NULL, L"SysPager", NULL);
HWND hWndToolBar = FindWindowEx(hWndSysPager, NULL, L"ToolbarWindow32", NULL);
RECT rect;
::GetClientRect(hWndToolBar, &rect);
for (WORD x = 0; x < rect.right; x += 8)
{
::SendMessage(hWndToolBar,
WM_MOUSEMOVE,
0,
MAKELPARAM(x,0));
}
}
Shutdown begins by signaling the exit event. Join blocks the calling thread until the input thread terminates.
Next, we start a new instance of the PowerMate Control Panel (after shutting it down during Initialization). It”s hardwired to the default installation directory. However, it’s advisable that you pull this path out of the registry. The InstallDir key is located under: HKEY_LOCAL_MACHINESOFTWAREGriffin TechnologyPowerMate1.5 and you can use regedit to verify this.
The last part is silly and if you have a better way to fix it please let me know. The icon in the task notification area does not update automatically. Here, we simulate a mouse traveling across the notification area to force an update.
The input thread drops into a while loop that only breaks when it receives an exit event. The exit event is checked during wait timeouts which occur every 2000 milliseconds. With each iteration a call to ReadFile is made. The OVERLAPPED structure is used for asynchronous I/O. The event member of the OVERLAPPED structure is signaled when the read operation is complete. Upon a successful read, a report buffer consisting of eight characters is returned. Indices in the report buffer are checked for a button or rotational event and the corresponding delegate is fired.
private: void InputThread()
{
char reportBuffer[8] = {0};
DWORD dwBytesRead = 0;
BOOL result = FALSE;
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
OVERLAPPED overLap;
ZeroMemory( &overLap, sizeof(overLap));
overLap.hEvent = hEvent;
while(true)
{
result = ReadFile(handleToDevice,
reportBuffer,
sizeof(reportBuffer),
&dwBytesRead,
&overLap);
DWORD dw = WaitForSingleObject(hEvent, 2000);
switch(dw)
{
case WAIT_OBJECT_0:
{
// test for pushbutton
if (reportBuffer[1] == 1)
{
ButtonState bs = ButtonState::Down;
EventsHelper::Fire(buttonDelegate, bs);
}
else if (reportBuffer[2] == 0 && reportBuffer[1] == 0)
{
ButtonState bs = ButtonState::Up;
EventsHelper::Fire(buttonDelegate, bs);
}
// test for knob rotation
if (reportBuffer[2] != 0)
{
rotationValue += reportBuffer[2];
if (rotationValue < rotationLowerBound)
{
rotationValue = rotationLowerBound;
break;
}
if (rotationValue > rotationUpperBound)
{
rotationValue = rotationUpperBound;
break;
}
EventsHelper::Fire(rotationDelegate,
(reportBuffer[2] < 0) ?
RotationalDirection::Left :
RotationalDirection::Right,
rotationValue);
} // end if
} break;
case WAIT_TIMEOUT:
{
if (exitEvent->WaitOne(20,false))
{
CloseHandle(hEvent);
return;
}
} break;
} // end switch
} // end while
} // end method
The EventsHelper class continues to publish events even if a subscriber throws an exception:
public ref class EventsHelper
{
public: static void Fire(Delegate^ del, ... array^ args)
{
if (nullptr == del)
return;
array^ delegates = del->GetInvocationList();
for each (Delegate^ sink in delegates)
{
try
{
sink->DynamicInvoke(args);
}
catch(Exception^ e)
{
}
}
}
};
Finally, we can start manipulating properties of the PowerMate input device. Setting various properties involve the same basic steps:
- Filling out report buffers and transmitting them.
- Receiving report buffers and acting on them.
For example, getting or setting the LED brightness:
public: property Byte LedBrightness
{
void set(Byte value)
{
if (NULL != handleToDevice)
{
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
OVERLAPPED overLap;
ZeroMemory(&overLap, sizeof(overLap));
overLap.hEvent = hEvent;
UCHAR reportBuffer[2];
reportBuffer[0] = 0;
reportBuffer[1] = value;
DWORD dwBytesTransmitted = 0;
BOOL result = WriteFile(handleToDevice,
reportBuffer,
sizeof(reportBuffer),
&dwBytesTransmitted,
&overLap);
DWORD dw = WaitForSingleObject(hEvent, 42);
switch(dw)
{
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
break;
}
CloseHandle(hEvent);
}
}
Byte get()
{
UCHAR reportBuffer[7] = { 0 };
if (NULL != handleToDevice)
{
BOOLEAN error = HidD_GetInputReport(handleToDevice,
reportBuffer,
sizeof(reportBuffer));
if( FALSE == error )
Trace::WriteLine("Failure to get input report - brightness");
}
return reportBuffer[4];
}
}
The two other properties are ”pulse on” and ”pulse speed”. Please reference the code for implementation details.
Those are the basics of PowerMate programming. Special thanks to Dustin at Griffin for making this possible. The code still needs refactoring and more support for error handling. However, it’s surely enough to get you started. You can download the source here.


























