December 2004 - Posts

HansVB has started a second blog on MSN Spaces. You'll find it over here: Don't get scared of the photos over there, Hans can be serious too... :-) | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Due to problems in the datacenter, my websites have been offline during the weekend :-(. The UPS event log indicates a power failure, so there's nothing wrong with the machine (hardware or software). Luckily... Everything seems to be up and running again | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Last days, I've been evaluating some medium-sized project on the field performance, more specifically on the level of CLR managed code. Let's give a collection of tips in this post to make your app more performing.

1. Let garbage collection do its work

Garbage collection is a great thing for various reasons (of which automatic memory management is the most important one of course). For the realtime software people, I know the GC is not a blessing for that kind of software, but it was never meant to be used for this class of scenarios. I won't cover the GC here in full detail but what you should know is that objects are grouped according to their lifetime and volatitly in what we call generations. Currently, there are three generations (Gen 0 - 2) and objects are promoted to higher generations when they live longer (0 --> 1 --> 2). The frequency of garbage collection is lower when the generation is growing. Garbage collection and finalization go hand in hand. Finalization is the process that is executed on an object before the managed memory of the object is reclaimed and has - in C# - the same syntax as a destructor in C++ (although the semantics are completely different). More info on finalization, object disposal and GC can be found on the MSDN website. The GC is smart enough to decide when to work, so there is not need to force the GC to start collecting objects using GC.Collect(). Consequently, the GC.WaitForPendingFinalizers() method should not be called as well (unless you have a really really serious reason - I can't think of one right now - to do this). There are two GCs on the system, one for workstations and one for servers with multiple processors (needed to split the heap over the various CPUs and to perform GC according to this model).

To Finalize or not to Finalize

Don't implement the Finalize method always because the GC needs to promote an object that supports finalization to older generations in order to be able to execute finalization. This behavior has the undesired side-effect that objects become long-lived, something you don't want if your object's lifetime is short. When adding a "destructor", use the IDisposable interface always (see sample further). Only implement finalization when needed (maybe disposal is sufficient) and keep the code simple and short! If combined with threading on your object, take especially care of the synchronization of the threads (cleanup code should be threadsafe if the type is).

Dispose correctly

If you've used the using syntax in C#, you probably know that this works only with IDisposable classes. I don't mean the "using" to import a namespace, rather I mean this:

