- Posts: 70
- Thank you received: 0
Online Forums
Technical support is provided through Support Forums below. Anybody can view them; you need to Register/Login to our site (see links in upper right corner) in order to Post questions. You do not have to be a licensed user of our product.
Please read Rules for forum posts before reporting your issue or asking a question. OPC Labs team is actively monitoring the forums, and replies as soon as possible. Various technical information can also be found in our Knowledge Base. For your convenience, we have also assembled a Frequently Asked Questions page.
Do not use the Contact page for technical issues.
- Forum
- Discussions
- QuickOPC-Classic in COM
- Reading, Writing, Subscriptions, Property Access
- Can Develop OPC XML DA C++ client in Visual Studio 2015?
Can Develop OPC XML DA C++ client in Visual Studio 2015?
Debugger Type Mixed and Auto doesn't generate Managed threads in thread window. So I changed it to Managed and generated thread window with call stack in attached file.
Please check it, let me know if you need more info.
Thanks
Please Log in or Create an account to join the conversation.
Also note that the call stack will be possibly needed from more threads - not just one - as in the instructions.
Thank you
Please Log in or Create an account to join the conversation.
Attached thread and callstack info, let me know if you need more info.
Thanks.
Please Log in or Create an account to join the conversation.
The steps are similar to those described here: kb.opclabs.com/Obtaining_.NET_call_stacks_in_a_COM_application
Except that in your case, as you are already running from Visual Studio, you might be able to just start the process under the debugger, instead of attaching to it.
Please Log in or Create an account to join the conversation.
I have an issue, When my OpcXmlDa server is not stable that time the below line hangs for long time and it not return backs at all.
ResultArray.Attach(ClientPtr->ReadMultipleItems(&pArgumentsArray));
It gives the below error and works for some time then hangs. If OpcXmlDa server is not stable this function should return some error and come out right? Is there any api to address this issue or any help?
2016-12-14T0:14:42Z COPCXMLDAClient: readOpcXmlDaTagData: Exception occured OpcLabs.EasyOpc.Implementations.NetApi.NetApiException: An exception occurred during processing in a NET API OPC Data Access client. The inner exception contains details about the problem. ---> System.Web.Services.Protocols.SoapException: Relogin attempted too soon
at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
at OpcXml.Da10.Service.Read(RequestOptions Options, ReadRequestItemList ItemList, ReplyItemList& RItemList, OPCError[]& Errors)
at OpcXml.Da.Server.Read(Item[] items)
at Opc.Da.Server.Read(Item[] items)
at OpcLabs.EasyOpc.Implementations.NetApi.DataAccess.NetApiOpcDaServer.Read(Item[] items, Exception& exception)
--- End of inner exception stack trace ---
Please Log in or Create an account to join the conversation.
More observations:
I have written that you have two alternative solutions: Either the one with CoInitializeEx, or the one with message pumping. You should not combine both. That is an overkill, and not a good one.
In a simple loop like this, it does not really matter that much where you put the message pump.
But with your level of experience, if your app allows it, you should definitely stay with just the CoInitializeEx approach, and forget about message pumping. It has many more intricacies to it. And, we are not here to give advise with them - it's a Microsoft stuff.
Our revised example code will be available together with version 2016.2, which is due in several days. But we do not show reads in a loop, and we do not use the message pumping either, so really the significant change there is the proper CoInitializeEx, which is trivial.
Regards
Please Log in or Create an account to join the conversation.
I have tested with options you have provided but still memory leak issue is not solved, memory leak is growing in same pattern only.
Option A: I replaced CoInitialize with CoInitializeEx and problem is not solved.
Option B: I included message loop inside the thread and still issue is not solved. Please let me know where I can add this message loop.
Please share your sample code where you fixed memory leak issue.
Thanks.
// Initialize the COM library
//CoInitialize(NULL);
[b]CoInitializeEx(NULL, COINIT_MULTITHREADED);[/b]
// Instatiate the EasyOPC-DA client object
_EasyDAClientPtr ClientPtr(__uuidof(EasyDAClient));
while(1){
_DAReadItemArgumentsPtr ReadItemArguments1Ptr(_uuidof(DAReadItemArguments));
ReadItemArguments1Ptr->ServerDescriptor->UrlString = L"http://opcxml.demo-this.com/XmlDaSampleServer/Service.asmx";
ReadItemArguments1Ptr->ItemDescriptor->ItemId = L"Dynamic/Analog Types/Double";
_DAReadItemArgumentsPtr ReadItemArguments2Ptr(_uuidof(DAReadItemArguments));
ReadItemArguments2Ptr->ServerDescriptor->UrlString = L"http://opcxml.demo-this.com/XmlDaSampleServer/Service.asmx";
ReadItemArguments2Ptr->ItemDescriptor->ItemId = L"Dynamic/Analog Types/Double[]";
_DAReadItemArgumentsPtr ReadItemArguments3Ptr(_uuidof(DAReadItemArguments));
ReadItemArguments3Ptr->ServerDescriptor->UrlString = L"http://opcxml.demo-this.com/XmlDaSampleServer/Service.asmx";
ReadItemArguments3Ptr->ItemDescriptor->ItemId = L"Dynamic/Analog Types/Int";
_DAReadItemArgumentsPtr ReadItemArguments4Ptr(_uuidof(DAReadItemArguments));
ReadItemArguments4Ptr->ServerDescriptor->UrlString = L"http://opcxml.demo-this.com/XmlDaSampleServer/Service.asmx";
ReadItemArguments4Ptr->ItemDescriptor->ItemId = L"Dynamic/Analog Types/Int[]";
CComSafeArray<VARIANT> ArgumentsArray(4);
ArgumentsArray.SetAt(0, _variant_t((IDispatch*)ReadItemArguments1Ptr));
ArgumentsArray.SetAt(1, _variant_t((IDispatch*)ReadItemArguments2Ptr));
ArgumentsArray.SetAt(2, _variant_t((IDispatch*)ReadItemArguments3Ptr));
ArgumentsArray.SetAt(3, _variant_t((IDispatch*)ReadItemArguments4Ptr));
LPSAFEARRAY pArgumentsArray = ArgumentsArray.Detach();
//CComSafeArray<VARIANT> ResultArray(ClientPtr->ReadMultipleItems(&pArgumentsArray));
CComSafeArray<VARIANT> ResultArray;
ResultArray.Attach(ClientPtr->ReadMultipleItems(&pArgumentsArray));
ArgumentsArray.Attach(pArgumentsArray);
for (int i = ResultArray.GetLowerBound(0); i <= ResultArray.GetUpperBound(0); i++)
{
_DAVtqResultPtr DAVtqResultPtr(ResultArray[i]);
_DAVtqPtr DAVtqPtr(DAVtqResultPtr->Vtq);
_variant_t vtqAsString(DAVtqPtr->ToString);
_tprintf(_T("results(%d).Vtq.ToString(): %s\n"), i, vtqAsString.bstrVal);
}
Sleep(50);
[b]MSG msg;
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}[/b]
// Release all interface pointers BEFORE calling CoUninitialize()
ResultArray.Detach();
ResultArray.Destroy();
ArgumentsArray.Detach();
ArgumentsArray.Destroy();
}
ClientPtr = NULL;
CoUninitialize();
Please Log in or Create an account to join the conversation.
Intentionally I ran OPCDA (COM Based) code to show the memory growth in basic OPCDA itself. Otherwise I have modified ReadMulipleItems for OPCXMLDA and introduced delay (Sleep(50)) as mentioned by you.
But, Still memory growth is same.
It has grown 89,752K in 40 minutes, Check attached Performance Counter. And also copying ReadMultipleItems for OPCXMLDA code here.
Thanks.
Code:
// Initialize the COM library
CoInitialize(NULL);
// Instatiate the EasyOPC-DA client object
_EasyDAClientPtr ClientPtr(__uuidof(EasyDAClient));
while(1){
_DAReadItemArgumentsPtr ReadItemArguments1Ptr(_uuidof(DAReadItemArguments));
ReadItemArguments1Ptr->ServerDescriptor->UrlString = L"http://opcxml.demo-this.com/XmlDaSampleServer/Service.asmx";
ReadItemArguments1Ptr->ItemDescriptor->ItemId = L"Dynamic/Analog Types/Double";
_DAReadItemArgumentsPtr ReadItemArguments2Ptr(_uuidof(DAReadItemArguments));
ReadItemArguments2Ptr->ServerDescriptor->UrlString = L"http://opcxml.demo-this.com/XmlDaSampleServer/Service.asmx";
ReadItemArguments2Ptr->ItemDescriptor->ItemId = L"Dynamic/Analog Types/Double[]";
_DAReadItemArgumentsPtr ReadItemArguments3Ptr(_uuidof(DAReadItemArguments));
ReadItemArguments3Ptr->ServerDescriptor->UrlString = L"http://opcxml.demo-this.com/XmlDaSampleServer/Service.asmx";
ReadItemArguments3Ptr->ItemDescriptor->ItemId = L"Dynamic/Analog Types/Int";
_DAReadItemArgumentsPtr ReadItemArguments4Ptr(_uuidof(DAReadItemArguments));
ReadItemArguments4Ptr->ServerDescriptor->UrlString = L"http://opcxml.demo-this.com/XmlDaSampleServer/Service.asmx";
ReadItemArguments4Ptr->ItemDescriptor->ItemId = L"Dynamic/Analog Types/Int[]";
CComSafeArray<VARIANT> ArgumentsArray(4);
ArgumentsArray.SetAt(0, _variant_t((IDispatch*)ReadItemArguments1Ptr));
ArgumentsArray.SetAt(1, _variant_t((IDispatch*)ReadItemArguments2Ptr));
ArgumentsArray.SetAt(2, _variant_t((IDispatch*)ReadItemArguments3Ptr));
ArgumentsArray.SetAt(3, _variant_t((IDispatch*)ReadItemArguments4Ptr));
LPSAFEARRAY pArgumentsArray = ArgumentsArray.Detach();
//CComSafeArray<VARIANT> ResultArray(ClientPtr->ReadMultipleItems(&pArgumentsArray));
CComSafeArray<VARIANT> ResultArray;
ResultArray.Attach(ClientPtr->ReadMultipleItems(&pArgumentsArray));
ArgumentsArray.Attach(pArgumentsArray);
for (int i = ResultArray.GetLowerBound(0); i <= ResultArray.GetUpperBound(0); i++)
{
_DAVtqResultPtr DAVtqResultPtr(ResultArray[i]);
_DAVtqPtr DAVtqPtr(DAVtqResultPtr->Vtq);
_variant_t vtqAsString(DAVtqPtr->ToString);
_tprintf(_T("results(%d).Vtq.ToString(): %s\n"), i, vtqAsString.bstrVal);
}
Sleep(50);
// Release all interface pointers BEFORE calling CoUninitialize()
ResultArray.Detach();
ResultArray.Destroy();
ArgumentsArray.Detach();
ArgumentsArray.Destroy();
}
ClientPtr = NULL;
CoUninitialize();
Please Log in or Create an account to join the conversation.
In the example we are discussing, you can either:
A ) Switch to the multithreaded object concurrency (MTA threads). This can be done by replacing the CoInitialize(...) call with:
CoInitializeEx(NULL, COINIT_MULTITHREADED);
B ) Alternatively, keep using STA, but add pump messaging to the thread loop - e.g.:
MSG msg;
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
In the end, the problem was really in the C/C++ code, and there is nothing wrong with the component. It has worked well from other tools (such as VBScript) because they are not so low-level, and already contain code that handles the thread-model intricacies well. Also, calling some parts of the code (such as OPC XML-DA) could have invoked parts that internally perform message pumping, effectively "healing" the issue as I have described in my earlier post.
The garbage collector was involved indirectly in this; probably what happens is that when the message are not pumped, the COM wrappers around .NET objects do not get a "chance" to actually release their references to .NET objects, making it impossible for the GC to collect them.
Regards
See also:
- support.microsoft.com/en-us/kb/136885
- blogs.msdn.microsoft.com/timng/2006/09/07/com-re-entrancy-and-message-pumping/
Please Log in or Create an account to join the conversation.
Microsoft provides general information about how the GC works, but not every detail of it.
So far I have been able to determine the following facts:
1. It seems to be necessary to give GC some time to actually perform the collection. Having a program that consists of a tight loop (with no idle time) appears to block the GC from performing its task. The code you have sent is an example of that - no wonder it consumes so much CPU, but what's worse, it has this bad effect on the GC. In a loop like this, you definitely should have some idle period, e.g. Sleep(50).
2. Resolving the issue above is necessary, but not always sufficient, to address the memory growth problem. I have found that if you use such code with OPC XML-DA, the memory does *not* grow. In the code you posted, you have switched to OPC DA (COM-based), and for some reason, the memory *does* grow with it. So if, in reality, you need to use OPC XML-DA and the code you have sent was just an experiment, you should be able to switch back to OPC XML-DA, add some idle time to the loop, and it should work.
The reason why the memory grows with OPC DA is unclear to me at the time. It is a mysterious matter, for several reasons:
a) The problem does not appear in .NET programs (C#).
b) The problem does not appear in COM programs written in other tools (e.g. VBScript).
a+b) Therefore it looks like that there is no problem on the component side - it is just the C++ code that has this problem.
c) The problem actually appears already when just the initial objects (DAReadItemArguments) are created (and then released) in a loop, with the rest of the code commented out. But there appears to be no bug in the remaining trivial code. Adding the actual call to EasyDAClient.ReadMultipleItems actually "heals" the problem - but ONLY if all the arguments refer to OPC XML-DA servers.
I will continue investigating this.
Regards
Please Log in or Create an account to join the conversation.
- Forum
- Discussions
- QuickOPC-Classic in COM
- Reading, Writing, Subscriptions, Property Access
- Can Develop OPC XML DA C++ client in Visual Studio 2015?