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

C# WCF服务应用程序-使用C++;对象调用导致Visual Basic 6.0 DLL文件挂起,c#,c++,wcf,vb6,C#,C++,Wcf,Vb6,我们目前正在移动一个系统以供使用,但遇到了一个我们无法解决的问题。设置有一个C + dll文件,包一个C++和一个Visual Basic 6 DLL文件。C#DLL文件为这两个对象都提供了包装器,并实例化了这两个对象。C++对象被初始化(从文件中获取数据),然后传递给Visual Basic 6对象,使用C++对象中的数据运行报表。这都是一个WCF服务应用程序,并且在大多数情况下它工作得很好,但是当VisualBasic 6代码调用C++对象中的方法时,整个事情就挂起了。 我只使用一个调用同一

我们目前正在移动一个系统以供使用,但遇到了一个我们无法解决的问题。设置有一个C + dll文件,包一个C++和一个Visual Basic 6 DLL文件。C#DLL文件为这两个对象都提供了包装器,并实例化了这两个对象。C++对象被初始化(从文件中获取数据),然后传递给Visual Basic 6对象,使用C++对象中的数据运行报表。这都是一个WCF服务应用程序,并且在大多数情况下它工作得很好,但是当VisualBasic 6代码调用C++对象中的方法时,整个事情就挂起了。 我只使用一个调用同一个C#DLL文件(在WCF之外)的简单应用程序进行了测试,它可以完美地工作。因此,WCF和C++ DLL文件有一些事情发生,但我们无法确定是什么。我已经将VisualBasic6.0DLL文件更改为使用无人值守运行并存储在内存中(以便能够使用线程),但这似乎并不重要

有没有人有过这样的经历,或者有没有想过为什么会把它挂起来?我的想法是WCF服务以某种方式锁定了DLL文件,这就是为什么当Visual Basic 6.0 DLL文件使用它时,它无法访问它,从而导致它死锁

C++包装器 info对象包含有关Summary对象需要生成的内容的信息。它只在Initialize方法中使用

Visual Basic 6.0对象通过接口加载:

public void LoadPageObject(Application info)
{
    _pageInfo = new PageInformation();
    _pageInfo.oInfo = info;
    _pageInfo.oSummary = _summary;
}
因此,现在VisualBasic6.0对象页面信息具有summary对象

接下来,我们调用该方法来生成报告:

_pageInfo.BuildReport();
这将进入Visual Basic 6.0 DLL文件中,当代码尝试使用summary对象时,它将挂起

// 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中运行。这可能吗


已解决: 我在这个堆栈溢出问题中找到了答案:

这让我想到了这篇文章

基本上,我必须创建一个设置为STA的线程,并在其中运行API(我的C#DLL文件)。由于我使用TaskFactory运行所有这些功能(因此我可以取消调用,并运行多个请求),所以这有点棘手。现在,我仍然能够在MTA中同时运行多个报告,但每个报告都在STA中运行。此外,我也不会失去WCF的取消功能

下面是代码(我还有一些清理工作要做):


我希望这能帮助任何遇到这个问题的人

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

WCF托管在IIS还是Windows服务中?什么版本的windows?发布一些代码片段,说明如何声明和调用C++、VB6对象。WCF托管在IIS、Windows 7上。我将更新帖子以包含一些代码。我将假设您运行的是32位Win7,因为您可以生成64位VB6 dll。我运行的是64位Win7,但项目的都是x86。是的,我的测试应用程序只是一个简单的winform应用程序。如何检查它是否正在MTA线程上调用VB6组件?System.Threading.Thread.CurrentThread.Plament(大致类似)System.Threading.Thread.CurrentThread.GetApartmentState(),明确显示MTA。我使用TaskFactory是为了允许取消服务,但不确定如何使线程切换到STA?一般来说,这是可能的(),但在WCF服务中,要使管道正确运行可能会很棘手。非常好的文章,谢谢。至少现在我知道了问题所在(STA与MTA)。我想知道,既然它是在IIS中运行的,那么它甚至可以在STA中运行吗?
double value = _summary.GetData(string parm1, string parm2);
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);
}