using (SqlConnection conn = new SqlConnection(dsn)) {
   //use conn


Disposing objects are used when external resources are called that need to be freed explicitly by the caller (in the case of "using", the disposing is done automagically and implicitly). You'll see this disposing pattern being applied on various places, such as database connections. Why don't we use finalization rather than disposing on this kind of objects? Well, the reason is that finalization is a GC-initiated process that is executed asynchronously, whileas the disposable pattern gives you the control needed to release resources in a timely fashion. One thing to keep in mind is that the GC can request finalization on an object, so this should be suppressed during the execution of the Dispose method call. In order to do so, don't ever forget to call GC.SuppressFinalization in the Dispose method. As a side-remark, database connection et al support a Close method as well, which is in most cases just a context-specific synonym for disposing an object (therefore more self-explaining to the developer). Another tip is to keep track of whether the Dispose object has been called already on an object. If that's the case, a second call should not be permitted which can be solved by throwing an ObjectDisposedException. Typically, the creation of an IDisposable object is more complex than just writing the Dispose method. A typical skeleton is this:

class MyDisposableClass : IDisposable //you can seal the class if you want
    private bool _hasDisposed = false;

    public void Dispose() {
       if (!_hasDisposed) {

    //Special method for object disposal; param indicates the call source
    protected virtual void Dispose(bool disp) {
       if (disp) {
          //dispose only when Dispose was called; used for managed resources

       //always do finalization; e.g. closing handles to unmanaged resources

       _hasDisposed = true;

    ~MyDisposableClass() {

Weak references

Weak referenes (class WeakReference) allows to releasenon-critical objects from memory when there is memory pressure and the GC comes into play. What I mean but non-critical can be illustrated using the sample of a cache. Objects in a cache are pretty useful for the caller to improve the performance, but strictly speaking these objects are not needed since these can be resurrected all the time. So, under memory pressure, these objects can be resurrected safely without affecting the functionality of the system. Weak references are typically used on non-trivial objects (objects that contain quite some data). Basically, the WeakReference class is a wrapper that is recognized during garbage collection and can be used to release the memory when needed. The usage is pretty easy:

//Some object is created
MyWeakReferencedObject obj = new MyWeakReferencedObject();

//Wrap the object
WeakReference ref = new WeakReference(obj);

//Store the reference somewhere, e.g. in a collection
//Retrieve the reference somewhere, e.g. get it from a collection

//Unwrap the object
MyWeakReferencedObject o = null;
if (ref.IsAlive)
   o = (MyWeakReferencedObject) ref.Target;
if (o == null)
   //GC has occurred, retrieve the object again (resurrection) from the source

//Normal operation continues


2. Use threading with care

Use multithreading with care

Multithreading makes your applications more responsive (in the presentation layer) and can be used to execute tasks that can't interfere with each other in a parallel way. This sounds great, and indeed it is. However, threads are the basic units of work on a processor and the OS is responsible to schedule threads for execution, which is done by context switching (therefore it can have a negative impact on the overall performance of the application when you have a bunch of threads). This is the reason why SQL 7 and higher are actually avoiding scheduling on the level of the kernel by means of the UMS (User Mode Scheduler) in order to reduce the number of context switches.

Threads are cute, ThreadPools are cuter

ThreadPools are a queuing mechanism that is using a set of pre-instantiated threads that are living in a pool. When work needs to be done, no new threads need to be initialized (which actually takes time to allocate the thread structure), a thread only needs to be grabbed from the pool when available. The way to use this mechanism is amazingly simple:

WaitCallback cb = new WaitCallback(myObject.MyTargetMethod);
ThreadPool.QueueUserWorkItem(cb); //will call the myObject.MyTargetMethod as soon as a thread becomed available in the pool

.NET v2.0 BackgroundWorkers

In .NET Framework v2.0 there is support for a BackgroundWorker that offloads you from the complexity of performing background work and communicating back to the calling thread for progress indication (typically used in a WinForms app). Use this technology if you can instead of your own implementations of this mechanism.

Timers versus threads

When tasks need to be performed on a regular basis, use the various Timer components to do the trick. It's threading, but offloads you as a developer from the plumbing of timer logic and wait loops. You'll find timers typically in WinForms apps (Controls --> Timer), Windows Services (Components --> Timer), etc. If you don't use components to use some kind of timer, use the System.Threading.Timer class (straightforward usage).

Let threads shut down gentry, don't use Thread.Abort

Threads should commit suicide in order to stop. This allows resources to be cleaned up in the right way, to release locks, etc. Thread.Abort should not be used, instead use some boolean value that is checked periodically inside the thread to detect whether it has to stop its work (i.e. to commit suicide). When you need to stop the thread, set the boolean stop indication value to true.

Avoid deadlocks, avoid Thread.Resume and Thread.Suspend

Synchronization of the work across threads should never be obtained using Suspend/Resume calls, rather you should use lock in C# or various objects in the Threading namespace such as Mutex, Monitor, etc to do this kind of work. Personally, the lock keyword is my reliable partner in threading development.


3. Don't wait if you don't have to - Asynchronous invocation

Why wait for a call to return if we can do other operations in the meantime. Or why wait for a call to return, therefore blocking the active thread. There are a series of classes in the BCL that support BeginInvoke and EndInvoke to make an asynchronous call. That means that when calling the BeginInvoke method, the method will return immediately. You supply the parameters to the method as well as a callback (delegate). When the work is done, the specified delegate's method will be called. In there, you can call the EndInvoke method on the object to retrieve the returned value (or any exceptions). The EndInvoke method will give your access to an AsyncState object that can be used to get more details about the original request and status. A typical example is an asynchonous web service call.


4. Why generics are great

Collections in .NET v1.x are based on the mother of all types, the type "Object". Thus, when working with a colleciton, you can add objects of any type to that collection. However, when you retrieve the object from the collection, you end up with an object of the type "Object", so you need to upcast the returned object to the right type. This operation is expensive. .NET v2.0 will support generics that help you to solve this problem (by specifying the type you want to use in the collection):

ArrayList lst = new ArrayList();
lst.Add(1); //store an int
int val = (int) lst[0]; //retrieve the int needs casting

ArrayList lst = new ArrayList();
int val = lst[0];

Note that generics work at development time, and the IDE will recognize the targeted type(s) of the collection.


5. Be aware of remote objects or objects in other appdoms

When using .NET Remoting, be aware that using a remote object can cause a bunch of calls to be sent to another appdomain or even another machine. Think of this:

MyRemoteObject o = new MyRemoteObject();
o.Property1 = "Hello";
string msg = o.Property1;
object zzz = o.DoSomething("World");

This piece of code - when invoked on a transparent proxy object that represents a remote object living somewhere else - causes a lot of message to be passed over the wire: create the object, call the property setter, retrieve a value from the getter, call a method and return the value.

Therefore, if you can, think of using a stateless approach, e.g. using web services or be at least aware of the negative impact remote objects can have. Thus, transparancy comes at the cost of performance.


6. Exceptions - only when needed

Exceptions should not be the default way to return from a method. It are exceptions, what's in a word? Make sure you're using exceptions the right way.

Finally finally

Everyone knows and uses try ... catch, but please don't forget the finally block. All too often, this block is omitted. Think of this classic sample:

try {
    SqlConnection conn = new SqlConnection(dsn);
    //use conn
catch {}

What's wrong here? A lot! First of all, a catch-all block is something to avoid whenever you can. Secondly, the connection should be closed when it has been used:

try {
    SqlConnection conn = new SqlConnection(dsn);
    //use conn
catch (SqlException ex) {
    //catch it

Better isn't it? Yes, but not good enough yet. Although we have replaced the catch block to catch only the exceptions we can and should catch, we don't close the connection when an error occurs. Instead, use this:

SqlConnection conn = new SqlConnection(dsn);
    //use conn
catch (SqlException ex) {
    //catch it; don't attempt to close over here
finally {
    conn.Close(); //or better, check connection state first using conn.State

Finally always executes, even when you perform a return statement inside the try block. That's why it's just great. If you only have try ... finally (without catch), it's possible that you can replace the code with the C# using syntax, as explained earlier in the IDisposable coverage.

Don't rethrow but wrap

Rethrowing an exception is simply, just use throw inside the catch block. However, this call is very expensive (stack unwinding etc), so avoid it. Maybe it's better not to catch the exception (e.g. in a helper method) and to catch it higher up the stack. Or - to help abstraction, at the cost of performance - wrap the exception in another exception-type (self-written) and throw that one. An example is to catch a SqlException and to throw it as a DALException (derived from ApplicationException) to the higher layer. Use an InnerException if you want the original exception to be passed to the higher level.


7. Strings are immutable

Strings are immutable. Once allocated, their size can't be increased or decreased. So, what happens over here?

string s = "Bart";
s += " De Smet";

Indeed, a temporary string needs to be created and the original string "Bart" needs to be GCed. A better example:

string str = "This string is becoming ";
for (int i = 0; i < 1000000; i++)
   str = str + "longer and "
str = str + "longer."

In such a case you don't want to play the heap of the CLR (strings are reference types) and the GC will get tired to clean the 1M phantom objects. Even worse, the loop will become slower and slower at each pass, since a lot of char copying needs to be performed to concat a long string with another string.

String operator overload '+' - use with care

Use the concatenation operator + if you know that the number of concats is limited. In fact, I like to use String.Format instead in quite some cases to split the layout from the content. E.g. (both okay):

//Solution 1:
string envelope = "Name: " + name + "\nAddress: " + address + "\nZIP: " + zip + "\tCity: " + city

//Solution 2:
string envelope = String.Format("Name: {0}\nAddress: {1}\nZIP: {2}\tCity: {3}", name, address, zip, city);

Use StringBuilder inside loops etc

StringBuilders use an internal buffer of characters (char array) to store the string and grow when the array is running out of space (by doubling the size of the array). An example:

System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("This string is becoming ");
for (int i = 0; i < 1000000; i++)
   sb.Append("longer and ");
string str = sb.ToString();


8. Reflection is slooooooooow

Relfection can be great if you use it with care. Late binding looks as it is a holy grail for software extensibility (and yes it is, as I explained in a post on my blog earlier). If you use late binding (e.g. when loading "providers" from a configuration file using reflection), make sure you can cache the retrieve object instance to avoid a second binding performance hit (caused by Activator.CreateInstance for example).


9. Ngen.exe

NGen.exe is a tool that comes with the .NET Framework SDK that allows you to precompile an assembly (containing MSIL code) to native code, targeting the current hardware platform and processor instruction set (of the machine on which you run ngen). This increases performance but comes at the price of cross-platform portability of assemblies. In v1.x you can't use it however with ASP.NET assemblies in the web folder's bin directory, but this will change with precompilation in .NET v2.0 for ASP.NET pages.


10. ASP.NET tips (sneak peak)

If I find some time later on, I'll cover ASP.NET specific performance tips. Let's give a fairly huge list of tips you can use:

  • Cache, cache, cache whenever possible. A 1 second cache is better than no cache in a lot of cases.
  • Limit the number of HttpModules.
  • Reduce round trips to the client (use client-side script and validation).
  • Don't call long-running tasks when handling events server-side (use async calls, message queues, fire-and-forget, etc if possible).
  • Disable viewstate if not needed.
  • In-process state is faster than using the ASP.NET state server or a SQL Server db; use these options only in web farms.
  • Use output buffering and Response.Flush to reduce roundtrips (chunky versus chatty).
  • Server.Transfer avoids a roundtrip to the server compared to the use of Response.Redirect. However, Server.Transfer remains on the server all the time and can therefore bypass the HttpModules on the system; and you have to keep in the scope of the same app.
  • In ASP.NET v2.0 use database cache invalidation with SQL Server 7/2000/2005.
  • Tweak the machine.config performance settings on the field of connections and worker/io threads and the threading pools.
  • Avoid aggressive use of IIS 6 app pool recycling.
  • Perfmon should be your guide to improve performance.
  • Never ever forget to call Dispose on database connections etc since these are typically used very very much inside web apps.
  • Avoid to open up a bunch of connections to a db, e.g. inside a OnItemDatabound event handler for a DataList, DataGrid, Repeater control. Chatty is bad, chunky is good.
  • Don't bypass SQL Server connection pooling; use the same connection string all the time to enable pooling to do its job.
  • Gzip/deflate compression can increase network speed, but can put additional pressure on the processor on both the client and the server; use it with care.
  • Page.IsPostBack should be used to avoid data rebinding on postbacks etc
  • Impersonation is slow; avoid to do this on a per-request basis; use a trusted subsystem for the database when you can.
  • Use user controls to apply different caching on various webcontrols individually.
  • In production, debugging and tracing should be disabled (e.g. trace.axd should not be reachable); use customErrors.
  • Use one dev language per folder; multiple languages (C#, VB.NET) cause different assemblies to be created in the bin folder.
  • Databind on the level of controls, not on the page level (e.g. grid.DataBind() instead of this.DataBind()).
  • Avoid <% .. %>regions as well as <%# ... %>regions containing embedded script and DataBinder.Eval calls. This makes separation of code and content vague (remember plain old ASP-spaghetti code?). Use the OnItemDataBound event of the controls instead.
  • Windows Server 2003 kernel caching (http.sys) should be used whenever possible ().
  • @OutputCache is more than "Duration" and "VaryByParam". Take a look at the Location attribute as well (Client <-> Server <-> ClientAndServer) for use with proxy servers etc.
  • Avoid Application[] and Session[] collections when you can; e.g. instead of using Application[] you can use static properties on the application object level as well.
  • State is using serialization to store objects; use basic types to make serialization simpler. This is only applicable when using StateServer or SQL as state containers.
  • Session state, avoid if not needed and make it read-only on pages where you don't need write-access (<%@ Page EnableSessionState="ReadOnly" %>).
  • When creating server controls, don't use string concat the generate HTML, but use HtmlTextWriter instead.
  • Use Server.GetLastError() as catch-it-all inside the global.asax's Application_Error event handler.
  • ASP.NET uses MTA (multithreaded apartment) for its threading; STA COM objects are discouraged if not needed (cf. AspCompat flag).
  • Limit what you render to the client; use paging on DataGrids, turn off viewstate if not needed, make repeating regions lightweight.
  • Remote middletiers offer a great benefit on the field of encapsulation and abstraction, but come at the cost of performance. | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

I'm actually working for quite some time with a huuugeee resolution on my dev laptop (1920 x 1200). However, the ALT-TAB window (one of my favorite key combinations) always displays 7 columns and 3 rows, which is far too less for such a resolution. Actually, you can tweak this setting and alter these fixed numbers in the registry. In regedit, locate HKCU\Control Panel\Desktop. Over there, add 3 keys of type REG_SZ:

  • CoolSwitch = 1
  • CoolSwitchColumns = 20
  • CoolSwitchRows = 10

There are some other cute settings in this registry key but as usual - when modifying the registry - the well-known warnings are applicable :-). Another cool thing is to enable MSVDM (virtual desktop manager) for Windows Server 2003 as posted on this blog: By "design", this powertoy is not available on Windows Server 2003 but with a little app compat trick the installation works on W2K3 as well. So, my machine looks pretty much like a Windows XP installation (Themes service enabled, MSVDM over there, IE improvements and WMP10 thanks to W2K3 SP1 RC1). | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Two guys from Microsoft Belux's EPG (Enterprise Partners Group) have joined the blogging community using MSN Spaces recently. Currently I've only found posts in Dutch, but maybe this will change in the future. However, here are the links:

Welcome to the blogosphere :-).

Tip on how to detect new MSN Spacers; just take a look in your MSN Messenger 7.0 (beta). People with an MSN Space (with new posts) have a little "star" on top of the buddy icon. Then right click on the name and choose "View This Person's Contact Card". | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Windows Explorer has the cool feature to be able to display a picture on a folder when you're viewing the root folder in Thumbnail view. Normally, it will just display 4 of the pictures inside the folder (e.g. when you have a folder with a Windows Forms application project from VS.NET you'll see the standard app.ico picture displayed on the folder). If you want to show another picture however, there's a very simple trick: put a file called "Folder.jpg" in the folder and you'll see that picture appear in the thumbnail view on the folder | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Windows XP Service Pack 2 and Windows Server 2003 Service Pack 1 introduce a very little "toy feature" (okay, there will be legal issues to do this) that can make you feel like a magician. Quite some people were pretty amazed in the last couple of weeks when I told them "Hey, you didn't install XP SP2 yet" or "Cool, you're running XP SP2 already" during their system boot (btw - you won't see this screen often on my laptop; I'm now running 15 days without reboot on W2K3, the last reboot caused by a low battery problem). The way to do this is pretty simple, there are a few modifications in the boot logo of XP and W2K3 when the latest service pack is applied. Let's show these:


Windows XP Professional RTM/SP1 to SP2:




Windows Server 2003 RTM to SP1 (RC1):


New: | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

SCW ... It's a great tool and you definitely should check it out if you're testing RC1 of W2K3 SP1. But if you can't find it, please remember it has to be installed separately via the Configuration Panel, Add/Remove Programs, Add/Remove Windows Components. If I'd have to give one point of feedback: please install this by default when applying | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

If you didn't get the chance to take a look at Windows Server 2003 SP1 during the beta program, download the RC1 release now via Of course, the standard disclaimer still holds: only do this on testing boxes. Just like Windows XP SP2, this service pack is a huge one (about 309 MB). The same reason as with XP SP2 holds: the whole core has been recompiled with new compiler technology in order to enhance the protection against buffer overruns. Let's give a quick overview of the enhancements in W2K3 SP1:

  • Security to the max using support for no execute hardware (hardware and software DEP) and better secure defaults concerning RPC, DCOM (cf. Windows XP SP2 enhancements) as well as the Windows Firewall integration.
  • IIS 6 metabase auditing
  • Magic words:
    • PSSU: Post-Setup Security Updates protects the computer after installation against vulnerabilities which need to be fixed first by using Windows Update (reduce attack surface immediately after installation)
    • SCW: (my favorite) Security Configuration Wizard is a role-based model to lock down the server by disabling unnecessary services and blocking unneeded ports in order to run the selected role(s). You can compare this with the IIS lockdown wizard in the pre-IIS 6 era, but now on a much wider level.
  • Network Access Quarantine Control
  • 64-bit enhancements

So, in one word, Windows Server 2003 SP1 is all about security.

My favorite cool features (rather comprehensive list of features :-)):

  • Access-based Directory Enumeration: "don't show what people shouldn't see" - this feature hides files and folders on shares which a user has no access to (domain setup required). Although it's not enabled by default (snif :-() you can take advantage of it using the NetShareSetInfo Win32 API (see updated Platform SDK documentation)
  • Install from media enhancements for domain controllers with DNS installation
  • Support for Virtual Server 2005 to run domain controllers (which was - obviously - still not supported in the past)
  • Improved security for attributes in Active Directory (e.g. lock access to certain attributes by certain users)
  • Administrative tools protection on the network (by blocking port 445 using the Windows Firewall)
  • Cleaner add/remove software window in the configuration panel (separate view on patches)
  • AES (attachment execution blocking) - new API, see XP SP2
  • IE improvements (add-ons, ActiveX "BindToObject", local lockdown, network protection lockdown, information bar, pop-up blocker, zone elevation blocking, etc) - XP SP2 gives you the whole picture of IE enhancements (as well as Outlook Express improvements)
  • RIS support for 64 bits images (yippie!)
  • RSoP (resultant set of policies) was a nice add-on for Windows Server 2003, it's now out there by default
  • DCOM/RPC restrictions
  • Setup now relies on a unified set of command line flags that will be the same across multiple releases of the Windows OS (but the old ones remain valid)
  • TCP/IP enhanced protection (by limiting the number of incomplete outbound TCP connections) to limit impact of worms and viruses that try to affect other external systems
  • DAVRdr (WebDAV Redirector) can be used to masquerade external systems as being normal file servers (e.g. WebDAV based systems such as MSN and SharePoint-based document libraries) - clear text basic authentication is disabled by default if used over a clear channel
  • Wireless Provisioning Services (WPS) allows to use WPA and PEAP in Windows Server 2003 SP1 (and XP SP2) and can be used by WISPs (Wireless ISPs) and HSPs (Hotspot SPs) to set up hotspots. To support this, there is a WAC service (Wireless Auto Configuration) that helps users to connect to the wireless network.

And - important for me - Windows Media Player 10 comes with Windows Server 2003 SP1 by default. As I'm using Windows Server 2003 as my primary OS on my laptop, this is pretty interesting to me since the WMP10 upgrade is not available currently for W2K3. The same holds for the Wireless Network Setup Wizard of Windows XP SP2 that is now available on W2K3 as well (hopefully my WPA-TKIP encryption will now work on my machine). | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

The .NET Framework v2.0 finally comes with an answer to the question: how to manage ACLs, ownership, auditing, etc in managed code. Maybe some of you know the Win32Security project that can be downloaded from the GotDotNet space. This assembly can be used to wrap the native Win32 calls in order to programmatically manage ACLs and that sort of things. Actually, I've been doing quite some ACL-stuff (pronounce this word in the cool way please: "accel" instead of "AjeSeeEl" - sorry but I don't know anything about phonetics) in the last two years for a project that needed to set up folders and security on these folders programmatically, with all the plumbing of ACL inheritance etc involved. I wanted to do this programmatically all the way since I'm not the biggest fan of calling System.Diagnostics.Process all the time to call and external command to do the work for you (in this case cacls.exe). That's what I call "managed scripting" :-). And for that, well, you probably want to wait for Monad to appear in the Windows/.NET universe (although the focus over there is to write commandlets instead of calling commands). I'm also glad to see that programming for object ownership is included too, something I've been struggling with a couple of days to get it right (even been using VB6 interop at some time to get it completely right :-().

You can find the API of the brand new namespace in v2.0 on MSDN2:

The basic usage looks as follows:

  1. Use static members on for example the File class to get a security descriptor, e.g. FileSecurity sd = File.GetAccessControl(fileName);
  2. Get the access rules, audit rules, owner, etc using some get methods: GetAccessRules, GetAuditRules, GetOwner and manipulate these using the corresponding "Set" methods (as well as Purge and Remove methods).

For fans of the SDDL format (Security Descriptor Definition Language) there's a SetSecurityDescriptorSddlForm method as well. | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

More Posts « Previous page - Next page »