I've actually used ANTS to determine that in fact I do have an unmanaged memory leak.
My code is based on the DxSnap sample included with the samples for DirectShow.Net, and uses the Capture class from that sample.
Code:
// allocate an array of bytes into which we will copy the image buffer from Click()
byte[] imgBuffer = new byte[Math.Abs(cam.Stride) * cam.Height];
// cam is a Capture object
IntPtr imgPtr = cam.Click();
// copy the buffer
Marshal.Copy(imgPtr, imgBuffer, 0, imgBuffer.Length);
// free the buffer from Click()
Marshal.FreeCoTaskMem(imgPtr);
// get a pinned array handle to the byte array
var pinnedArray = GCHandle.Alloc(imgBuffer, GCHandleType.Pinned);
// get the pointer to the array
var arrayPtr = pinnedArray.AddrOfPinnedObject();
// create the new bitmap
Bitmap bMap = new Bitmap(cam.Width, cam.Height, cam.Stride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, arrayPtr);
// free the pinned array handle
pinnedArray.Free();
I don't think I'm doing anything wrong, but I'm not very experienced with COM and DirectX, especially in the .Net framework.