~Destructor should call method for exit sequence, but instance isalready null, example code attached

R

Rudi

Hello,

following problem: At program end or release an assembly a serial device
should get a final exit sequence. How can I do this?

With Dispose() it's no problem, but this assembly is used in a com
interop dll and it must be guaranteed, that the final sequence is send
to the serial device, when the calling application dont send Close() or
Dispose().

How can we solve this problem? Please see last 3 lines of sample code?

I tried many ways for now, but also with IDisposable it does'nt work.

kind regards, Rudi

8<---------------------------------------------------------------------
using System;
using System.IO.Ports;
using System.Threading;

namespace ConsoleApplication3
{
class Test : IDisposable
{
SerialPort sio = new SerialPort();
int loopCtr = 0;
private bool disposeFlag = true;

public Test(int lpCtr)
{
loopCtr = lpCtr;
Console.WriteLine("constructor " + loopCtr);
sio.PortName = "COM1";
sio.Open();
if (sio.IsOpen)
{
sio.Write("DEVICE INIT");
Console.WriteLine("sio: INIT OK");
}
}

public void Say(string text)
{
Console.WriteLine(text);
}

~Test()
{
Dispose(false);
Console.WriteLine("destructor " + loopCtr);
}

public void Dispose()
{
Dispose(true);
}

internal void DoExitSequence()
{
if (sio.IsOpen)
{
sio.Write("DEVICE EXIT");
Console.WriteLine("sio: EXIT OK");
}
else
Console.WriteLine("sio: EXIT failed");
sio.Close();
GC.KeepAlive(sio);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
GC.SuppressFinalize(this);
}
}
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
}
}
}

class Program
{
static void DoTest(int i)
{
Test t = new Test(i);
using(t)
{
t.Say("Test " + i);
}
//t.Dispose();
}

static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("loop " + i);
DoTest(i);
/*
GC.Collect();
Thread.Sleep(100);
GC.Collect();
*/
}
Console.ReadLine();
Test t = new Test(99);
t.Say("sio closed?");
//t.Dispose(); destructor should clean up
}
}
}
8<---------------------------------------------------------------------



using System;
using System.IO.Ports;
using System.Threading;

namespace ConsoleApplication3
{
class Test : IDisposable
{
SerialPort sio = new SerialPort();
int loopCtr = 0;
private bool disposeFlag = true;

public Test(int lpCtr)
{
loopCtr = lpCtr;
Console.WriteLine("constructor " + loopCtr);
sio.PortName = "COM1";
sio.Open();
if (sio.IsOpen)
{
sio.Write("DEVICE INIT");
Console.WriteLine("sio: INIT OK");
}
}

public void Say(string text)
{
Console.WriteLine(text);
}

~Test()
{
Dispose(false);
Console.WriteLine("destructor " + loopCtr);
}

public void Dispose()
{
Dispose(true);
}

internal void DoExitSequence()
{
if (sio.IsOpen)
{
sio.Write("DEVICE EXIT");
Console.WriteLine("sio: EXIT OK");
}
else
Console.WriteLine("sio: EXIT failed");
sio.Close();
GC.KeepAlive(sio);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
GC.SuppressFinalize(this);
}
}
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
}
}
}

class Program
{
static void DoTest(int i)
{
Test t = new Test(i);
using(t)
{
t.Say("Test " + i);
}
//t.Dispose();
}

static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("loop " + i);
DoTest(i);
/*
GC.Collect();
Thread.Sleep(100);
GC.Collect();
*/
}
Console.ReadLine();
Test t = new Test(99);
t.Say("sio closed?");
//t.Dispose(); destructor should clean up
}
}
}

using System;
using System.IO.Ports;
using System.Threading;

