WCF服务应用程序-使用c++对象调用导致Visual Basic 6.0 DLL文件挂起



我们目前正在将一个系统迁移到使用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文件使用它时,它无法访问它,这导致它死锁。

c++包装

    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问题中找到了答案:

如何创建一个WCF服务STA(单线程)

这让我想到了文章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);
}

我希望这能帮助到其他遇到这个问题的人!

VB6 (COM)需要从STA线程运行。您的WCF代码可能在一个或多个MTA线程上调用VB6组件。我打赌你的测试(非WCF)应用程序,一个工作,是一个桌面应用程序。你需要确保VB6组件不是从任意。net线程调用。

最新更新