Category Archives: .net Framework

Serialize and Deserialize objects as Xml using generic types in VB.Net

Standard

image

In his blog post Serialize and deserialize objects as xml using generic types in C# 2.0 , Paul Whitaker creates some ubiquitous functions for doing Xml Serialization with generics.

 

To serialize:

Dim lsXml As String = GenericXmlSerializer.SerializeObject(of MyObjectType)(myObject)

 

To deserialize:

Dim myObject As MyObjectType = GenericXmlSerializer.DeserializeObject(of MyObjectType)(lsXml)

 

Here’s the code

Imports System.Collections.Generic
Imports System.Text
Imports System.Xml
Imports System.IO
Imports System.Xml.Serialization

Class GenericXmlSerializer
    ''' <summary>
    ''' To convert a Byte Array of Unicode values (UTF-8 encoded) to a complete String.
    ''' </summary>
    ''' <param name="characters">Unicode Byte Array to be converted to String</param>
    ''' <returns>String converted from Unicode Byte Array</returns>
    Public Shared Function UTF8ByteArrayToString(ByVal characters As Byte()) As String

        Dim encoding As New UTF8Encoding
        Dim constructedString As String = encoding.GetString(characters)
        Return constructedString

    End Function

    ''' <summary>
    ''' Converts the String to UTF8 Byte array and is used in De serialization
    ''' </summary>
    ''' <param name="pXmlString"></param>
    ''' <returns></returns>
    Public Shared Function StringToUTF8ByteArray(ByVal pXmlString As String) As Byte()

        Dim encoding As New UTF8Encoding()
        Dim byteArray As Byte() = encoding.GetBytes(pXmlString)
        Return byteArray
    End Function

    ''' <summary>
    ''' Serialize an object into an XML string
    ''' </summary>
    ''' <typeparam name="T"></typeparam>
    ''' <param name="obj"></param>
    ''' <returns></returns>
    Public Shared Function SerializeObject(Of T)(ByVal obj As T) As String

        Try
            Dim xmlString As String = Nothing

            Dim memoryStream As New MemoryStream()
            Dim xs As New XmlSerializer(GetType(T))
            Dim xmlTextWriter As New XmlTextWriter(memoryStream, Encoding.UTF8)
            xs.Serialize(xmlTextWriter, obj)
            memoryStream = CType(xmlTextWriter.BaseStream, MemoryStream)
            xmlString = UTF8ByteArrayToString(memoryStream.ToArray())
            memoryStream.Dispose()

            Return xmlString
        Catch

            Return String.Empty
        End Try
    End Function

    ''' <summary>
    ''' Reconstruct an object from an XML string
    ''' </summary>
    ''' <param name="xml"></param>
    ''' <returns></returns>
    Public Shared Function DeserializeObject(Of T)(ByVal xml As String) As T

        Dim xs As New XmlSerializer(GetType(T))
        Dim memoryStream As New MemoryStream(StringToUTF8ByteArray(xml))
        Dim xmlTextWriter As New XmlTextWriter(memoryStream, Encoding.UTF8)

        Dim theObject As T = CType(xs.Deserialize(memoryStream), T)
        memoryStream.Dispose()
        Return theObject

    End Function

End Class

 

Rather than be original, I converted most of his code verbatim and have found the functions useful. 

I offer them to you warranty free, unadorned and hopefully useful.

 

 


Share this post :

A Simple Scroll Controller for Winforms

Standard

 image

I am currently researching “flick” scrolling for Windows XP and over.  As part of this I need the ability to scroll controls in code.

To do this I have created a wrapper around the scrolling apis to assist with this.

Here’s how to use it for an autoscrolling panel:

 

Dim loScrollIt As New ScrollController(Panel1)

loScrollIt.VerticalScroll(20)

 

Here’s the code:

Imports System.Runtime.InteropServices
Public Class ScrollController

    ' Scrollbar direction
    '
    Const SBS_HORZ = 0
    Const SBS_VERT = 1

    ' Windows Messages
    '
    Const WM_VSCROLL = &H115
    Const WM_HSCROLL = &H114
    Const SB_THUMBPOSITION = 4

    Private Declare Function GetScrollPos Lib "user32.dll" ( _
        ByVal hWnd As IntPtr, _
        ByVal nBar As Integer) As Integer

    'Example: position = GetScrollPos(textbox1.handle, SBS_HORZ)
    Private Declare Function SetScrollPos Lib "user32.dll" ( _
        ByVal hWnd As IntPtr, _
        ByVal nBar As Integer, _
        ByVal nPos As Integer, _
        ByVal bRedraw As Boolean) As Integer

    'Example: SetScrollPos(hWnd, SBS_HORZ, position, True

    Private Declare Function PostMessageA Lib "user32.dll" ( _
        ByVal hwnd As IntPtr, _
        ByVal wMsg As Integer, _
        ByVal wParam As Integer, _
        ByVal lParam As Integer) As Boolean

    'Example: PostMessageA(hWnd, WM_HSCROLL, SB_THUMBPOSITION _
    '                         + &H10000 * position, Nothing)

    Private moControl As Control

    Public Sub New(ByVal controlToScroll As Control)

        moControl = controlToScroll
    End Sub
    Public Sub VerticalScroll(ByVal amount As Integer)
        Dim liHwnd As IntPtr = moControl.Handle
        Dim Position = GetScrollPos(liHwnd, SBS_VERT) + amount

        If (SetScrollPos(liHwnd, SBS_VERT, Position, True) <> -1) Then

            PostMessageA(liHwnd, WM_VSCROLL, SB_THUMBPOSITION + _
                                       &H10000 * Position, Nothing)
        End If

    End Sub

End Class

More VSTO fun : WindowActivate in Outlook/Word 2007 does not fire for Wordmail more than once

Standard

image

Welcome to the horrible world of creating Outlook addins that work for both 2003 and 2007.

People have commented that wordmail has a horrible side effect in Word 2003, where toolbars used in Outlook inspectors show up in Word.  See the this Kevin Slovak’s discussion of the problem in the links section.

Anyway, the workaround mentioned in the article doesn’t work (of course) in Outlook 2007.  It is therefore necessary to check the version of Word (which doesn’t have toolbars) for the code to work for both versions.

It makes me wish Microsoft would hand out free office upgrades.

Anyway, here’s the complete code:

 

' Listen for inspector activate
Private WithEvents moWord As Microsoft.Office.Interop.Word.Application
Private moInspector As Inspector
Private Sub moWord_WindowActivate(ByVal Doc As Microsoft.Office.Interop.Word.Document, ByVal Wn As Microsoft.Office.Interop.Word.Window) Handles moWord.WindowActivate

     If Wn.EnvelopeVisible AndAlso moInspector IsNot Nothing Then
         mShowInspector(moInspector)
         moInspector = Nothing
         moWord = Nothing
         If Doc.AttachedTemplate IsNot Nothing Then Doc.AttachedTemplate.Saved = True
     End If

End Sub
Private Sub moInspectors_NewInspector(ByVal Inspector As Microsoft.Office.Interop.Outlook.Inspector) Handles moInspectors.NewInspector

     Dim lbRaiseEvents As Boolean = True

     If Inspector.IsWordMail Then
         ' HACK: Wordmail does not work in the same way in
         ' Word 2008 as it does in 2003.  I think this
         ' may be because of the lack of real toolbars
         ' in Word 2007.
         If Val(Inspector.WordEditor.Application.Version) < 12 Then
             moInspector = Inspector
             moWord = Inspector.WordEditor.Application
             lbRaiseEvents = False
         End If
     End If
     If lbRaiseEvents Then mShowInspector(Inspector)

End Sub

 

This code is provided in case some poor soul out there gets the problem as well. 

 

Links

Problem with Inspector CommandBars and MS Word [WiredBox.Net - Office Newsgroups]

Inspector Wrapper for Wordmail VSTO and vb.net

Musings about the memory usage of WPF and Winforms Applications

Standard

Newcomers to Winforms and WPF .net applications are sometimes alarmed about the amount of memory they are shown to use in Task Manager.  

When these applications run in a Terminal Server environment (or Citrix) the memory consumption seems to add up.  I have heard of products that somehow reduce the memory consumption of processes in this environment.   The makers of these products claimed to be able to fit more processes on the one server. I made it my mission to find out how these applications may work.

Minimizing as a Lifestyle

A hint as to how they may work is the behaviour of the application when it’s minimised.  Memory usage definitiely goes down.  When the application is normalised, memory consumption goes back up, but not as much.

For example, with a WPF application with a single window and no controls, according to Task Manager uses 27388K.  On minimise of the application, memory consumption goes down to 3128K.  Normalising the application, the memory consumption goes back to 8306K.

Apparently minimizing the application makes a call to the Win32 api call SetWorkingSet, this is what causes the apparent memory reduction.  Looking at the history of Windows, this behaviour makes sense.  In the Windows 3.1 user interface, minimizing applications was the way a user typically indicated to Windows they were switching to something else.

Something I wonder about is whether this is still appropriate?  Since Windows 95 introduced the Task Switcher, do people still minimise?    Another question.  In the Terminal Server environment, is the call being made when a user becomes idle?  Should it be?

SetWorkingSet(-1,-1)

Microsoft’s documentation of the call indicates that application calls to SetWorkingSet are not necessary, as the operating system will manage the memory as required.   Is this automatic memory management assuming the Windows 3.1 usage pattern? 

Two years ago I created a prototype that called SetWorkingSet when an application had become idle for a certain amount of time.  It seemed to work well, but I had no evidence it helped system performance particularly.

I apologise for the inconclusive nature of this post, but I thought I would put my musing out there in case others had anything to comment.

 

Links

Reducing WinForm Memory Footprint with SetWorkingSet

How much memory does my .NET application use?

WPF Memory Usage

 


Share this post :

iPhone

Standard

“In more bad news for Windows Mobile, Apple shipped more iPhones in the quarter than the total of all handsets based on the Microsoft system, even though their numbers actually increased by 42%.” (See article Apple and RIM tussle for Q4 prizes as smartphones boom)

I’m really sorry, but I don’t think Windows Mobile 6.5 or 7 is going to make any difference, it seems that Microsoft is not going to be able to produce anything compelling enough, soon enough.   I don’t think there’s going to be a “comeback”.

In case you think that the iPhone is all hype and marketing, I have a question for you.  Have you ever tried using one?  I have yet to meet anyone who has had less than a “revelatory” experience using an iPhone. 

I’ve had a great experience developing for the platform (I think the Compact Framework is excellent).

I believe the future of mobile computing is and is going to be huge, but I’m afraid Windows Mobile isn’t going to play a significant role anymore. 

Future handset operating systems will be various, including of course the iPhone and Android.

Other mobile devices such as tablet computers and netbooks will probably continue to run Windows, so I think Microsoft should concentrate their efforts on continuing to ensure relevance on this form factor.

For developers I think the news is all good.  There are great opportunities out their in mobility, if we are willing to “move on” in our choice of development technologies. 

 

Links

Permanent Link to IE6 for Windows Mobile – Better, but not brilliant

The Mobile Spoon: What happens when a Windows Mobile Addict breaks his vow and buys an iPhone?

Apple and RIM tussle for Q4 prizes as smartphones boom

 


Share this post :

Estranged Siblings – Silverlight Mobile and the Compact Framework CLR

Standard

An illustration that contemplates the cojoined nature of Silverlight and the Compact Framework

 

At PDC 2008 in the session Microsoft Silverlight 2 for Mobile- Developing for Mobile Devices, Amit Chopra revealed some interesting details about Silverlight Mobile that I haven’t seen written elsewhere.

The presentation mainly consists of “here’s Silverlight running on Windows Mobile”.   The interesting bits are at the end.  I dear reader, present these to you here.

 

Compact Framework, small but mighty

The compact framework, is small yet mighty.  (Some may say mighty ugly...by default)

The Compact Framework provides a great deal of rich device integration and sheer programmability. It is a pleasure to create software for.  I even think that Compact Framework based applications have been a reason for customers, particularly in the Mobile Fieldworker areas to adopt Windows Mobile devices.

If you’re a Compact Framework developer like me, you may have been frustrated by the default “yesterday” appearance of the System.Windows.Forms forms engine.  It takes a great deal of work to make forms look good. 

Silverlight, a Framework Apart?

Two frameworks, Silverlight and Compact Framework, so alike, yet so different.

I for one was very excited last year at the thought of a Silverlight (ie WPF) graphics engine being available for Mobile Device development.  I was then saddened to learn that the Silverlight runtime would not be extending the Compact Framework, but would exist in addition to Compact Framework.

This is disappointing news. (I am now showing my disappointed face….beware!) It appears that Microsoft (at the moment) seem determined to give Silverlight minimal access to the device.  This is aligned with the idea that Silverlight is only a competitor to traditional Flash app that runs from a Web page. 

Although this may be acceptable for certain kinds of desktop oriented Silverlight applications, I doubt this is a believable use case for mobile developers.  The power of mobility is not only the ability to access data anywhere, but the ability to talk to the features of the device.

In the presentation mentioned at the outset, there was declaration of the intention to support Webcams, accelerometer and other device features in the future, depending on the support for this on other platforms (eg Windows and Mac). 

So, we are restricted to the lowest common denominator.  You may believe this is for technical reasons.  It isn’t. Because….

Silverlight Uses the Compact Framework!

Diagram of Silverlight Architecture on Windows Mobile

Amit said that the Silverlight runtime for Windows Mobile may be smaller because it may omit certain codecs and it also shares the Compact Framework Clr.    He went on to say that the Compact Framework would be a prerequisite to installing the Silverlight runtime.

A new and nobler purpose for Silverlight Mobile

I give you....Silver-Knight

It’s Microsoft’s belief that targeting Windows Mobile 6.? (Sorry about that WM2003 and 5) and Nokia phones will mean Web users will eschew DHTML and Flash websites for Silverlight ones.  

With the proliferation of device operating systems (particularly that pesky iPhone) I don’t think this is a likely scenario.  Do you?  Well do you?

I’ve I got an idea!  As it’s unlikely Microsoft will backtrack on their “one platform everywhere” philosophy for Silverlight, why not, in addition enable Silverlight as a cutdown WPF for the Compact Framework on Windows Mobile? Microsoft, I know you can!  Doing this would earn the gratitude of all Compact Developers and provide a side benefit to their current Silverlight efforts on Mobile.

Call to Action

If you agree, please add a comment or bookmark this article using the links below, blog about this yourself or email the people at Microsoft.   Amit said they wanted feedback, lets give it!

(BTW Any physical resemblance between Amit and myself is entirely coincidental, so don’t say it)

 

Links

Microsoft Silverlight 2 for Mobile- Developing for Mobile Devices

Amit Chopra’s Blog (Email) – WIndows Mobile Program Mananger

Giorgio Sardo’s Blog (Email: gisardo @ microsoft  com) – Evangelist for Windows Mobile

 


Share this post :

Twittering using the Compact Framework

Standard

 image

My twitter updates are done using a variety of code.  At the moment I use the Curl command line  in conjunction with SlickRun.  In the past I did this with Outlook (see the article Twittering from Outlook Using VBA).  In the past I’ve found this pretty easy to do using the Twitter Api.

 

I also like to do updates from my Pocket PC, but am not keen on SMS charges.  The solution of course is to create my own client, which I’ll be posting about shortly.

For my client program I intend to use TwitterLib library.  Sadly this does not work for the compact framework unaltered.  I am currently working on porting it to the compact framework.

 

In the interim I want to share with you a simple code sample for submitting tweets to Twitter. 

The example is simple enough to be used by people who want to do HTTP posts to similar services.

 

    Dim lsParams As String = "status=" & Uri.EscapeDataString(tweetText)

    Dim loRequest As HttpWebRequest = CType(HttpWebRequest.Create("http://twitter.com/statuses/update.xml"), HttpWebRequest)
           With loRequest
               .Proxy = System.Net.GlobalProxySelection.GetEmptyWebProxy()

               .Timeout = 10000
               .AllowAutoRedirect = True
               .AllowWriteStreamBuffering = True

               .Method = "POST"

               .ContentType = "application/x-www-form-urlencoded"
               .ContentLength = Len(lsParams)

               Dim loCred As New System.Net.NetworkCredential("someusername", "somepassword")
               .Credentials = loCred

               ' Write the request paramater
               Dim stOut As New StreamWriter(.GetRequestStream(), System.Text.Encoding.ASCII)
               stOut.Write(lsParams)
               stOut.Flush()
               stOut.Close()

               Dim loResp = .GetResponse
               With loResp

                   .Close()
               End With

           End With

 

The most important difference with the full .net framework is the “GetEmptyWebProxy” and “AllowWriteStreamBuffering” lines.  It won’t work without it.


Share this post :

Easy InkCanvas in Winforms for capturing signatures

Standard

image

In WPF there’s an interesting control called “InkCanvas” that can be used to capture signatures and the like in Tablet PC applications.

Recently I created a prototype for a windows forms application.  I did consider using WPF, but the overhead seemed unnecessary considering the simplicity of the requirement.

I am aware there’s a specific SDK for Tablet PC that provides winforms controls for the requirement, but I thought it might be fun to create my own.  As it turns out, it was pretty easy.

The requirement was to have a signature panel which could be used to collect a bitmap that would be attached to an entity to act as an authorisation.

Signature Panel

In the prototype I decided to create the signature panel as a standalone usercontrol called it, strangely enough “SignaturePanel”.

It uses Mousedown and MouseUp events to determine stylus pressure and mousemove for drawing.  Like the WPF control it keeps a collection of strokes internally.

Unlike the wpf control, I just decided to expose a “SignatureImage” property to allow a developer to retrieve a bitmap.  This is rendered from the strokes in realtime.

I assume it works for the Tablet PC, but I don’t know for sure.

Write your signature here

An additional feature of this control is a label which tells the user the last time the signature was last updated.  I thought the user may find this helpful.

If the user hasn’t written anything then it just reads “Write your signature here”.

The user control consists of a picturebox called “inkPanel” and a button for resetting the content:

 

What the control looks like in design time

Here’s the code:

 

Public Class SignaturePanel

    Private moCurrentWriting As New List(Of Point)
    Private moRememberInk As New List(Of List(Of Point))
    Private mbPenDown As Boolean = False
    Private Sub inkPanel_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles inkPanel.MouseDown
        mbPenDown = True
    End Sub

    Private Sub inkPanel_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles inkPanel.MouseMove
        If mbPenDown Then
            moCurrentWriting.Add(e.Location)
            mdLastSignatureUpdate = Now
            Me.Refresh()
        End If
    End Sub

    Private Sub inkPanel_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles inkPanel.MouseUp
        If moCurrentWriting.Count > 2 Then
            moRememberInk.Add(moCurrentWriting)
        End If

        moCurrentWriting = New List(Of Point)
        mbPenDown = False
    End Sub

    Private Sub inkPanel_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles inkPanel.Paint

        mRenderSignature(e.Graphics)

        Dim lsText As String = ""
        If Not Me.DesignMode Then
            If mdLastSignatureUpdate.HasValue Then
                lsText = "Signature last updated " & Me.LastSignatureUpdate.ToLongTimeString
            End If
            e.Graphics.DrawString(lsText, Me.Font, Brushes.Black, 5, inkPanel.Height - 20)
        Else
            lsText = "... Write your signature here ..."

            e.Graphics.DrawString(lsText, Me.Font, Brushes.Black, (inkPanel.Width / 2) - (e.Graphics.MeasureString(lsText, Me.Font).Width / 2), (inkPanel.Height / 2) - (e.Graphics.MeasureString(lsText, Me.Font).Height / 2))
        End If
    End Sub
    Private Sub mRenderSignature(ByVal g As Graphics)
        Using loPen As New Pen(Color.Black)
            loPen.Width = 2
            For Each loLines As List(Of Point) In moRememberInk
                g.DrawLines(loPen, loLines.ToArray)
            Next
            If moCurrentWriting.Count > 1 Then
                g.DrawLines(loPen, moCurrentWriting.ToArray)
            End If
        End Using
    End Sub
    Private Sub cmdClearInk_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdClearInk.Click
        moRememberInk.Clear()
        inkPanel.Refresh()
    End Sub
    Private mdLastSignatureUpdate As Nullable(Of Date) = Nothing
    <System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)> _
    Public Property LastSignatureUpdate() As Date
        Get
            Return mdLastSignatureUpdate
        End Get
        Set(ByVal value As Date)
            mdLastSignatureUpdate = value
        End Set
    End Property

    <System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)> _
    Public ReadOnly Property SignatureImage() As Image
        Get
            Dim loBitmap As New Bitmap(Me.Width, Me.Height)
            Using loGfx As Graphics = Graphics.FromImage(loBitmap)
                mRenderSignature(loGfx)
            End Using

            Return loBitmap
        End Get
    End Property

End Class

 

If anyone’s interested in a standalone sample project, post the request to this blog and I’ll see what I can do.

Possible Uses

It may be interesting to use this code as:

  • Signature recognition
  • “Mud map” style sketching for business applications
  • A basis for a drawing program ;)

Of course, if you’re using WPF, use InkPanel.  But if you don’t want the overhead, maybe this will be the foundation of a lightweight solution.

 


Share this post :

Give the "Gift of Text" by sending an SMS with VB.NET and Skype

Standard

image

Never underestimate the value (to some) of sending someone a note.  In my case, a note to my beloved is an excellent way of ensuring a happier homecoming.

Each note has to have an appropriately original sentiment, and be sent in a timely fashion.  Sometimes circumstances do not permit time for the creative effort required.   (Although I’ve heard that Generation Y can shoot of a text message faster than I just wrote the word faster…)

So why not queue up some appropriate messages and let my computer send one a day at the appropriate time?  Yes I’m sure you can think of plenty, but this is just an intro to a code sample….

So to send an SMS, all you need to do is install Skype and get a reference to the COM type library:

image

 

After this, the following code sample should be enough to get you in trouble:

 

Public Sub SendSMS(ByVal strNumber As String, ByVal strBody As String)
    Dim loSkype As New SKYPE4COMLib.Skype
    Dim loSmsMessage As SKYPE4COMLib.SmsMessage

    loSmsMessage = loSkype.CreateSms(SKYPE4COMLib.TSmsMessageType.smsMessageTypeOutgoing, strNumber)
    loSmsMessage.Body = strBody
    loSmsMessage.Send()
    Trace.WriteLine(loSmsMessage.Status.ToString)

    loSmsMessage = Nothing
    loSkype = Nothing

End Sub

 

Have fun!

Links

Original VBA post by Conrad Sharry


Share this post :

Getting the SMTP Email Address of an Exchange Sender of a MailItem from Outlook in VB.NET VSTO

Standard

 image

Sometimes the easiest things in software development are the hardest things.  Particularly when it comes to VSTO development.

The Problem

You’d think that programmatically retrieving the sender’s SMTP email address from an email item in Outlook would be easy wouldn’t you?  It isn’t.

The “Address” property of the MailItem object is supposed to return the email address.   And it does if your email comes from an internet sender, such as a friendly Nigerian businessman.  If the email sender a user of Microsoft Exchange, you get a wierd X400 formatted email address.  Not very handy if you want the SMTP email address.

If you search for this information you’ll find solutions that usually involve obsolete components (CDO 1.21 – Unsupported by Microsoft) or 3rd party dlls (such as the excellent “Outlook Redemption”).   See the links section at the bottom of this article for a list of the good articles that discuss these techniques.

If you want your application to “remain pure” .NET however, there is no “cut and dried” solution.

The Solution

After much experimentation and agony my colleague Puji Arsana and myself have devised a solution that uses pure VSTO and VB.Net, without any 3rd party dependencies.

It seems to me that many people have already created this solution (and published parts of this), but so far noone has published a complete solution before.

So without further ado, here’s the VB.NET code to allow you to extract the SMTP email address, regardless of whether it’s an exchange or internet email.

Private Function GetSMTPEmailAddress(mailItem As MailItem) As String

    If mailItem.SenderAddressType = "EX" Then
         Return GetEmailAddressForExchangeServer(loItem.Name)
    Else
         Return mailItem.Address
    End If

End Function

Private Function GetEmailAddressForExchangeServer(ByVal emailName As String) As String
        Dim loDummyMsg As MailItem = moMailItem.Application.CreateItem(OlItemType.olMailItem)
        Dim loAddress As Recipient = loDummyMsg.Recipients.Add(emailName)

        loAddress.Resolve()
        Dim lsSmtpAddress As String = GetMAPIProperty(loAddress.AddressEntry.MAPIOBJECT, PR_SMTP_ADDRESS)
        Return lsSmtpAddress

End Function

#Region "MAPI Interface ID'S"
    ' The Interface ID's are used to retrieve the specific MAPI Interfaces from the IUnknown Object
    Private Const IID_IMAPISession As String = "00020300-0000-0000-C000-000000000046"
    Private Const IID_IMAPIProp As String = "00020303-0000-0000-C000-000000000046"
    Private Const IID_IMAPITable As String = "00020301-0000-0000-C000-000000000046"
    Private Const IID_IMAPIMsgStore As String = "00020306-0000-0000-C000-000000000046"
    Private Const IID_IMAPIFolder As String = "0002030C-0000-0000-C000-000000000046"
    Private Const IID_IMAPISpoolerService As String = "0002031E-0000-0000-C000-000000000046"
    Private Const IID_IMAPIStatus As String = "0002031E-0000-0000-C000-000000000046"
    Private Const IID_IMessage As String = "00020307-0000-0000-C000-000000000046"
    Private Const IID_IAddrBook As String = "00020309-0000-0000-C000-000000000046"
    Private Const IID_IProfSect As String = "00020304-0000-0000-C000-000000000046"
    Private Const IID_IMAPIContainer As String = "0002030B-0000-0000-C000-000000000046"
    Private Const IID_IABContainer As String = "0002030D-0000-0000-C000-000000000046"
    Private Const IID_IMsgServiceAdmin As String = "0002031D-0000-0000-C000-000000000046"
    Private Const IID_IProfAdmin As String = "0002031C-0000-0000-C000-000000000046"
    Private Const IID_IMailUser As String = "0002030A-0000-0000-C000-000000000046"
    Private Const IID_IDistList As String = "0002030E-0000-0000-C000-000000000046"
    Private Const IID_IAttachment As String = "00020308-0000-0000-C000-000000000046"
    Private Const IID_IMAPIControl As String = "0002031B-0000-0000-C000-000000000046"
    Private Const IID_IMAPILogonRemote As String = "00020346-0000-0000-C000-000000000046"
    Private Const IID_IMAPIForm As String = "00020327-0000-0000-C000-000000000046"
#End Region

#Region "MAPI Properties"

    ' MAPI Properties
    Public Const PR_TRANSPORT_MESSAGE_HEADERS As UInteger = 8192030
    Public Const PR_BODY As UInteger = 268435486
    Public Const PR_BODY_HTML As UInteger = 269680670
    Public Const PR_HTML As UInteger = 269680898
    Public Const PR_DISPLAY_NAME As UInteger = 805371934
    Public Const PR_SUBJECT As UInteger = 3604510
    Public Const PR_EMAIL_ADDRESS As UInteger = 805503006
    'public const uint PR_NEG_EMAIL_ADDRESS = -2146496482;
    Public Const PR_SMTP_ADDRESS As UInteger = 972947486
    Public Const PR_ADDRTYPE As UInteger = 805437470
#End Region

#Region "structure and variables "
    Private Structure SPropValue
        Public ulPropTag As UInteger
        Public dwAlignPad As UInteger
        Public Value As Long
    End Structure

    ' return codes
    Private Const S_OK As Integer = 0
#End Region

#Region "MAPI Functions"

    <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrGetOneProp@12")> _
    Private Shared Sub HrGetOneProp(ByVal pmp As IntPtr, ByVal ulPropTag As UInteger, ByRef ppProp As IntPtr)
    End Sub

    <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="HrSetOneProp@8")> _
    Private Shared Sub HrSetOneProp(ByVal pmp As IntPtr, ByVal pprop As IntPtr)
    End Sub

    <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi, EntryPoint:="MAPIFreeBuffer@4")> _
    Private Shared Sub MAPIFreeBuffer(ByVal lpBuffer As IntPtr)
    End Sub

    <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi)> _
    Private Shared Function MAPIInitialize(ByVal lpMapiInit As IntPtr) As Integer
    End Function

    <DllImport("MAPI32.DLL", CharSet:=CharSet.Ansi)> _
    Private Shared Sub MAPIUninitialize()
    End Sub

    ''' <summary>
    ''' Get a property from a passed MAPI object
    ''' </summary>
    ''' <param name="oMAPIObject"></param>
    ''' <param name="uiPropertyTag"></param>
    ''' <returns></returns>
    Private Shared Function GetMAPIProperty(ByVal oMAPIObject As Object, ByVal uiPropertyTag As UInteger) As String

        If oMAPIObject.Equals(Nothing) Then
            'No MAPI Object
            Return ""
        End If

        Dim sProperty As String = ""
        Dim pPropValue As IntPtr = IntPtr.Zero

        Dim IUnknown As IntPtr = IntPtr.Zero
        Dim IMAPIProperty As IntPtr = IntPtr.Zero

        Try

            ' initialize MAPI
            MAPIInitialize(IntPtr.Zero)

            ' get the unknown object.
            IUnknown = Marshal.GetIUnknownForObject(oMAPIObject)

            'get the property
            Dim guidIMAPIProp As New Guid(IID_IMAPIProp)
            If Marshal.QueryInterface(IUnknown, guidIMAPIProp, IMAPIProperty) <> S_OK Then
                'Failed to get IMAPIProperty
                Return ""
            End If

            Try

                ' get the field from the MAPI Property
                HrGetOneProp(IMAPIProperty, uiPropertyTag, pPropValue)
                ' Is the property actually there?
                If pPropValue = IntPtr.Zero Then
                    Return ""
                End If
                ' Get the value back
                Dim propValue As SPropValue = DirectCast(Marshal.PtrToStructure(pPropValue, GetType(SPropValue)), SPropValue)
                ' convert to string

                sProperty = Marshal.PtrToStringAnsi(New IntPtr(propValue.Value))
            Catch ex As System.Exception
                Throw ex
            End Try
        Finally
            ' CLEAN UP
            If pPropValue <> IntPtr.Zero Then
                MAPIFreeBuffer(pPropValue)
            End If

            If IMAPIProperty <> IntPtr.Zero Then
                Marshal.Release(IMAPIProperty)
            End If
            If IUnknown <> IntPtr.Zero Then
                Marshal.Release(IUnknown)
            End If

            MAPIUninitialize()
        End Try

        Return sProperty
    End Function
#End Region

How it Works

If the email is from exchange it translates the  email address into a “Recipient” object. 

Recipient objects expose “AddressEntry” objects, which in turn can be utilised by “ExtendedMAPI”.

The code then uses ExtendedMAPI which can “see” the property where the normal Outlook API cant.

Links

See http://www.outlookcode.com/d/code/getsenderaddy.htm#redemption and http://www.cdolive.com/cdo5.htm#EMailAddressOfSender for Redemption and CDO examples and http://groups.google.com/group/microsoft.public.outlook.program_vba/browse_frm/thread/4d4d5fece24a2a7/ad2fcbb691d5bf18 for a discussion of the property to use with Cached Exchange Mode in Outlook 2003 or later.


Share this post :