namespace ConsoleApplication3
{
class Test : IDisposable
{
SerialPort sio = new SerialPort();
int loopCtr = 0;
private bool disposeFlag = true;

public Test(int lpCtr)
{
loopCtr = lpCtr;
Console.WriteLine("constructor " + loopCtr);
sio.PortName = "COM1";
sio.Open();
if (sio.IsOpen)
{
sio.Write("DEVICE INIT");
Console.WriteLine("sio: INIT OK");
}
}

public void Say(string text)
{
Console.WriteLine(text);
}

~Test()
{
Dispose(false);
Console.WriteLine("destructor " + loopCtr);
}

public void Dispose()
{
Dispose(true);
}

internal void DoExitSequence()
{
if (sio.IsOpen)
{
sio.Write("DEVICE EXIT");
Console.WriteLine("sio: EXIT OK");
}
else
Console.WriteLine("sio: EXIT failed");
sio.Close();
GC.KeepAlive(sio);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
GC.SuppressFinalize(this);
}
}
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
}
}
}

class Program
{
static void DoTest(int i)
{
Test t = new Test(i);
using(t)
{
t.Say("Test " + i);
}
//t.Dispose();
}

static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("loop " + i);
DoTest(i);
/*
GC.Collect();
Thread.Sleep(100);
GC.Collect();
*/
}
Console.ReadLine();
Test t = new Test(99);
t.Say("sio closed?");
//t.Dispose(); destructor should clean up
}
}
}
 
N

Nicholas Paldino [.NET/C# MVP]

Rudi,

The issue here is that when a .NET component is exposed to COM, once the
component is released, it is left to the GC for collection, so you have no
explicit calling of the Dispose method on IDisposable on your component.

I would actually handle this in the unmanaged side, creating a smart
pointer to your object, and then when that smart pointer is released, making
the call to dispose on your object. You should be able to call Dispose on
IDisposable on your exposed object, as IDisposable is marked as ComVisible.

Of course, this assumes that the COM environment you are working in
allows such a thing (C++ and VB6 do, in their own separate ways), but I
doubt that it isn't the case.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Rudi said:
Hello,

following problem: At program end or release an assembly a serial device
should get a final exit sequence. How can I do this?

With Dispose() it's no problem, but this assembly is used in a com
interop dll and it must be guaranteed, that the final sequence is send
to the serial device, when the calling application dont send Close() or
Dispose().

How can we solve this problem? Please see last 3 lines of sample code?

I tried many ways for now, but also with IDisposable it does'nt work.

kind regards, Rudi

8<---------------------------------------------------------------------
using System;
using System.IO.Ports;
using System.Threading;

namespace ConsoleApplication3
{
class Test : IDisposable
{
SerialPort sio = new SerialPort();
int loopCtr = 0;
private bool disposeFlag = true;

public Test(int lpCtr)
{
loopCtr = lpCtr;
Console.WriteLine("constructor " + loopCtr);
sio.PortName = "COM1";
sio.Open();
if (sio.IsOpen)
{
sio.Write("DEVICE INIT");
Console.WriteLine("sio: INIT OK");
}
}

public void Say(string text)
{
Console.WriteLine(text);
}

~Test()
{
Dispose(false);
Console.WriteLine("destructor " + loopCtr);
}

public void Dispose()
{
Dispose(true);
}

internal void DoExitSequence()
{
if (sio.IsOpen)
{
sio.Write("DEVICE EXIT");
Console.WriteLine("sio: EXIT OK");
}
else
Console.WriteLine("sio: EXIT failed");
sio.Close();
GC.KeepAlive(sio);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
GC.SuppressFinalize(this);
}
}
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
}
}
}

class Program
{
static void DoTest(int i)
{
Test t = new Test(i);
using(t)
{
t.Say("Test " + i);
}
//t.Dispose();
}

static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("loop " + i);
DoTest(i);
/*
GC.Collect();
Thread.Sleep(100);
GC.Collect();
*/
}
Console.ReadLine();
Test t = new Test(99);
t.Say("sio closed?");
//t.Dispose(); destructor should clean up
}
}
}
8<---------------------------------------------------------------------



using System;
using System.IO.Ports;
using System.Threading;

