Ureader.com  
Microsoft software help and Community
   home   |   control panel login   |   archive   |  
 
tools
vsnet.act
vsnet.debugging
vsnet.documentation
vsnet.enterprise.tools
vsnet.general
vsnet.ide
vsnet.jlca
vsnet.servicepacks
vsnet.setup
vsnet.vsip
vsnet.vss
vsnet.vstools.office
vstudio.development
vstudio.extensibility
vstudio.general
vstudio.helpauthoring
vstudio.setup
vstudio.sourcesafe
  
 
date: Wed, 4 Jun 2008 06:46:05 -0700,    group: microsoft.public.vstudio.extensibility        back       


Issue with regular expression in VS2008 Macro   
I've been working on a macro for several weeks now (VS2008 on Vista 
Ultimate) that is designed to open every file in a  Visual Studio solution 
and inserts custom "trace" commands into every function and every "catch" 
block.  Currently, I use a regular expression that searches the current 
CodeElement (the function) for "catch" blocks.  The regex breaks the block up 
into 5 groups...the "catch" statement, the exception type, the exception 
variable, the open curly brace, and the contents inside the block.  
	The problem I am having, I feel, comes from how I am calculating where in 
the block to insert the trace command.  I use the AbsoluteCharOffset of the 
current function and add that to the index of the '{' group for that specific 
catch block.  The problem with doing this is that my regular expression does 
not seem to take newlines into account and adds the number of newlines from 
the start of the function to the catch block onto the index of insertion.
First, here is some example code before the macro is run on it:
private void func1(int num)
{
	try
	{
		try
		{
			func2(num);
			throw new Exception("test1");
			throw new NullReferenceException("test2");
			throw new NotImplementedException("test3");
		} 
		catch (NullReferenceException r)
		{
			MessageBox.Show(r.Message);
		}
		catch (NotImplementedException e)
		{
			MessageBox.Show(e.Message);
		}
		catch (Exception t)
		{
			MessageBox.Show(t.Message);
		}
	}
	catch (Exception err)
	{
		MessageBox.Show(err.Message);
	}
}
	The more catch blocks present, the more offset the commands to insert 
become.  In the following, you can see as the commands are inserted, they 
become more and more offset (normally I use a SmartFormat function after all 
the insertions are completed, however I turned that off to show you how 
things are being inserted.)  The "function level" trace commands (inside the 
try/finally block) are inserted without problem, but the catch level commands 
become increasingly offset.

private void func1(int num)
{
	try
	{
		ZynTraceLog.Trace("Threadz.Form1.func1", messageToShow.enter);
            try
            {
                try
                {
                    func2(num);

                    throw new Exception("one");
                    throw new NullReferenceException("two");
                    throw new NotImplementedException("three");
                }
                catch (NullReferenceException r)
                {
           ZynTraceLog.TraceException("Threadz.Form1.func1", r, 
messageToShow.exception);
         MessageBox.Show(r.Message);
                }
                catch (NotImplementedException e)
                {
               ZynTraceLog.TraceException("Threadz.Form1.func1", e, 
messageToShow.exception);
     MessageBox.Show(e.Message);
                }
                catch (Exception t)
                {
                   ZynTraceLog.TraceException("Threadz.Form1.func1", t, 
messageToShow.exception);
 MessageBox.Show(t.Message);
                }
            }
            catch (Exception err)
            {
                MessageBZynTraceLog.TraceException("Threadz.Form1.func1", 
err, messageToShow.exception);
ox.Show(err.Message);
            }
	}
	finally
	{
		ZynTraceLog.Trace("Threadz.Form1.func1", messageToShow.exit);
	}
}
The following piece of code is the sub-routine that does the checks for 
"catch" blocks (you will also see the regular expression used):
'This sub-routine contains the logic for checking to see if a specific ' 
'CodeElement contains an exception block...specifically a 'catch' block.  It 
'starts by using a regular expression to search the CodeElement and return 
'matches.  After the list of matches has been populated,
'the next step is to loop through the list and insert the "enter" trace 
'command after the opening curly brace.  The "exit" command is inserted at 
'the end of the catch block.
Sub CheckForException(ByVal codeElem As CodeElement)

   Dim beginFunction As TextPoint = codeElem.StartPoint
   Dim startWrite As EditPoint2 = codeElem.StartPoint.CreateEditPoint()
   Dim ending As EditPoint = codeElem.EndPoint.CreateEditPoint()
   Dim expression As String = startWrite.CreateEditPoint().GetText(ending)
   Dim traceStmt1 As String = ""
   Dim matchList As List(Of Match) = New List(Of Match)

'The following object is of type Regex. It will be given a specific regular 
'expression to search for in the text. It will attempt to find any instance 
'of the following code segment:  Catch ( Exception e ) { <code> } 
'The regular expression class allows you to split the match up into a 
"group" 'collection that can be indexed to find a specific group. An 
explanation of 'the regular expression is as follows:

\w*(?<Catch>catch)\s*\(\s*   --->   "<0 or more words>Catch<0 or more 
whitespaces>("

(?<Exception>\w*Exception)\s*   --->   "'<0 or more word(s)>Exception'<0 or 
more whitespaces" ... i.e. "\w*Exception" will match NullReferenceException

(?<VarName>\w*\s*[^\)])   --->   <0 or more words> followed by <0 or more 
whitespaces> UP TO (not including) the first occurence of )

