On The Blink (Matt Gertz)

On The Blink (Matt Gertz)

  • Comments 5

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--*

 

Leave a Comment
  • Please add 6 and 3 and type the answer here:
  • Post
  • Any time you need to access C or C++ APIs through VB, just create the code automatically with the P/Invoke Interop Assistant from the Microsoft Interop team.

    Copy the C++ structure declarations and function definitions into the tool and it will generate the VB automatically for you.

    It's fantastic and lets you avoid time-consuming and error prone manual conversion. You still need to help it out a little (e.g. in this case change the PVOID pOutput into

    NLED_SETTINGS_INFO *pOutput and NLED_COUNT_INFO *pOutput)

    http://www.codeplex.com/clrinterop

  • ... there's been several posts on this blog about the P/Invoke assistant from Jared Parsons, who helped write it

  • Nice, I hope the new version of VB will be beter

  • A WIN32 LONG is a 32-bit integer, so why would you translate it to a VB Long (64-bit) in your structure?

  • Couple comments, then:

    Yes, the P/Invoke tool is very cool!  In fact, I was the dev manager of VB at the time Jared demonstrated it to me, and I remember nearly falling out of my chair, I was so pleased to see it!  That doesn't mean that we can't have fun figuring the translations out on our own, though.

    That being said, "Bleh" is correct that I goofed on on the LONG->long translation (and should have thereby used the tool :-)).  I'll fix it in the blog.

    --Matt--*

Page 1 of 1 (5 items)