有没有办法在 Mac OS X 上的主/子进程之间共享 NSWindows/NSView?

问题描述

我们使用 qt(https://www.qt.io) 来开发应用程序。

Qt 通过传递 QWidget::winId() 来共享小部件(QWidget),接收方调用 QWindow::fromID() 来取回小部件。 像下面的代码

Code A:
    WId winId = this->winId(); //QWidget::winId()
    SendToB(winId)

Code B:
    WId winId = ReceiveFromA()
    QWindow *window = QWindow::fromWinId( winId );
    QWidget* widgetofA = QWidget::createWindowContainer(window,this,Qt::Widget );

然后 QWidget::winId() 在 Windows 操作系统上获取 HWND,这是有效的,因为 Windows 上的 HWND 由操作系统全局管理。所以“代码B”可以从“代码A”中获取HWND/QWidget并渲染进去。

我们的qt app是按照“进程A获取进程B的widget并管理它”的设计构建的。

但在 Mac OS X 上,QWidget::winId() 是通过将 NSView* 的地址作为 QWidget::winId() 传递,并使用 (NSView*)winID 将其转换回 NSView* 来实现的。

这并不优雅,但它确实在相同的过程中起作用。

当然,当“代码A”和“代码B”属于两个主/子进程时。这不起作用。

当我们尝试通过从其他进程的地址转换来访问 NSView* 时,我们的应用程序会崩溃。

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x00007fea7e8d9770
Exception Note:        EXC_CORPSE_NOTIFY

我尝试使用 [NSWindow windowNumber] 和 [[NSApplication sharedApplication] windowWithWindowNumber:windowNumber] 如下:

- (void)applicationDidFinishLaunching()
{
    NSWindow* mainWindow =  [[NSApplication sharedApplication] windows] [0];  //the NSWindow* to be shared.
    mainWindow.sharingType = NSWindowSharingReadWrite; //to allow other process access its content.

    if (fork() > 0 )   //create a sub process
    {
        // the original/main process
        NSInteger windowNumber = mainWindow.windowNumber;
        SendToSubProcess(windowNumber);
    }
    else
    {
        NSInteger windowNumber = ReceiveFromMainProcess();
        NSWindow * receivedWindow = [[NSApplication sharedApplication] windowWithWindowNumber:windowNumber];
        /*
            We Could get the correct NSWindow* here (Only if sharingType has been set to NSWindowSharingReadWrite)
            But we Could not add sub-ui to receivedWindow, otherwise we'll get following error:

            The process has forked and you cannot use this CoreFoundation functionality safely.
            You MUST exec(). Break on __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_
            COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__() to debug.

            so we have to use execv() here
        */

        char        execPath[PATH_MAX];
        uint32_t    execPathSize = sizeof(execPath);
        (void) _NSGetExecutablePath(execPath,&execPathSize);
        const char * args[] = {execPath,"in_subprocess",windowNumber.c_str(),nullptr};
        (void) execv(execPath,(char * const *) args);

        kill(getpid(),SIGKILL);
    }

}


int main(int argc,const char * argv[]) {
    @autoreleasepool {
        if (argc >= 3)
        {
            if (strcmp(argv[1],"in_subprocess") == 0)
            {
                //launched by execv() of sub process
                intptr_t windowNumber = atoll(argv[2]);
                

            
                NSWindow * mainWindow = [[NSApplication sharedApplication] windowWithWindowNumber:windowNumber];
                //mainWindow is nil because it's another NSApplication
                //How Could we shared it here?
                
                return 0;
            }
        }
    }
    return NSApplicationMain(argc,argv);
}
    

我也试过CGWindowXXX函数,还没有结果。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)