[\)]\w*\s*[\r\n]\w*\s*   --->   Now INCLUDE the ) in the match, followed by 
<0 or more words><0 or more whitespaces><Carriage return or newline><0 or 
more words><0 or more whitespaces>

(?<OpenCurly>[\{])   --->   Will match the first open curly brace found

(?<CurlyContents>.*?[\}])   --->    Matches everything inside of the "catch" 
block...after the { and before the }
   
   Dim regexAddr As Regex = New 
Regex("\w*(?<Catch>catch)\s*\(\s*(?<Exception>\w*Exception)\s*(?<VarName>\w*\s*[^\)])[\)]\w*\s*[\r\n]\w*\s*(?<OpenCurly>[\{])(?<CurlyContents>.*?[\}])", _
RegexOptions.Multiline Or RegexOptions.CultureInvariant Or 
RegexOptions.IgnoreCase Or RegexOptions.Singleline)

   ' Create a match
   Dim aMatch As Match = regexAddr.Match(expression)

   ' Check to see if there is a match in the current function, if so, continue
   If aMatch.Success Then

   ' Call the FillMatchArray to fill in the Match list
   ' Each item will have groups when each match is broken down into parts
   FillMatchArray(matchList, aMatch)

   ' Used as an index for the list...must work backwards as to not corrupt
   ' the already set AbsoluteCharOffets of the open curly braces ({) for 
   ' the catch blocks
   Dim index As Integer = matchList.Count - 1

   While index > -1

     ' Group(4) is the "curly brace" group for each match.  The value is "{".
     If matchList.Item(index).Groups(4).Success Then

      ' Move the edit point to the beginning of the function the catch is 
      ' in PLUS the location of the "{" for each catch block

      startWrite.MoveToAbsoluteOffset(beginFunction.AbsoluteCharOffset + _ 
      (matchList.Item(index).Groups(4).Index))
                    
      traceStmt1 = "ZynTraceLog.TraceException(" & Chr(34) & _ 
      CodeElem.FullName & Chr(34) & ", " & 
      matchList.Item(index).Groups(3).Value & ", messageToShow.exception);" & 
      vbCrLf

      ' Check to see if the catch trace cmd is already written
      Dim blockContents = matchList.Item(index).Groups(5).ToString()

      If Not blockContents.Contains(traceStmt1) Then
         startWrite.Insert(traceStmt1)
      End If

     End If

     ' Decrement the index to move backwards through the list
     index = index - 1

  End While

 End If

End Sub
date: Wed, 4 Jun 2008 06:46:05 -0700   author:   Andy am

RE: Issue with regular expression in VS2008 Macro   
Hi Andy,

Welcome to MSDN Managed Newsgroup!

So far my understanding of your question is:
 * you are using regular expressions to parse C# code and insert some
   statements at matching positions
 * your current code will insert code into incorrect positions
Please feel free to let me know if I've misunderstood anything. I'll be 
glad to
revise my proposed solution below.

The key issue here is that the text returned from EditPoint.GetText() is
not having the same length as the one that computed from
TextPoint.AbsoluteOffset: a linefeed takes one char when computing the
AbsoluteOffset, but the returned text will use CR LF (2 chars), thus when
you use the Match.Index and the offset together, the position is incorrect.

The solution is to replace vbCrLf with vbLf after you get the text with
EditPoint.GetText():

	Dim expression As String = startWrite.CreateEditPoint().GetText(ending)
	expression = expression.Replace(vbCrLf, vbLf)
	Debug.Assert(expression.Length = ending.AbsoluteCharOffset - 
beginFunction.AbsoluteCharOffset)

Also, when you insert statements, you need to add 1 since the offset is
1-based and Match.Index is 0-based:

	startWrite.MoveToAbsoluteOffset(beginFunction.AbsoluteCharOffset + _
	(matchList.Item(index).Groups(4).Index + 1))


Hope this helps. Please let me know if you have further questions.



Delighting our customers is our #1 priority. We welcome your comments and 
suggestions about how we can improve the support we provide to you. Please 
feel free to let my manager know what you think of the level of service 
provided. You can send feedback directly to my manager at: 
msdnmg@microsoft.com.

==================================================
Get notification to my posts through email? Please refer to 
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues 
where an initial response from the community or a Microsoft Support 
Engineer within 1 business day is acceptable. Please note that each follow 
up response may take approximately 2 business days as the support 
professional working with you may need further investigation to reach the 
most efficient resolution. The offering is not appropriate for situations 
that require urgent, real-time or phone-based interactions or complex 
project analysis and dump analysis issues. Issues of this nature are best 
handled working with a dedicated Microsoft Support Engineer by contacting 
Microsoft Customer Support Services (CSS) at 
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
date: Thu, 05 Jun 2008 09:42:19 GMT   author:   (Walter Wang [MSFT])

RE: Issue with regular expression in VS2008 Macro   
That seems to have fixed the issue I was having.  Thank you for the help.  
The new problem however, which seems completely unpredicatable, is that when 
I run this macro on my solution, it will break randomly (most of the time 
with the same error).  For example...on one run, the following exception was 
caught:

   in file MessageConfigurationControl.cs...
   System.Runtime.InteropServices.COMException (0x80010100):System call 
failed.
   (Exception from HRESULT: (0x80010100) (RPC_E_SYS_CALL_FAILED)) at
   EnvDTE.ProjectItem.get_Name()

