Archive

Posts Tagged ‘compact framework’

Memory Stats on Mobile Device

December 10th, 2009 robber.baron No comments

Recently I was working on a mobile project that was experiencing some serious memory leak issues. It turns out that I wasn’t disposing for a SqlCeResultSet properly and on each query (of which there was substantial amount) I was calling New() without Dispose().

After a day of feeling like my project was falling apart before my eyes and getting weird and unreliable results from the .NETCF Remote Profiler I searched far a wide for some way to determine the available memory on a device. On MSDN I found the following code (slightly modified by me) to get relevant information.

Basically two P/Invokes later I had the information I needed, saw that I was in fact leaking memory, quickly determined where, and restored sanity to my project. Within thirty minutes of implementing this class the world was a happier place for me.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
Imports System.Text
 
Public Class MEMORYSTATUSINFO
    Public Structure MEMORYSTATUS
        Public dwLength As UInt32
        Public dwMemoryLoad As UInt32
        Public dwTotalPhys As UInt32
        Public dwAvailPhys As UInt32
        Public dwTotalPageFile As UInt32
        Public dwAvailPageFile As UInt32
        Public dwTotalVirtual As UInt32
        Public dwAvailVirtual As UInt32
    End Structure
 
    Public Declare Function GlobalMemoryStatus Lib "CoreDll.Dll" _
    (ByRef ms As MEMORYSTATUS) As Integer
 
    Public Declare Function GetSystemMemoryDivision Lib "CoreDll.Dll" _
        (ByRef lpdwStorePages As UInt32, _
        ByRef ldpwRamPages As UInt32, _
        ByRef ldpwPageSize As UInt32) As Integer
 
    Public Shared Function ShowMemory() As String
        Dim storePages As UInt32
        Dim ramPages As UInt32
        Dim pageSize As UInt32
        Dim res As Integer = _
            GetSystemMemoryDivision(storePages, ramPages, pageSize)
 
        ' Call the native GlobalMemoryStatus method
        ' with the defined structure.
        Dim memStatus As New MEMORYSTATUS
        GlobalMemoryStatus(memStatus)
 
        Dim load As Integer = memStatus.dwMemoryLoad / (1024 * 1024)
        Dim totPhys As Integer = memStatus.dwTotalPhys / (1024 * 1024)
        Dim availPhys As Integer = memStatus.dwAvailPhys / (1024 * 1024)
        Dim totalPageFile As Integer = memStatus.dwTotalPageFile
        Dim availPageFile As Integer = memStatus.dwAvailPageFile
        Dim totVirtual As Integer = memStatus.dwTotalVirtual / (1024 * 1024)
        Dim availVirtual As Integer = memStatus.dwAvailVirtual / (1024 * 1024)
 
        ' Use a StringBuilder for the message string.
        Dim MemoryInfo As New StringBuilder
        MemoryInfo.Append("Memory Load: " _
            & load.ToString() & "Mb" & vbCrLf)
        MemoryInfo.Append("Total Physical: " _
            & totPhys & "Mb" & vbCrLf)
        MemoryInfo.Append("Avail Physical: " _
            & availPhys & "Mb" & vbCrLf)
        MemoryInfo.Append("Total Page File: " _
            & totalPageFile.ToString() & vbCrLf)
        MemoryInfo.Append("Avail Page File: " _
            & availPageFile.ToString() & vbCrLf)
        MemoryInfo.Append("Total Virtual: " _
            & totVirtual.ToString() & "Mb" & vbCrLf)
        MemoryInfo.Append("Avail Virtual: " _
            & availVirtual.ToString() & "Mb" & vbCrLf)
 
        ' Show the available memory.
        Return MemoryInfo.ToString()
    End Function
End Class

Hope this helps someone else find greedy memory hog code in their apps.

log4net with Compact Framework

October 13th, 2009 robber.baron 1 comment

My favorite logging library log4net work not only on desktop and web applications but can be used on mobile devices with the compact framework.

Granted not all of the Appenders make sense within the compact framework. Since mobile devices have neither a system log nor a console it doesn’t make much sense to have these appenders. For a complete list of supported frameworks and appenders see here.

Applications written in the compact framework don’t have app.config files.  This presents an issue when attempting to create an ILog.  Here is my work around.

First create an XML that will be copied to source on deployment.  Here is an example:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <log4net debug="false">
    <appender name="logger" type="log4net.Appender.RollingFileAppender">
      <file value="\SD Card\Assessment\DamageAssessmentLog.txt" />
      <appendToFile value="true" />
      <maxSizeRollBackups value="2" />
      <maximumFileSize value="1048576" />
      <rollingStyle value="Composite" />
      <staticLogFileName value="false" />
      <layout type="log4net.Layout.PatternLayout">
        <header value="[Header]&#13;&#10;"/>
        <footer value="[Footer]&#13;&#10;"/>
        <conversionPattern value="%newline Date: %date ,Application: %a ,Logger: %logger ,Thread: [%thread] ,Level: %level %newline Message: %message%newline Exception: %exception%newline" />
      </layout>
      </appender>
      <!-- Setup the root category, add the appenders and set the default level -->
      <root>
        <level value="DEBUG" />
        <appender-ref ref="logger" />
      </root>
    </log4net>
</configuration>

This should all be familiar so far. Basically whatever was going to go into app.config will now be in this file.

Then within your application’s startup you need to explicitly point log4net to that configuration file.

Shared Sub Main()
        Try
            Dim logconfig = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase), "logging.config")
 
            log4net.Config.XmlConfigurator.Configure(New FileInfo(logconfig))
 
            Dim logger As ILog = log4net.LogManager.GetLogger("logger")
 
            Using map As formMap = New formMap()
                logger.Debug("Staring application")
                map.ShowDialog()
                logger.Debug("Application closed")
            End Using
            AssessmentConnector.Instance.CloseConnection()
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        Finally
            log4net.LogManager.Shutdown()
        End Try
    End Sub

Here you can see that I get the application’s executing path and append the xml file’s name. Then I call the log4net.Config.XmlConfigurator to initialize the logging service from the provided xml file.

Note: Due to limitations of the compact framework it is important that you call log4net.LogManager.Shutdown() to explicitly close your log files. Otherwise you’ll end up with a hanging process and open log files.

Scope Identity with SqlCe

October 6th, 2009 robber.baron No comments

Normally I write my data access layer using an ORM like nHibernate as I thoroughly enjoy the speed, flexibility and power that comes from using such tools. Recently at work, however, I have been doing a lot of Windows Mobile development and I although there are compact framework ORMs I didn’t have the experience enough to want to use one in a very important project.

As a result the following code is something I had to look up and was actually an annoying thing to find. I wanted to return the identity of a newly inserted object. Normally nHibernate would set this for me but as I’m not using nHibernate I have to do this myself.

using (connection = new SqlCeConnection(CONNECTION_STRING))
{
     connection.Open();
 
     var command = new SqlCeCommand(@"INSERT INTO photos (path, format) VALUE (?, ?) SELECT SCOPE_IDENTITY();");
     command.Parameters.Add(New SqlCeParameter());
     command.Parameters.Add(New SqlCeParameter());
     command.Parameters(0).Value = path;
     command.Parameters(1).Value = format;
 
     var id = command.ExecuteScalar();
 
     Debug.WriteLine(String.Format("New Identifier = {0}", id));
}

This is insert the photo into the database and return the new id for the photo rather than the number of rows effected. The key is the ExecuteScalar function.

How cool is it that log4net works for Compact Framework apps. Has been a huge lifesaver.