A simple macro to change the Target Framework for a project from .NET 2.0 to .NET 3.5, hacked together in a few minutes. This will fail for C++/CLI projects, and possibly VB.NET projects (haven't checked). Works fine for regular C# projects, as well as web projects:
For Each proj As Project In DTE.Solution.Projects
Try
proj.Properties.Item("TargetFramework").Value = 196613
Debug.Print("Upgraded {0} to 3.5", proj.Name)
Catch ex As Exception
Debug.Print("Failed to upgrade {0} to 3.5", proj.Name)
End Try
Next proj
Why 196613, you ask? Well, when I see such a number my first instinct is to feed it into calc.exe and switch it to hexadecimal. And I was right: 196613 in decimal converts to 0x00030005 - You can see the framework version hiding in there. Major version in the high word, minor in the low word. The previous TargetFramework number was 131072 - 0x00020000, or 2.0.
(Nitpickers might point out that I could simply set it to &30005 rather than messing with the obscure decimal number. They would be correct - but I got to this number through the debugger, so that's how it will stay)
I'm writing a piece of code that regularly polls a MOSS server and checks several files. This can happen quite a few times a day, and this totally skews my Usage Reports for my sites.
Does anyone know of a way to tell MOSS to ignore calls made by a specific user, a specific IP (my code runs on a dedicated server), a specific UserAgent or whatever?
Has anything been released - or, in fact, talked about - since August's release of the v1.1 CTP?
Haven't done any web-part development in a while and wanted to get back in the game. I last used the v1.0 extensions, and was surprised that nothing much has changed in that field except for the CTP release, and even that can't be downloaded - I just get a broken link.
I'll use my blog for a bit of fishing for advice and guidance on an issue that's been bugging me.
We've been moving towards using strong names on all of our assemblies. The benefits are obvious, and it's a must before we deploy to clients out in the wild.
The problem is that we have several different processes running, each with its own app.config or web.config file. These config files contain references to custom configuration sections, whether they're application configuration, Enterprise Library extensions or whatnot. Seeing as my DLLs are signed, I have to use the fully qualified assembly name in all my references. This means that in a nightly build scenario where my version number is bumped continuously, I have to change 5-6 references in 5-6 configuration files with every build.
Doing this kind of string manipulation on a large scale scares me, since it can break, or we miss something. I've tried using <assemblyBinding> and <bindingRedirect> directives, but they require a specific version to point to as well.
I'm sure I'm not the first person to encounter this problem. What are the solutions that you use to bypass this? Scripts as part of the automated installation? Moving all configuration sections to a separate assembly whose version is static? What's the least painful way to manage this?
Just a quick heads-up in case you're stumped with this problem, or just passing by:
The System.Diagnostics.EventLogEntry class implements the Equals method to check if two entries are identical, even if they're not the same instance. However, contrary to best practices, it does NOT overload the operator==, so these two bits of code will behave differently:
EventLog myEventLog = new EventLog("Application");
EventLogEntry entry1 = myEventLog.Entries[0];
EventLogEntry entry2 = myEventLog.Entries[0];
bool correct = entry1.Equals(entry2);
bool incorrect = entry1 == entry2;
After running this code, correct will be true while incorrect will be false.
Good to know, if you're reading event logs in your code.
I'm writing some benchmarking code, which involves a Console application calling a COM+ hosted process and measuring performance. I want to constantly display results on my active console, but since some of my code is running out-of-process, I can't really write directly to the console from all parts of the system. Not to mention the fact that I want it logged to a file as well.
So I cobbled together a quick Log class that does two things - it writes to a shared log file, keeping no locks so several processes can access it (I do serialize access to the WriteLine method itself, though). I don't mind the overhead of opening/closing the file every time, since this isn't production code.
The second method is the interesting one - it monitors the log file and returns every new line that is appended to it. If no lines are available, it will block until one is reached. The fun part was using the yield return keyword, which I've been looking for an excuse to use for quiet a while now.
Note that there are many places this code can go wrong or should be improved. There is no way to stop it running, only when the application is stopped. I bring this as the basic idea, and it can be cleaned up and improved later:
1: public static IEnumerable<string> ReadNextLineOrBlock()
2: { 3: // Open the file without locking it.
4: using (FileStream logFile = new FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
5: using (StreamReader reader = new StreamReader(logFile))
6: { 7: while (true)
8: { 9: // Read the next line.
10: string line = reader.ReadLine();
11: if (!string.IsNullOrEmpty(line))
12: { 13: yield return line;
14: }
15: else
16: { 17: Thread.Sleep(100);
18: }
19: }
20: }
21: }
This method can now be called from a worker thread:
1: private static void StartListenerThread()
2: { 3: Thread t = new Thread(delegate()
4: { 5: while (true)
6: { 7: foreach (string line in Log.ReadNextLineOrBlock())
8: { 9: // I added some formatting, too.
10: if (line.Contains("Total")) 11: Console.ForegroundColor = ConsoleColor.Green;
12:
13: Console.WriteLine(line);
14:
15: Console.ForegroundColor = ConsoleColor.White;
16: }
17: }
18: });
19:
20: t.Start();
21: }
Another minor detail that bit me for a few minutes today. I'm posting this so I'll see it and remember, and possibly lodge it in other people's consciousness.
Using Visual Studio 2005, adding an existing .cs file to a C# project will cause a copy of that file to be created in the project directory. If I want to link to the original file, I need to explicitly choose Add As Link from the dropdown on the Add button.
C++ projects, however, have the opposite behavior. Adding an existing item in a different folder will add a link to the original file in the original location. To create a copy, manually copy it to the project dir.
That is all.
Working with the Web Services in WSSv2 and v3 is an exercise in sheer frustration. I've written before about serious and glaring omissions in the Web Services API that Sharepoint exposes, but it's not only the things missing, it's the lack of consistency in the APIs that ARE implemented that's annoying.
For most of the APIs, the return value for most methods is an XmlNode, usually documented pretty well in the SDK. It means we have to fiddle with XML to extract data, but it's not so bad. For instance the Lists WS's GetListCollection() returns a known, documented, schema-linked XML containing many properties for a given list.
For some reason, though, the SiteData web service chooses to return data as typed objects instead - oddly named objects at that (_sList, _sWebWithMetadata[], and so forth), but only for SOME data. The GetSite() method, for instance, will return a typed metadata object for the site metadata and the list of Lists, but a completely untyped STRING containing XML data for the list of groups and group members.
Futhermore, these XML snippets are sometimes undocumented, and you have to try it out to see what you get. What you get, sometimes, is the same XML you would get from a call to a different web service, but stripped of the namespace - so even though it's the same structure, you can't use the same code to parse both.
Similarly the GetListCollection() method in SiteData. It returns a typed array of _sList objects which map to the XML that's returned from the ListsWS method, only for some reason, some oft the XML attributes are NOT mapped, and are inaccessible. So if I want to see whether a list is Hidden or not, I am unable to use the typed objects and have to resort to the loose XML manipulation.
Seriously, these rants are adding up. Does anyone know who's in charge of the web services for Sharepoint and why they're so badly designed? No consistency, missing functionality, bad documentation - what's going on here? This isn't v1, either, but very little seems to have changed since the original documentation for WSS v2 in 2003.
A few days ago I wrote of a GC hole I accidently created in my C++/CLI code (with thanks to commenter Nish for the name of the phenomenon). I had the following code snippet set inside a private function:
pin_ptr<Byte> pinnedBuffer = &data[0];
char* buffer = (char*)pinnedBuffer;
Because the pin_ptr went out of scope when the function ended, I had a pointer to a memory location that was quickly moved by the GC, and got me a whole buffer full of garbage. To avoid that, I moved those two lines of code to my main method and pinned it throughout that method.
I thought that was the end of that, but today I noticed that I was still getting garbage occasionally in my buffer. This stumped me for a bit - it seemed like the pin_ptr wasn't doing its job - until I noticed how my C++ code looked:
public void DoStuff (array<Byte>^ data)
char* buffer;
if (data.Length > 0)
{
pin_ptr<Byte> pinnedBuffer = &data[0];
buffer = (char*)pinnedBuffer;
}
// Now do stuff
For C++ programmers this is probably obvious. For me and other C# devs, not so much. C++'s scoping rules say that when the if block terminates, the pin_ptr<Byte> goes out of scope immediately. This causes the pinned memory to be released, and allows the GC to move my array. Meanwhile my unmanaged code goes over the buffer and reads data. If I'm lucky, the GC won't start until I'm finished and all is well. If the GC IS active, I can either get garbage data (whatever is now pointed to by buffer) or, even worse, an access violation if I now point to protected memory.
In my current project, we have a windows service that we are developing and debugging, which involves a lot of installing/uninstalling the service. One common problem when uninstalling a service is that while the uninstallation is successful, you still see the service listed in the Services console(services.msc). If you try to start it, stop it or uninstall it again (using installutil.exe or sc delete) you get an uninformative "This service has been marked for deletion".
The KB article about this problem suggests you restart the computer, which is pretty much overkill. Sure, it'll work, but you'll never find out what caused it in the first place. Turns out it's a pretty simple affair: just make sure you close the Services console, which apparently holds a handle of some sort to it. You don't have to do it before you uninstall. The minute you close the console, all services marked for deletion will be deleted, and all will be well.
When doing it on a server, it's important to make sure you've closed all Services consoles on all active sessions. I've seen this error happen when no console was open, and it was fixed by running Task Manager and killing all instances of mmc.exe. I could have logged on to the other sessions and closed it gracefully, but I was lazy. Did the trick.
More Posts
Next page »