The odd thing is...if I run the macro again, it can pass through that file 
with no problem.  I ran a "sample" of 5 runs and received that same error 4 
out of 5 times, but all on different files (never the same file).  The one 
time I didn't receive that error, I got a different error:

   in file RecipeControlTemplateForm.cs...
   System.InvalidCastException: Unable to cast COM object of 
   type 'System._ComObject' to interface type 'EnvDTE.CodeFunction'.  This
   operation failed because the QueryInterface call on the COM component
   for the interface with IID '{0CFBC2B9-0D4E-11D300C04F688DDE}' failed due to
   the following error: System call failed. (Exception from HRESULT: 
(0x80010100) 
   (RPC_E_SYS_CALL_FAILED))

Thanks again for your help.
date: Thu, 5 Jun 2008 07:15:00 -0700   author:   Andy am

RE: Issue with regular expression in VS2008 Macro   
Hi Andy,

Thanks for your quick update.

First, we need to make two changes for the above code:

    Function CheckForException(ByVal codeElem As CodeElement)
        Dim changed As Boolean = False


		...
				If Not blockContents.Contains(traceStmt1.Replace(vbCrLf, vbLf)) Then
					startWrite.Insert(traceStmt1)
					changed = True
				End If
		...

        Return changed
    End Function

1) We need to replace the vbCrLf in traceStmt1 too when determine if we had 
already inserted the statements, otherwise we will insert duplicated 
statements.
2) Change the procedure to function and return True if we really did some 
changes, this will be used later.

As for your new question, I think it's probably because you're not checking 
for correct CodeElement.Kind, in this case, my understanding is that we 
only need to insert these statements in CodeElement with Kind = 
vsCMElementFunction or vsCMElementProperty.

Also, the CodeElements in a FileCodeModel is tree-like. We can use code 
like following to get all functions/properties kind CodeElements:

    Sub GetInterestedCodeElements(ByVal result As List(Of CodeElement), 
ByVal elements As CodeElements)
        For Each ce As CodeElement In elements
            Dim members As CodeElements = Nothing
            Select Case ce.Kind
                Case vsCMElement.vsCMElementNamespace
                    members = CType(ce, CodeNamespace).Members
                Case vsCMElement.vsCMElementClass
                    members = CType(ce, CodeClass).Members
                Case vsCMElement.vsCMElementFunction, 
vsCMElement.vsCMElementProperty
                    result.Add(ce)
            End Select
            If Not members Is Nothing Then
                GetInterestedCodeElements(result, members)
            End If
        Next
    End Sub

As we can see, we need to recursively walk into Namespace and Class to get 
the functions/properties. This should also work for nested classes.

With this, we can write following macros to insert statements in various 
contexts:

    Sub CheckExceptionForCurrentFunction()
        Dim ts As TextSelection = DTE.ActiveDocument.Selection
        Dim interestedKinds() As vsCMElement = 
{vsCMElement.vsCMElementFunction, vsCMElement.vsCMElementProperty}
        For Each kind In interestedKinds
            Dim ce As CodeElement = ts.ActivePoint.CodeElement(kind)
            If Not ce Is Nothing Then
                CheckForException(ce)
                Exit For
            End If
        Next
    End Sub

    Sub CheckExceptionForCurrentFile()
        CheckExceptionForProjectItem(DTE.ActiveDocument.ProjectItem)
    End Sub

    Sub CheckExceptionForSelectedProjects()
        For Each proj As Project In DTE.ActiveSolutionProjects
            For Each pi As ProjectItem In proj.ProjectItems
                CheckExceptionForProjectItem(pi)
            Next
        Next
    End Sub

    Sub CheckExceptionForAllProjects()
        For Each proj As Project In DTE.Solution.Projects
            For Each pi As ProjectItem In proj.ProjectItems
                CheckExceptionForProjectItem(pi)
            Next
        Next
    End Sub

NOTE: you probably need to check for correct project type (C#) before 
processing all project items in a project. Check Project.Kind, which is a 
GUID.

You can put these macros in context menu or toolbar for easier access.

Following is the helper function CheckExceptionForProjectItem used:

    Function CheckExceptionForProjectItem(ByVal pi As ProjectItem)
        Dim changed As Boolean = False
        If Not pi.FileCodeModel Is Nothing Then
            Dim result As New List(Of CodeElement)
            GetInterestedCodeElements(result, pi.FileCodeModel.CodeElements)
            For Each ce As CodeElement In result
                If CheckForException(ce) Then
                    changed = True
                End If
            Next
            If changed Then
                pi.Open(Constants.vsViewKindCode).Visible = True
            End If
        End If
        Return changed
    End Function


Hope this helps. Again, please feel free to let me know if there's anything 
unclear.


Delighting our customers is our #1 priority. We welcome your comments and 
suggestions about how we can improve the support we provide to you. Please 
feel free to let my manager know what you think of the level of service 
provided. You can send feedback directly to my manager at: 
msdnmg@microsoft.com.

This posting is provided "AS IS" with no warranties, and confers no rights.
date: Thu, 05 Jun 2008 23:30:39 GMT   author:   (Walter Wang [MSFT])

RE: Issue with regular expression in VS2008 Macro   
Thanks again for the information.  The thing I'm unsure of though is how 
exactly I am to integrate the examples you gave me into my current macro.  I 
feel like I am kind of soing the same thing you are, only a little 
differently.  I am going to post the whole macro I guess, just so you can see 
if I am going about it the correct way.  I'm sure there are more efficient 
ways to do what I'm looking to do, but functionality comes first right now at 
the sake of efficiency, as it is not too important for this macro to be 
efficient (just has to get the job done).  Thanks again.

Ryan...

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
Imports System.Text.RegularExpressions
Imports System.Collections.Generic

Public Module ScanSolution

    ' First, this sub-routine checks to see that there is even a solution 
open to begin
    ' working with.  When there is a solution open, this will begin to 
examine the projects in the solution.
    ' For each project, we want to examine the project items.
    Sub NavigateSolution()

        'Declare a project object
        Dim objProject As EnvDTE.Project

        Try
            'First, check to see that a solution is open in the VS environment
            If Not DTE.Solution.IsOpen Then
                MsgBox("Please load or create a solution")
            Else
                'Traverse through each project item of the project inside 
the solution
                For Each objProject In DTE.Solution.Projects
                    NavigateProjectItems(objProject.ProjectItems)
                Next
            End If
        Catch objException As System.Exception
            MsgBox(objException.ToString)
        End Try

    End Sub

    ' Called from the NavigateSolution routine, this sub-routine is 
responsible for checking the contents
    ' of a specific project item...whether a namespace, class, variable, etc.
    Private Sub NavigateProjectItems(ByVal projItems As ProjectItems)

        'Declare a project item
        Dim projItem As EnvDTE.ProjectItem

        ' Check to see if there is any "project items" (code files, resource 
files, etc.)
        ' in the project...if not, return to function call
        If Not (projItems Is Nothing) Then
            For Each projItem In projItems
                GoThroughProjectItem(projItem)
            Next
        End If

    End Sub

    ' Once called, this routine must process the ProjectItem passed to it.  
