471,610 Members | 1,368 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,610 software developers and data experts.

COM objs problems with .net

zq
Hi!

I am have a COM object which occupies more and more memory everytime it's
used.

The "VM size" counter (private bytes) of the process that uses the COM
object keeps on growing when instantiating and using the object frequently.
I tried following strategies for using the object:

1. instantiate the object when the app starts, use it frequently (inside an
event handler), and destroy it when the app ends.
2. instantiate the object inside the event handler, and release it every
time the event handler is finished

I tried using System.Marshal.ReleaseCOMObject() by calling it untill it's
result returns zero, but it has no effect.

Once the VM size reaches the pagefile size, the process starts throwing "out
of memory" exceptions if it tries to instantiate other objects (e.g. a
Bitmap object).

I have narrowed-down the problem to the COM object, because if I comment-out
the part where I use the COM object the "VM size" falls from cca 250MB to
1MB once when it's done working (i guess it means all other objects are
properly disposed and GC-ed).

My questions are:

Is there a way to see if there are any dependant COM objects created during
the COM's work?
Is there a way to destroy all dependant COM objects?

Can anybody help me please!

zkeber
Dec 13 '05 #1
5 2602
zq,

Well, if you are returning other COM objects as properties from the COM
object that you are using, then you have to make sure you release those when
you are done with them.

If you aren't using any properties which return any other COM object (or
methods which return them), then I would say it is a problem with the COM
object itself. However, the behavior you describe indicates that you are
probably getting more objects from the object you created, and not releasing
them correctly.

If your use of the COM object is localized, and you have a huge number
of references to other COM objects, then I would actually call GC.Collect to
force a collection, which will release the COM objects (assuming you have no
other references to them). The finalization of the runtime callable wrapper
will cause the reference to be released, and the COM object to be released.

Surprizingly enough, there are some areas of the framework that do just
that (I think it was something in the System.Windows.Forms namespace, I
remember being floored when I saw it a while ago).

Ideally, you want to try and release your COM objects when they are
done. Being unable to handle the lifetime of your objects is indicative of
poor design. If you perform the creation and retreival of the COM objects
in the scope of a method, then it should not be a problem at all to do this.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"zq" <zv*****@yahoo.com> wrote in message
news:uG*************@TK2MSFTNGP15.phx.gbl...
Hi!

I am have a COM object which occupies more and more memory everytime it's
used.

The "VM size" counter (private bytes) of the process that uses the COM
object keeps on growing when instantiating and using the object
frequently. I tried following strategies for using the object:

1. instantiate the object when the app starts, use it frequently (inside
an event handler), and destroy it when the app ends.
2. instantiate the object inside the event handler, and release it every
time the event handler is finished

I tried using System.Marshal.ReleaseCOMObject() by calling it untill it's
result returns zero, but it has no effect.

Once the VM size reaches the pagefile size, the process starts throwing
"out of memory" exceptions if it tries to instantiate other objects (e.g.
a Bitmap object).

I have narrowed-down the problem to the COM object, because if I
comment-out the part where I use the COM object the "VM size" falls from
cca 250MB to 1MB once when it's done working (i guess it means all other
objects are properly disposed and GC-ed).

My questions are:

Is there a way to see if there are any dependant COM objects created
during the COM's work?
Is there a way to destroy all dependant COM objects?

Can anybody help me please!

zkeber

Dec 13 '05 #2
zq
Hi!

Thank you so much for your quick response.

things are as follows. somewhere, inside the code...

