Re: Calling kernel.dll from Mathematica
- To: mathgroup at smc.vnet.net
- Subject: [mg108446] Re: Calling kernel.dll from Mathematica
- From: Todd Gayley <tgayley at wolfram.com>
- Date: Thu, 18 Mar 2010 04:32:01 -0500 (EST)
At 04:46 AM 3/16/2010, Alexey Popkov wrote: >Hello, >There are nice examples in the Documentstion on calling simple functions >defined in the Windows's kernel.dll from Mathematica. For example, it is >easy to call GetTickCount function: > >Needs["NETLink`"] >getTickCount == DefineDLLFunction["GetTickCount", "kernel32.dll", "int", {}] > >But I have not found any example on calling a function that returns a >structure. I am interested in use GlobalMemoryStatusEx function that >"retrieves information about the system's current usage of both physical and >virtual memory." In C# it is easy to get this information in numerical form: > >http://msdn.microsoft.com/en-us/library/aa366589%28VS.85%29.aspx > >But how can this be done in Mathematica? Alexey, There are several ways to get this memory information via .NET/Link, including calling the GlobalMemoryStatusEx function directly from Mathematica. The first approach I would recommend is to see if the information is available via a managed API (that is, in .NET itself), so you can avoid the hassles of calling a complex DLL function. A little googling reveals that you can get memory info from the Windows Management Interface. The relevant class is Win32_OperatingSystem (http://msdn.microsoft.com/en-us/library/aa394239(VS.85).aspx). Translating a little WMI sample code directly into Mathematica gives: In[52]:== query == NETNew["System.Management.ManagementObjectSearcher", "SELECT * FROM Win32_OperatingSystem"]; In[53]:== resultCollection == query@Get[] Out[53]== =ABNETObject[System.Management.ManagementObjectCollection]=BB The NETObjectToExpression function conveniently turns IEnumerable objects like collections into Mathematica lists. This collection will only have one item in it: In[54]== mo == First[NETObjectToExpression[resultCollection]] Out[54]== =ABNETObject[System.Management.ManagementObject]=BB The URL page above gives the various properties you can extract from this object, including TotalVisibleMemorySize, FreePhysicalMemory, TotalVirtualMemorySize, and FreeVirtualMemorySize. For example (note that the values are in Kb): In[55]:== mo["TotalVisibleMemorySize"] Out[56]== 3143336 If that isn't to your liking, or you just want to know how to call a DLL function that takes a struct, read on. One approach that is often overlooked when using .NET/Link in complex scenarios is to simply write a little C# or VB code that encapsulates the task and call it from Mathematica. The spirit of .NET/Link (and J/Link) is that you can do virtually anything directly from Mathematica without dipping into any other languages, but in some cases it's easier to write some code of your own, especially since you can often find example code out there that does almost exactly what you want. I won't demonstrate this because we can do what we need here without ever leaving Mathematica, but it's an important option to keep in mind. Here's how to call the GlobalMemoryStatusEx function from Mathematica. The first thing to do is to find some C# code that demonstrates how to do it. A great reference for calling Windows DLL functions from .NET is www.pinvoke.net, and that's where you can find a C# example for GlobalMemoryStatusEx (http://www.pinvoke.net/default.aspx/kernel32.GlobalMemoryStatusEx). Here's the C# declaration, copied from that site: [StructLayout(LayoutKind.Sequential, CharSet==CharSet.Auto)] private class MEMORYSTATUSEX { public uint dwLength; public uint dwMemoryLoad; public ulong ullTotalPhys; public ulong ullAvailPhys; public ulong ullTotalPageFile; public ulong ullAvailPageFile; public ulong ullTotalVirtual; public ulong ullAvailVirtual; public ulong ullAvailExtendedVirtual; public MEMORYSTATUSEX() { this.dwLength == (uint) Marshal.SizeOf(typeof( MEMORYSTATUSEX )); } } [return: MarshalAs(UnmanagedType.Bool)] [DllImport("kernel32.dll", CharSet == CharSet.Auto, SetLastError == true)] static extern bool GlobalMemoryStatusEx( [In, Out] MEMORYSTATUSEX lpBuffer); As you can see, you need to define a custom struct that the GlobalMemoryStatusEx function fills in. DefineDLLFunction provides some options for handling complex function declarations, but nothing that can handle this level of complexity. For situations like this, DefineDLLFunction provides a fallback mode where you can just supply a string of C# code that represents an external function declaration in all its complexity: In[57]:== globalMemoryStatusEx == DefineDLLFunction[ "[StructLayout(LayoutKind.Sequential, CharSet==CharSet.Auto)] public class MEMORYSTATUSEX { public uint dwLength; public uint dwMemoryLoad; public ulong ullTotalPhys; public ulong ullAvailPhys; public ulong ullTotalPageFile; public ulong ullAvailPageFile; public ulong ullTotalVirtual; public ulong ullAvailVirtual; public ulong ullAvailExtendedVirtual; public MEMORYSTATUSEX() { this.dwLength == (uint) Marshal.SizeOf(typeof( MEMORYSTATUSEX )); } } [return: MarshalAs(UnmanagedType.Bool)] [DllImport(\"kernel32.dll\", CharSet == CharSet.Auto, SetLastError == true)] public static extern bool GlobalMemoryStatusEx( [In, Out] MEMORYSTATUSEX lpBuffer);" ]; Out[57]== Function[Null, If[NETLink`DLL`Private`checkArgCount[GlobalMemoryStatusEx,{##1},1], Wolfram`NETLink`DynamicDLLNamespace`DLLWrapper1`GlobalMemoryStatusEx[##1], $Failed], {HoldAll}] Note that I had to make one change to the declaration from the website--the MEMORYSTATUSEX struct was declared private, but it needs to be public. So far, that was easy--find an example C# declaration for the function and paste it directly into Mathematica. There is one complication, though, which is that we need to instantiate the MEMORYSTATUSEX structure to pass it in as the argument to globalMemoryStatusEx. That struct was defined when the C# compiler was called by .NET/Link to compile the external function declaration. But what is its type name? The answer to that can be found by looking at the function definition returned by DefineDLLFunction. You can see that the GlobalMemoryStatusEx function is in the context Wolfram`NETLink`DynamicDLLNamespace`DLLWrapper1`, which corresponds to a .NET class name of Wolfram.NETLink.DynamicDLLNamespace.DLLWrapper1. That is the class that was automatically created to hold the GlobalMemoryStatusEx function. You can also see that this is the created class name by looking at LoadedNETTypes[]: In[58]:== LoadedNETTypes[] Out[58]== {NETType[Wolfram.NETLink.DynamicDLLNamespace.DLLWrapper1,1]} That class is the class in which the MEMORYSTATUSEX class definition is nested, and if you know how nested class names in .NET work, this means that it will have the .NET name of Wolfram.NETLink.DynamicDLLNamespace.DLLWrapper1+MEMORYSTATUSEX. If you don't know this, you can see the class name by asking for information about the definition of the GloalMemoryStatusEx function: In[59]:== NETTypeInfo["Wolfram.NETLink.DynamicDLLNamespace.DLLWrapper1", "Methods", "GlobalMemoryStatusEx"] (prints: ) \[FilledCircle] Methods (matching string pattern GlobalMemoryStatusEx) static bool GlobalMemoryStatusEx(Wolfram.NETLink.DynamicDLLNamespace.DLLWrapper1+MEMORYSTATUSEX lpBuffer) You can see the type name in the line above. Now we are ready to call the function. First create a new instance of the struct: In[60]:== struct == NETNew["Wolfram.NETLink.DynamicDLLNamespace.DLLWrapper1+MEMORYSTATUSEX"]; Call the function: In[61]:== globalMemoryStatusEx[struct]; In[62]:== struct@ullTotalPhys Out[62]== 3218776064 Todd Gayley Wolfram Research