Because this could be any number of
    ' "project items", we must first check what kind of project item it is.  
We will only be processing files ending 
    ' in .cs (C# files).  If the project item has a file code model (i.e. it 
has a code structure like a class file), then
    ' we must traverse through it finding code elements (functions and other 
code blocks)
    Sub GoThroughProjectItem(ByVal projItem As ProjectItem)

        'Declare a code element...Could be a statement, namespace, class, 
function, etc.
        Dim elem As CodeElement

        If Not IsNothing(projItem) Then
            'Is it a file?
            If projItem.Kind = "{6BB5F8EE-4483-11D3-8BCF-00C04F8EC28C}" And 
projItem.Name.EndsWith(".cs") And Not 
(projItem.Name.EndsWith(".Designer.cs")) Then

                projItem.Open(Constants.vsViewKindCode) 'it's a file, open it
                projItem.Document.Activate() 'Now make it active (this is 
for the smart formatting function

            End If
            If Not projItem.Name.EndsWith(".Designer.cs") Then
                If Not IsNothing(projItem.FileCodeModel) Then
                    GoThroughCodeElements(elem, projItem)
                ElseIf Not IsNothing(projItem.ProjectItems) Then
                    NavigateProjectItems(projItem.ProjectItems)
                End If
            End If
        End If
    End Sub

    ' Called when a project item has a FileCodeModel property.
    ' This will walk through a code model until it finds the "class" code 
element.
    ' Once that is found, it is passed to the GoThroughClass sub-routine to 
process the elements in the class
    Sub GoThroughCodeElements(ByVal elem As CodeElement, ByVal projItem As 
ProjectItem)

        'Drill down through code elements
        For Each elem In projItem.FileCodeModel.CodeElements
            If elem.Kind = vsCMElement.vsCMElementNamespace Then
                GoThroughClass(elem)
            End If
        Next
        ' Now call SmartFormat() which will set all of the tabs and indents 
for the code
        SmartFormat()
    End Sub

    ' This routine handles all of the logic for inserting the trace commands 
or calling another routine
    ' to insert instead.  The function will call itself until it finds 
either a get/set block or a function.
    ' When a function is found, it is first scanned for any exceptions 
inside.  After that, the edit points are
    ' created (where the trace commands are inserted in the code).  Once 
they are set, the actual commands are 
    ' set up as strings using things like the code element name and the 
enter or exit option.
    Sub GoThroughClass(ByVal elem As CodeElement)

        'This variable will be children of elem
        Dim childCodeElem As CodeElement
        Dim enterString1 As String
        Dim exitString1 As String
        Dim newLine As String = vbCrLf
        Dim entryEdit As EditPoint
        Dim exitEdit As EditPoint
        Dim getOrSet As CodeProperty

        For Each childCodeElem In elem.Children
            If childCodeElem.Kind = vsCMElement.vsCMElementClass Then
                GoThroughClass(childCodeElem)
            ElseIf childCodeElem.Kind = vsCMElement.vsCMElementFunction Then

                CheckForException(childCodeElem)

                Dim codeElemFunction = CType(childCodeElem, CodeFunction)

                If Not codeElemFunction.MustImplement Then

                    'Set the point in the code at which you plan to edit
                    Try
                        entryEdit = 
codeElemFunction.GetStartPoint(vsCMPart.vsCMPartBody).CreateEditPoint()
                    Catch ex As Exception
                    End Try

                    Try
                        exitEdit = 
codeElemFunction.GetEndPoint(vsCMPart.vsCMPartBody).CreateEditPoint()
                    Catch ex As Exception
                    End Try

                    If Not IsNothing(entryEdit) And Not IsNothing(exitEdit) 
Then
                        'Set the strings to insert into the code
                        'Chr(135) is delimeter for string
                        enterString1 = "try" & newLine & "{" & newLine & 
