Application.Run(form) 永远不会返回(使用 System::Management 之后)

Posted

技术标签:

【中文标题】Application.Run(form) 永远不会返回(使用 System::Management 之后)【英文标题】:Application.Run(form) never returns (after using System::Management) 【发布时间】:2010-12-02 09:46:54 【问题描述】:

我有一个类库程序集,一旦加载就会打开一个表单 (form1),并在提示 form1 时打开其他表单 (form2)。每个表单都在一个单独的线程中运行,这是因为在每个表单中都运行一个 flashweave 应用程序,并且为了提高性能,我需要在单独的线程中运行它们。 如果我使用用 c# 编写的托管加载器加载库,一切正常。 如果我使用混合的 clr/c++ 程序集加载库,当 form2 关闭时,Application.Run() 不会返回,导致许多线程卡住。 我还尝试使用 Thread.Abort() 强制中止线程,但线程仍然没有中止。 如果我关闭 form1 application.run() 返回并且它的线程可以停止。 我还尝试打开没有任何 flashwave 对象的简单空表单而不是 form2,但它仍然没有返回。

也许问题与我有时收到的这条消息有关:

CLR 无法转换 从 COM 上下文 0x197060 到 COM 上下文 0x196ef0 持续 60 秒。这 拥有目的地的线程 上下文/公寓最有可能 要么进行非抽水等待,要么 处理很长时间的运行 无抽水操作 Windows 消息。这种情况一般有 负面的性能影响,并可能 甚至导致应用程序变成 无响应或内存使用 随着时间的推移不断积累。到 避免这个问题,全单 线程单元 (STA) 线程 应该使用抽水等待原语 (例如 CoWaitForMultipleHandles)和 在很长一段时间内定期发送消息 运行操作。

关于form2打开:

