473,416 Members | 1,778 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

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

C# COM object not being released

I have a VC++ client that creates a COM object that is implemented in C#.
The problem is the COM object doesn't get released, so I get a memory leak.

I have no problems creating the COM object and getting a pointer to the
required interface (IATImportFilter) in the client:

COleDispatchDriver* resultInterface = new COleDispatchDriver;
if ( resultInterface == NULL ) return FALSE;
COleException oOleException;
BOOL success = resultInterface->CreateDispatch( progID, &oOleException );

IUnknown *p;
if ( resultInterface->m_lpDispatch->QueryInterface( *pRequiredInterface,
(void**)&p ) != S_OK )

where prodID ("SI.ValidatorImport") is the name of COM object.

My C# COM object class is declared like this:

[ClassInterface(ClassInterfaceType.None)]
[ProgId("SI.ValidatorImport")]
public class ValidatorImport : IATImportFilter
{

The object is being released in the client like this:

((IUnknown*) pIF)->Release();
pIF = NULL;

The call to Release() on the interface does not actually destroy the object.
It doesn't call the destructor of my COM object. I tried putting a
breakpoint in there and even added a MessageBox() call but the program just
doesn't go in there.

Not sure why this happening. Since every interface supports IUnkown,
Release() should release the COM object. The COM object is only created
once.

I'm new to C# so I would appreciate some help.
Nov 17 '05 #1
18 5864
>The call to Release() on the interface does not actually destroy the object.
It doesn't call the destructor of my COM object.


Since it's a managed object it will eventually be cleaned up by the
garbage collector.

Mattias

--
Mattias Sjögren [MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.
Nov 17 '05 #2
In message <jr********************@biscit.net>, techie
<te****@nospam.biz> writes
The call to Release() on the interface does not actually destroy the object.
It doesn't call the destructor of my COM object. I tried putting a
breakpoint in there and even added a MessageBox() call but the program just
doesn't go in there.

Not sure why this happening. Since every interface supports IUnkown,
Release() should release the COM object. The COM object is only created
once.


As I understand it, when you call Release(), you're calling it on a
COM-callable wrapper, not on the object itself. The COM callable wrapper
reference counts as you would expect a COM object to. When all
references are released, it releases its own reference to the object you
wrote in C#, which is then available for garbage collection. This will
happen at some point, but not necessarily when you expect it to. So, you
don't get deterministic finalisation of your C# object. You *may* get DF
of the wrapper, the documentation I've seen isn't clear on whether it is
destroyed immediately you are done with it It's pinned on a non GC'd
heap, again AIUI, so presumably it is destroyed immediately in the
normal COM fashion.

You probably don't have a memory leak, you more than likely have an
unreferenced object sat waiting to be GC'd.

--
Steve Walker
Nov 17 '05 #3
"Steve Walker" <st***@otolith.demon.co.uk> wrote in message
news:pI**************@otolith.demon.co.uk...
You probably don't have a memory leak, you more than likely have an
unreferenced object sat waiting to be GC'd.


Is there any I can force GC? Maybe if my class also implements IDisposable
I can call dispose from my client?
Nov 17 '05 #4
"Mattias Sjögren" <ma********************@mvps.org> wrote in message
news:uf*************@TK2MSFTNGP15.phx.gbl...
The call to Release() on the interface does not actually destroy the object.It doesn't call the destructor of my COM object.


Since it's a managed object it will eventually be cleaned up by the
garbage collector.


The call to Release() on my interface pointer returns 0 - the reference
count on the interface - which indicates the COM object should have been
released or at least queued to be released by the GC. When VC++ debugger
halts it reports a lot of memory leaks which worries me.

Can I assume the COM object is released by the GC eventually? It must
happen some time after my VC++ client closes. Is there any way of forcing
the GC to delete my object immediately from my client?
Nov 17 '05 #5
The call to Release() on my interface pointer returns 0 - the reference
count on the interface - which indicates the COM object should have been
released or at least queued to be released by the GC. When VC++ debugger
halts it reports a lot of memory leaks which worries me.
The COM callable wrapper will be deleted, but the managed object is
just eligible for GC.

Can I assume the COM object is released by the GC eventually?
Yes

It must happen some time after my VC++ client closes.
It will happen the next time GC runs, or at process shutdown if the
executable calls CorExitProcess.

Is there any way of forcing
the GC to delete my object immediately from my client?


Yes, with the GC class, but that doesn't mean it's a good idea to do
so.
And implementing IDisposable as you suggested in the other thread will
not affect GC, only let have deterministic cleanup of unmanaged
resources.

Mattias

--
Mattias Sjögren [MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Please reply only to the newsgroup.
Nov 17 '05 #6
My VC++ 6 client doesn't make any GC.Collect() call. Its legacy code and I
don't want to change it.

I put a breakpoint in the destructor of my C# COM object class. I then ran
the C# project with the Start Application set to my VC++ client. At no
point did the program ever break into my breakpoint which implies the object
never got deleted. Why is that? Doesn't the GC run every now and then?
Does it only run when the memory usage of the machine reaches a high level?

In this particular solution, my C# COM object will be created via a C++ COM
object. A C# web service will create the C++ COM object. After I'm
finished with the COM object I can call GC.Collect() which will force
garbage collection. I'm just concerned my web server will run out of
memory. This is an important project and there are going to be a large
number of users.
Nov 17 '05 #7
Object "Finalizers" (also called destructor) are executed by/on the
finalizer thread, at some point in time, after the GC has run for the second
time after the object became eligible for collecting.
Note however that when your object has been instantiated in a STA thread,
this thread needs to pump the window message queue, failing to do so will
block the finalizer thread.
So first thing you should do is; check the apartment you are running in, if
it's a STA change it to MTA, and make sure your object is thread safe.

Willy.

"techie" <te****@nospam.biz> wrote in message
news:u7********************@biscit.net...
My VC++ 6 client doesn't make any GC.Collect() call. Its legacy code and
I
don't want to change it.

I put a breakpoint in the destructor of my C# COM object class. I then
ran
the C# project with the Start Application set to my VC++ client. At no
point did the program ever break into my breakpoint which implies the
object
never got deleted. Why is that? Doesn't the GC run every now and then?
Does it only run when the memory usage of the machine reaches a high
level?

In this particular solution, my C# COM object will be created via a C++
COM
object. A C# web service will create the C++ COM object. After I'm
finished with the COM object I can call GC.Collect() which will force
garbage collection. I'm just concerned my web server will run out of
memory. This is an important project and there are going to be a large
number of users.

Nov 17 '05 #8
My VC++ 6 client is a normal .exe application. No MTA here. Could this be
reason for my memory leak?

However, my C++ COM object, which can be created from a ASP.NET web page, is
MTA. This COM object in turn will create my new C# object. I will test if
the destructor is called in this case.

"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:uO**************@TK2MSFTNGP10.phx.gbl...
Object "Finalizers" (also called destructor) are executed by/on the
finalizer thread, at some point in time, after the GC has run for the second time after the object became eligible for collecting.
Note however that when your object has been instantiated in a STA thread,
this thread needs to pump the window message queue, failing to do so will
block the finalizer thread.
So first thing you should do is; check the apartment you are running in, if it's a STA change it to MTA, and make sure your object is thread safe.

Willy.

Nov 17 '05 #9
Even 'normal' applications must initialize the thread to enter an apartment
before they can create COM instances and use their interfcaces. Question
is - what do you mean with a normal exe? Does it initialize the thread by
calling CoInitialize(Ex) to enter an STA or an MTA? And more importantly,
does it pump messages (all windows programs pump the message queue as part
of their UI message loop, console style applications don't pump however).

I'm not sure what you mean with your C++ object is MTA, COM objects can have
a 'threadingmodel' apartment, both and free, if no threadingmode is
specified the object lives in an OLE supplied host apartment.
'apartment' requires a STA to live in, 'both' can live in a STA as well as a
MTA while 'free' requires a MTA.
MTA initialized threads don't need to pump messages.

If your C++ COM is marked 'apartment' in the registry it must run in a
asp.net application with <%@ page aspcompat=true %> attribute set.

Willy.


"techie" <te****@nospam.biz> wrote in message
news:qK********************@biscit.net...
My VC++ 6 client is a normal .exe application. No MTA here. Could this
be
reason for my memory leak?

However, my C++ COM object, which can be created from a ASP.NET web page,
is
MTA. This COM object in turn will create my new C# object. I will test
if
the destructor is called in this case.

"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:uO**************@TK2MSFTNGP10.phx.gbl...
Object "Finalizers" (also called destructor) are executed by/on the
finalizer thread, at some point in time, after the GC has run for the

second
time after the object became eligible for collecting.
Note however that when your object has been instantiated in a STA thread,
this thread needs to pump the window message queue, failing to do so will
block the finalizer thread.
So first thing you should do is; check the apartment you are running in,

if
it's a STA change it to MTA, and make sure your object is thread safe.

Willy.


Nov 17 '05 #10
In message <GZ********************@biscit.net>, techie
<te****@nospam.biz> writes
"Steve Walker" <st***@otolith.demon.co.uk> wrote in message
news:pI**************@otolith.demon.co.uk...
You probably don't have a memory leak, you more than likely have an
unreferenced object sat waiting to be GC'd.


Is there any I can force GC?


You can, but you probably shouldn't. The algorithm for garbage
collection itself is well documented. I can't find similar documentation
for the *scheduling* of garbage collection, but I get the impression
from its behaviour that it works on the principle that when there is
plenty of memory still free, CPU cycles should not be wasted on
recovering what has been used.

--
Steve Walker
Nov 17 '05 #11
I tried changing the 'normal' AfxOleInit() call to CoInitializeEx( NULL,
COINIT_MULTITHREADED) but it made no difference.

Coming from a C++ background I am used to all allocated memory being
released when an application closes down. If my main application is in C# I
can call Dispose() on the object but I can't in this case.
Nov 17 '05 #12
It's getting confusing, it would suggest you explain in detail your exact
scenario.
I see you called AfxOleInit() initially, that means the application is a MFC
application right? But what kind of application is it, a windows application
or something else, or simply put - does it have a UI?
If it has a UI you MUST not change the threads apartment to MTA, you must
keep the call to AfxOleInit.

Second, what kind of COM object are you creating from this C++ application;
1- a native C++ COM object, or
3- a native COM object creating a .NET COM object, or
2- a .NET COM object .

If it's 1 or 2, and the native COM object's 'threadingmodel' is set to
'Apartment', the object will be created on a STA thread no matter how you
initialize the (main) thread. And that thread must pump the message queue,
that's why I asked if it's a windows type client (having a UI). In case 2,
both objects will be created in the same apartment - so it's the native COM
objects who determines the apartment type.
In case 3, the 'COM' object will live in the creators apartment irrespective
it's type, but I can't stress it enough, - STA threads must pump messages,
and UI threads do have a message pump, non UI threads don't!

Note that in .NET the memory will also be released when the application
closes down.
I'm also not clear why you have a destructor defined for your "COM" class,
what is it used for?
A general rule is to avoid finalizers, unless your object is disposable
(implements IDisposable) and the destructor is only used as a safety net.

Willy.

"techie" <te****@nospam.biz> wrote in message
news:ar********************@biscit.net...
I tried changing the 'normal' AfxOleInit() call to CoInitializeEx( NULL,
COINIT_MULTITHREADED) but it made no difference.

Coming from a C++ background I am used to all allocated memory being
released when an application closes down. If my main application is in C#
I
can call Dispose() on the object but I can't in this case.

Nov 17 '05 #13
The main application is an MFC Windows application with a UI. The main MFC
application creates a COM object. The COM object is a C# .NET COM object.
Therefore, my COM object is a STA thread? How do I get my COM object to
pump the message queue?
"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:%2***************@TK2MSFTNGP09.phx.gbl...
It's getting confusing, it would suggest you explain in detail your exact
scenario.
I see you called AfxOleInit() initially, that means the application is a MFC application right? But what kind of application is it, a windows application or something else, or simply put - does it have a UI?
If it has a UI you MUST not change the threads apartment to MTA, you must
keep the call to AfxOleInit.

Second, what kind of COM object are you creating from this C++ application; 1- a native C++ COM object, or
3- a native COM object creating a .NET COM object, or
2- a .NET COM object .

If it's 1 or 2, and the native COM object's 'threadingmodel' is set to
'Apartment', the object will be created on a STA thread no matter how you
initialize the (main) thread. And that thread must pump the message queue,
that's why I asked if it's a windows type client (having a UI). In case 2,
both objects will be created in the same apartment - so it's the native COM objects who determines the apartment type.
In case 3, the 'COM' object will live in the creators apartment irrespective it's type, but I can't stress it enough, - STA threads must pump messages,
and UI threads do have a message pump, non UI threads don't!

Note that in .NET the memory will also be released when the application
closes down.
I'm also not clear why you have a destructor defined for your "COM" class,
what is it used for?
A general rule is to avoid finalizers, unless your object is disposable
(implements IDisposable) and the destructor is only used as a safety net.

Willy.

"techie" <te****@nospam.biz> wrote in message
news:ar********************@biscit.net...
I tried changing the 'normal' AfxOleInit() call to CoInitializeEx( NULL,
COINIT_MULTITHREADED) but it made no difference.

Coming from a C++ background I am used to all allocated memory being
released when an application closes down. If my main application is in C# I
can call Dispose() on the object but I can't in this case.


Nov 17 '05 #14
It sounds to me as if a quick primer on how the garbage collector works
might be useful to you. Here's a pretty good article:
http://msdn.microsoft.com/msdnmag/issues/1100/gci/

In general:
Avoid calling GC.Collect() in production code -- trying to manually control
the GC will usually do more harm than good. (GC.Collect() can be useful in
testing scenarios, though).

Avoid using a Finalizer (destructor in C#) -- only use them if you need to
release some kind of unmanaged resource, like a handle.

IDisposable probably isn't what you think it is. You use it to provide a
deterministic way to call the finalizer (but you probably don't want to use
a finalizer).

Here's some additional reading:
http://www.bluebytesoftware.com/blog...3-20c06ae539ae

A whole catalog of links:
http://blogs.msdn.com/clyon/archive/...14/229477.aspx
"techie" <te****@nospam.biz> wrote in message
news:GZ********************@biscit.net...
"Steve Walker" <st***@otolith.demon.co.uk> wrote in message
news:pI**************@otolith.demon.co.uk...
You probably don't have a memory leak, you more than likely have an
unreferenced object sat waiting to be GC'd.


Is there any I can force GC? Maybe if my class also implements
IDisposable
I can call dispose from my client?

Nov 17 '05 #15

"techie" <te****@nospam.biz> wrote in message
news:Ee********************@biscit.net...
The main application is an MFC Windows application with a UI. The main
MFC
application creates a COM object. The COM object is a C# .NET COM object.
Therefore, my COM object is a STA thread? How do I get my COM object to
pump the message queue?


Ok, its a MFC windows application creating a .NET COM object.
1. You don't have to pump messages, like I said this is handled by the UI
thread.
2. You COM object will live in the same STA thread as the UI.
3. COM objects are released when you call Release() on their inferfaces.
4. The .NET object will be collected when the GC runs (for objects without
destructors), or when the finalizer runs (for objects that have destructors
but not implementing IDisposable). Note the the finalizer runs on a
finalizable object after the second sweep of the GC. If this object is the
sole .NET COM object in the process chances are that the GC never runs, so
the finalizer will not run either, in that case the finalizer will run when
the process shuts down.
So I suggest you apply the Disposing pattern and remove the finalizer
(destructor).

Willy.
Nov 17 '05 #16
Can you please check my code for the Disposing pattern. I'm still getting a
memory leak. Dispose nevers gets called. I've removed the destructor
(~Import).

[ClassInterface(ClassInterfaceType.None)]
[ProgId("SI.Import")]
public class Import :IImportFilter, IDisposable
{
Session m_Session;
Content m_Content;
bool disposed = false;
..
..
public void Dispose()
{
Dispose(true);
}

protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
ComHelper.SafeReleaseComObject(m_Content);
ComHelper.SafeReleaseComObject(m_Session);
}
disposed = true;
}
}

}

There are two unmanaged COM objects that need to be destroyed.
..

"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:OJ**************@TK2MSFTNGP09.phx.gbl...

"techie" <te****@nospam.biz> wrote in message
news:Ee********************@biscit.net...
The main application is an MFC Windows application with a UI. The main
MFC
application creates a COM object. The COM object is a C# .NET COM object. Therefore, my COM object is a STA thread? How do I get my COM object to
pump the message queue?
Ok, its a MFC windows application creating a .NET COM object.
1. You don't have to pump messages, like I said this is handled by the UI
thread.
2. You COM object will live in the same STA thread as the UI.
3. COM objects are released when you call Release() on their inferfaces.
4. The .NET object will be collected when the GC runs (for objects without
destructors), or when the finalizer runs (for objects that have

destructors but not implementing IDisposable). Note the the finalizer runs on a
finalizable object after the second sweep of the GC. If this object is the
sole .NET COM object in the process chances are that the GC never runs, so
the finalizer will not run either, in that case the finalizer will run when the process shuts down.
So I suggest you apply the Disposing pattern and remove the finalizer
(destructor).

Willy.

Nov 17 '05 #17

The CCW will not call Dispose() automatically when it's ref. count reaches
0, your client has to call Dispose() explicitly just like any other managed
client should do, I guess you don't call Dispose() from the client.
Also note that calling Dispose won't free the "Import" 'managed COM'
object, this is done at the next GC run (non-deterministically), I hope this
is not the 'memory leak' your are referring to - this is not a memory leak
but the normal behavior.
I also suppose that SafeReleaseComObject is used to eagerly release other
unmanaged resources, if not you shouldn't implement IDisposable at all.

Willy.

"techie" <te****@nospam.biz> wrote in message
news:i7********************@biscit.net...
Can you please check my code for the Disposing pattern. I'm still getting
a
memory leak. Dispose nevers gets called. I've removed the destructor
(~Import).

[ClassInterface(ClassInterfaceType.None)]
[ProgId("SI.Import")]
public class Import :IImportFilter, IDisposable
{
Session m_Session;
Content m_Content;
bool disposed = false;
..
..
public void Dispose()
{
Dispose(true);
}

protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
ComHelper.SafeReleaseComObject(m_Content);
ComHelper.SafeReleaseComObject(m_Session);
}
disposed = true;
}
}

}

