We are currently moving the system to use WCF and are facing a problem that we cannot understand. In the configuration, there is a C # DLL file that wraps C ++ and a Visual Basic 6.0 DLL file. The C # DLL file has wrappers for both of them and instantiates both objects. The C ++ object is initialized (captures data from files) and then passed to the Visual Basic 6.0 object, which launches the report using the data in the C ++ object. It all happens as a WCF service application, and for the most part it works fine, but when Visual Basic 6.0 code calls a method in a C ++ object, it all freezes.
I tested using a simple application that calls the same C # DLL file (outside of WCF) and it works flawlessly. So, something is happening with WCF and this C ++ DLL file, but we cannot understand that. I changed the Visual Basic 6.0 DLL file to use Run Unattended and Store in Memory (to use it in streaming mode), but that does not seem to matter.
Does anyone have experience with this, or have any thoughts on why it will be hanging? My idea is that the WCF service somehow blocks the DLL file, and therefore, when the Visual Basic 6.0 DLL file uses it, it cannot access it, which leads to its blocking.
C ++ Wrapper
public interface ISummaryWrapper { void LoadInfo(Application info); SummaryApp GetSummary(); } public class SummaryWrapper : ISummaryWrapper { private SummaryApp _summary; public SummaryWrapper() { _summary = new SummaryApp(); } public SummaryWrapper(Application info) { _summary = new SummaryApp(); LoadInfo(info); } public void LoadInfo(Application info) { _summary.Initialize(info); } public SummaryApp GetSummary() { return _summary; } }
The information object contains information about what the Summary object should generate. It is used only in the Initialize method.
A Visual Basic 6.0 object is loaded through the interface:
public void LoadPageObject(Application info) { _pageInfo = new PageInformation(); _pageInfo.oInfo = info; _pageInfo.oSummary = _summary; }
So now the Visual Basic 6.0 page object has a summary object.
Then we call the method to create the report:
_pageInfo.BuildReport();
This is included in the Visual Basic 6.0 DLL file, and the moment the code tries to use the resulting object, it freezes
If I use the same call in C #, it just restores the data.
double value = _summary.GetData(string parm1, string parm2);
Again, when I use this shell outside of WCF, it goes through the code in order. This is only when he works in the WCF, which he hangs.
It seems that the problem is running in the MTA, and I'm not sure if the WCF service application running in IIS can be configured to run in the STA. Is it possible?
SOLVE: I found the answer in this stack overflow question:
How to make a WCF STA service (single-threaded)
Which led me to article XXX .
Basically, I had to create a thread that is installed in the STA and run the API (my C # DLL file) in it. Since I run all of this with TaskFactory (so that I can cancel calls and run multiple queries), it was a bit complicated. Now I have the ability to run multiple reports at the same time in the MTA, but each report runs in the STA. In addition, I do not lose the undo function from WCF.
Here is the code (I have a cleanup to do everything still):
public class Builder { public string OperationId { get; set; } public IServiceCallback CallBack { get; set; } public Dictionary<string, CancellationTokenSource> Queue { get; set; } public void BuildReport() { OperationContext context = OperationContext.Current; Thread thread = new Thread( new ThreadStart( delegate { using (OperationContextScope scope = new OperationContextScope(context)) { try { CancellationToken token = Queue[OperationId].Token; CallBack.SendStatus(OperationId, Status.Processing); IAPI api = new API(token); api.MessagingEvents += MessageEvent;
And my service calls this via:
// I initialize taskfactory when the service is created, omitting other code for brevity public void BuildReport(ReportRequest request) { CallBack.SendReportStatus(request.OperationId, Status.Received); CancellationTokenSource cancelSource = new CancellationTokenSource(); Queue.Add(request.OperationId, cancelSource); Builder builder = new Builder { OperationId = request.OperationId, CallBack = CallBack, Queue = _queue }; _taskFactory.StartNew(builder.BuildReport, cancelSource.Token); }
I hope this helps anyone who encounters this problem!