[DllImport("...\\Program Files\\ABBYY FineReader Engine
7.0\\Bin\\FREngine.dll", CharSet=CharSet.Unicode), PreserveSig]
private static extern uint GetEngineObject(String devSN, String reserved1,
String reserved2, ref FREngine.IEngine engine);

and then, later in the evening... :-)
using FREngine; // of course, i had to tlbimport the frengine first, and
get my Interop.frengine.dll which I use here
....
// this is an event that runs when a MSMQ message is received
private void onMessageReceive(object sender, ReceiveCompletedEventArgs e)
{
private IEngine eng; //
comes with the FREngine

eng = GetEngineObject("unimportant_string", null, null, ref eng);

eng.AnalyzePage("some_bitmap.jpg"); // and that's all

// do {} while(System.Marshal.ReleaseComObject() != 0);
eng = null;
// I don't need it anymore
// GC.Collect();
// GC.WaitForPendingFinalizers();
}

this is supposed to work normally, right?
every time it encounters eng=null; the com object is ready for disposal,
right?

anyway, even if i uncomment last four lines, it still clogs
up my memory (perfmon's Private Bytes) with additional 45 MBs every time the
event is fired

btw, there are no internal functions inside the dll for cleaning up memory
occupied by abbyy's object
or anything even similar.

thanks a lot for your help!

zvonko

"Nicholas Paldino [.NET/C# MVP]" <mv*@spam.guard.caspershouse.com> wrote in
message news:eD*************@TK2MSFTNGP12.phx.gbl...
zq,

Well, if you are returning other COM objects as properties from the COM
object that you are using, then you have to make sure you release those
when you are done with them.

If you aren't using any properties which return any other COM object
(or methods which return them), then I would say it is a problem with the
COM object itself. However, the behavior you describe indicates that you
are probably getting more objects from the object you created, and not
releasing them correctly.

If your use of the COM object is localized, and you have a huge number
of references to other COM objects, then I would actually call GC.Collect
to force a collection, which will release the COM objects (assuming you
have no other references to them). The finalization of the runtime
callable wrapper will cause the reference to be released, and the COM
object to be released.

Surprizingly enough, there are some areas of the framework that do just
that (I think it was something in the System.Windows.Forms namespace, I
remember being floored when I saw it a while ago).

Ideally, you want to try and release your COM objects when they are
done. Being unable to handle the lifetime of your objects is indicative
of poor design. If you perform the creation and retreival of the COM
objects in the scope of a method, then it should not be a problem at all
to do this.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"zq" <zv*****@yahoo.com> wrote in message
news:uG*************@TK2MSFTNGP15.phx.gbl...
Hi!

I am have a COM object which occupies more and more memory everytime it's
used.

The "VM size" counter (private bytes) of the process that uses the COM
object keeps on growing when instantiating and using the object
frequently. I tried following strategies for using the object:

1. instantiate the object when the app starts, use it frequently (inside
an event handler), and destroy it when the app ends.
2. instantiate the object inside the event handler, and release it every
time the event handler is finished

I tried using System.Marshal.ReleaseCOMObject() by calling it untill it's
result returns zero, but it has no effect.

Once the VM size reaches the pagefile size, the process starts throwing
"out of memory" exceptions if it tries to instantiate other objects (e.g.
a Bitmap object).

I have narrowed-down the problem to the COM object, because if I
comment-out the part where I use the COM object the "VM size" falls from
cca 250MB to 1MB once when it's done working (i guess it means all other
objects are properly disposed and GC-ed).

My questions are:

Is there a way to see if there are any dependant COM objects created
during the COM's work?
Is there a way to destroy all dependant COM objects?

Can anybody help me please!

zkeber


Dec 13 '05 #3
This looks like the result of a mixing of incompatible technologies, MSMQ is
server class while your COM server looks like client side stuff (involving
bitmaps).
It's also weird that you need to PInvoke to get at a COM interface
reference. That said, you need to make sure that your COM reference lives in
the same apartment as the caller (the Interface user).
IMO your COM object is an STA type object and the eventhandler runs in a
threadpool thread which per default is initialized as MTA, that means that
your COM calls need to be marshaled. This is not a problem as the CLR and
COM takes care of this, but there is a small problem when running this on
the CLR, the STA thread needs to pump messages or the finalizer thread (an
MTA) will block when calling the RCW's Finalize method. A blocked finalizer
is realy a bad thing, no finalizable objects can ever get collected by the
GC and memory consumption will increase until an OOM exception is raised and
your application dies.
So, what I suggest is that you check the COM apartment requirements and the
apartment state of the eventhandler (onMessageReceive). When they are
incompatible you need to create your object in an auxiliary thread
initialized as STA and make sure it pumps messages (you can do this by
calling WatForPendingFinalizers() or WaitOne() ).
Willy.

"zq" <zv*****@yahoo.com> wrote in message
news:un**************@TK2MSFTNGP11.phx.gbl...
Hi!

Thank you so much for your quick response.

things are as follows. somewhere, inside the code...

[DllImport("...\\Program Files\\ABBYY FineReader Engine
7.0\\Bin\\FREngine.dll", CharSet=CharSet.Unicode), PreserveSig]
private static extern uint GetEngineObject(String devSN, String reserved1,
String reserved2, ref FREngine.IEngine engine);

and then, later in the evening... :-)
using FREngine; // of course, i had to tlbimport the frengine first, and
get my Interop.frengine.dll which I use here
...
// this is an event that runs when a MSMQ message is received
private void onMessageReceive(object sender, ReceiveCompletedEventArgs
e)
{
private IEngine eng; //
comes with the FREngine

eng = GetEngineObject("unimportant_string", null, null, ref eng);

eng.AnalyzePage("some_bitmap.jpg"); // and that's all

// do {} while(System.Marshal.ReleaseComObject() != 0);
eng = null; // I don't need it anymore
// GC.Collect();
// GC.WaitForPendingFinalizers();
}

this is supposed to work normally, right?
every time it encounters eng=null; the com object is ready for disposal,
right?

anyway, even if i uncomment last four lines, it still clogs
up my memory (perfmon's Private Bytes) with additional 45 MBs every time
the event is fired

btw, there are no internal functions inside the dll for cleaning up memory
occupied by abbyy's object
or anything even similar.

thanks a lot for your help!

zvonko

"Nicholas Paldino [.NET/C# MVP]" <mv*@spam.guard.caspershouse.com> wrote
in message news:eD*************@TK2MSFTNGP12.phx.gbl...
zq,

Well, if you are returning other COM objects as properties from the
COM object that you are using, then you have to make sure you release
those when you are done with them.

If you aren't using any properties which return any other COM object
(or methods which return them), then I would say it is a problem with the
COM object itself. However, the behavior you describe indicates that you
are probably getting more objects from the object you created, and not
releasing them correctly.

If your use of the COM object is localized, and you have a huge number
of references to other COM objects, then I would actually call GC.Collect
to force a collection, which will release the COM objects (assuming you
have no other references to them). The finalization of the runtime
callable wrapper will cause the reference to be released, and the COM
object to be released.

Surprizingly enough, there are some areas of the framework that do
just that (I think it was something in the System.Windows.Forms
namespace, I remember being floored when I saw it a while ago).

Ideally, you want to try and release your COM objects when they are
done. Being unable to handle the lifetime of your objects is indicative
of poor design. If you perform the creation and retreival of the COM
objects in the scope of a method, then it should not be a problem at all
to do this.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"zq" <zv*****@yahoo.com> wrote in message
news:uG*************@TK2MSFTNGP15.phx.gbl...
Hi!

I am have a COM object which occupies more and more memory everytime
it's used.

The "VM size" counter (private bytes) of the process that uses the COM
object keeps on growing when instantiating and using the object
frequently. I tried following strategies for using the object:

1. instantiate the object when the app starts, use it frequently (inside
an event handler), and destroy it when the app ends.
2. instantiate the object inside the event handler, and release it every
time the event handler is finished

I tried using System.Marshal.ReleaseCOMObject() by calling it untill
it's result returns zero, but it has no effect.

Once the VM size reaches the pagefile size, the process starts throwing
"out of memory" exceptions if it tries to instantiate other objects
(e.g. a Bitmap object).

I have narrowed-down the problem to the COM object, because if I
comment-out the part where I use the COM object the "VM size" falls from
cca 250MB to 1MB once when it's done working (i guess it means all other
objects are properly disposed and GC-ed).

My questions are:

Is there a way to see if there are any dependant COM objects created
during the COM's work?
Is there a way to destroy all dependant COM objects?

Can anybody help me please!

zkeber



Dec 13 '05 #4
zq
Thanx.

I'm all new to using multithreading in C#, but I tried something I found in
an example
on the msdn:

class XXX {
[STAThread]
public void Run() {
eng = GetEngineObject(...);

// some code that uses the COM object

eng = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}

clas YYY {
...
public void onMessageReceive() {
XXX obj = new XXX();
Thread at = new Thread(new ThreadStart(obj.Run));
at.Start();
at.Join(); // wait until the other thread is finished
}
...
}

And now I get "No such interface supported" exception inside the XXX.Run().
I have to dig in deeper on this to find out why is that it.

This code above should do fine, right?
"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:uc**************@TK2MSFTNGP10.phx.gbl...
This looks like the result of a mixing of incompatible technologies, MSMQ
is server class while your COM server looks like client side stuff
(involving bitmaps).
It's also weird that you need to PInvoke to get at a COM interface
reference. That said, you need to make sure that your COM reference lives
in the same apartment as the caller (the Interface user).
IMO your COM object is an STA type object and the eventhandler runs in a
threadpool thread which per default is initialized as MTA, that means that
your COM calls need to be marshaled. This is not a problem as the CLR and
COM takes care of this, but there is a small problem when running this on
the CLR, the STA thread needs to pump messages or the finalizer thread (an
MTA) will block when calling the RCW's Finalize method. A blocked
finalizer is realy a bad thing, no finalizable objects can ever get
collected by the GC and memory consumption will increase until an OOM
exception is raised and your application dies.
So, what I suggest is that you check the COM apartment requirements and
the apartment state of the eventhandler (onMessageReceive). When they are
incompatible you need to create your object in an auxiliary thread
initialized as STA and make sure it pumps messages (you can do this by
calling WatForPendingFinalizers() or WaitOne() ).
Willy.

"zq" <zv*****@yahoo.com> wrote in message
news:un**************@TK2MSFTNGP11.phx.gbl...
Hi!

Thank you so much for your quick response.

things are as follows. somewhere, inside the code...

[DllImport("...\\Program Files\\ABBYY FineReader Engine
7.0\\Bin\\FREngine.dll", CharSet=CharSet.Unicode), PreserveSig]
private static extern uint GetEngineObject(String devSN, String
reserved1, String reserved2, ref FREngine.IEngine engine);

and then, later in the evening... :-)
using FREngine; // of course, i had to tlbimport the frengine first,
and get my Interop.frengine.dll which I use here
...
// this is an event that runs when a MSMQ message is received
private void onMessageReceive(object sender, ReceiveCompletedEventArgs
e)
{
private IEngine eng; //
comes with the FREngine

eng = GetEngineObject("unimportant_string", null, null, ref eng);

eng.AnalyzePage("some_bitmap.jpg"); // and that's all

// do {} while(System.Marshal.ReleaseComObject() != 0);
eng = null; // I don't need it anymore
// GC.Collect();
// GC.WaitForPendingFinalizers();
}

this is supposed to work normally, right?
every time it encounters eng=null; the com object is ready for disposal,
right?

anyway, even if i uncomment last four lines, it still clogs
up my memory (perfmon's Private Bytes) with additional 45 MBs every time
the event is fired

btw, there are no internal functions inside the dll for cleaning up
memory occupied by abbyy's object
or anything even similar.

thanks a lot for your help!

zvonko

"Nicholas Paldino [.NET/C# MVP]" <mv*@spam.guard.caspershouse.com> wrote
in message news:eD*************@TK2MSFTNGP12.phx.gbl...
zq,

Well, if you are returning other COM objects as properties from the
COM object that you are using, then you have to make sure you release
those when you are done with them.

If you aren't using any properties which return any other COM object
(or methods which return them), then I would say it is a problem with
the COM object itself. However, the behavior you describe indicates
that you are probably getting more objects from the object you created,
and not releasing them correctly.

If your use of the COM object is localized, and you have a huge
number of references to other COM objects, then I would actually call
GC.Collect to force a collection, which will release the COM objects
(assuming you have no other references to them). The finalization of
the runtime callable wrapper will cause the reference to be released,
and the COM object to be released.

Surprizingly enough, there are some areas of the framework that do
just that (I think it was something in the System.Windows.Forms
namespace, I remember being floored when I saw it a while ago).

Ideally, you want to try and release your COM objects when they are
done. Being unable to handle the lifetime of your objects is indicative
of poor design. If you perform the creation and retreival of the COM
objects in the scope of a method, then it should not be a problem at all
to do this.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"zq" <zv*****@yahoo.com> wrote in message
news:uG*************@TK2MSFTNGP15.phx.gbl...
Hi!

I am have a COM object which occupies more and more memory everytime
it's used.

The "VM size" counter (private bytes) of the process that uses the COM
object keeps on growing when instantiating and using the object
frequently. I tried following strategies for using the object:

1. instantiate the object when the app starts, use it frequently
(inside an event handler), and destroy it when the app ends.
2. instantiate the object inside the event handler, and release it
every time the event handler is finished

I tried using System.Marshal.ReleaseCOMObject() by calling it untill
it's result returns zero, but it has no effect.

Once the VM size reaches the pagefile size, the process starts throwing
"out of memory" exceptions if it tries to instantiate other objects
(e.g. a Bitmap object).

I have narrowed-down the problem to the COM object, because if I
comment-out the part where I use the COM object the "VM size" falls
from cca 250MB to 1MB once when it's done working (i guess it means all
other objects are properly disposed and GC-ed).

My questions are:

Is there a way to see if there are any dependant COM objects created
during the COM's work?
Is there a way to destroy all dependant COM objects?

Can anybody help me please!

zkeber



Dec 14 '05 #5
No it's not and IMO this should not compile.
[STAThread]
public void Run() { [STAThread] is only valid on the Main static entry point.
What you need to do is set the apartment state to STA before starting the
thread.
at.ApartmentState = STA; at.Start();
Note that before doing this you still need to check the COM object
requirements using oleview or regedit, it makes no sense to set the state to
STA for a "free threaded" object, only objects that are flagged "apartment"
or "both" can live in an STA.

Willy.

"zq" <zv*****@yahoo.com> wrote in message
news:uv*************@TK2MSFTNGP14.phx.gbl... Thanx.

I'm all new to using multithreading in C#, but I tried something I found
in an example
on the msdn:

class XXX { eng = GetEngineObject(...);

// some code that uses the COM object

eng = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}

clas YYY {
...
public void onMessageReceive() {
XXX obj = new XXX();
Thread at = new Thread(new ThreadStart(obj.Run));
at.Start();
at.Join(); // wait until the other thread is finished
}
...
}

And now I get "No such interface supported" exception inside the
XXX.Run().
I have to dig in deeper on this to find out why is that it.

This code above should do fine, right?
"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:uc**************@TK2MSFTNGP10.phx.gbl...
This looks like the result of a mixing of incompatible technologies, MSMQ
is server class while your COM server looks like client side stuff
(involving bitmaps).
It's also weird that you need to PInvoke to get at a COM interface
reference. That said, you need to make sure that your COM reference lives
in the same apartment as the caller (the Interface user).
IMO your COM object is an STA type object and the eventhandler runs in a
threadpool thread which per default is initialized as MTA, that means
that your COM calls need to be marshaled. This is not a problem as the
CLR and COM takes care of this, but there is a small problem when running
this on the CLR, the STA thread needs to pump messages or the finalizer
thread (an MTA) will block when calling the RCW's Finalize method. A
blocked finalizer is realy a bad thing, no finalizable objects can ever
get collected by the GC and memory consumption will increase until an OOM
exception is raised and your application dies.
So, what I suggest is that you check the COM apartment requirements and
the apartment state of the eventhandler (onMessageReceive). When they are
incompatible you need to create your object in an auxiliary thread
initialized as STA and make sure it pumps messages (you can do this by
calling WatForPendingFinalizers() or WaitOne() ).
Willy.

"zq" <zv*****@yahoo.com> wrote in message
news:un**************@TK2MSFTNGP11.phx.gbl...
Hi!

Thank you so much for your quick response.

things are as follows. somewhere, inside the code...

[DllImport("...\\Program Files\\ABBYY FineReader Engine
7.0\\Bin\\FREngine.dll", CharSet=CharSet.Unicode), PreserveSig]
private static extern uint GetEngineObject(String devSN, String
reserved1, String reserved2, ref FREngine.IEngine engine);

and then, later in the evening... :-)
using FREngine; // of course, i had to tlbimport the frengine first,
and get my Interop.frengine.dll which I use here
...
// this is an event that runs when a MSMQ message is received
private void onMessageReceive(object sender,
ReceiveCompletedEventArgs e)
{
private IEngine eng;
// comes with the FREngine

eng = GetEngineObject("unimportant_string", null, null, ref eng);

eng.AnalyzePage("some_bitmap.jpg"); // and that's all

// do {} while(System.Marshal.ReleaseComObject() != 0);
eng = null; // I don't need it anymore
// GC.Collect();
// GC.WaitForPendingFinalizers();
}

this is supposed to work normally, right?
every time it encounters eng=null; the com object is ready for disposal,
right?

anyway, even if i uncomment last four lines, it still clogs
up my memory (perfmon's Private Bytes) with additional 45 MBs every time
the event is fired

btw, there are no internal functions inside the dll for cleaning up
memory occupied by abbyy's object
or anything even similar.

thanks a lot for your help!

zvonko

"Nicholas Paldino [.NET/C# MVP]" <mv*@spam.guard.caspershouse.com> wrote
in message news:eD*************@TK2MSFTNGP12.phx.gbl...
zq,

Well, if you are returning other COM objects as properties from the
COM object that you are using, then you have to make sure you release
those when you are done with them.

If you aren't using any properties which return any other COM object
(or methods which return them), then I would say it is a problem with
the COM object itself. However, the behavior you describe indicates
that you are probably getting more objects from the object you created,
and not releasing them correctly.

If your use of the COM object is localized, and you have a huge
number of references to other COM objects, then I would actually call
GC.Collect to force a collection, which will release the COM objects
(assuming you have no other references to them). The finalization of
the runtime callable wrapper will cause the reference to be released,
and the COM object to be released.

Surprizingly enough, there are some areas of the framework that do
just that (I think it was something in the System.Windows.Forms
namespace, I remember being floored when I saw it a while ago).

Ideally, you want to try and release your COM objects when they are
done. Being unable to handle the lifetime of your objects is
indicative of poor design. If you perform the creation and retreival
of the COM objects in the scope of a method, then it should not be a
problem at all to do this.

Hope this helps.

--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com

"zq" <zv*****@yahoo.com> wrote in message
news:uG*************@TK2MSFTNGP15.phx.gbl...
> Hi!
>
> I am have a COM object which occupies more and more memory everytime
> it's used.
>
> The "VM size" counter (private bytes) of the process that uses the COM
> object keeps on growing when instantiating and using the object
> frequently. I tried following strategies for using the object:
>
> 1. instantiate the object when the app starts, use it frequently
> (inside an event handler), and destroy it when the app ends.
> 2. instantiate the object inside the event handler, and release it
> every time the event handler is finished
>
> I tried using System.Marshal.ReleaseCOMObject() by calling it untill
> it's result returns zero, but it has no effect.
>
> Once the VM size reaches the pagefile size, the process starts
> throwing "out of memory" exceptions if it tries to instantiate other
> objects (e.g. a Bitmap object).
>
> I have narrowed-down the problem to the COM object, because if I
> comment-out the part where I use the COM object the "VM size" falls
> from cca 250MB to 1MB once when it's done working (i guess it means
> all other objects are properly disposed and GC-ed).
>
> My questions are:
>
> Is there a way to see if there are any dependant COM objects created
> during the COM's work?
> Is there a way to destroy all dependant COM objects?
>
> Can anybody help me please!
>
> zkeber
>



Dec 14 '05 #6

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

reply views Thread by Jerome Lefebvre | last post: by
14 posts views Thread by Jim Hubbard | last post: by
5 posts views Thread by Corky | last post: by
2 posts views Thread by Ellen Graves | last post: by
reply views Thread by Sergistm | last post: by
1 post views Thread by XIAOLAOHU | last post: by
reply views Thread by leo001 | last post: by
reply views Thread by CCCYYYY | last post: by
1 post views Thread by ZEDKYRIE | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.