"ZynTraceLog.Trace(" & Chr(34) & codeElemFunction.FullName & Chr(34) & ", 
messageToShow.enter"

                        exitString1 = "}" & newLine & "finally" & newLine & 
"{" & newLine & "ZynTraceLog.Trace(" & Chr(34) & codeElemFunction.FullName & 
Chr(34) & ", messageToShow.exit"

                        enterString1 += ");"
                        exitString1 += ");" & newLine & "}"

                        'Check to see if the strings already exist in the 
code, if so, do not write anything
                        'We do the exitString first in the case that there 
is no text written within the function.
                        If IsWrittenInFunction(childCodeElem, exitString1) = 
False Then
                            exitEdit.Insert(exitString1 & newLine)
                        End If
                        If IsWrittenInFunction(childCodeElem, enterString1) 
= False Then
                            entryEdit.Insert(enterString1 & newLine)
                        End If
                    End If
                End If
            ElseIf childCodeElem.Kind = vsCMElement.vsCMElementProperty Then
                ' Set getOrSet to a CodeProperty object
                getOrSet = CType(childCodeElem, CodeProperty)

                ' Call ManipGetSet() to add the trace code to the get/set 
properties that may are present
                ManipGetSet(getOrSet)

            End If

        Next

    End Sub

    ' This will make a collection of arguments that is the list of arguments 
for a specific function.
    ' This list must also be appended to the trace command that inserted 
into the code.
    Sub setStringWithParams(ByRef enterStringCopy As String, ByRef 
exitStringCopy As String, ByVal codeElem As CodeFunction)

        Dim index As Integer = 1
        Dim param As CodeParameter
        Dim argDict As Dictionary(Of String, Object) = New Dictionary(Of 
String, Object)

        For Each param In codeElem.Parameters
            argDict.Add(codeElem.Parameters.Item(index).FullName, 
codeElem.Parameters.Item(index))
            index = index + 1
        Next

        enterStringCopy += ", argdict"
        exitStringCopy += ", argdict"

    End Sub

    ' This sub-routine contains the logic for checking to see if a specific 
CodeElement contains
    ' an exception block...specifically a 'catch' block.  It starts by using 
a regular expression
    ' to search the CodeElement and return matches.  After the list of 
matches has been populated,
    ' the next step is to loop through the list and insert the "enter" trace 
command after the opening
    ' curly brace.  The "exit" command is inserted at the end of the catch 
block.
    Function CheckForException(ByVal codeElem As CodeElement)

        Dim beginFunction As TextPoint = codeElem.StartPoint
        Dim startWrite As EditPoint2 = codeElem.StartPoint.CreateEditPoint()
        Dim ending As EditPoint = codeElem.EndPoint.CreateEditPoint()
        Dim expression As String = 
startWrite.CreateEditPoint().GetText(ending)
        expression = expression.Replace(vbCrLf, vbLf)
        Dim traceStmt1 As String = ""
        Dim matchList As List(Of Match) = New List(Of Match)
        Dim changed As Boolean = False

        ' The following object is of type Regex.  It will be given a 
specific regular expression to search for in the text.
        ' It will attempt to find any instance of the following code 
segment:  Catch ( Exception e ) { <code> }
        ' The regular expression class allows you to split the match up into 
a "group" collection that can be indexed to find a specific group
        ' An explanation of the regular expression is as follows:
        '       \w*(?<Catch>catch)\s*\(\s*   --->   "<0 or more 
words>Catch<0 or more whitespaces>("
        '       (?<Exception>\w*Exception)\s*   --->   "'<0 or more 
word(s)>Exception'<0 or more whitespaces" ... i.e. "\w*Exception" will match 
NullReferenceException
        '       (?<VarName>\w*\s*[^\)])   --->   <0 or more words> followed 
by <0 or more whitespaces> UP TO (not including) the first occurence of )
        '       [\)]\w*\s*[\r\n]\w*\s*   --->   Now INCLUDE the ) in the 
match, followed by <0 or more words><0 or more whitespaces><Carriage return 
or newline><0 or more words><0 or more whitespaces>
        '       (?<OpenCurly>[\{])   --->   Will match the first open curly 
brace found
        '       (?<CurlyContents>.*?[\}])   --->    Matches everything 
inside of the "catch" block...after the { and before the }
        Dim regexAddr As Regex = New 
Regex("\w*(?<Catch>catch)\s*\(\s*(?<Exception>\w*Exception)\s*(?<VarName>\w*\s*[^\)])[\)]\w*\s*[\r\n]\w*\s*(?<OpenCurly>[\{])(?<CurlyContents>.*?[\}])", _
                                           RegexOptions.Multiline Or 
RegexOptions.CultureInvariant Or RegexOptions.IgnoreCase Or 
RegexOptions.Singleline)

        ' Create a match
        Dim aMatch As Match = regexAddr.Match(expression)

        ' Check to see if there is a match in the current function, if so, 
continue
        If aMatch.Success Then

            ' Call the FillMatchArray to fill in the Match list
            ' Each item will have groups when each match is broken down into 
parts
            FillMatchArray(matchList, aMatch)

            ' Used as an index for the list...must work backwards as to not 
