Garbage Collection Considerations

team lib

The previous section showed how to use a delegate to provide a managed equivalent of a callback. Although this is a useful technique, there is a potential problem with this code. The delegate object EnumWinStaDelegate is used by the unmanaged function EnumWindowStations , and unmanaged code is invisible to the .NET garbage collector. If the unmanaged function is the only code using the delegate, no references are held on the delegate object by managed code. This means the object might get collected if the garbage collector performs a collection. As far as the collector is concerned , the object is unused.

This problem particularly affects delegates because the garbage collector has no way of knowing when unmanaged code has finished with the delegate object. Premature garbage collection doesn't tend to affect COM objects because the COM AddRef / Release mechanism provides a way for unmanaged client code to signal that a managed object is no longer required.

Using the KeepAlive Method

The System.GC.KeepAlive method can be used to prevent an object from being garbage collected, even if there are no references to it in managed code. Here is an example of how it could have been used in the previous example to prevent the delegate object from being prematurely collected:

 staticvoidMain(string[]args) { //Createthedelegate,andbindittothecallback //function del=newEnumWinStaDelegate(Callback); //Callthefunction,passinginthenameofthe //WinStationwewanttolookfor strings= "WinSta0"; IntPtrip=Marshal.StringToHGlobalAuto(s); Int32result=EnumWindowStations(del,ip); if(result!=0) Console.WriteLine("EnumOK"); else Console.WriteLine("Enumfailed");  //Guardthedelegateobject   GC.KeepAlive(del);    //Freethememoryusedtomarshalthestring Marshal.FreeHGlobal(ip); } 

You might think something looks wrong with this code because the call to KeepAlive is placed after the call to the unmanaged function that uses the object you want to protect. This placement is used because KeepAlive is used in a rather unusual manner: Instead of being treated as a normal function call, a KeepAlive statement signals to the runtime that the variable passed in as an argument must be kept alive -not garbage collected-until after the KeepAlive statement. The bottom line is that we need to place a call to KeepAlive after the Platform Invoke call for it to work correctly.

Using the HandleRef Type

You'll encounter another circumstance in which you might need to protect against objects being collected at the wrong time. Many resources used or provided by the Windows operating system are represented by handles . Examples include files, threads, and registry keys. Sometimes you will need to use .NET objects that wrap these handles, such as a FileStream object that wraps a handle to a file. You want to protect against the possibility that the objects that you use to hold these handles in your managed code are garbage collected during the execution of a lengthy Platform Invoke call.

Such handles will often be represented by IntPtr objects, as shown by the following Platform Invoke prototype for the Windows ReadFile API function:

 //VisualC#PlatformInvokeprototype [DllImport("kernel32.dll")] publicstaticexternboolReadFile(IntPtrhandle,//thefilehandle StringBuilderbuff,//buffertoreceivedata intnToRead,//numberofbytestoread outintnRead,//numberactuallyread inOverlappedflag//overlappedstructure); 

The function could be used like this:

 //CreateaFileStream FileStreamfs=newFileStream("Test.txt",FileMode.Open); //Createabufferof100characters StringBuilderbuff=newStringBuilder(100); //CallReadFile intnumRead=0; ReadFile(fs.Handle,buff,100,outnumRead,0); 

The Handle member of FileStream returns the file handle as an IntPtr so that it can be passed to ReadFile . But the IntPtr handle is used only by the unmanaged ReadFile function, so there are no managed references to it. It is therefore possible that the handle object would be collected if the garbage collector performed a collection while the call to ReadFile is executing.

Although you could use GC.KeepAlive to protect the handle, the System.Runtime.InteropService.HandleRef type provides a neater alternative for this situation. As the name implies, a HandleRef is a type that wraps a handle in a managed object so that the handle has at least one managed reference. You can use a HandleRef object wherever you would have used an IntPtr to represent a handle. Here is how you would use a HandleRef with the call to ReadFile :

 //CreateaFileStream FileStreamfstm=newFileStream("Test.txt",FileMode.Open); //Createabufferof100characters StringBuilderbuff=newStringBuilder(100); //CreateaHandleRef HandleRefhref=newHandleRef(fstm,fstm.Handle); //CallReadFileusingtheHandleRef intnumRead=0; ReadFile(href,buff,100,outnumRead,0); 

The HandleRef constructor takes two arguments: a reference to the managed object to be kept alive, and an IntPtr representing the unmanaged handle held by the object. The HandleRef object will ensure that the object it is wrapping will not be garbage collected until the HandleRef is destroyed .

 
team lib


COM Programming with Microsoft .NET
COM Programming with Microsoft .NET
ISBN: 0735618755
EAN: 2147483647
Year: 2006
Pages: 140

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net