我们目前正在移动系统以使用WCF并遇到一个我们无法弄清楚的问题。设置是有一个C#DLL文件包装C ++和Visual Basic 6.0 DLL文件。 C#DLL文件具有这两者的包装器,并实例化这两个对象。初始化C ++对象(从文件中获取数据),然后将其传递给Visual Basic 6.0对象,该对象使用C ++对象中的数据运行报表。这一切都发生在WCF服务应用程序中,并且大多数情况下都很好用,但是当Visual Basic 6.0代码调用C ++对象中的方法时,整个事情就会挂起。
我只使用一个调用相同C#DLL文件(在WCF之外)的简单应用程序进行测试,并且它可以正常运行。所以,WCF和那个C ++ DLL文件还有一些东西,但我们无法弄清楚是什么。我已经将Visual Basic 6.0 DLL文件更改为使用运行无人值守和存储在内存中(以便能够使用它),但这似乎并不重要
有没有人对此有任何经验,或者对为什么会挂起来有任何想法?我的想法是WCF服务以某种方式锁定DLL文件,这就是为什么当Visual Basic 6.0 DLL文件使用它时,它无法访问它,导致它死锁。
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;
}
}
info对象包含有关Summary对象需要生成的内容的信息。它仅用于Initialize方法。
Visual Basic 6.0对象通过接口加载:
public void LoadPageObject(Application info)
{
_pageInfo = new PageInformation();
_pageInfo.oInfo = info;
_pageInfo.oSummary = _summary;
}
现在,Visual Basic 6.0对象PageInformation具有摘要对象。
接下来,我们调用该方法来生成报告:
_pageInfo.BuildReport();
这在Visual Basic 6.0 DLL文件中,并且在代码尝试使用摘要对象时,它会挂起
// Omitted actual params for brevity, though all the params exist
double value = oSummary.GetData(string parm1, string parm2)
如果我在C#中使用同一个调用,它会将数据拉回来。
double value = _summary.GetData(string parm1, string parm2);
同样,当我在WCF之外使用这个包装器时,它会很好地完成代码。只有当它在WCF中运行时它才会挂起。
这似乎是在MTA中运行的问题,我不确定在IIS上运行的WCF服务应用程序是否可以设置为在STA中运行。这可能吗?
解决: 我在Stack Overflow问题中找到了答案:
How to make a WCF service STA (single-threaded)
引导我阅读文章 XXX 。
基本上,我必须创建一个设置为STA的线程,并在其中运行API(我的C#DLL文件)。由于我使用TaskFactory运行所有这些(所以我可以取消调用,并运行多个请求),这有点棘手。现在,我仍然可以在MTA中同时运行多个报告,但每个报告都在STA中运行。另外,我也不会失去WCF的取消功能。
这是代码(我还有一些清理工作):
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;
// Build Report
CallBack.SendStatus(OperationId, Status.BuildingReport);
if (!api.BuildReport())
return;
CallBack.SendStatus(OperationId, Status.Completed);
}
catch (OperationCanceledException oc)
{
// Sending this on the method that receives the cancel request, no need to send again
}
catch (Exception ex)
{
// May not be able to use callback if it's a Timeout Exception, log error first
// TODO: Log Error
CallBack.SendMessage(OperationId, MessageType.Error, ex.Message);
CallBack.SendStatus(OperationId, Status.Error);
}
finally
{
Queue.Remove(OperationId);
}
}
}));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
}
我的服务通过以下方式调用:
// 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);
}
我希望这可以帮助遇到此问题的其他任何人!
答案 0 :(得分:1)
VB6(COM)需要从STA线程运行。您的WCF代码可能正在调用一个或多个MTA线程上的VB6组件。我打赌你的测试(非WCF)应用程序,一个有效的,是一个桌面应用程序。您需要确保不从任意.NET线程调用VB6组件。