corrupt the already set AbsoluteCharOffets of the open curly braces ({) for 
the catch blocks
            Dim index As Integer = matchList.Count - 1

            While index > -1

                ' Group(4) is the "curly brace" group for each match.  The 
value is "{".
                If matchList.Item(index).Groups(4).Success Then

                    ' Move the edit point to the beginning of the function 
the catch is in PLUS the location of the "{" for each catch block
                    
startWrite.MoveToAbsoluteOffset(beginFunction.AbsoluteCharOffset + 
(matchList.Item(index).Groups(4).Index) + 1)
                    'startWrite.StartOfLine()

                    traceStmt1 = "ZynTraceLog.TraceException(" & Chr(34) & 
codeElem.FullName & Chr(34) & ", " & matchList.Item(index).Groups(3).Value & 
", messageToShow.exception);" & vbCrLf

                    ' Check to see if the catch trace cmd is already written
                    Dim blockContents = 
matchList.Item(index).Groups(5).ToString()

                    If Not blockContents.Contains(traceStmt1.Replace(vbCrLf, 
vbLf)) Then
                        startWrite.Insert(traceStmt1)
                        changed = True
                    End If

                End If

                ' Decrement the index to move backwards through the list
                index = index - 1

            End While

        End If

        Return changed

    End Function

    ' This sub-routine will add all of the successful matches to a List of 
type Match
    ' Each Match in the list will be broken down into groups
    Sub FillMatchArray(ByRef matchList As List(Of Match), ByVal theMatch As 
Match)

        Do While Not (theMatch.Value = "")
            matchList.Add(theMatch)
            theMatch = theMatch.NextMatch
        Loop

    End Sub

    ' This sub-routine sets up the arguments for the WritePropertyTrace 
routine to insert the necessary
    ' trace commands into the code file.  Not every property block has BOTH 
a 'get' and 'set' block, and
    ' this is the routine that checks for that.
    Sub ManipGetSet(ByVal prop As CodeProperty)

        ' Get the "getter" property block
        Dim getter As CodeFunction = prop.Getter
        ' Get the "setter" property block
        Dim setter As CodeFunction = prop.Setter

        If Not IsNothing(getter) Then
            WritePropertyTrace(getter, "get")
        End If
        If Not IsNothing(setter) Then
            WritePropertyTrace(setter, "set")
        End If

    End Sub

    ' This sub-routine
    Sub WritePropertyTrace(ByVal accessor As CodeFunction, ByVal type As 
String)

        Dim writeEnter As String = "ZynTraceLog.Trace(" & Chr(34) & 
accessor.FullName & Chr(34) & ", messageToShow.enter_" & type & ");"
        Dim writeExit As String = "ZynTraceLog.Trace(" & Chr(34) & 
accessor.FullName & Chr(34) & ", messageToShow.exit_" & type & ");"

        ' Set the editpoints for each property block
        Dim enterEdit As EditPoint = 
accessor.GetStartPoint(vsCMPart.vsCMPartBody).CreateEditPoint()
        Dim exitEdit As EditPoint = 
accessor.GetEndPoint(vsCMPart.vsCMPartBody).CreateEditPoint()

        If type.Equals("get") Then
            writeEnter = "try" & vbCrLf & "{" & vbCrLf & writeEnter
            writeExit = "}" & vbCrLf & "finally" & vbCrLf & "{" & vbCrLf & 
writeExit & vbCrLf & "}"
        End If

        ' Check to see if the string to instert is already written within 
the property block
        If IsAlreadyWrittenInGetSet(accessor, writeEnter) = False Then
            enterEdit.Insert(writeEnter & vbCrLf)
        End If
        If IsAlreadyWrittenInGetSet(accessor, writeExit) = False Then
            exitEdit.Insert(writeExit & vbCrLf)
        End If


    End Sub

    ' This function has a CodeFunction and String as arguments and returns a 
bool.
    ' It examines the start and end points of a get or set block and copies 
the contents of that into a string.
    ' It then checks to see if that new string contains the other string 
that is passed in as an argument
    ' If it contains the string, true is returned, else false
    Function IsAlreadyWrittenInGetSet(ByVal getterOrSetter As CodeFunction, 
ByVal str As String) As Boolean

        ' Set the start point for the text selection
        Dim start As TextPoint = getterOrSetter.StartPoint()
        ' Set the end point for the text selection
        Dim finish As TextPoint = getterOrSetter.EndPoint()
        ' Select the text from those pre-set points and push it into a 
string to parse later
        Dim selection As String = start.CreateEditPoint().GetText(finish)
        Dim removedWS = selection.Replace(" ", "")
        Dim strNonWS = str.Replace(" ", "")

        ' Returns true if the text selection contains str
        Return removedWS.Contains(strNonWS)

    End Function

    ' This function does the same thing as IsAlreadyWrittenInGetSet except 
it checks the whole function rather
    ' than just a piece like a get block.
    Function IsWrittenInFunction(ByVal elem As CodeElement, ByVal str As 
String) As Boolean

        ' Set the start point for the text selection
        Dim startPoint As TextPoint = elem.StartPoint()
        ' Set the end point for the text selection
        Dim endPoint As TextPoint = elem.EndPoint()
        ' Select the text from those pre-set points and push it into a 
string to parse later
        Dim functionAsString As String = 
startPoint.CreateEditPoint.GetText(endPoint)
        ' Remove the white space from the strings
        Dim removedWS = functionAsString.Replace(" ", "")
        Dim strNonWS = str.Replace(" ", "")

        ' Returns true if the text selection contains the string
        Return removedWS.Contains(strNonWS)

    End Function

    ' This sub-routine is responsible for formatting the active document.
    ' It will move all the indents and other spacing into the corrent format
    Sub SmartFormat()

        ' Before running this example, open a code document.
        Dim objSel As TextSelection = DTE.ActiveDocument.Selection
        ' Select all code in open document.
        objSel.SelectAll()
        ' Apply smart formatting rules for the language.
        objSel.SmartFormat()

    End Sub
End Module
date: Fri, 6 Jun 2008 07:56:02 -0700   author:   Andy am

RE: Issue with regular expression in VS2008 Macro   
I'm glad that my understanding of your question is on track and the sample 
code helped. However, it's not easy to tell what is a best approach without 
clear requirement. Anyway, I managed to copy the code in your message and 
re-formatted it to have a good understanding. It seems your objective here 
also includes to insert correct function/property signature, right? 

My suggestion is to divide the code into several categories:
 * core functions: which are used to insert statements at correct positions
 * helper functions: such as those to get correct signature, get interested 
CodeElemtns, etc.
 * macros: various macros that using various context

By the way, is it a requirement to remove the inserted trace statements 
later?



Delighting our customers is our #1 priority. We welcome your comments and 
suggestions about how we can improve the support we provide to you. Please 
feel free to let my manager know what you think of the level of service 
provided. You can send feedback directly to my manager at: 
msdnmg@microsoft.com.

This posting is provided "AS IS" with no warranties, and confers no rights.
date: Fri, 06 Jun 2008 15:39:22 GMT   author:   (Walter Wang [MSFT])

RE: Issue with regular expression in VS2008 Macro   
Essentially, the requirement is to insert the custom trace statement into one 
of three possible code elements:  the beginning and end of all functions, the 
beginning and end of all get/set property blocks, and finally, inside of a 
catch block.  It is not a requirement at this time to remove the inserted 
trace statement later, however I can see that being a feature later.
date: Fri, 6 Jun 2008 09:58:04 -0700   author:   Andy am

RE: Issue with regular expression in VS2008 Macro   
Thanks for further information.

I think for simple projects, using regex to insert/remove trace statements 
should be fine. However, this approach does have some limitations: it's 
difficult to accurately find valid functions/properties, for example, what 
if some commented out code that also have functions/properties? That's 
where a language lexer/parser comes into play. And building a lexer/parser 
is not trivial.

Also, to log when entering/leaving a function, this is what AOP 
(Aspect-Oriented Programming) does best. You can find more information 
about AOP in .NET here: 
http://blogs.msdn.com/abhinaba/archive/2006/01/23/516163.aspx. 

One of the approaches that AOP takes to add log when entering/leaving 
function is to create a dynamic proxy that wraps the actual function call. 
Unfortunately this also means it cannot help you to insert statements in 
every try/catch block which is inside the function body.

As a side note, I'm curious to know why you need to add trace statements in 
every try/catch block. Some best practice is to only catch an exception if 
you can handle. Then, at places where you do catch the exception, you can 
get the exception's full stack trace which contains correct information for 
logging purpose. You might find following articles useful:

http://www.codeproject.com/KB/cs/csmverrorhandling.aspx
http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx


I'm sorry that this discussion seems a bit off-topic to the original 
question. Have you fixed the second question about CodeElement casting 
error? Please feel free to let me know if it's still not fixed.



Delighting our customers is our #1 priority. We welcome your comments and 
suggestions about how we can improve the support we provide to you. Please 
feel free to let my manager know what you think of the level of service 
provided. You can send feedback directly to my manager at: 
msdnmg@microsoft.com.

This posting is provided "AS IS" with no warranties, and confers no rights.
date: Sat, 07 Jun 2008 03:14:02 GMT   author:   (Walter Wang [MSFT])

RE: Issue with regular expression in VS2008 Macro   
Unfortunatly, I have not fixed the issue I am having with the casting of the 
COM object as well as the issue with calling the .Name property (.get_Name() 
function).  It is still randomly occurring throughout the runtime of the 
macro.  As I mentioned before, I've run several samples to see if there is 
any pattern to how or when the errors occur, but I can't seem to spot any 
pattern.
date: Mon, 9 Jun 2008 05:36:02 -0700   author:   Andy am

RE: Issue with regular expression in VS2008 Macro   
Since my local test cannot reproduce the issue easily, I'm wondering if you 
could send me your macro project along with some testing projects to test 
the macro?

To do this, you need to first find where's your macro project located, when 
you first create a new macro project, the default location is "Visual 
Studio 2008\Projects\VSMacro80" in your personal folder. Please send me the 
whole macro project directory and some testing C# projects. Let me know the 
testing steps so that I can reproduce the issues as you did. I'll be glad 
to help you find the root cause and fix it.

You can send the files to wawang@online.microsoft.com (remove "online."). 
Thanks very much for your effort.




Delighting our customers is our #1 priority. We welcome your comments and 
suggestions about how we can improve the support we provide to you. Please 
feel free to let my manager know what you think of the level of service 
provided. You can send feedback directly to my manager at: 
msdnmg@microsoft.com.

This posting is provided "AS IS" with no warranties, and confers no rights.
date: Mon, 09 Jun 2008 23:04:49 GMT   author:   (Walter Wang [MSFT])

RE: Issue with regular expression in VS2008 Macro   
Walter,

I can send you the macro project (but it is nothing more than you have).  
The only thing that I've tested it on is our product, and I cannot send you 
our source code for the product unfortunatly.  Hopefully there is still some 
information you could provide as to why this error could be happening, 
however I understand if we are at a standstill.  One thing test that I would 
like to try, and perhaps you could tell me how (and if it is even possible), 
is if let's say I run the macro, and it fails on file 1000 for example, 
rather than restarting at file 0 when I re-run the macro, is it possible to 
start at file 1001, and proceed down the list of files?
date: Tue, 10 Jun 2008 05:55:02 -0700   author:   Andy am

RE: Issue with regular expression in VS2008 Macro   
Hi,

I was able to reproduce the issue as your described on a large solution 
with 400+ files, though I'm not sure if it's the same exception you're 
experiencing. I have done some research and testing, and I think I may have 
found some workaround.

First, a CodeFunction may not have implementation at all, therefore calling 
its GetStartPoint(vsCMPart.vsCMPartBody).CreateEditPoint() will fail. Two 
examples of such CodeFunctions are:
 * functions with [DllImport] attribute
 * auto property in C# 3.0

To fix this, we can use following function to determine if a CodeFunction 
has body at all:

    Function HasCSharpCodeBody(ByVal cf As CodeFunction)
        Return 
cf.StartPoint.CreateEditPoint().GetText(cf.EndPoint).Contains("{")
    End Function

The second issue here is that we may encounter random exception when 
running the macro on a large solution. I haven't been able to find the root 
cause, but I found if we do the same thing from an addin, then it's working 
fine. I will forward these information to developer team for further 
investigation. For now, I suggest you to use following steps to migrate the 
macro to an addin:
 1) In Visual Studio 2008, Create a new project with type "Visual Studio 
