Andrew Pollack's Blog

Technology, Family, Entertainment, Politics, and Random Noise

Marshaling data in and out of managed code -- may have some value for Domino API work as well

By Andrew Pollack on 03/14/2006 at 12:19 PM EDT

Part of the work I've been doing requires that I make use of an external library in the form of a DLL. This article describes about 2/3 of the techniques needed for the process. If you like it, I'll write another one that deals with the other side of the coin --- "callbacks". That's a tricky bit of work in vs.Net.

vs.Net applications are like Java in that they run in their own runtime engine. In Java, its the JVM. In vs.Net its called the "CLR" (Common Language Runtime). One big disadvantage to the CLR is that you can't just easily pass data in and out of it. Its actually pretty complicated -- and in doing so you remove the benefits of the runtime environment. Normally, you don't worry about memory allocation at all. The CLR does that for you. When you're working with Unmanaged Memory, you have to allocate it to your variables and if you don't remember to free it when you're done, it creates a "memory leak". If all your memory leaks away the computer will forget how to work until you reboot.

The code that follows is a snippet which I find well illustrates about 2/3 of the issues surrounding these calls. There are three parts to look at. First, is the actual declaration that defines the function call and what data gets sent to and returned from the DLL itself. In this case, its a bit complicated. You pass in five pointers (memory locations) with room to store numeric values. The function will fill those locations with useful information, and will return to your function another integer value just indicating if it succeed or failed.

So, in the first part, we're telling the CLR that we want call a function called "iaxc_audio_devices_get" which is located in "iaxclient.dll". We want to pass it five numbers, and get back a number. We don't really tell the CLR what the numbers are for, just that they're numbers. In this case, each number is a POINTER to a memory slot -- like a cubbyhole -- which has enough room to hold pretty big number. What we know, because we read the documentation for the library, is that these numbers we get back when we go back and read the data at those locations will represent the following:

1. Another pointer, which is a memory location where we can find an array of records, each of which is an audio device description.
2. The total number of records held in the memory location described in number 1 above
3. Of the records pointed to by the value in number 1, which device is currently set to accept input
4. Of the records pointed to by the value in number 1, which device is currently set to accept output
5. Of the records pointed to by the value in number 1, which device is currently set to be used for RINGING noises

The second bit of code defines the structure of an audio device definition. Remember, we're going to get back a pointer to a group of these and a number telling us how many in the group. Notice that one part of the structure is itself a pointer to yet another place in memory where a text value is stored with the name of the device. That's because text strings vary in size, so you can't stack them neatly.

Finally, the third bit of code is the subroutine that allocates the 5 memory locations (cubbyholes), calls the library, and reads the data that got placed in the cubbyholes. Then it copies that information to variables we don't have to worry about and tells the system that it doesn't need the cubbyholes any more. Finally, it looks at the data and reads the text descriptions from the locations given for each, and prints the result.

' this is the function declaration for the call to the unmanaged dll itself
' note that the use of "_" allows me to wrap the lines so it isn't just
' just one long line of text.
Friend Declare Function iaxc_audio_devices_get Lib "iaxclient.dll" Alias _
"iaxc_audio_devices_get" (ByVal devlistptrInt As Integer, _
ByVal ndevsptrInt As Integer, ByVal InputptrInt As Integer, _
ByVal OutputptrInt As Integer, ByVal ringptrInt As Integer) As Integer

Structure iaxc_audio_device
' this is the structure definition - we will read an array of these
' from unmanaged memory by their address in memory
Public devnameptrint As Integer
Public caps As UInt32
Public devid As Integer
End Structure