There are two unmanaged COM objects that need to be destroyed.
.

"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:OJ**************@TK2MSFTNGP09.phx.gbl...

"techie" <te****@nospam.biz> wrote in message
news:Ee********************@biscit.net...
> The main application is an MFC Windows application with a UI. The main
> MFC
> application creates a COM object. The COM object is a C# .NET COM object. > Therefore, my COM object is a STA thread? How do I get my COM object
> to
> pump the message queue?
>
>


Ok, its a MFC windows application creating a .NET COM object.
1. You don't have to pump messages, like I said this is handled by the UI
thread.
2. You COM object will live in the same STA thread as the UI.
3. COM objects are released when you call Release() on their inferfaces.
4. The .NET object will be collected when the GC runs (for objects
without
destructors), or when the finalizer runs (for objects that have

destructors
but not implementing IDisposable). Note the the finalizer runs on a
finalizable object after the second sweep of the GC. If this object is
the
sole .NET COM object in the process chances are that the GC never runs,
so
the finalizer will not run either, in that case the finalizer will run

when
the process shuts down.
So I suggest you apply the Disposing pattern and remove the finalizer
(destructor).

Willy.


Nov 17 '05 #18
OK, I will call Dispose() from my MFC client. Thanks for your help so far.
Is actually possible to QueryInterface for IDisposable?

