NAV and the .Net Garbage Collector
Many NAV solutions, including internal ones, have the necessity to use .Net as a supplement of NAV technology, in order to complete their logic; however, dealing with managed and unmanaged world takes some extra challenges, especially when the managed code uses unmanaged resources. This is further important, when the .Net component will be hosted on the NAS, as it restarts (traditionally because of a deadlock) giving a short time for the unmanaged resources to be disposed.
When a .Net component ends, it is marked as to be released by the .Net runtime garbage collector, however, the time when the component will be freed from memory is not known (this is especially true for IDisposable objects). This can be problematic when the managed component is using unmanaged resources (such as TCP ports, files and the like), as these are not available to the host computer until they are completely de-referenced.
.Net architecture is aware of this issue, and has means to deal with it, this is further explained in the IDisposable Interface (https://msdn.microsoft.com/en-us/library/system.idisposable.aspx). What this document explains, is that your .Net component has to have the IDisposable interface in order to stop the unmanaged resources within your managed code. This works well, except that it adds the extra constrain that the Dispose method should be called specifically in order to trigger the logic, this is somehow a little bit tricky in NAV, as there is no specific method that is called on a codeunit, when it is exiting (there is no “OnStop” method).
There are at least two different ways to solve this problem. One is to create a mixed mode wrapper, and the other one is to call the Dispose (or Stop, or whatever you want to call your method) when the CompanyClose trigger is executed.
As an example, let’s assume that your solution needs to query the NAS for information using a TCP Socket. For that, you create a .Net COM component that will use the System.Net.Sockets.TcpClient object to be opened once the component starts, and you add this component to codeunit 50000. You modify codeunit 1 trigger 99 to start your single instance codeunit 50000. Everything seems to be working great, until the NAS restarts and your component refuses to start again (or does not start on the first restart try). The error is something like: “The TCP port x, is already in use”.
It might take some thinking before knowing exactly what went wrong over here, but it is easy to explain. As we mention before, when a .Net component is deleted, it is marked to be discarded by the .Net runtime at a later time (and if you still have references to your object, this time might be never), while the garbage collector “kicks in”, all unmanaged resources are still owned and opened by the previous component instance, preventing new instances on reusing the same unmanaged resource (in this example a TCP port).
The solution here is simple. We should add the Dispose method to the .Net component, and somehow call it when the single instance codeunit stops (on the “OnStop” event).
Now, where should we locate such a Dispose call? Since the NAS needs a company to work, we can use the OnCompanyClose trigger to further call our Stop method, which will call the Dispose method. We cannot only code this logic on the single instance codeunit (50000), as there is no trigger that will be called when the NAS is restarting.
Our hypothetical (and simplified) codeunit could look something like:
And one way to call the OnStop trigger would be:
There can be other ways to solve this problem (as we also pointed out, mixed mode code is another obvious option), but the key is to know when the .Net COM component should be discarded if this component is using unmanaged resources.
Jorge Alberto Torres
Software Developer Engineer
These postings are provided "AS IS" with no warranties and confer no rights. You assume all risk for your use.
Comments
- Anonymous
June 09, 2009
PingBack from http://weakbladder.info/story.php?id=6673