Friend Sub getDevices()
' allocate pointer space in unmanaged memor
Dim devListPtr As IntPtr = Marshal.AllocHGlobal(4)
Dim devNoPtr As IntPtr = Marshal.AllocHGlobal(4)
Dim inputPtr As IntPtr = Marshal.AllocHGlobal(4)
Dim outputPtr As IntPtr = Marshal.AllocHGlobal(4)
Dim ringPtr As IntPtr = Marshal.AllocHGlobal(4)
' create managed memory value holders
Dim devlistPointer As IntPtr, devno As Integer, output As Integer, _
input As Integer, ring As Integer, result As Integer
' ********************************************************************
' Here is the actual API call to iaxclient.dll
' ********************************************************************
' fill the unmanaged locations with values
result = iaxc_audio_devices_get(devListPtr.ToInt32, devNoPtr.ToInt32, _
inputPtr.ToInt32, outputPtr.ToInt32, ringPtr.ToInt32)
' ********************************************************************
' ********************************************************************
' copy the unmanaged values to managed variables
devlistPointer = Marshal.ReadIntPtr(devListPtr, 0)
devno = Marshal.ReadInt32(devNoPtr, 0)
input = Marshal.ReadInt32(inputPtr, 0)
output = Marshal.ReadInt32(outputPtr, 0)
ring = Marshal.ReadInt32(ringPtr, 0)
' free the unmanaged memory to prevent leaks
' now try to read the array
Dim devices(devno - 1) As iaxc_audio_device
Dim devicenames(devno - 1) As String
Dim x As Integer
For x = 0 To devno - 1
' read raw bytes from unmanaged memory and convert them into a managed structure
devices(x) = CType(Marshal.PtrToStructure(devlistPointer, GetType(iaxc_audio_device)), iaxc_audio_device)
' One of the structure members is itself a pointer to a memory location with
' a character string, containing the text value we actually want.
' read that string from a memory location we get from the pointer
devicenames(x) = Marshal.PtrToStringAnsi(New IntPtr(devices(x).devnameptrint))
' now advance the memory location pointer forward by the size of one record
devlistPointer = IntPtr.op_Explicit(devlistPointer.ToInt32 + Marshal.SizeOf(devices(x)))
' lets see what we ended up with
Debug.WriteLine("Audio in: " + devicenames(input))
Debug.WriteLine("Audio Out: " + devicenames(output))
Debug.WriteLine("Ring Sound: " + devicenames(ring))
End Sub

There are  - loading -  comments....

Bring it on!By Julian Robichaux on 03/14/2006 at 10:25 PM EDT
I'd like to see the rest of the technique, even if I'm not going to use it
right away. It's always good to see that kind of stuff and keep it in the back
of your mind.

Even more interesting to me would be examples of calling a .NET DLL from
LotusScript. I haven't had to venture down that road yet, but I know I will
I doubt you'll ever do quite that...By Andrew Pollack on 03/15/2006 at 12:41 AM EDT
..the idea of a ".net dll" isn't really something you'd ever call from outside
so far as I know. .NET applications compile to byte code that runs in the CLR
-- just like java classes compile to byte code that runs in the JVM. It would
be very unusual, I think, to call into a CLR unless I'm missing something.

Other Recent Stories...

  1. 01/26/2023Better Running VirtualBox or VMWARE Virtual Machines on Windows 10+ Forgive me, Reader, for I have sinned. I has been nearly 3 years since my last blog entry. The truth is, I haven't had much to say that was worthy of more than a basic social media post -- until today. For my current work, I was assigned a new laptop. It's a real powerhouse machine with 14 processor cores and 64 gigs of ram. It should be perfect for running my development environment in a virtual machine, but it wasn't. VirtualBox was barely starting, and no matter how many features I turned off, it could ...... 
  2. 04/04/2020How many Ventilators for the price of those tanks the Pentagon didn't even want?This goes WAY beyond Trump or Obama. This is decades of poor planning and poor use of funds. Certainly it should have been addressed in the Trump, Obama, Bush, Clinton, Bush, and Reagan administrations -- all of which were well aware of the implications of a pandemic. I want a military prepared to help us, not just hurt other people. As an American I expect that with the ridiculous funding of our military might, we are prepared for damn near everything. Not just killing people and breaking things, but ...... 
  3. 01/28/2020Copyright Troll WarningThere's a copyright troll firm that has automated reverse-image searches and goes around looking for any posted images that they can make a quick copyright claim on. This is not quite a scam because it's technically legal, but it's run very much like a scam. This company works with a few "clients" that have vast repositories of copyrighted images. The trolls do a reverse web search on those images looking for hits. When they find one on a site that looks like someone they can scare, they work it like ...... 
  4. 03/26/2019Undestanding how OAUTH scopes will bring the concept of APPS to your Domino server 
  5. 02/05/2019Toro Yard Equipment - Not really a premium brand as far as I am concerned 
  6. 10/08/2018Will you be at the NYC Launch Event for HCL Domino v10 -- Find me! 
  7. 09/04/2018With two big projects on hold, I suddenly find myself very available for new short and long term projects.  
  8. 07/13/2018Who is HCL and why is it a good thing that they are now the ones behind Notes and Domino? 
  9. 03/21/2018Domino Apps on IOS is a Game Changer. Quit holding back. 
  10. 02/15/2018Andrew’s Proposed Gun Laws 
Click here for more articles.....

pen icon Comment Entry
Your Name
*Your Email
* Your email address is required, but not displayed.
Your thoughts....
Remember Me  

Please wait while your document is saved.