One of the really fun things about being associated with the Visual Basic team is getting to see all of the varied usages to which our customers put it. From enterprise software to games, our customer base covers a wide range. The most fun to see, however, are the hobby applications, because the writers of that code come up with some pretty off-the-wall (but very cool) uses for it.
Case in point: I was contacted two weeks ago by a gentleman named David Swoboda (name used with permission), who was working on a magic trick that required him to blink the LED on an old iPac smart device. He’d found a couple of C++ APIs here that would do the trick, and he was wondering if I could help him translate it to VB so that he could use it at his next magician’s meeting.
I was intrigued, so I set about looking at the APIs that David had pointed me to. We iterated over the best solution in e-mail and, with work on both sides, came up with what follows.
LED through the code
The functionality is contained in the coredll.dll library on the device, and although the prototypes aren’t given in the SDK (as noted in the link above), the APIs are listed as:
BOOL NLedGetDeviceInfo(INT nID, PVOID pOutput);
BOOL NLedSetDevice(INT nID, PVOID pOutput);
These APIs work by passing in an integer which indicates what information was being asked for as well as memory in which to store the answer. The size and shape of the memory (actually, a structure) varies based on the integer, but in David’s case he just needed to support one structure for each API, so that nothing too complicated was needed.
I could translate these using either Declare statements or DllImport attributes. I’ve done the former several times in my blog posts, so I decided to do the latter. That, in turn, requires me to import that namespace:
Import System.Runtime.InteropServices
So, for the first API, I’ll indicate that we are going to import from the appropriate DLL:
<System.Runtime.InteropServices.DllImport(“coredll.dll”)> _
This is a function that I’m pulling from the DLL, and it isn’t part of a class, so that leads us to:
Private Shared Function NLedGetDeviceInfo( _
The first argument is an INT, which these days is defined as a 32-bit integer. Thus, we get:
ByVal nID As Integer, _
The second argument is a PVOID. Now, a PVOID is simply a pointer to an arbitrary structure – kind of like referring to a class as an Object type instead of its own specific type, but not exactly. The nature of the structure will vary based on the nID that we specified in the last step, but in David’s case I knew exactly what kind of structure he’d need to call, since his needs were simple. Let’s call the structure NLED_COUNT_INFO (as it’s called in the C++ case), and we’ll define it later. The structure must be passed ByRef, since it will be modified (see this link), so:
ByRef pOutput As NLED_COUNT_INFO) _
And then we finish off the signature by noting that this will return a Boolean:
As Boolean
End Function
Similarly, the prototype for the other call will be:
<System.Runtime.InteropServices.DllImport(“coredll.dll”)> _
Private Shared Function NLedSetDevice(ByVal nID As Integer, _
ByRef pOutput As NLED_SETTINGS_INFO) As Boolean
End Function
Now we have to deal with the structures that will be passed in, but those map pretty well to VB. There are two different structures that we care about, based on the interesting constants:
Const NLED_COUNT_INFO_ID As Integer = 0
Const NLED_SETTINGS_INFO_ID As Integer = 2
The original C++ structures are (as given in MSDN):
struct NLED_COUNT_INFO {
UINT cLeds;
};
struct NLED_SETTINGS_INFO {
UINT LedNum;
INT OffOnBlink;
LONG TotalCycleTime;
LONG OnTime;
LONG OffTime;
INT MetaCycleOn;
INT MetaCycleOff;
};
Which in turn are, for VB:
<Runtime.InteropServices.StructLayout(LayoutKind.Sequential)> _
Private Structure NLED_COUNT_INFO
Dim cLeds As UInteger
End Structure
<Runtime.InteropServices.StructLayout(LayoutKind.Sequential)> _
Private Structure NLED_SETTINGS_INFO
Dim LedNum As UInteger
Dim OffOnBlink As Integer
Dim TotalCycleTime As Integer
Dim OnTime As Integer
Dim OffTime As Integer
Dim MetaCycleOn As Integer
Dim MetaCycleOff As Integer
End Structure
[EDIT: As I was reminded in the comments, LONG translates to a 32-bit Integer and not Long on 32-bit systems (in this case, fundementally equivalent to INT), and I have therefore changed the translation appropriately above.]
That’s pretty much it. The code to make the first (i.e., 0th) LED blink would be something like:
Dim NumLeds As New NLED_COUNT_INFO
If NLedGetDeviceInfo(NLED_COUNT_INFO_ID, NumLeds) = True Then
‘ There’s at least one LED
Dim SettingsInfo As New NLED_SETTINGS_INFO
SettingsInfo.LedNum = 0
SettingsInfo.OffOnBlink = 2
If NLedSetDevice(NLED_SETTINGS_INFO_ID, SettingsInfo) <> True Then
‘ Deal with error condition somehow…
End If
Else
‘ Deal with error condition somehow
End If
Or you could similarly make clever helper functions like in the web page; i.e.:
Public Function GetLedCount() As Integer
Dim NumLeds As New NLED_COUNT_INFO
If NLedGetDeviceInfo(NLED_COUNT_INFO_ID, NumLeds) = True Then
Return CInt(NumLeds.cLeds)
End If
Return 0
End Function
Public Sub SetLedStatus(ByVal wLed As Integer, ByVal wStatus As Integer)
Dim SettingsInfo As New NLED_SETTINGS_INFO
SettingsInfo.LedNum = CUInt(wLed)
SettingsInfo.OffOnBlink = wStatus
NLedSetDevice(NLED_SETTINGS_INFO_ID, SettingsInfo)
End Sub
That’s it! I have no idea what magic trick David will be using this for – but hey, with VB behind it, it’s gotta work.
We’ll meet again…
And now, time for a bit of an announcement, I suppose. For the past couple of years or so, after my stint as Dev Manager of Visual Basic, I’ve been directing the traffic in Visual Studio, making sure that the code all got integrated correctly together and that our builds were solid. It’s been just amazingly fun (and hectic!), but now my role has changed – as of two weeks ago, I’m now the Development Manager of my division’s engineering systems, which means that I’ve acquired four additional teams under me, focused on improving the tools that both you and my division use.
Unfortunately, that means that my opportunity to write entries for this blog has diminished significantly, as my work load is now quite large (though exciting!). I will certainly try to be a “guest author” when I can in the future, and I will continue to follow up on questions posed against my previous blog posts, but for now, I’m going to have to take a hiatus on new blog work.
However, VB has the best customers, and such a solid language to work with, that I know it’s going to be hard for me to stay away!
‘Til we meet again…
–Matt–*
0 comments
Be the first to start the discussion.