OLD | NEW |
| (Empty) |
1 /// Copyright (c) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System; | |
4 using System.Collections.Generic; | |
5 using System.Globalization; | |
6 using Microsoft.VisualStudio; | |
7 using Microsoft.VisualStudio.Shell.Interop; | |
8 using IServiceProvider = System.IServiceProvider; | |
9 | |
10 namespace Microsoft.VisualStudio.Project | |
11 { | |
12 /// <summary> | |
13 /// This object is in charge of reloading nodes that have file monikers
that can be listened to changes | |
14 /// </summary> | |
15 internal class FileChangeManager : IVsFileChangeEvents | |
16 { | |
17 #region nested objects | |
18 /// <summary> | |
19 /// Defines a data structure that can link a item moniker to the
item and its file change cookie. | |
20 /// </summary> | |
21 private struct ObservedItemInfo | |
22 { | |
23 /// <summary> | |
24 /// Defines the id of the item that is to be reloaded. | |
25 /// </summary> | |
26 private uint itemID; | |
27 | |
28 /// <summary> | |
29 /// Defines the file change cookie that is returned when
listening on file changes on the nested project item. | |
30 /// </summary> | |
31 private uint fileChangeCookie; | |
32 | |
33 /// <summary> | |
34 /// Defines the nested project item that is to be reload
ed. | |
35 /// </summary> | |
36 internal uint ItemID | |
37 { | |
38 get | |
39 { | |
40 return this.itemID; | |
41 } | |
42 | |
43 set | |
44 { | |
45 this.itemID = value; | |
46 } | |
47 } | |
48 | |
49 /// <summary> | |
50 /// Defines the file change cookie that is returned when
listenning on file changes on the nested project item. | |
51 /// </summary> | |
52 internal uint FileChangeCookie | |
53 { | |
54 get | |
55 { | |
56 return this.fileChangeCookie; | |
57 } | |
58 | |
59 set | |
60 { | |
61 this.fileChangeCookie = value; | |
62 } | |
63 } | |
64 } | |
65 #endregion | |
66 | |
67 #region Fields | |
68 /// <summary> | |
69 /// Event that is raised when one of the observed file names hav
e changed on disk. | |
70 /// </summary> | |
71 internal event EventHandler<FileChangedOnDiskEventArgs> FileChan
gedOnDisk; | |
72 | |
73 /// <summary> | |
74 /// Reference to the FileChange service. | |
75 /// </summary> | |
76 private IVsFileChangeEx fileChangeService; | |
77 | |
78 /// <summary> | |
79 /// Maps between the observed item identified by its filename (i
n canonicalized form) and the cookie used for subscribing | |
80 /// to the events. | |
81 /// </summary> | |
82 private Dictionary<string, ObservedItemInfo> observedItems = new
Dictionary<string, ObservedItemInfo>(); | |
83 | |
84 /// <summary> | |
85 /// Has Disposed already been called? | |
86 /// </summary> | |
87 private bool disposed; | |
88 #endregion | |
89 | |
90 #region Constructor | |
91 /// <summary> | |
92 /// Overloaded ctor. | |
93 /// </summary> | |
94 /// <param name="nodeParam">An instance of a project item.</para
m> | |
95 internal FileChangeManager(IServiceProvider serviceProvider) | |
96 { | |
97 #region input validation | |
98 if(serviceProvider == null) | |
99 { | |
100 throw new ArgumentNullException("serviceProvider
"); | |
101 } | |
102 #endregion | |
103 | |
104 this.fileChangeService = (IVsFileChangeEx)serviceProvide
r.GetService(typeof(SVsFileChangeEx)); | |
105 | |
106 if(this.fileChangeService == null) | |
107 { | |
108 // VS is in bad state, since the SVsFileChangeEx
could not be proffered. | |
109 throw new InvalidOperationException(); | |
110 } | |
111 } | |
112 #endregion | |
113 | |
114 #region IDisposable Members | |
115 /// <summary> | |
116 /// Disposes resources. | |
117 /// </summary> | |
118 public void Dispose() | |
119 { | |
120 // Don't dispose more than once | |
121 if(this.disposed) | |
122 { | |
123 return; | |
124 } | |
125 | |
126 this.disposed = true; | |
127 | |
128 // Unsubscribe from the observed source files. | |
129 foreach(ObservedItemInfo info in this.observedItems.Valu
es) | |
130 { | |
131 ErrorHandler.ThrowOnFailure(this.fileChangeServi
ce.UnadviseFileChange(info.FileChangeCookie)); | |
132 } | |
133 | |
134 // Clean the observerItems list | |
135 this.observedItems.Clear(); | |
136 } | |
137 #endregion | |
138 | |
139 #region IVsFileChangeEvents Members | |
140 /// <summary> | |
141 /// Called when one of the file have changed on disk. | |
142 /// </summary> | |
143 /// <param name="numberOfFilesChanged">Number of files changed.<
/param> | |
144 /// <param name="filesChanged">Array of file names.</param> | |
145 /// <param name="flags">Array of flags indicating the type of ch
anges. See _VSFILECHANGEFLAGS.</param> | |
146 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code.</returns> | |
147 int IVsFileChangeEvents.FilesChanged(uint numberOfFilesChanged,
string[] filesChanged, uint[] flags) | |
148 { | |
149 if(this.FileChangedOnDisk != null) | |
150 { | |
151 for(int i = 0; i < numberOfFilesChanged; i++) | |
152 { | |
153 string fullFileName = Utilities.Canonica
lizeFileName(filesChanged[i]); | |
154 if(this.observedItems.ContainsKey(fullFi
leName)) | |
155 { | |
156 ObservedItemInfo info = this.obs
ervedItems[fullFileName]; | |
157 this.FileChangedOnDisk(this, new
FileChangedOnDiskEventArgs(fullFileName, info.ItemID, (_VSFILECHANGEFLAGS)flags
[i])); | |
158 } | |
159 } | |
160 } | |
161 | |
162 return VSConstants.S_OK; | |
163 } | |
164 | |
165 /// <summary> | |
166 /// Notifies clients of changes made to a directory. | |
167 /// </summary> | |
168 /// <param name="directory">Name of the directory that had a cha
nge.</param> | |
169 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code. </returns> | |
170 int IVsFileChangeEvents.DirectoryChanged(string directory) | |
171 { | |
172 return VSConstants.S_OK; | |
173 } | |
174 #endregion | |
175 | |
176 #region helpers | |
177 /// <summary> | |
178 /// Observe when the given file is updated on disk. In this case
we do not care about the item id that represents the file in the hierarchy. | |
179 /// </summary> | |
180 /// <param name="fileName">File to observe.</param> | |
181 internal void ObserveItem(string fileName) | |
182 { | |
183 this.ObserveItem(fileName, VSConstants.VSITEMID_NIL); | |
184 } | |
185 | |
186 /// <summary> | |
187 /// Observe when the given file is updated on disk. | |
188 /// </summary> | |
189 /// <param name="fileName">File to observe.</param> | |
190 /// <param name="id">The item id of the item to observe.</param> | |
191 internal void ObserveItem(string fileName, uint id) | |
192 { | |
193 #region Input validation | |
194 if(String.IsNullOrEmpty(fileName)) | |
195 { | |
196 throw new ArgumentException(SR.GetString(SR.Inva
lidParameter, CultureInfo.CurrentUICulture), "fileName"); | |
197 } | |
198 #endregion | |
199 | |
200 string fullFileName = Utilities.CanonicalizeFileName(fil
eName); | |
201 if(!this.observedItems.ContainsKey(fullFileName)) | |
202 { | |
203 // Observe changes to the file | |
204 uint fileChangeCookie; | |
205 ErrorHandler.ThrowOnFailure(this.fileChangeServi
ce.AdviseFileChange(fullFileName, (uint)(_VSFILECHANGEFLAGS.VSFILECHG_Time | _VS
FILECHANGEFLAGS.VSFILECHG_Del), this, out fileChangeCookie)); | |
206 | |
207 ObservedItemInfo itemInfo = new ObservedItemInfo
(); | |
208 itemInfo.ItemID = id; | |
209 itemInfo.FileChangeCookie = fileChangeCookie; | |
210 | |
211 // Remember that we're observing this file (used
in FilesChanged event handler) | |
212 this.observedItems.Add(fullFileName, itemInfo); | |
213 } | |
214 } | |
215 | |
216 /// <summary> | |
217 /// Ignore item file changes for the specified item. | |
218 /// </summary> | |
219 /// <param name="fileName">File to ignore observing.</param> | |
220 /// <param name="ignore">Flag indicating whether or not to ignor
e changes (1 to ignore, 0 to stop ignoring).</param> | |
221 internal void IgnoreItemChanges(string fileName, bool ignore) | |
222 { | |
223 #region Input validation | |
224 if(String.IsNullOrEmpty(fileName)) | |
225 { | |
226 throw new ArgumentException(SR.GetString(SR.Inva
lidParameter, CultureInfo.CurrentUICulture), "fileName"); | |
227 } | |
228 #endregion | |
229 | |
230 string fullFileName = Utilities.CanonicalizeFileName(fil
eName); | |
231 if(this.observedItems.ContainsKey(fullFileName)) | |
232 { | |
233 // Call ignore file with the flags specified. | |
234 ErrorHandler.ThrowOnFailure(this.fileChangeServi
ce.IgnoreFile(0, fileName, ignore ? 1 : 0)); | |
235 } | |
236 } | |
237 | |
238 /// <summary> | |
239 /// Stop observing when the file is updated on disk. | |
240 /// </summary> | |
241 /// <param name="fileName">File to stop observing.</param> | |
242 internal void StopObservingItem(string fileName) | |
243 { | |
244 #region Input validation | |
245 if(String.IsNullOrEmpty(fileName)) | |
246 { | |
247 throw new ArgumentException(SR.GetString(SR.Inva
lidParameter, CultureInfo.CurrentUICulture), "fileName"); | |
248 } | |
249 #endregion | |
250 | |
251 string fullFileName = Utilities.CanonicalizeFileName(fil
eName); | |
252 | |
253 if(this.observedItems.ContainsKey(fullFileName)) | |
254 { | |
255 // Get the cookie that was used for this.observe
dItems to this file. | |
256 ObservedItemInfo itemInfo = this.observedItems[f
ullFileName]; | |
257 | |
258 // Remove the file from our observed list. It's
important that this is done before the call to | |
259 // UnadviseFileChange, because for some reason,
the call to UnadviseFileChange can trigger a | |
260 // FilesChanged event, and we want to be able to
filter that event away. | |
261 this.observedItems.Remove(fullFileName); | |
262 | |
263 // Stop observing the file | |
264 ErrorHandler.ThrowOnFailure(this.fileChangeServi
ce.UnadviseFileChange(itemInfo.FileChangeCookie)); | |
265 } | |
266 } | |
267 #endregion | |
268 } | |
269 } | |
OLD | NEW |