private void OpenTable()

            if (!this.InvokeRequired)
            

                Thread TableRun = new Thread(new ThreadStart(OpenTable));
                TableRun.ApartmentState = ApartmentState.STA;
                TableRun.IsBackground = false;
                TableRun.Name = "T2"; 
                TableRun.Start();

                return;
            
            try
            
                FormTable T = new FormTable(;
                T.MyThread = Thread.CurrentThread;
                Application.Run(T);
            
            catch (Exception ex)
            

            

卡住线程的堆栈跟踪:

ntdll.dll!_ZwWaitForMultipleObjects@20() + 0x15 字节 ntdll.dll!_ZwWaitForMultipleObjects@20() + 0x15 字节 KernelBase.dll!_WaitForMultipleObjectsEx@20() + 0x36 字节 kernel32.dll!_WaitForMultipleObjectsExImplementation@20() + 0x8e 字节 user32.dll!_RealMsgWaitForMultipleObjectsEx@20() + 0xe2 字节 ole32.dll!CCliModalLoop::BlockFn() + 0x96 字节 ole32.dll!_CoWaitForMultipleHandles@20() - 0x51b9 字节 mscorwks.dll!NT5WaitRoutine() + 0x39 字节 mscorwks.dll!MsgWaitHelper() + 0x97 字节 mscorwks.dll!Thread::DoAppropriateAptStateWait() - 0xf32e5 字节 mscorwks.dll!Thread::DoAppropriateWaitWorker() + 0x104 字节 mscorwks.dll!Thread::DoAppropriateWait() + 0x40 字节 mscorwks.dll!CLREvent::WaitEx() + 0x1438a9 字节 mscorwks.dll!CLREvent::Wait() + 0x17 字节 mscorwks.dll!WKS::GCHeap::FinalizerThreadWait() + 0xec 字节 mscorwks.dll!ReleaseRCWsInCaches() + 0xe34fd 字节 mscorwks.dll!ReleaseRCWsInCachesNoThrow() + 0x67 字节 mscorwks.dll!Thread::CleanupCOMState() + 0x1b8f83 字节 mscorwks.dll!Thread::OnThreadTerminate() + 0x46 字节 mscorwks.dll!DestroyThread() + 0x3b 字节 mscorwks.dll!ThreadNative::KickOffThread() + 0xf2 字节 mscorwks.dll!Thread::intermediateThreadProc() + 0x46 字节 kernel32.dll!@BaseThreadInitThunk@12() + 0x12 字节

关于显示第一个线程的代码是:

//c++ code
        Assembly::form^ f= gcnew Assembly::form() ; 
        f->Load();
        gcroot<Assembly::form^>* dsa3_gc_p= new gcroot<Assembly::form^>(f);
        this->obMainLib = (void *)dsa3_gc_p;
//------------------------
//The c++ loader just calls the Load() method
//c#library   
        public void Load()
                
                    FormThread = new Thread(new ThreadStart(this.Start));
                    FormThread.Name = "T7";
                    FormThread.IsBackground = true;
                    FormThread.SetApartmentState(ApartmentState.STA);
                    FormThread.Start();    
                
        private void Start()
                
                    Config = GlobalConfig.GetConfig(GlobalConfig.ConfigurationFile);
                    HttpInterface = new DHttpInterface(Config);
                    Lobby = new FormLobby(HttpInterface, false);


                    WorkerThread = new Thread(new ThreadStart(this.Start));
                    WorkerThread.Name = "T6";
                    WorkerThread.IsBackground = true;
                    WorkerThread.ApartmentState = ApartmentState.STA;
                    WorkerThread.Start();

                    Application.Run(Lobby);
                    Config.SaveToDisk();
                

新闻: 最后我找到了产生这种行为的原因。在实例化 c# 库之前,加载器尝试使用 .net System::Management 获取 cpu 序列号,如果我删除了这样的部分,那么一切正常。 这是有罪的部分:

std::string Loader::GetCPUID()

    std::string lsCPUID = "";
    try
    
        System::Management::ManagementObjectCollection^ moReturn  = nullptr;
        System::Management::ManagementObjectSearcher^ moSearch  ;

        moSearch = gcnew System::Management::ManagementObjectSearcher("Select * from Win32_Processor");

        moReturn = moSearch->Get();
        for each ( System::Management::ManagementObject^ mo in moReturn )
        
            char * chp = (char *) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(mo["ProcessorID"]->ToString()).ToPointer();
            lsCPUID.assign(chp);
        
    
    catch(System::Exception^ ex )
    
    

    return lsCPUID;

谢谢。

【问题讨论】:

T.MyThread 如何不抛出 NullReferenceException? 为什么要这样?但是你可以跳过它......我只是稍后添加它以提供对线程的引用并尝试粗暴地中止它,但它没有工作。 哎呀,我修剪了一些无用的部分,我还修剪了 FormTable 实例化的部分......我要编辑它。 如果您添加用于加载程序集并显示第一个表单的 C++ 代码会很有帮助。 使用调试器的线程窗口找出它在做什么。启用 Microsoft 符号服务器以获得良好的堆栈跟踪。 【参考方案1】:

您是否有可能从 MTA 线程调用 OpenTable,InvokeRequired 返回 true,因此它不是创建另一个 STA 线程,而是直接在 MTA 线程上调用 Application.Run()?

尝试从 STA 线程调用 Application.Run()...

【讨论】:

不,所有线程都是 STA 线程。而且那个线程不能是 MTA 线程,因为它是 form1 的线程。【参考方案2】:

我终于找到了路。我在其中创建了一个新的 AppDomain,我运行了获取 cpu 序列的方法,然后卸载了该域,因此导致问题的 System::Management 库被卸载。

参考类 MarshalByRefType : MarshalByRefObject 上市: // 通过代理调用此方法。

std::string GetCPUID()

    //char * chp = (char *) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(R671::R671::Value()).ToPointer();
    std::string lsCPUID = "";
    //return lsCPUID.assign(chp);
    try
    
        System::Management::ManagementObjectCollection^ moReturn  = nullptr;
        System::Management::ManagementObjectSearcher^ moSearch  ;

        moSearch = gcnew System::Management::ManagementObjectSearcher("Select * from Win32_Processor");

        moReturn = moSearch->Get();
        for each ( System::Management::ManagementObject^ mo in moReturn )
        
            char * chp = (char *) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(mo["ProcessorID"]->ToString()).ToPointer();
            lsCPUID.assign(chp);
        
    
    catch(System::Exception^ ex )
    
    
    AppDomainSetup^ ads = AppDomain::CurrentDomain->SetupInformation;
    String^ str = String::Format("AppName=0, AppBase=1, ConfigFile=2", 
        ads->ApplicationName, 
        ads->ApplicationBase, 
        ads->ConfigurationFile);

    char * chp = (char *) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(str).ToPointer();
    lsCPUID.assign(chp);
    return lsCPUID;

class Loader

  std::string  GetCPUID()
    
        String^ callingDomainName = Thread::GetDomain()->FriendlyName;

        // Construct and initialize settings for a second AppDomain.
        AppDomainSetup^ ads = gcnew AppDomainSetup();
        ads->ApplicationBase = 
            "file:///" +  Assembly::GetExecutingAssembly()->Location;
        ads->DisallowBindingRedirects = false;
        ads->DisallowCodeDownload = true;
        ads->ConfigurationFile = 
            AppDomain::CurrentDomain->SetupInformation->ConfigurationFile;

        // Create the second AppDomain.
        AppDomain^ ad2 = AppDomain::CreateDomain("AD #2", 
            AppDomain::CurrentDomain->Evidence, ads);
        String^ sam = R671::R671::typeid->FullName;
        // Create an instance of MarshalbyRefType in the second AppDomain. 
        // A proxy to the object is returned.
        MarshalByRefType^ mbrt = 
            (MarshalByRefType^) ad2->CreateInstanceFromAndUnwrap(
            Assembly::GetExecutingAssembly()->Location, 
                MarshalByRefType::typeid->FullName
            );
        string lsCPUID;
        //char * chp = (char *) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(mbrt2->Value()).ToPointer();
        lsCPUID = mbrt->GetCPUID();
        try
        
            AppDomain::Unload(ad2);
           
        catch ( AppDomainUnloadedException^ /*e*/ ) 
        

        

        return lsCPUID;
    

    void Load()
    
        //do things
        std:string cpuid = this->GetCPUID();
        //do things
        //load c# library and open forms
    

【讨论】:

以上是关于Application.Run(form) 永远不会返回(使用 System::Management 之后)的主要内容,如果未能解决你的问题,请参考以下文章

WINFORM如何关闭主窗口?

C# 窗口和程序的退出

如何在SysInit._InitExe调用之前放置代码?

Application.Run 用于宏返回数组

C#中两个窗体将相互跳转

Winforms 控制表单