Add-in" in category "Other Project Types\Extensibility".
 2) Follow the wizard and select "Visual Basic" as the programming language 
in Page 1 of 6
 3) Next, we only need to select "Microsoft Visual Studio 2008" as 
Application Host
 4) In the step "Choosing Add-in Options", check the first option to 
"Create a 'Tools' menu item", we can use this menu item to invoke our 
function that was called by a macro
 5) After the add-in project is created, create a new module and copy/paste 
your macro module into it, and declare a module variable DTE:

    Public DTE As DTE2

 6) In Connect.vb, modify the Exec procedure as:

    Public Sub Exec(ByVal commandName As String, ByVal executeOption As 
vsCommandExecOption, ByRef varIn As Object, ByRef varOut As Object, ByRef 
handled As Boolean) Implements IDTCommandTarget.Exec
        handled = False
        If executeOption = vsCommandExecOption.vsCommandExecOptionDoDefault 
Then
            If commandName = "MyAddin1.Connect.MyAddin1" Then
                ScanSolution.DTE = _applicationObject
                ScanSolution.NavigateSolution()
                handled = True
                Exit Sub
            End If
        End If
	End Sub
 
 7) Your macro module should compile without further modification. Now 
build the addin and start a new instance of Visual Studio, you should be 
able to see a new menu item in the Tools menu to launch your addin.

