// Standard ctor - Stream to be used to buffer, the Tag we read the data from
        public BlobReadStream(Stream s, FPTag t)
            : base(s, t)
        {
            myCallbacks = new Callbacks(holdingArea);

            fpStream           = new FPGenericStream(holdingArea, FPStream.StreamDirection.OutputFromCentera, myCallbacks, IntPtr.Zero);
            fpStream.StreamLen = t.BlobSize;

            FPLogger.ConsoleMessage("\nStarting blob read thread");
            blobThread      = new Thread(delegate() { t.BlobRead(fpStream); });
            blobThread.Name = "BlobReadThread";
            blobThread.Start();
        }
        // Standard ctor - Stream to be used to buffer, the Tag we write the data to
        public BlobWriteStream(Stream s, FPTag t)
            : base(s, t)
        {
            myCallbacks = new Callbacks(holdingArea);
            fpStream    = new FPGenericStream(holdingArea, FPStream.StreamDirection.InputToCentera, myCallbacks, (IntPtr)this.GetHashCode());

            blobThread      = new Thread(delegate() { theTag.BlobWrite(fpStream); });
            blobThread.Name = "BlobWriteThread";

            FPLogger.ConsoleMessage("\nStarted stream " + this.GetHashCode() + " blob write thread " + blobThread.GetHashCode());

            // We keep track of the current "open" streams being used in a hashtable
            // When user closes the stream we remove this to signal the callback that we are done writing
            openStreams.Add(this.GetHashCode(), blobThread.GetHashCode());
        }
        // Standard ctor - as for BlobWriteStream but also specify the sequence number of the segment.
        public PartialBlobWriteStream(Stream s, FPTag t, int id)
        {
            // We cannot call the base constructor because we need use the additional id parameter
            // Even if we split out some of the functionality using a virtual init method with no
            // parameters, the sequenceId will not be initialized when the virtual function was called
            // by the base constructor. So we just have to duplicate the (small)initialization code.
            holdingArea = s;
            theTag      = t;
            sequenceId  = id;

            myCallbacks = new Callbacks(holdingArea);
            fpStream    = new FPGenericStream(holdingArea, FPStream.StreamDirection.InputToCentera, myCallbacks, (IntPtr)this.GetHashCode());

            FPLogger.ConsoleMessage("\n ** Partial blob write stream with sequence id = " + sequenceId);
            blobThread      = new Thread(delegate() { theTag.BlobWritePartial(fpStream, sequenceId); });
            blobThread.Name = "PartialBlobWriteThread";
        }
        // Standard ctor - as for BlobReadStream but also specify the offset and number of bytes for the partial read.
        public PartialBlobReadStream(Stream s, FPTag t, long offset, long numBytes)
        {
            // We cannot call the base constructor because we need use the additional offset and numBytes parameters
            // Even if we split out some of the functionality using a virtual init method with no
            // parameters, these paraameters will not be initialized when the virtual function was called
            // by the base constructor. So we just have to duplicate the (small)initialization code.
            holdingArea = s;
            theTag      = t;
            myCallbacks = new Callbacks(holdingArea, 16 * 1024);

            fpStream           = new FPGenericStream(holdingArea, FPStream.StreamDirection.OutputFromCentera, myCallbacks, IntPtr.Zero);
            fpStream.StreamLen = numBytes;

            blobThread      = new Thread(delegate() { theTag.BlobReadPartial(fpStream, offset, numBytes); });
            blobThread.Name = "PartialBlobReadThread";
            blobThread.Start();
        }
        static void Main(string[] args)
        {
            IntPtr userData = new IntPtr(0);

            try
            {
                FPPool myPool = new FPPool("128.221.200.56?c:\\pea\\emea1.pea");
                FPTag  myTag;

                FPLogger log = new FPLogger();
                log.LogPath = "C:\\GenStreamsLog.txt";
                log.Start();

                // First we'll write a test clip to the Centera
                string   fileName = "c:\\discovery.xml";
                FileInfo info     = new FileInfo(fileName);

                FPClip myClip = myPool.ClipCreate("GenericStreamWrite_testClip");
                myTag = myClip.AddTag("testTag");

                FPGenericStream myStream = new FPGenericStream(File.OpenRead(fileName), userData);
                myStream.StreamLen = info.Length;
                myTag.BlobWrite(myStream);
                myTag.Close();
                myStream.Close();
                string clipID = myClip.Write();
                myClip.Close();
                FPLogger.ConsoleMessage("\nGenericStreamWrite test succeeded");

                // Now we will test reading it back from the Centera			{
                myClip = myPool.ClipOpen(clipID, FPMisc.OPEN_ASTREE);
                myTag  = myClip.NextTag;


                myStream           = new FPGenericStream(File.OpenWrite("c:\\test.out"), userData);
                myStream.StreamLen = myTag.BlobSize;

                myTag.BlobRead(myStream);
                myStream.Close();
                FPLogger.ConsoleMessage("\nGenericStreamRead test succeeded for FileStream");


                // We could use a Memory stream. As it is a bi-directional stream we
                // need to use a ctor that specifies the direction we want to use.
                // We'll set up the space first.

                MemoryStream s = new MemoryStream((int)myTag.BlobSize);
                myStream = new FPGenericStream(s, FPStream.StreamDirection.OutputFromCentera, userData);

                myStream.StreamLen = myTag.BlobSize;

                myTag.BlobRead(myStream, FPMisc.OPTION_DEFAULT_OPTIONS);
                myStream.Close();
                FPLogger.ConsoleMessage("\nGenericStreamRead test succeeded for MemoryStream");
                myTag.Close();
                myClip.Close();


                myPool.Close();
            }
            catch (FPLibraryException e)
            {
                ErrorInfo err = e.errorInfo;
                FPLogger.ConsoleMessage("\nException thrown in FP Library: Error " + err.error + " " + err.message);
            }
        }