namespace ConsoleApplication3
{
class Test : IDisposable
{
SerialPort sio = new SerialPort();
int loopCtr = 0;
private bool disposeFlag = true;

public Test(int lpCtr)
{
loopCtr = lpCtr;
Console.WriteLine("constructor " + loopCtr);
sio.PortName = "COM1";
sio.Open();
if (sio.IsOpen)
{
sio.Write("DEVICE INIT");
Console.WriteLine("sio: INIT OK");
}
}

public void Say(string text)
{
Console.WriteLine(text);
}

~Test()
{
Dispose(false);
Console.WriteLine("destructor " + loopCtr);
}

public void Dispose()
{
Dispose(true);
}

internal void DoExitSequence()
{
if (sio.IsOpen)
{
sio.Write("DEVICE EXIT");
Console.WriteLine("sio: EXIT OK");
}
else
Console.WriteLine("sio: EXIT failed");
sio.Close();
GC.KeepAlive(sio);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
GC.SuppressFinalize(this);
}
}
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
}
}
}

class Program
{
static void DoTest(int i)
{
Test t = new Test(i);
using(t)
{
t.Say("Test " + i);
}
//t.Dispose();
}

static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("loop " + i);
DoTest(i);
/*
GC.Collect();
Thread.Sleep(100);
GC.Collect();
*/
}
Console.ReadLine();
Test t = new Test(99);
t.Say("sio closed?");
//t.Dispose(); destructor should clean up
}
}
}


--------------------------------------------------------------------------------


using System;
using System.IO.Ports;
using System.Threading;

namespace ConsoleApplication3
{
class Test : IDisposable
{
SerialPort sio = new SerialPort();
int loopCtr = 0;
private bool disposeFlag = true;

public Test(int lpCtr)
{
loopCtr = lpCtr;
Console.WriteLine("constructor " + loopCtr);
sio.PortName = "COM1";
sio.Open();
if (sio.IsOpen)
{
sio.Write("DEVICE INIT");
Console.WriteLine("sio: INIT OK");
}
}

public void Say(string text)
{
Console.WriteLine(text);
}

~Test()
{
Dispose(false);
Console.WriteLine("destructor " + loopCtr);
}

public void Dispose()
{
Dispose(true);
}

internal void DoExitSequence()
{
if (sio.IsOpen)
{
sio.Write("DEVICE EXIT");
Console.WriteLine("sio: EXIT OK");
}
else
Console.WriteLine("sio: EXIT failed");
sio.Close();
GC.KeepAlive(sio);
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
GC.SuppressFinalize(this);
}
}
if (disposeFlag)
{
DoExitSequence();
disposeFlag = false;
}
}
}

class Program
{
static void DoTest(int i)
{
Test t = new Test(i);
using(t)
{
t.Say("Test " + i);
}
//t.Dispose();
}

static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine("loop " + i);
DoTest(i);
/*
GC.Collect();
Thread.Sleep(100);
GC.Collect();
*/
}
Console.ReadLine();
Test t = new Test(99);
t.Say("sio closed?");
//t.Dispose(); destructor should clean up
}
}
}
 
R

Rudi

Nicholas said:
Rudi,

The issue here is that when a .NET component is exposed to COM, once
the component is released, it is left to the GC for collection, so you
have no explicit calling of the Dispose method on IDisposable on your
component.

I would actually handle this in the unmanaged side, creating a smart
pointer to your object, and then when that smart pointer is released,
making the call to dispose on your object. You should be able to call
Dispose on IDisposable on your exposed object, as IDisposable is marked
as ComVisible.

Of course, this assumes that the COM environment you are working in
allows such a thing (C++ and VB6 do, in their own separate ways), but I
doubt that it isn't the case.

Hello Nicholas,

thanks for your answer. It is not a problem with COM Interop. I made
this simple code example for demonstrating my problem.

In the meantime, I tried other things, reading smart books about Dispose
and Finalizers but still no solution. I tried also, to catch the
SerialPort instance, set to sio=null in Dispose, make new instance,
again sio.open(), sio.Write(), then sio.Close(). But this seems to bring
just exceptions.

Thank you for the hint wit a pointer to the object, this could work.
Please could you explain, may be a short sample, how you make s smart
pointer and call Dispose from this when it will be released? Thank you.

kind regards, Rudi
 
N

Nicholas Paldino [.NET/C# MVP]

Rudi,

Well, in unmanaged C++, you can create classes on the stack which will
have their destructor called when they are taken off the stack. All you
have to do is create one of these classes which will basically call dispose
in the destructor, and make sure you use it on the stack.
 
Top