<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ProgTips &#187; VB un .net</title>
	<atom:link href="http://tips.naivist.net/category/net-pasaule/vb-un-net/feed/" rel="self" type="application/rss+xml" />
	<link>http://tips.naivist.net</link>
	<description>Kodējot radušās domeles</description>
	<lastBuildDate>Fri, 08 Jul 2011 11:17:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Always, ALWAYS initialize!</title>
		<link>http://tips.naivist.net/2006/10/20/always_always_initialize/</link>
		<comments>http://tips.naivist.net/2006/10/20/always_always_initialize/#comments</comments>
		<pubDate>Fri, 20 Oct 2006 09:28:56 +0000</pubDate>
		<dc:creator>Krišs Rauhvargers</dc:creator>
				<category><![CDATA[VB un .net]]></category>

		<guid isPermaLink="false">http://naivist.net/tips/index.php/2006/10/20/always_always_initialize/</guid>
		<description><![CDATA[What output would you expect from such a piece of code (VB.net, of course): Imports System.Globalization .... Dim CurrentCalendar As System.Globalization.Calendar = _ CultureInfo.CurrentUICulture.Calendar 'We want to output all the dates in the current year for every month. For iMonth As Integer = 1 To 12 Dim sOutput As String 'accumulate all the dates For [...]]]></description>
			<content:encoded><![CDATA[<p>What output would you expect from such a piece of code (VB.net, of course):</p>

<pre><code>    Imports System.Globalization
    .... 
    Dim CurrentCalendar As System.Globalization.Calendar = _
                CultureInfo.CurrentUICulture.Calendar

    'We want to output all the dates in the current year for every month.
    For iMonth As Integer = 1 To 12
        Dim sOutput As String
        'accumulate all the dates
        For iDay As Integer = 1 To CurrentCalendar.GetDaysInMonth(Now.Year, iMonth)
            sOutput &amp;= iDay &amp; ","
        Next
         'print out all the accumulated dates
        Console.WriteLine(sOutput.TrimEnd(","c))
    Next
</code></pre>

<p>I&#8217;d say it must be</p>

<pre><code>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
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
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
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
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
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
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
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
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
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
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
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
</code></pre>

<p>but, the hell, no, it is:</p>

<pre><code>(1st line)
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
(2nd line)
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,1,2,3,
                   ...other Februray dates..28
(3rd line)
...January dates...February dates...March dates...
(4th line)
...January dates...February dates...March dates...April dates...
(5th line)
...January dates...February dates...March dates...May dates...   
....
(12th line)
...January dates...February dates...March dates...May dates...December dates...
</code></pre>

<p>This means that even though the variable s is <em>re-declared</em>, it is not being <em>re-initialized</em>! Just&#8230; keep it in mind.</p>

<p>Hint: instead of being <code>Dim sOutput As String</code> it should be <code>Dim sOutput As String=""</code> if we still want to keep this syntax. </p>
]]></content:encoded>
			<wfw:commentRss>http://tips.naivist.net/2006/10/20/always_always_initialize/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>IsUserInGroup updated</title>
		<link>http://tips.naivist.net/2006/05/11/isuseringroup_updated/</link>
		<comments>http://tips.naivist.net/2006/05/11/isuseringroup_updated/#comments</comments>
		<pubDate>Thu, 11 May 2006 11:19:17 +0000</pubDate>
		<dc:creator>Krišs Rauhvargers</dc:creator>
				<category><![CDATA[.NET pasaule]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[VB un .net]]></category>

		<guid isPermaLink="false">http://naivist.net/tips/index.php/2006/05/11/isuseringroup_updated/</guid>
		<description><![CDATA[In an earlier article I&#8217;ve been talkin about how to check if a user belongs to a specific domain group. However, the code in that article doesn&#8217;t work on local groups and WinNT ADSI provider. I&#8217;ve made an update to the code posted there, this should work both on domain and local groups. Usage: If [...]]]></description>
			<content:encoded><![CDATA[<p>In an <a href="http://naivist.net/tips/index.php/2005/10/13/user_group_member/" onclick="pageTracker._trackPageview('/outgoing/naivist.net/tips/index.php/2005/10/13/user_group_member/?referer=');">earlier article</a> I&#8217;ve been talkin about how to check if a user belongs to a specific domain group. However, the code in that article doesn&#8217;t work on local groups and WinNT ADSI provider. I&#8217;ve made an update to the code posted there, this should work both on domain and local groups.</p>

<p>Usage:</p>

<pre><code>If IsUserInGroup("Contoso\JohnDoe", "Contoso\Managers") Then
   '...do something
End If
</code></pre>

<p>Notes: </p>

<ul>
<li>Option Strict Off. Sorry, I&#8217;m using the <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vblr7/html/vafctGetObject.asp" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/vblr7/html/vafctGetObject.asp&amp;referer=');">GetObject</a> magic function here, so there&#8217;s no way I could leave the option strict on.</li>
<li>Seems to be quite slow when local groups are scanned from outside.</li>
<li>Could be a problem when you have users from two different domains + local users.</li>
</ul>

<p><span id="more-106"></span></p>

<pre><code>'''&lt;created&gt;
'''  &lt;author&gt;Krišs Rauhvargers&lt;/author&gt;
'''  &lt;date&gt;2006.04.26&lt;/date&gt;
'''&lt;/created&gt;
'''&lt;summary&gt;Checks if a user belongs to the specified group.&lt;/summary&gt;
'''&lt;remarks&gt;Should now work both on domain and local groups/users&lt;/remarks&gt;
'''&lt;param name="spUserLogin"&gt;Login name, e.g. Contoso\JohnDoe&lt;/param&gt;
'''&lt;param name="spGroupLogin"&gt;Group name, e.g. Contoso\Managers&lt;/param&gt;
Public Function IsUserInGroup(ByVal spUserLogin As String, _
                              ByVal spGroupLogin As String) As Boolean
    Dim oSeenGroups As New Specialized.StringCollection
    Return IsUserInGroup(spUserLogin, spGroupLogin, oSeenGroups)
End Function


'''&lt;created&gt;
'''  &lt;author&gt;Krišs Rauhvargers&lt;/author&gt;
'''  &lt;date&gt;2006.04.26&lt;/date&gt;
'''&lt;/created&gt;
'''&lt;summary&gt;Performs a recursive check if the user specified belongs to the 
'''group.&lt;/summary&gt;
'''&lt;param name="spUserLogin"&gt;User login name, authority\account&lt;/param&gt;
'''&lt;param name="spGroupLogin"&gt;Group login name, authority\account&lt;/param&gt;
'''&lt;param name="opSeenGroups"&gt;A collection of previously visited groups&lt;/param&gt;
'''&lt;param name="opUserObj"&gt;An instance of COM object for user entry (saving resources)&lt;/param&gt;
'''&lt;returns&gt;Boolean&lt;/returns&gt;
Private Function IsUserInGroup(ByVal spUserLogin As String, _
                              ByVal spGroupLogin As String, _
                              ByVal opSeenGroups As Collections.Specialized.StringCollection, _
                              Optional ByVal opUserObj As Object = Nothing) As Boolean
    Dim oUserObj As Object
    Dim oGroupObj As Object
    Dim oGroupsToCheck As New Specialized.StringCollection

    'it's theoretically possible to get a loop - "A includes B" and "B includes A"
    'so it's more safe to remember the seen groups
    If opSeenGroups.Contains(spGroupLogin) Then Return False
    opSeenGroups.Add(LCase(spGroupLogin))


    If opUserObj Is Nothing Then
        oUserObj = GetAdsiObj(spUserLogin)
    Else
        'if we already have found the "user object" use that one
        oUserObj = opUserObj
    End If

    'get an ADSI object instance for the group
    oGroupObj = GetAdsiObj(spGroupLogin)

    'chek all the group members, perform a recursive call
    'when a member of type "group" is found
    For Each oMember As Object In oGroupObj.Members
        If LCase(oMember.class) = "user" Then
            '"normal" case when a group contains members of type "user"
            If SidsEqual(oMember.objectsid, oUserObj.objectsid) Then
                'assume the user is in group if SIDs of both objects are equal
                Return True
            End If
        ElseIf LCase(oMember.class) = "group" Then
            'if a group contains a member of type "group"
            'have to check - if the user object we're looking is probably a group itself.
            If LCase(oUserObj.class) = "group" Then
                If SidsEqual(oMember.objectsid, oUserObj.objectsid) Then
                    'if so - see if sids are equal or not
                    Return True
                End If
            Else
                'we've found a group within a group. have to perform a recursive check
                If LCase(oGroupObj.parent).indexof("ldap://") &gt; -1 Then
                    'a domain group contains a subgroup
                    Dim sTmpDomain, sTmpAccount As String
                    SplitLogin(spGroupLogin, sTmpDomain, sTmpAccount)
                    oGroupsToCheck.Add(LCase(sTmpDomain &amp; "\" &amp; oMember.samaccountname))
                Else
                    'a local group contains a subgroup (a domain group can be registered as a
                    'member of a local group
                    oGroupsToCheck.Add(LCase(LastAuthority(oMember.parent) &amp; "\" &amp; oMember.name))
                End If
            End If
        End If
    Next

    'see if we have found sub-groups
    If oGroupsToCheck.Count &gt; 0 Then
        'if any of the subgroups contains the member we're looking for- return true
        For Each sGroupLogin As String In oGroupsToCheck
            If IsUserInGroup(spUserLogin, sGroupLogin, opSeenGroups, oUserObj) = True Then
                Return True
            End If
        Next
    End If

    'if we have come this far - the member is not found
    Return False
End Function

'''&lt;created&gt;
'''  &lt;author&gt;Krišs Rauhvargers&lt;/author&gt;
'''  &lt;date&gt;2006.04.27&lt;/date&gt;
'''&lt;/created&gt;
'''&lt;summary&gt;Checks if the object is registered in local security authority
'''or it is a domain level entry.&lt;/summary&gt;
'''&lt;remarks&gt;Assumes WINNT provider format&lt;/remarks&gt;
'''&lt;param name="spParentURI"&gt;WinNT://blabla&lt;/param&gt;
'''&lt;returns&gt;Boolean&lt;/returns&gt;
Private Shared Function IsLocal(ByVal spParentURI As String) As Boolean
    If spParentURI Is Nothing OrElse spParentURI.Length &lt; 7 Then
        Throw New FormatException("Incorrect parent property! " &amp; spParentURI)
    End If
    Return Not (spParentURI.LastIndexOf("/") = 7)
End Function

'''&lt;created&gt;
'''  &lt;author&gt;Krišs Rauhvargers&lt;/author&gt;
'''  &lt;date&gt;2006.04.27&lt;/date&gt;
'''&lt;/created&gt;
'''&lt;summary&gt;Gets the last security authority name&lt;/summary&gt;
'''&lt;remarks&gt;Can be WinNT://domain/computer or WinNT://domain. The first case
''' returns 'computer', the second one - 'domain'&lt;/remarks&gt;
'''&lt;param name="spParentUri"&gt;&lt;/param&gt;
'''&lt;returns&gt;String&lt;/returns&gt;
Private Shared Function LastAuthority(ByVal spParentUri As String) As String
    If spParentUri Is Nothing OrElse spParentUri.Length &lt; 7 Then
        Return "" 'must have WinNT providers
    End If
    Return spParentUri.Substring(spParentUri.LastIndexOf("/") + 1)
End Function

'''&lt;created&gt;
'''  &lt;author&gt;Krišs Rauhvargers&lt;/author&gt;
'''  &lt;date&gt;2006.04.26&lt;/date&gt;
'''&lt;/created&gt;
'''&lt;summary&gt;Creates the ADSI object for a member.&lt;/summary&gt;
'''&lt;remarks&gt;Returns LDAP:// or WINNT:// object, according to member type&lt;/remarks&gt;
'''&lt;param name="spGroupLogin"&gt;Members login name&lt;/param&gt;
Private Function GetAdsiObj(ByVal spLogin As String) As Object
    Try
        Dim sDomain, sAccount As String
        Dim oAdsiObj As Object
        SplitLogin(spLogin, sDomain, sAccount)

        oAdsiObj = GetObject("WinNT://" &amp; sDomain &amp; "/" &amp; sAccount)

        'if the member found is not local, upgrade to an LDAP object
        If Not IsLocal(oAdsiObj.parent) Then
            oAdsiObj = GetObject("LDAP://" &amp; Me.DistingishedNameFromLogin(spLogin))
        End If
        Return oAdsiObj
    Catch ex As Exception
        Throw New Exception("Member not found: " &amp; spLogin, ex)
    End Try
End Function

'''&lt;created&gt;
'''  &lt;author&gt;Krišs Rauhvargers&lt;/author&gt;
'''  &lt;date&gt;2006.04.26&lt;/date&gt;
'''&lt;/created&gt;
'''&lt;summary&gt;Checks if two SIDs are equal&lt;/summary&gt;
'''&lt;remarks&gt;Compares two byte arrays.&lt;/remarks&gt;
Public Function SidsEqual(ByVal bapFirst As Byte(), ByVal bapSecond As Byte()) As Boolean
    'if one of arrays is not initiated or sizes differ
    If bapFirst Is Nothing Or bapSecond Is Nothing Then Return False
    If bapFirst.Length &lt;&gt; bapSecond.Length Then Return False

    For i As Integer = 0 To bapFirst.Length - 1
        If bapFirst(i) &lt;&gt; bapSecond(i) Then
            Return False
        End If
    Next
    Return True
End Function


'''&lt;created&gt;
'''  &lt;author&gt;Krišs Rauhvargers&lt;/author&gt;
'''  &lt;date&gt;2005.10.12&lt;/date&gt;
'''&lt;/created&gt;
'''&lt;summary&gt;Gets the full user reference from login name (domain members only!)&lt;/summary&gt;
'''&lt;param name="spLogin"&gt;domain\user&lt;/param&gt;
Public Shared Function DistingishedNameFromLogin(ByVal spLogin As String) As String
    Dim sLogin, sDomain As String
    Dim sFilter As String
    Dim dsEntryObj As DirectoryEntry
    Dim dsSearcher As DirectorySearcher
    Dim dsResult As SearchResultCollection

    If Trim(spLogin) = "" Then Return ""

    'get the domain and login part
    If Not (SplitLogin(spLogin, sDomain, sLogin)) Then
        Return ""
    End If

    Try
        'meklēšana LDAP://LB
        dsEntryObj = New DirectoryEntry("LDAP://" &amp; sDomain)
    Catch e As Exception
        Throw New System.Exception(String.Format("Can't create the LDAP object!" &amp; "LDAP://" &amp; sDomain))
    End Try

    'izveido meklētāja objektu
    dsSearcher = New DirectorySearcher(dsEntryObj)

    'looking for a specific SAMAccountName - persons or groups only
    dsSearcher.Filter = String.Concat("(&amp;(sAMAccountName=", sLogin, ")(|(objectCategory=group)(objectCategory=person)))")
    Try
        dsSearcher.SearchScope = SearchScope.Subtree
        dsResult = dsSearcher.FindAll()
    Catch e As Exception
        Throw New Exception(String.Format("Error in directory lookup. Filter: {0}", dsSearcher.Filter))
    End Try

    '''have to dispose this kind of objects
    dsEntryObj.Dispose()
    dsSearcher.Dispose()

    Try
        If dsResult.Count = 0 Then
            Return ""
        ElseIf dsResult.Count &gt; 1 Then
            Throw New ArgumentException("More than one entries found having the same account name! Account:" &amp; spLogin)
        Else
            'the good case
            Return dsResult(0).Properties("distinguishedname")(0).ToString
        End If
    Finally
        dsResult.Dispose()
    End Try
End Function

'''&lt;created&gt;
'''  &lt;author&gt;Krišs Rauhvargers&lt;/author&gt;
'''  &lt;date&gt;2005.10.12&lt;/date&gt;
'''&lt;/created&gt;
'''&lt;summary&gt;Splits login name into parts (domain\user)&lt;/summary&gt;
'''&lt;remarks&gt;&lt;/remarks&gt;
'''&lt;param name="spLogin"&gt;Full login name&lt;/param&gt;
'''&lt;param name="spDomain"&gt;OUT: domain&lt;/param&gt;
'''&lt;param name="spAccount"&gt;OUT: account&lt;/param&gt;
'''&lt;returns&gt;Boolean&lt;/returns&gt;
Public Shared Function SplitLogin(ByVal spLogin As String, ByRef spDomain As String, ByRef spAccount As String) As Boolean
    Dim iSlashPos As Integer
    spLogin = spLogin.Replace("/"c, "\"c)
    iSlashPos = spLogin.IndexOf("\"c)
    If iSlashPos &gt; -1 Then
        With spLogin.Split("\"c)
            spAccount = .GetValue(1).ToString
            spDomain = .GetValue(0).ToString
        End With
        Return True
    Else
        Return False
    End If
End Function
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://tips.naivist.net/2006/05/11/isuseringroup_updated/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Does this user belong to that domain group?</title>
		<link>http://tips.naivist.net/2005/10/13/user_group_member/</link>
		<comments>http://tips.naivist.net/2005/10/13/user_group_member/#comments</comments>
		<pubDate>Thu, 13 Oct 2005 08:54:47 +0000</pubDate>
		<dc:creator>Krišs Rauhvargers</dc:creator>
				<category><![CDATA[Datorlietas]]></category>
		<category><![CDATA[VB un .net]]></category>

		<guid isPermaLink="false">http://naivist.net/tips/index.php/2005/10/13/user_group_member/</guid>
		<description><![CDATA[A naivist-approach solution to check if a user belongs to a particular domain group.]]></description>
			<content:encoded><![CDATA[<p>UPD: This works only on LDAP groups and LDAP:// provider, not WinNT groups!</p>

<p>If we know two things &#8211; a login name of a user for instance <code>northwind\JohnDoe</code> and a domain group <code>northwind\board</code>. We want to check if John Doe belongs to the &#8220;board&#8221; group or not.</p>

<p>This is how you find it:  <code>IsUserMember( "northwind\JohnDoe", "northwind\board")</code> </p>

<p><span id="more-97"></span></p>

<p>Here is the VB.Net code you need :</p>

<pre><code>'''&lt;summary&gt;Checks if a user belongs to the domain group&lt;/summary&gt;
'''&lt;param name="spLogin"&gt;Full  login name domain\user&lt;/param&gt;
'''&lt;param name="spGroupLogin"&gt;Group full login name domain\group&lt;/param&gt;
'''&lt;returns&gt;True if belongs, False if not&lt;/returns&gt;
Public Function IsUserMember(ByVal spLogin As String, _
                             ByVal spGroupLogin As String) As Boolean
    Dim sUserDistinguish, sGroupDistinguish As String
    Dim sFilter As String
    Dim sAccount, sGroupAccount, sDomain As String


    If Not Me.SplitLogin(spLogin, sDomain, sAccount) Then
        'if username is not formed as domain\user, return false
        Return False
    End If

    If Not Me.SplitLogin(spGroupLogin, sDomain, sGroupAccount) Then
        'if group name is not formed as domain\group, return false
        Return False
    End If

    'get the distinguishedName of both user and group
    sUserDistinguish = DistingishedNameFromLogin(spLogin)
    sGroupDistinguish = DistingishedNameFromLogin(spGroupLogin)

    'check in a recursive manner if user belongs to group (or its subgroups)
    Return Me.IsUserInGroupRecursive(sUserDistinguish, sGroupDistinguish, sDomain, New Specialized.StringDictionary)
End Function


'''&lt;summary&gt;Recursively (in subgroups, too) finds a user in a group&lt;/summary&gt;
'''&lt;remarks&gt;&lt;/remarks&gt;
'''&lt;param name="spUserDistinguish"&gt;DistinguishedName of a user&lt;/param&gt;
'''&lt;param name="spGroupDistinguish"&gt;DistinguishedName of a group&lt;/param&gt;
'''&lt;param name="spDomain"&gt;domain name&lt;/param&gt;
'''&lt;param name="opCheckedGroups"&gt;Groups already sought&lt;/param&gt;
'''&lt;returns&gt;Boolean&lt;/returns&gt;
Public Function IsUserInGroupRecursive(ByVal spUserDistinguish As String, _
                                       ByVal spGroupDistinguish As String, _
                                       ByVal spDomain As String, _
                                       ByRef opCheckedGroups As Collections.Specialized.StringDictionary) As Boolean

    If opCheckedGroups(spGroupDistinguish) = "DONE" Then
        'there may be occasions when a group is somehow a subgroup of itself 
        'so we have to remember if the group is already sought or not
        'if thr group is already sought, there is NO such user
        Return False
    Else
        'mark the group as sought otherwise
        opCheckedGroups(spGroupDistinguish) = "DONE"
    End If

    If Me.IsUserDirectlyInGroup(spUserDistinguish, spGroupDistinguish, spDomain) Then
        'if the user is can be found in the group directly
        Return True
    Else
        'if not in the group directly, get all the subgroups
        For Each sSubGroup As String In Me.GetSubGroups(spGroupDistinguish, spDomain)
            'check subgroup recursively 
            If IsUserInGroupRecursive(spUserDistinguish, sSubGroup, spDomain, opCheckedGroups) Then
                'if user is found in a subgroup, return true
                Return True
                'otherwise check the next subgroup
            End If
        Next
    End If

    'if we are here, the user is not found
    Return False
End Function


'''&lt;summary&gt;Finds all subgroups of a group&lt;/summary&gt;
'''&lt;remarks&gt;&lt;/remarks&gt;
'''&lt;param name="spGroupDistinguishName"&gt;Distinguished name of the parent group&lt;/param&gt;
'''&lt;param name="spDomain"&gt;Domain name&lt;/param&gt;
'''&lt;returns&gt;Collections.Specialized.StringCollection&lt;/returns&gt;
Public Function GetSubGroups( _
        ByVal spGroupDistinguishName As String, _
        ByVal spDomain As String) As Collections.Specialized.StringCollection

    Dim sFilter As String
    Dim oResults As SearchResultCollection
    Dim oRet As Collections.Specialized.StringCollection

    'searches for subgroups that are members of this group
    sFilter = "(&amp;(objectCategory=group)(memberof=" &amp; spGroupDistinguishName &amp; "))"

    oResults = Me.FindByFilter(sFilter, spDomain)

    oRet = New Collections.Specialized.StringCollection
    For Each oResult As SearchResult In oResults
        oRet.Add(oResult.Properties("distinguishedname")(0).ToString)
    Next
    Return oRet
End Function


'''&lt;summary&gt;According to the filter specified, searches LDAP&lt;/summary&gt;
'''&lt;remarks&gt;&lt;/remarks&gt;
'''&lt;param name="spFilter"&gt;what to search for&lt;/param&gt;
'''&lt;param name="spDomain"&gt;The domain to search&lt;/param&gt;
'''&lt;returns&gt;DirectoryServices.SearchResultCollection&lt;/returns&gt;
Private Function FindByFilter(ByVal spFilter As String, ByVal spDomain As String) As SearchResultCollection
    Dim dsEntryObj As DirectoryEntry
    Dim dsSearcher As DirectorySearcher
    Dim dsResult As SearchResultCollection
    Try
        'search LDAP://domain
        dsEntryObj = New DirectoryEntry("LDAP://" &amp; spDomain)
    Catch e As Exception
        Throw New System.Exception(String.Format("Error creating LDAP directory object {0}!", "LDAP://" &amp; spDomain))
    End Try

    'create the searcher object
    dsSearcher = New DirectorySearcher(dsEntryObj)
    dsSearcher.Filter = spFilter
    Try
        'search in the directory
        dsResult = dsSearcher.FindAll()
    Catch e As Exception
        Throw New Exception(String.Format("Error searching in directory. Filter {0}", dsSearcher.Filter))
    End Try

    Return dsResult
End Function


'''&lt;summary&gt;Does user belong to the group directly?(i.e. not in subgroups)&lt;/summary&gt;
'''&lt;param name="spUserDistinguishName"&gt;Distinguished name of the user&lt;/param&gt;
'''&lt;param name="spGroupDistinguishName"&gt;distinguished name of the group&lt;/param&gt;
'''&lt;param name="spDomain"&gt;domain name&lt;/param&gt;
Public Function IsUserDirectlyInGroup(ByVal spUserDistinguishName As String, _
                                      ByVal spGroupDistinguishName As String, _
                                      ByVal spDomain As String) As Boolean
    Dim sFilter As String
    sFilter = "(&amp;(&amp;(objectCategory=group)" &amp; _
                  "(distinguishedname=" &amp; spGroupDistinguishName &amp; ")" &amp; _
                 ")" &amp; _
                "(member=" &amp; spUserDistinguishName &amp; "))"

    Return (Me.FindByFilter(sFilter, spDomain).Count &gt; 0)
End Function


'''&lt;summary&gt;Gets the distinguished name from user name&lt;/summary&gt;
'''&lt;param name="spLogin"&gt;Full login name domain\user&lt;/param&gt;
Public Function DistingishedNameFromLogin(ByVal spLogin As String) As String
    'empty input results in empty output
    If Trim(spLogin) = "" Then Return ""
    Dim sLogin, sDomain As String
    Dim sFilter As String
    Dim oResult As SearchResultCollection

    'separate the login name into parts
    If Not (SplitLogin(spLogin, sDomain, sLogin)) Then
        Return ""
    End If

    'search by sam account, only groups and users
    sFilter = "(&amp;(sAMAccountName=" &amp; sLogin &amp; ")(|(objectCategory=group)(objectCategory=person)))"

    oResult = Me.FindByFilter(sFilter, sDomain)


    If oResult.Count = 0 Then
        Return ""
    ElseIf oResult.Count &gt; 1 Then
        Throw New ArgumentException("More than one entry for this sAMAccountName")
    Else
        Return oResult(0).Properties("distinguishedname")(0).ToString
    End If
End Function


'''&lt;summary&gt;Splits the login name into parts - domain and account&lt;/summary&gt;
'''&lt;param name="spLogin"&gt;Full login name:  domain\account&lt;/param&gt;
'''&lt;param name="spDomain"&gt;OUT: Domain name&lt;/param&gt;
'''&lt;param name="spAccount"&gt;OUT: Account&lt;/param&gt;
'''&lt;returns&gt;Boolean&lt;/returns&gt;
Public Function SplitLogin(ByVal spLogin As String, ByRef spDomain As String, ByRef spAccount As String) As Boolean
    Dim iSlashPos As Integer
    spLogin = spLogin.Replace("/"c, "\"c)
    iSlashPos = spLogin.IndexOf("\"c)
    If iSlashPos &gt; -1 Then
        With spLogin.Split("\"c)
            spAccount = .GetValue(1).ToString
            spDomain = .GetValue(0).ToString
        End With
        Return True
    Else
        Return False
    End If
End Function
</code></pre>

<p>Note: Add system.directoryservices.dll to project references, import <code>System.DirectoryServices</code> namespace!</p>
]]></content:encoded>
			<wfw:commentRss>http://tips.naivist.net/2005/10/13/user_group_member/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Month names. i18n, you know</title>
		<link>http://tips.naivist.net/2005/09/26/month_names/</link>
		<comments>http://tips.naivist.net/2005/09/26/month_names/#comments</comments>
		<pubDate>Mon, 26 Sep 2005 13:44:26 +0000</pubDate>
		<dc:creator>Krišs Rauhvargers</dc:creator>
				<category><![CDATA[ASP.Net]]></category>
		<category><![CDATA[VB un .net]]></category>

		<guid isPermaLink="false">http://naivist.net/tips/index.php/2005/09/26/month-names-i18n-you-know/</guid>
		<description><![CDATA[Whenever you see something like this you should come to an idea something is wrong (for instance, the guy who wrote this is still alive): 'month names in Latvian Dim sMonth() As String = _ {"Janvāris", "Februāris", "Marts", "Aprīlis", _ "Maijs", "Jūnijs", "Jūlijs", "Augusts", _ "Septembris", "Oktobris", "Novembris", "Decembris"} Dim oListItem as ListItem For iMonth [...]]]></description>
			<content:encoded><![CDATA[<p>Whenever you see something like this you should come to an idea something is wrong (for instance, the guy who wrote this is still alive):</p>

<pre><code>'month names in Latvian
Dim sMonth() As String = _
            {"Janvāris", "Februāris", "Marts", "Aprīlis", _
            "Maijs", "Jūnijs", "Jūlijs", "Augusts", _
            "Septembris", "Oktobris", "Novembris", "Decembris"}

Dim oListItem as ListItem
For iMonth as Integer = 1 To 12
  oListItem = New ListItem(sMonth(iMonth-1),  iMonth.ToString())
  Me.lstYears.Items.Add(oListItem)
Next
</code></pre>

<p>Explanation &#8211; if MS Windows knows quite well how months are named in Latvia, why should you try to be smarter than <em>the big brother</em>? Another explanation &#8211; if this is a web project (and in my case it is), you should think of internationalization whenever possible. </p>

<p>So a more correct way to add month names to a dropdown menu is:</p>

<pre><code>Imports System.Globalization
....
Dim oListItem as ListItem
For iMonth As Integer = 0 To 11
   oEntry = New Web.UI.WebControls.ListItem( _
         CultureInfo.CurrentUICulture.DateTimeFormat.MonthNames(iMonth), _
            iMonth.ToString)
   Me.lstMonths.Items.Add(oEntry)
Next
</code></pre>

<p>And, to be even more precise, you could first check how many months there are in the current culture:</p>

<pre><code>For iMonth As Integer = 0 To _
                CultureInfo. CurrentUICulture.Calendar. _ 
                GetMonthsInYear(Now.Year) - 1
    ....
</code></pre>

<p>/Just trying to be a good boy and use the facilities provided by the framework/</p>
]]></content:encoded>
			<wfw:commentRss>http://tips.naivist.net/2005/09/26/month_names/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Parse the Enum!</title>
		<link>http://tips.naivist.net/2005/09/14/parse_the_enum/</link>
		<comments>http://tips.naivist.net/2005/09/14/parse_the_enum/#comments</comments>
		<pubDate>Wed, 14 Sep 2005 07:37:38 +0000</pubDate>
		<dc:creator>Krišs Rauhvargers</dc:creator>
				<category><![CDATA[VB un .net]]></category>

		<guid isPermaLink="false">http://naivist.net/tips/index.php/2005/09/14/parse-the-enum/</guid>
		<description><![CDATA[What if we need to save the value of an instance of an enumeration (Enum) as string and then get back the value again? For instance, we have the following code: Dim eDay As System.DayOfWeek = DayOfWeek.Monday So now we can get a textual representation of eDay using the built-in ToString() method: MessageBox.Show(eDay.ToString()) This yields [...]]]></description>
			<content:encoded><![CDATA[<p>What if we need to save the value of an instance of an <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vblr7/html/vastmenum.asp" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/vblr7/html/vastmenum.asp&amp;referer=');">enumeration (Enum)</a> as string and then get back the value again?</p>

<p>For instance, we have the following code:</p>

<pre><code>Dim eDay As System.DayOfWeek = DayOfWeek.Monday
</code></pre>

<p>So now we can get a textual representation of eDay using the built-in ToString() method:</p>

<pre><code>MessageBox.Show(eDay.ToString())
</code></pre>

<p>This yields a messagebox saying &#8220;Monday&#8221;. But how do we do the reverse (i.e., we know only the string representation &#8220;Monday&#8221;, but we need the enum value)? The trivial approach would be: </p>

<pre><code>    Dim sStr As String = "Monday"
    Dim eDay As System.DayOfWeek
    Select Case sStr
        Case "Monday" : eDay = DayOfWeek.Monday
        Case "Tuesday" : eDay = DayOfWeek.Tuesday
            '...
    End Select
</code></pre>

<p>No, this sounds too dumb. We can use the built-in shared function <code>Parse</code> of the <code>System.Enum</code> class. Method accepts two parameters, the type of enumeration and the actual value being parsed. </p>

<pre><code>eDay = System.Enum.Parse(GetType(System.DayOfWeek), "Monday")
</code></pre>

<p>This only works with option strict set to <em>off</em>, because <code>Enum.Parse()</code> returns a <code>System.Object</code> value. So when using option strict set to <em>on</em>, the code gets more obfuscated:</p>

<pre><code>eDay = CType( _ 
          System.Enum.Parse(GetType(System.DayOfWeek), "Monday"),  _ 
          System.DayOfWeek)
</code></pre>

<p>Anyway, now we have the value! :)</p>
]]></content:encoded>
			<wfw:commentRss>http://tips.naivist.net/2005/09/14/parse_the_enum/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>String.Concat instead of &amp;</title>
		<link>http://tips.naivist.net/2005/09/05/string_concat/</link>
		<comments>http://tips.naivist.net/2005/09/05/string_concat/#comments</comments>
		<pubDate>Mon, 05 Sep 2005 14:03:00 +0000</pubDate>
		<dc:creator>Krišs Rauhvargers</dc:creator>
				<category><![CDATA[VB un .net]]></category>

		<guid isPermaLink="false">http://naivist.net/tips/index.php/2005/09/05/stringconcat-instead-of/</guid>
		<description><![CDATA[We all know that the concatenation operator &#38; is evil when you use it intensively. We know writing like this is no good: Dim s as String = "" s &#38;= "&#60;" s &#38;= spFieldName s &#38;= "&#62;" s &#38;= spFieldValue s &#38;= "&#60;/" s &#38;= spFieldName s &#38;= "&#62;" This is no good because [...]]]></description>
			<content:encoded><![CDATA[<p>We all know that the concatenation operator &amp; is evil when you use it intensively.<br />
We know writing like this is <em>no good</em>: </p>

<pre><code>Dim s as String = ""
s &amp;= "&lt;"
s &amp;= spFieldName
s &amp;= "&gt;"
s &amp;= spFieldValue
s &amp;= "&lt;/"
s &amp;= spFieldName
s &amp;= "&gt;"
</code></pre>

<p>This is <em>no good</em> because a new String object is being created for every line of this code and the value is being copied to the newly created object and no optimizations can be made during compilation. One could avoid this using a StringBuilder object and calling ToString() method to achieve the same result. However the readability wouldn&#8217;t be too good. </p>

<p>So today I was paging through code of  some library not written by me. I used <a href="http://www.aisto.com/roeder/dotnet/" onclick="pageTracker._trackPageview('/outgoing/www.aisto.com/roeder/dotnet/?referer=');">Lutz Roeder&#8217;s Reflector</a>.  I came accross the following approach of string concatenation:  </p>

<pre><code>Dim s as String
s = String.Concat("&lt;",  spFieldName,  "&gt;", spFieldValue, "&lt;/", spFieldName, "&gt;")
</code></pre>

<p>They had used the &#8220;<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dotfuscator/dotf3e5x.asp" title="Code obfuscation utility" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/dotfuscator/dotf3e5x.asp&amp;referer=');">Dotfuscator</a>&#8221; to obfuscate the code of the library. 
As far as I know, the Reflector also makes some optimizations to the code. That&#8217;s why I&#8217;m not really sure if this is a coding approach or an effect by Dotfuscator/Reflector. Anyway, I believe it is quite a good solution: it doesn&#8217;t use much memory for execution and it&#8217;s quite readable!</p>
]]></content:encoded>
			<wfw:commentRss>http://tips.naivist.net/2005/09/05/string_concat/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Stulbas beigas?</title>
		<link>http://tips.naivist.net/2005/06/17/trim/</link>
		<comments>http://tips.naivist.net/2005/06/17/trim/#comments</comments>
		<pubDate>Fri, 17 Jun 2005 07:48:51 +0000</pubDate>
		<dc:creator>Krišs Rauhvargers</dc:creator>
				<category><![CDATA[VB un .net]]></category>

		<guid isPermaLink="false">http://naivist.net/tips/index.php/2005/06/17/stulbas-beigas/</guid>
		<description><![CDATA[Par VB.Net String klases metodēm TrimStart() un TrimEnd()]]></description>
			<content:encoded><![CDATA[<p>Piemērs no dzīves: ko dara nekārtīgs programmētājs, ja vajadzīga funkcija, kas  &#8220;sakabina&#8221; kopā divus faila pilnās takas fragmentus? Raksta funkciju</p>

<pre><code>Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    Return spRoot &amp; "/" &amp; spSub
End Function
</code></pre>

<p>Viss būtu labi, bet gadījumos , ja <code>spRoot</code> jau beidzas ar &#8220;/&#8221; simbolu, izveidosies neglīta taka ar divām slīpsvītrām pēc kārtas. Tāpēc kārtīgāks programmētājs cenšas notīrīt liekos &#8220;/&#8221; no Root beigām:</p>

<pre><code>Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    'korekti ir nemainīt parametra vērtību funkcijā - nokopēsim
    Dim sRoot As String = spRoot

    'kamēr vien rinda beidzas ar /, dzēšam tos nost
    While Right(sRoot, 1) = "/"
        'ja būs atkārtoti / beigās, notīrīs visus
        sRoot = Left(spRoot, Len(sRoot) - 1)
    End While

    Return sRoot &amp; "/" &amp; spSub
End Function
</code></pre>

<p>Ir jau labi, tikai darba daudz. Un vispār &#8211; kāpēc tā darīt, ja ir izveidota String klases funkcija <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemstringclasstrimendtopic.asp" title="Par TrimEnd no MSDN" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemstringclasstrimendtopic.asp&amp;referer=');">TrimEnd</a>. Lietojot to, varam atkal funkcijas būtību ierakstīt vienā rindiņā: </p>

<pre><code>Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    Return spRoot.TrimEnd("/"c) &amp; "/" &amp; spSub
End Function
</code></pre>

<p>TrimEnd un TrimStart funkcijām parametros var nodot sarakstu ar dažādiem simboliem, kurus mēģināt nodzēst no rindas beigām. Piemēram, ja gribam pārliecināties, ka apakšdirektorijas daļa <code>spSub</code> patiesībā nav līdzīga šai &#8220;../../../../windows/system32/&#8221;, varam nodzēst arī punkta simbolus no <s>otrā parametra</s> abu parametru savienojamajiem galiem.</p>

<pre><code>Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    Return spRoot.TrimEnd("/"c, "/"c) &amp; "/" &amp; spSub.TrimStart("."c, "/"c)
End Function
</code></pre>

<p>Cīņa par drošu kodu? Nu, tā laikam būs.</p>

<p>P.S. Uzmanīgi, ja kāds no parametriem būs Nothing, funkcija metīs kļūdu!</p>
]]></content:encoded>
			<wfw:commentRss>http://tips.naivist.net/2005/06/17/trim/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Mainīgo deklarēšana</title>
		<link>http://tips.naivist.net/2005/05/31/mainigo_deklaresana/</link>
		<comments>http://tips.naivist.net/2005/05/31/mainigo_deklaresana/#comments</comments>
		<pubDate>Tue, 31 May 2005 07:39:07 +0000</pubDate>
		<dc:creator>Krišs Rauhvargers</dc:creator>
				<category><![CDATA[VB un .net]]></category>

		<guid isPermaLink="false">http://naivist.net/tips/index.php/2005/05/31/mainigo_deklaresana/</guid>
		<description><![CDATA[Starp citu &#8230; VB6 skaitījās sliktais stils rakstīt šādi: Dim i, j As Integer Šādas deklarācijas rezultātā i bija ar tipu Variant, bet j bija Integer (nevis kā varētu šķist, abi Integer). Toties VB.net šī pieeja ir tiek atbalstīta un saprasta tieši tā, kā tā izskatās.]]></description>
			<content:encoded><![CDATA[<p>Starp citu &#8230; VB6 skaitījās sliktais stils rakstīt šādi:</p>

<pre><code>Dim i, j As Integer
</code></pre>

<p>Šādas deklarācijas rezultātā <em>i</em> bija ar tipu Variant, bet <em>j</em> bija Integer (nevis kā varētu šķist, abi Integer). </p>

<p>Toties VB.net šī pieeja ir tiek atbalstīta un saprasta tieši tā, kā tā izskatās.</p>
]]></content:encoded>
			<wfw:commentRss>http://tips.naivist.net/2005/05/31/mainigo_deklaresana/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MSDN mazliet mānās par ASP.Net failu upload</title>
		<link>http://tips.naivist.net/2005/05/27/msdn_maanaas/</link>
		<comments>http://tips.naivist.net/2005/05/27/msdn_maanaas/#comments</comments>
		<pubDate>Fri, 27 May 2005 14:09:48 +0000</pubDate>
		<dc:creator>Krišs Rauhvargers</dc:creator>
				<category><![CDATA[ASP.Net]]></category>
		<category><![CDATA[VB un .net]]></category>

		<guid isPermaLink="false">http://naivist.net/tips/index.php/2005/05/27/msdn-mazliet-m%c4%81n%c4%81s-par-aspnet-failu-upload/</guid>
		<description><![CDATA[Mēģinām augšuplādēt (upload) failu ASP.net lapā. Priekš tam lapas kodā jāievieto apmēram šāds: &#60;input id="myFile" type="file" runat="server" /&#62; &#60;input type=button id="cmdUp" runat="server" /&#62; Pēc tam varam ķert notikumu, kad nospiesta upload poga. Pēc MSDN rādītā piemēra, pietiek pārbaudīt vai UploadedFile atribūts nav nothing, tas ir, šādi: Private Sub Uploads (ByVal s As System.Object, ByVal e [...]]]></description>
			<content:encoded><![CDATA[<p>Mēģinām augšuplādēt (<em>upload</em>) failu ASP.net lapā. Priekš tam lapas kodā jāievieto apmēram šāds:  </p>

<pre><code>&lt;input id="myFile" type="file" runat="server" /&gt;
&lt;input type=button id="cmdUp" runat="server" /&gt;
</code></pre>

<p>Pēc tam varam ķert notikumu, kad nospiesta upload poga.  Pēc <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwebuihtmlcontrolshtmlinputfileclasstopic.asp" title="Stāsts par FileUpload objektu MSDNā" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwebuihtmlcontrolshtmlinputfileclasstopic.asp&amp;referer=');">MSDN rādītā piemēra</a>, pietiek pārbaudīt vai UploadedFile atribūts nav nothing, tas ir, šādi:</p>

<pre><code>Private Sub Uploads (ByVal s As System.Object, ByVal e As System.EventArgs) Handles cmdUp.ServerClick
         If  Not Me.myFile.PostedFile Is Nothing Then
        '... darām kaut ko
        End If
End Sub
</code></pre>

<p>Tomēr patiesībā &#8220;Not Me.myFile.PostedFile is Nothing&#8221; vienmēr atgriež True &#8211; pat tad, ja faila nav. Vismaz pie konfigurācijas, kāda tā ir man. Līdz ar to vienīgais veids, kā pārbaudīt, ka tiešām kaut kas ir uploadēts, ir: </p>

<pre><code>Private Sub Uploads (ByVal s As System.Object, ByVal e As System.EventArgs) Handles cmdUp.ServerClick
         If  (Not Me.myFile.PostedFile Is Nothing) AndAlso _ 
            (Me.myFile.PostedFile.ContentLength&gt;0) Then
        '... darām kaut ko
        End If
End Sub
</code></pre>

<p>Tā, redz!</p>
]]></content:encoded>
			<wfw:commentRss>http://tips.naivist.net/2005/05/27/msdn_maanaas/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MsExchange piekļuve caur MAPI</title>
		<link>http://tips.naivist.net/2005/05/26/mapi_cdo/</link>
		<comments>http://tips.naivist.net/2005/05/26/mapi_cdo/#comments</comments>
		<pubDate>Thu, 26 May 2005 10:37:24 +0000</pubDate>
		<dc:creator>Krišs Rauhvargers</dc:creator>
				<category><![CDATA[Datorlietas]]></category>
		<category><![CDATA[VB un .net]]></category>

		<guid isPermaLink="false">http://naivist.net/tips/index.php/2005/05/25/msexchange-lietosana-caur-mapi/</guid>
		<description><![CDATA[Pastāstīšu, cik viegli vai grūti ir programmatiski darboties ar MS Exchange objektiem, no koda "lasīt" e-pastu un tamlīdzīgi.]]></description>
			<content:encoded><![CDATA[<h4>Tāds kā ievads</h4>

<p><p>Savajadzējās pašam &#8220;notēlot Outlook-u&#8221;, lai lasītu vēstules. Tādēļ nācās iegūt nelielu pieredzi darbā ar <a target="_blank" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/exchanchor/htms/msexchsvr_mapi.asp" title="MAPI" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/exchanchor/htms/msexchsvr_mapi.asp&amp;referer=');">MAPI</a>, konkrētāk, ar tā &#8220;frontendu&#8221; <a target="_blank" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/exchanchor/htms/msexchsvr_cdowin2000.asp" title="Collaboration Data Objects for Windows 2000" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/exchanchor/htms/msexchsvr_cdowin2000.asp&amp;referer=');">MS CDO 1.2.1.</a>, lai piekļūtu <a target="_blank" href="http://www.microsoft.com/exchange/default.mspx" title="Exchange mājas" onclick="pageTracker._trackPageview('/outgoing/www.microsoft.com/exchange/default.mspx?referer=');">Microsoft Exchange server</a> direktorijām un &#8220;lasītu&#8221; e-pasta vēstules. Par to tad arī uzrakstīšu.</p>
<span id="more-72"></span>
<h4>Nosaukumu murgs</h4>
<p>Jāsāk ar to, ka Microsoft ir krietni pacentušies, lai sajauktu nomenklatūru šajos jautājumos. </p>
<p>Termins MAPI ir atšifrējams kā Messaging API, kuru nodrošina <a target="_blank" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/exchanchor/htms/msexchsvr_mapi.asp" title="MS skaidrojums par MAPI subsistēmu" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/exchanchor/htms/msexchsvr_mapi.asp&amp;referer=');">MAPI subsistēma</a>.  MAPI aptver daudz plašākas iespējas kā tikai e-pasta sistēmu veidošana, tas ir dzinējs gandrīz jebkāda veida ziņojumapmaiņai (skatīt aprakstu par subsistēmu). </p>
<p> Savukārt CDO jeb Collaboration Data Objects, kas sākotnēji saucies kā &#8220;OLE messaging&#8221; un mazliet vēlāk &#8220;Active messaging&#8221;, ir MAPI implementācija, kas paredzēta, lai no klientaplikācijām varētu pieslēgties MAPI serverim.  Taču CDO neatbalsta pilno MAPI implementāciju, bet tikai daļu no tās. Tādēļ Microsoft apgalvo, ka CDO implementē &#8220;Collaboration Data Objects API specification&#8221; otro versiju. </p>
<p>Bez šiem diviem terminiem parādās vēl arī trešais &#8211; Simple MAPI, kas ļauj no klientaplikācijām veikt pavisam vienkāršas darbības ar MAPI serveri. To šobrīd neapskatīsim. Mazliet precīzāk par atšķirībām starp MAPI, CDO, SimpleMAPI Microsoft paši <a target="_blank" href="http://support.microsoft.com/kb/q200018/" title='Differences between CDO, Simple MAPI, and Extended MAPI' onclick="pageTracker._trackPageview('/outgoing/support.microsoft.com/kb/q200018/?referer=');">skaidro šeit.</a></p>
<h4>Kas kur atrodams</h4>
<p>Nu, lūk. Tātad ir mums tāda bibliotēka MAPI32.dll, kas atrodas %windows%\system32 katalogā. Tā nu nemaz nav pareizā bibliotēka (ak jā, te vēl jāpiebilst, ka mērķa valoda, kurā jāveido mana programma, ir MS Visual Basic .net) &#8211; ar šo bibliotēku var programmēt tie, kas pārvalda C/C++. Cik saprotu, MAPI objekti neatbalsta <a target="_blank" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/automat/htm/chap5_78v9.asp" title="par IDispatch no MSDN" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/automat/htm/chap5_78v9.asp&amp;referer=');">IDispatch</a> interfeisu, līdz ar to nav piemērojami valodām, kuras izmanto late-binding. Piemēram, skriptu valodām vai VB.Net. Vēl turpat system32 ir arī bibliotēka cdo32.dll &#8211; arī tā nav &#8220;pareizā&#8221; &#8211; tā ir Crystal Data Objects, pavisam cita bibliotēka. Turpat system32 atrodas arī vēl viena bibliotēka ar uzvedinošu nosaukumu cdosys.dll &#8211; tā paredzēta SMTP un tamlīdzīgām lietām, vismaz tā <a target="_blank" href="http://support.microsoft.com/default.aspx?scid=kb;EN-US;q195683" title="INFO: Relationship between 1.x CDO Libraries and CDOSYS.DLL" onclick="pageTracker._trackPageview('/outgoing/support.microsoft.com/default.aspx?scid=kb_EN-US_q195683&amp;referer=');">skaidro Microsoft</a>. &#8220;Pareizais&#8221; fails ir meklējams takā <em>C:\Program Files\Common Files\System\MSMAPI\1033</em>. Ja jautāsiet, vai šis fails tur atrodas, ja datorā instalēts &#8220;pliks&#8221; Windows, atbildēšu, ka nē, neatrodas vis. CDO tiek instalēts tikai tad, ja uz datora instalēts MS Outlook (pilnais, nevis Outlook Express, ja nepieciešama piekļuve Ms Exchange serverim) vai arī pats MS Exchange server.</p>
<h4>Interop asamblejas</h4>
<p>Lai šo bibliotēku varētu izmantot VB.net projektā, nepieciešams izveidot tai Interop asambleju (man bija nepieciešams parakstīt manis veidoto programmu, tādēļ visām referencētajām bibliotēkām arī jābūt parakstītām). To darām šādi:</p>
<pre>rem aizejam uz MAPI direktoriju
C:&#62;cd C:\Program Files\Common Files\System\MSMAPI\1033
rem izveidojam savu atslēgas failu, ja nu tāda vēl nav
C:\Progra~1\Commo~1\System\MSMAPI\1033>sn -k snk.key
rem ģenerējam interop asambleju
C:\Progra~1\Commo~1\System\System\MSMAPI\1033>tlbimp cdo.dll /keyfile:snk.key /out:interop.cdo.dll</pre>
<p>Šeit gan tiek pieņemts, ka uz datora ir instalēts <a target="_blank" href="http://www.microsoft.com/downloads/details.aspx?FamilyId=9B3A2CA6-3647-4070-9F41-A333C6B9181D&#038;displaylang=en" title="SDK downloads" onclick="pageTracker._trackPageview('/outgoing/www.microsoft.com/downloads/details.aspx?FamilyId=9B3A2CA6-3647-4070-9F41-A333C6B9181D_038_displaylang=en&amp;referer=');">.NET SDK</a> un taka <em>C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin</em> ir iekļauta PATH.</p>
<p>Kad nu ir izdevies sagatavot interop asambleju, var sākt kodēt. Veidojam jaunu VB.net projektu, liekam referencēs šo interop.cdo.dll, rakstām kodu. Protams, kodu, kas darbosies tikai uz datoriem, kur uzstādīts MS Outlook.</p>
<h4>Sesijas veidošana</h4>
<p>Pirmā lieta, kas jādara, lai varētu strādāt gandrīz jebkurā klienta-servera vidē, ir ielogošanās. Ar to arī sāksim. 
Exchange konekciju veidojam šādi: </p>
<pre>Dim oSession As New CDO.<a target="_blank" href='http://msdn.microsoft.com/library/en-us/cdo/html/_olemsg_session_object.asp' title='par session no MSDN' onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/en-us/cdo/html/_olemsg_session_object.asp?referer=');">Session</a>
Dim sServerName As String = &quot;server&quot;
Dim sProfileName As String = &quot;user&quot;
oSession.<a target="_blank" href=http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdo/html/_olemsg_logon_method_session.asp onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/cdo/html/_olemsg_logon_method_session.asp&amp;referer=');">Logon</a>(NewSession:=True, NoMail:=False, ProfileInfo:=sServerName &amp; vbLf &amp; sProfileName)
</pre>
<p>Šeit fonā notiek NTLM autorizācija, kuras rezultātā tiek uzjautāta arī lietotāja parole un pārējās lietas. Par to manā gadījumā nebija jāuztraucas. Interesanti ir tas, ka nepareizi norādīts profils vai servera vārds nenozīmē, ka šis koda gabals izpildīsies ar kļūdām. Laba pārbaude ir tests, vai CurrentUser atribūts sesijai atšķiras no Nothing.</p>
<h4>Public folders meklēšana</h4>
<p>Kas tālāk? Man bija nepieciešams piekļūt nevis paša lietotāja Inbox folderim, bet folderiem no &#8220;Public folders&#8221; zara. Tātad, meklējam, kur ir publiskie folderi. Sākums jau ir pavisam vienkāršs:</p><pre>Dim oInfoStores As CDO.<a target="_blank" href=http://msdn.microsoft.com/library/en-us/cdo/html/_olemsg_infostores_collection_object.asp onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/en-us/cdo/html/_olemsg_infostores_collection_object.asp?referer=');">InfoStores </a>'InfoStoru kolekcija, ko dabū no sesijas objekta
Dim oInfoStore, otInfoStore As CDO.<a target="_blank" href=http://msdn.microsoft.com/library/en-us/cdo/html/_olemsg_infostore_object.asp onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/en-us/cdo/html/_olemsg_infostore_object.asp?referer=');">InfoStore</a> 'InfoStore objekts, kur atrodam &quot;Public Folders&quot;</p>

<p>'šeit dabūjam visas InfoStores, kas zināmas lietotāja sesijai
'tur būs &quot;Inbox&quot;, &quot;Public Folders&quot; un varbūt vēl kāds
oInfoStores = CType(oSession.InfoStores, CDO.InfoStores)</p>

<p>'folderu kolekcijā meklējam &quot;Public Folders&quot;
'jāņem vērā 1-bāzētās kolekcijas!!!
For i As Integer = 1 To CInt(oInfoStores.Count)
   otInfoStore = oInfoStores.Item(i)
    If CStr(otInfoStore.Name) = &quot;Public Folders&quot; Then
       oInfoStore = CType(oInfoStores.Item(i), CDO.InfoStore)
       '"Public folders" ir atrasts, oInfoStore satur referenci uz to
        Exit For
   End If
Next 
</pre><p>Tomēr tas, ko esam atraduši, NAV tas, kas mums vajadzīgs. Mums ir vajadzīgs CDO.<a target="_blank" href="http://msdn.microsoft.com/library/en-us/cdo/html/_olemsg_folder_object.asp" title="No MSDN par Folder" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/en-us/cdo/html/_olemsg_folder_object.asp?referer=');">Folder</a> tipa objekts, bet atraduši esam CDO.<a target="_blank" href="http://msdn.microsoft.com/library/en-us/cdo/html/_olemsg_infostore_object.asp" title="no MSDN par InfoStore" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/en-us/cdo/html/_olemsg_infostore_object.asp?referer=');">InfoStore</a> objektu. Tāpēc no InfoStore objekta atribūtiem jāatrod foldera ID un jādabū pats foldera objekts. To darām šādi:</p>
<pre>Dim oFields As CDO.Fields
Dim oFieldsItem As CDO.Field
Const PR<em>IPM</em>PUBLIC<em>FOLDERS</em>ENTRYID As Integer = &amp;H66310102
'noskaidrojam visus <code>public folders</code> atribūtus
oFields = CType(oInfoStore.Fields, MAPI.Fields)
'atrodam atribūtos <code>Public folders</code> ID
oFieldsItem = CType(oFields.Item(PR<em>IPM</em>PUBLIC<em>FOLDERS</em>ENTRYID), MAPI.Field)</p>

<p>'dabū referenci uz Public Folders FOLDERA (nevis InfoStore) objektu
'te jālieto GetFolder pēc foldera ID, citādi notiek kļūda.
oFolder = CType(ocMapiSession.Session.GetFolder(oFieldsItem.Value, oInfoStore.ID), MAPI.Folder)</pre>
<p>Ja viss būtu bijis normāli, es būtu varējis šo jocīgo koda gabalu aizvietot ar vienkāršu oFolder=oInfoStore.RootFolder. Taču nē. Kā microsoft <a target="_blank" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdo/html/_olemsg_fields_property_infostore_object_.asp" title="Par InfoStore objekta fields propertiju no MSDN" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/cdo/html/_olemsg_fields_property_infostore_object_.asp&amp;referer=');">raksta šeit</a>, &#8220;If your application is running as a Microsoft Windows NT® service, you cannot access the Microsoft Exchange Public Folders through the normal hierarchy because of a notification conflict.&#8221;, tas ir, ja kods darbojas kā Windows serviss (kas arī tieši mans gadījums), tad publiskajiem folderiem nevar piekļūt &#8220;pa tiešo&#8221;.</p>
<h4>Apakšfoldera atrašana</h4>
<p>Pēc tam atliek publiskajos folderos sameklēt nepieciešamo apakšfolderi. Arī mazliet netriviāli, jo apakšfolderu kolekcija nav pieejama pēc nosaukumiem, bet tikai apstaigājama pēc kārtas numuriem. Pieņemsim, ka gribu piekļūt pie &#8220;Public Folders/All Public folders/Internet Newsgroups/alt/2600&#8243;</p>
<pre>Dim strFolderi() As String = {&quot;All public folders&quot;, &quot;Internet Newsgroups&quot;, &quot;alt&quot;, &quot;2600hz&quot;}
oFolders = CType(oFolder.Folders, CDO.Folders)
For Each sFolder As String In strFolderi
    For iSk As Integer = 1 To CInt(oFolders.Count)
          'iterē cauri objektiem darba folderī, 
          'līdz atrod nākamo līmeni
          oFolder = CType(oFolders.Item(iSk), CDO.Folder)
           If CStr(oFolder.Name) = sFolder Then
              'ja vārds sakrīt, tad varam iet vienu līmeni zemāk
              oFolders = CType(oFolder.Folders, CDO.Folders)
              Exit For
          End If
      Next
Next
</pre>
<h4>Ziņojumu lasīšana</h4>
<p>Tā, tagad nu gan esam atraduši vajadzīgo taku. Sāksim lasīt ziņojumus. Pirmkārt jau, negribas kārtējo reizi pārlasīt visus ziņojumus. Filtrēsim tikai nelasītos.</p>
<pre>Dim oMessages As CDO.<a target="_blank" href=http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdo/html/_olemsg_messages_collection_object.asp onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/cdo/html/_olemsg_messages_collection_object.asp&amp;referer=');">Messages</a>
Dim oMessage As CDO.<a target="_blank" href=http://msdn.microsoft.com/library/en-us/cdo/html/_olemsg_message_object.asp onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/en-us/cdo/html/_olemsg_message_object.asp?referer=');">Message</a>
Dim oFilter As CDO.<a target="_blank" href=http://msdn.microsoft.com/library/en-us/cdo/html/_olemsg_messagefilter_object.asp onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/en-us/cdo/html/_olemsg_messagefilter_object.asp?referer=');">MessageFilter</a></p>

<p>oMessages = CType(oFolder.Messages, CDO.Messages)
oFilter = CType(oMessages.Filter, CDO.MessageFilter)
oFilter.Unread = True
For iCnt As Integer = CInt(oMessages.Count) To 1 Step -1
      oMessage = CType(oMessages.Item(iCnt), CDO.Message)
      Debug.WriteLine(CStr(oMessage.Subject))
Next</pre>
<p>Kodam izpildoties, debug logā tiek izdrukātas rindas ar atrasto e-pasta vēstuļu nosaukumiem. Jaukāk gan faktiski būtu izdrukāt arī sūtītāju vārdus. Pieliekam papildus koda rindu:</p><pre>Debug.Write(CStr(CType(oMessage.Sender, CDO.AddressEntry).Address))</pre>
<h4>Outlook security patch</h4>
<p>Un tagad sākas&hellip; Diemžēl šis kods vairs neizpildīsies, ja vien būs instalētas relatīvi jaunas MS Exchange server un Outlook versijas. Kāpēc? Pirms dažiem gadiem populāri kļuva dažādi e-pasta vīrusi. Vēstulēm tika pielikti VBS attachmenti un, tos izpildot, tie darīja savu slikto darbu. Sākotnēji dažādu MS Outlook nekorektas izpildes dēļ šie VBS faili izpildījās paši, tikai PASKATOTIES uz tiem caur &#8220;preview pane&#8221;, vēlāk Outlook tika labots un atradās citi veidi, kā šos VBS izpildīt. Taču galvenā īpatnība bija tā, ka šie skripti, lietojot SimpleMAPI un CDO, mēģināja Outlook Address Book meklēt adreses, uz kurām pārsūtīties tālāk, lai izplatītu vīrusu. Microsoft-ieši izdomāja šo problēmu &#8220;nocirst pašā saknē&#8221;, proti, <a target="_blank" href="http://www.microsoft.com/office/ork/2000/download/OutSecWP.doc" title="Pilna informācija par patchu" onclick="pageTracker._trackPageview('/outgoing/www.microsoft.com/office/ork/2000/download/OutSecWP.doc?referer=');">izveidot drošības ielāpu</a>, kas aizliedz šiem skriptiem piekļūt noteiktiem MAPI laukiem. Ne gluži aizliegt, bet, tikko programma mēģināja nolasīt kāda &#8220;aizliegtā&#8221; lauka vērtību, tā uz ekrāna parādījās jautājums &#8220;Šī programma grib darīt sliktas lietas. Vai atļaut?&#8221;&hellip; un podziņas ar iespējām &#8220;yes/no&#8221;.  Viss jau būtu jauki, tikai &#8211; kur parādīsies paziņojums, ja kods tiek izpildīts kā MSWindows serviss? Nu, tieši  tā, nekur neparādīsies! Un, pat ja parādītos uz servera ekrāna, kurš tad to visu laiku spaidītu?</p>
<p>Pilna informācija par to, kuri MAPI lauki tiek bloķēti ar zināmo security patch un kāda funkcionalitāte vairs nav pieejama, <a target="_blank" href="http://support.microsoft.com/kb/262701/EN-US/?FR=1" onclick="pageTracker._trackPageview('/outgoing/support.microsoft.com/kb/262701/EN-US/?FR=1&amp;referer=');">atrodama Microsoft Knowledge Base</a>.  MapiLab arī publicējuši apakstu, kā izkļūt no situācijas, lai <a target="_blank" href="http://www.mapilab.com/articles/vb_outlook_security_2.html" title="How to avoid security prompts in Visual Basic programs for Outlook" onclick="pageTracker._trackPageview('/outgoing/www.mapilab.com/articles/vb_outlook_security_2.html?referer=');">neredzētu drošības paziņojumus</a>. Cik saprotu, viens no pagaidām labākajiem veidiem ir lietot <a target="_blank" href="http://www.dimastr.com/redemption/" title="produkta mājaslapa" onclick="pageTracker._trackPageview('/outgoing/www.dimastr.com/redemption/?referer=');">Outlook Redemption</a>, kas patiesībā ir virsbūve virs MAPI.dll bibliotēkas, uz kuru šis patch neattiecas.  Bet tā ir trešās puses slēgta koda bibliotēka, kas bez maksas pieejama tikai izstrādes mērķiem.</p>
<p>Ir arī citas iespējas, proti, šos aizliegumus var &#8220;atspējot&#8221; kādam noteiktam lietotāja profilam. To var izdarīt MS Exchange administrators norādot, ka dotajam profilam MAPI security patch nav spēkā. Papildus tam uz lietotāja datora jāizdara izmaiņas Windows reģistrā:</p>
<pre>[HKEY<em>CURRENT</em>USER\Software\Policies\Microsoft\Security]
&quot;CheckAdminSettings&quot;=dword:00000001</pre>
<p>Kad šīs izmaiņas ir veiktas, jāpārstartē MS Outlook un varam darboties tālāk. Minētais koda fragments, kas izdrukā arī vēstules sūtītāju, izpildās veiksmīgi. </p>
<h4>Piekļuve MSExchange profiliem</h4>
<p>Bet ja nu mēs iedomājamies palasīt vēstules  kādā citā publiskajā folderī, kurā var iesūtīt gan vēstules no ārpuses, gan arī no paša MS Exchange? Bammm&hellip; kļūda. Kā izrādās darbs ar <a target="_blank" href="http://msdn.microsoft.com/library/en-us/cdo/html/_olemsg_sender_property_message.asp" title="Par sender no MSDN" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/en-us/cdo/html/_olemsg_sender_property_message.asp?referer=');">Sender</a> atribūtu nebūt nav triviāls gadījumā, kad par to ir zināms kas vairāk nekā, piemēram &#8220;Kriss Rauhvargers &lt;kriss@naivist.net&gt;&#8221;, proti, ja lietotājs ir MS Exchange lietotājs. Šajā gadījumā lietotāja informācija ir sinhrona ar MS <a title="_blank" href="http://www.microsoft.com/windowsserver2003/technologies/directory/activedirectory/default.mspx" title="Active Directory mājas" onclick="pageTracker._trackPageview('/outgoing/www.microsoft.com/windowsserver2003/technologies/directory/activedirectory/default.mspx?referer=');">Active Directory</a> ierakstu par lietotāju un, kā zināms, AD vienam un tam pašam lietotājam var reģistrēt daudzas e-pasta adreses.  Bez visa šeit minētā vismaz pie tādas konfigurācijas, kāda pieejama man, Sender atribūta lielākā daļa atribūtu man nebija pieejami (un apskatāmi atkļūdošanas laikā tos apskatot, meta kļūdas).</p>
<p>Microsoft iesaka interesantu šīs <a target="_blank" href="http://support.microsoft.com/default.aspx?scid=kb;en-us;Q324530" title="How to Programmatically Retrieve Fully Qualified E-mail Addresses" onclick="pageTracker._trackPageview('/outgoing/support.microsoft.com/default.aspx?scid=kb_en-us_Q324530&amp;referer=');"> problēmas apkārtceļu</a>. Proti, ņemam vēstules objektu, veidojam tam Reply (itin kā taisītos sūtīt atbildi) un pēc tam no iegūtās atbildes vēstules nokopējam saņēmēju.  Atbildi izmetam. Un, lūk, šim te objektam jau ir pieejams Name atribūts un viss pārējais nepieciešamais. Kodā tas izskatās šādi:</p>
<pre>Private Function MessageSender(ByVal opMsg As CDO.Message) As String
    Dim sAddr As String = &quot;&quot;
    Dim oMsgRepl As CDO.Message
    Dim oRec As CDO.<a target="_blank" href=http://msdn.microsoft.com/library/en-us/cdo/html/_olemsg_recipient_object.asp onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/en-us/cdo/html/_olemsg_recipient_object.asp?referer=');">Recipient</a>
    Dim bRightsOk As Boolean = False
    Try
        'mēģina izveidot reply vēstuli
        oMsgRepl = CType(opMsg.Reply(), CDO.Message)
        oRec = CType(CType(oMsgRepl.Recipients, CDO.Recipients).Item(1), CDO.Recipient)
        sAddr = UCase(CStr(oRec.Address()))   'paņem atbildes saņēmēja adresi
        bRightsOk = True
    Catch ex As Exception
        sAddr = Nothing 'dabūta kļūda skatoties uz e-pasta adresi
    Finally
        'outlook security brīdinājums mēģināts izmest servisam, 
        'bet tas nav iespējams. Izpilde uzreiz nokļuvusi šeit.
        If Not bRightsOk Then sAddr = Nothing
    End Try
    Return sAddr
End Function</pre><p>Funkcijas rezultāts atgriež saņēmēja adresi, kas gan ir nedaudz neparasta. Gadījumā, ja saņēmējs ir no ārpuses, adrese būs, piemēram SMTP:kriss@naivist.net, savukārt tad, kad lietotājs ir MS Exchange lietotājs, tā būs, piemēram, EX:/O=Org/OU=SomeUnit/CN=RECIPIENTS/CN=Katalogs/CN=Kriss (ieraksts atbilst <a target="_blank" href="http://msdn.microsoft.com/library/en-us/adschema/adschema/active_directory_schema.asp" title="par Active Directory Schema" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/en-us/adschema/adschema/active_directory_schema.asp?referer=');">Active Directory shēmai</a>. Tomēr skaidrs, ka no šī &#8220;izlobīt&#8221; vajadzīgos fragmentus ir pavisam vienkārši. 
Šī funkcija bez kļūdām (bet arī bez pozitīviem rezultātiem) apstrādā arī situāciju, kad nav ir ieslēgta Outlook Security un Recipient objekts nav pieejams. Tādā brīdī ir novērojama interesanta anomālija, kad koda izpilde pēc rindas, kur paņem atbildes saņēmēja adresi, izpildes uzreiz nokļūst Finally blokā, bet ne Catch blokā, ja kods tiek izpildīts kā windows serviss. Zinu, tas izklausās neiespējami, jo patiesībā bija vai nu a) jānotiek kļūdai b) jāizpildās nākamjai rindai, tomēr tā tas nav.</p>
<h4>Tādas kā beigas</h4>
<p>Ar šo laikam varu arī beigt savu aprakstu. Jā, galvenais- neaizmirst pasaukt <a target="_blank" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdo/html/_olemsg_logoff_method_session.asp" title="no msdn par logoff metodi" onclick="pageTracker._trackPageview('/outgoing/msdn.microsoft.com/library/default.asp?url=/library/en-us/cdo/html/_olemsg_logoff_method_session.asp&amp;referer=');">Logoff</a> sesijas objektam. Tikai uzmanīgi &#8211; arī tajā brīdī var kaut kas &#8220;uzkārties&#8221; :-)</p>
<h4>Downloads</h4>
<a href="http://naivist.net/stufi/pamaaciib.htm" title="VB kods" onclick="pageTracker._trackPageview('/outgoing/naivist.net/stufi/pamaaciib.htm?referer=');">Rakstā minēto koda paraugu</a> pilns apkopojums vienā strādājošā funkcijā. </p>
]]></content:encoded>
			<wfw:commentRss>http://tips.naivist.net/2005/05/26/mapi_cdo/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