NOTE: do not try to debug the addin under Visual Studio debugger, it will 
have the same issue as we are experiencing from the macro. 

I hope this workaround will also work for you. Please let me know if you 
have any questions.



Delighting our customers is our #1 priority. We welcome your comments and 
suggestions about how we can improve the support we provide to you. Please 
feel free to let my manager know what you think of the level of service 
provided. You can send feedback directly to my manager at: 
msdnmg@microsoft.com.

This posting is provided "AS IS" with no warranties, and confers no rights.
date: Wed, 11 Jun 2008 14:24:52 GMT   author:   (Walter Wang [MSFT])

RE: Issue with regular expression in VS2008 Macro   
Hi,

How's it going? Have you got a chance to try out the workaround? Does it 
work? Please feel free to reply here or email me at 
wawang@online.microsoft.com (remove 'online.') if you have any questions. 
I'll be glad to help.

Best regards,
Walter Wang
Microsoft Online Communtiy Support

Delighting our customers is our #1 priority. We welcome your comments and 
suggestions about how we can improve the support we provide to you. Please 
feel free to let my manager know what you think of the level of service 
provided. You can send feedback directly to my manager at: 
msdnmg@microsoft.com.

This posting is provided "AS IS" with no warranties, and confers no rights.
date: Thu, 12 Jun 2008 23:16:46 GMT   author:   (Walter Wang [MSFT])

RE: Issue with regular expression in VS2008 Macro   
So far it seems that the work around is working without any problems.  I was 
able to insert the trace commands into every file without an issue.  Thank 
you very much for your help.
date: Fri, 13 Jun 2008 05:11:01 -0700   author:   Andy am

RE: Issue with regular expression in VS2008 Macro   
Hi,

Thanks very much for your update and I'm really glad the workaround worked.

In case of anything else in this issue, please don't hesitate to reply here 
to open a new post in this newsgroup, we will be glad to help. Thanks again 
for using MSDN Managed Newsgroup support service.

Best regards,
Walter Wang (wawang@online.microsoft.com, remove 'online.')
Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and 
suggestions about how we can improve the support we provide to you. Please 
feel free to let my manager know what you think of the level of service 
provided. You can send feedback directly to my manager at: 
msdnmg@microsoft.com.

This posting is provided "AS IS" with no warranties, and confers no rights.
date: Fri, 13 Jun 2008 22:09:35 GMT   author:   (Walter Wang [MSFT])

Google
 
Web ureader.com


    COPYRIGHT 2007, YARDI TECHNOLOGY LIMITED, ALL RIGHT RESERVE  |   contact us