Hello,
I'm solving quite difficult problem (for me). I have large web site or
web application project with about 1300 code files. For now, I will talk
only about Web application project.
I cannot get FileCodeModel objects for all code files. When trying from
my add-in, the VS simply crashes or becomes unstable. It happens after
several hundreds ProjectItems are already processed. So I initially
thought it was insufficient RAM problem. But there was still a lot of
free RAM available when this happened. After some testing I was able
(only sometimes) to catch this exception when system crashed:
"InvalidOperationException: BufferedGraphicsContext cannot be disposed
of because a buffer operation is currently in progress."
This pointed me to http://tinyurl.com/2eaxf8, especially the last posts.
Indeed, if I opened Task Manager and added "GDI Objects" column in
Processes view, the devenv.exe always crashed not when there was no free
RAM but when number of GDI objects reached 9999. In .NET 2.0 there seems
to be a bug when this happens.
Bingo! So I isolated the problem and found that each call of
pi.FileCodeModel creates 8 new GDI objects which stay there. With more
than 1000 code files we easily reach 9999. And I'm stuck here.
I've created sample Web Application project with about 1380 code files.
Then I created a macro which should reproduce the problem. The macro
simply gets list of all project items as plain collection and then gets
FileCodeModel from each of them (if possible). I expected it to behave
the same as add-in - number of GDI reaches 9999 and VS becomes unstable.
In macro environment, there however appeared different symptoms but I
believe they have the same reason. GDI count stopped at about 8000 and
pi.FileCodeModel started to throw exceptions:
"System.Runtime.InteropServices.COMException (0x80010100): System call
failed. (Exception from HRESULT: 0x80010100 (RPC_E_SYS_CALL_FAILED))
at EnvDTE.ProjectItem.get_FileCodeModel()"
Therefore the count of code models shown when macro finished differs
every time I run the macro.
You can download the project from
http://www.helixoft.com/CsharpWebApplication2.zip (1.2 MB) and the macro is:
Sub GdiLeakTest()
Try
' First, get all project items as plain collection
Dim projectItemsCol As New Collection
getAllProjectItems(CType(DTE.ActiveSolutionProjects(0),
Project).ProjectItems, projectItemsCol)
' So far, so good.
Dim codeModels As New Collection
Dim pi As ProjectItem
For Each pi In projectItemsCol
Dim filecm As FileCodeModel
Try
' the following operation creates 8 new
' GDI objects for each .aspx.cs file in
' Web application project.
filecm = pi.FileCodeModel
Catch exx As Exception
' 'Here we get exceptions randomly.
End Try
If Not filecm Is Nothing Then
codeModels.Add(filecm)
End If
Next
Dim res As String = "Done" & vbCrLf
res &= "Project items: " & projectItemsCol.Count & vbCrLf
res &= "Code models: " & codeModels.Count
MsgBox(res)
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
Private Sub getAllProjectItems(ByVal projectItems As ProjectItems,
ByVal result As Collection)
Dim pi As ProjectItem
Try
For Each pi In projectItems
result.Add(pi)
Try
If pi.ProjectItems.Count > 0 Then
getAllProjectItems(pi.ProjectItems, result)
End If
Catch exx As Exception
End Try
Next pi
Catch ex As Exception
End Try
End Sub
Can you reproduce the problems? If yes, how do you handle large projects?
Note, unlike web site projects, web applications return correct non-null
FileCodeModel for all files. But I tried also "open-close" method
described at http://www.mztools.com/Articles/2006/MZ2006017.aspx. I
replaced:
filecm = pi.FileCodeModel
with:
Dim w As Window
w = pi.Open(EnvDTE.Constants.vsViewKindCode)
filecm = pi.FileCodeModel
w.Close(vsSaveChanges.vsSaveChangesNo)
Then the macro works correctly, GDI count doesn't increase and the
result is OK. This seems like partial solution but...
1. I have supposed that Web application projects should behave like
normal projects and there is no need to open documents in the background.
2. I need all FileCodeModel objects after reading them. Using w.Close
invalidates them and trying to access them causes exceptions.
Any idea?
--
Peter Macej
Helixoft - http://www.helixoft.com
VSdocman - Commenter and generator of class documentation for C#, VB
.NET and ASP .NET code