If I have an ASP.NET application, instead of my MFC client, that creates a
C++ COM object which in turn creates my C# COM object do I have to implement
the IDisposable interface? Isn't this necessary for the GC to destroy all
unreferenced objects? Or do I just call Dispose() explicitly like in the
MFC client?
"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:Oy*************@tk2msftngp13.phx.gbl...

The CCW will not call Dispose() automatically when it's ref. count reaches
0, your client has to call Dispose() explicitly just like any other managed client should do, I guess you don't call Dispose() from the client.
Also note that calling Dispose won't free the "Import" 'managed COM'
object, this is done at the next GC run (non-deterministically), I hope this is not the 'memory leak' your are referring to - this is not a memory leak
but the normal behavior.
I also suppose that SafeReleaseComObject is used to eagerly release other
unmanaged resources, if not you shouldn't implement IDisposable at all.

Willy.


Nov 17 '05 #19

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

18
by: lylefair | last post by:
Can you post code, or a reference to a md? or ad? file on a website, where object variables are not released when they go out of scope and cause a problem, (but causing a problem is extraneous to...
5
by: Sunny | last post by:
Hi, I have to implement client/server application. The client have to instaniate an remoting object via http and pass some auth info. If the auth is OK, the client should invoke a method (or...
10
by: Steven Blair | last post by:
Hi, Quick overview of the problem: public bool Something( out DataSet ds ) { bool ret=false; try {
2
by: VinDotNet | last post by:
Here's the Scenario I am facing: I've got a winform managed c++ client which connects to my managed COM+ server application (by making createinstance, then typecasting the object to an interface,...
5
by: david.kao | last post by:
Hi All: I am creating a COM+ Pool object in C#. I set up the following attributes: JIT (true),Pool size; and at the end of each public method I called ContextUtil.DeactivateOnReturn=true to set...
5
by: Michael Moreno | last post by:
Hello, In a class I have this code: public object Obj; If Obj is a COM object I would like to call in the Dispose() method the following code: ...
3
FishVal
by: FishVal | last post by:
Windows Script Host Object library. Full name: Windows Script Host Object Model LibName: IWshRuntimeScripting Location: ...\WINDOWS\system32\wshom.ocx The present tip is closely related to...
3
by: abhimanyu | last post by:
I have a method Marshal.IsComObject(...) that returns TRUE if an object is a COM object. I have an object of Excel.Worksheet that I released using Marshal.ReleaseComObject(...). I want to iterate...
7
by: Raymond Chiu | last post by:
Dear all, I have written the following code in the button click event but find that excel session object always remain in the server as seen from taskmanager. Do you know why?...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...

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.