VSCode源码分析 - 进程通信

目录

进程通信

主进程

src/vs/code/electron-main/main.ts

main.ts在启动应用后就创建了一个主进程 main process,它可以通过electron中的一些模块直接与原生GUI交互。

  1. server = await serve(environmentService.mainIPCHandle);
  2. once(lifecycleService.onWillShutdown)(() => server.dispose());

渲染进程

仅启动主进程并不能给你的应用创建应用窗口。窗口是通过main文件里的主进程调用叫BrowserWindow的模块创建的。

主进程与渲染进程之间的通信

在electron中,主进程与渲染进程有很多通信的方法。比如ipcRenderer和ipcMain,还可以在渲染进程使用remote模块。

ipcMain & ipcRenderer

  • 主进程:ipcMain
  • 渲染进程:ipcRenderer

ipcMain模块和ipcRenderer是类EventEmitter的实例。

在主进程中使用ipcMain接收渲染线程发送过来的异步或同步消息,发送过来的消息将触发事件。

在渲染进程中使用ipcRenderer向主进程发送同步或异步消息,也可以接收到主进程的消息。

  • 发送消息,事件名为 channel .
  • 回应同步消息, 你可以设置 event.returnValue .
  • 回应异步消息, 你可以使用 event.sender.send(…)

创建IPC服务 src/vs/base/parts/ipc/node/ipc.net.ts

这里返回一个promise对象,成功则createServer

  1. export function serve(hook: any): Promise<Server> {
  2. return new Promise<Server>((c, e) => {
  3. const server = createServer();
  4. server.on('error', e);
  5. server.listen(hook, () => {
  6. server.removeListener('error', e);
  7. c(new Server(server));
  8. });
  9. });
  10. }

创建信道

src/vs/code/electron-main/app.ts

  • mainIpcServer

    • launchChannel
  • electronIpcServer

    • updateChannel
    • issueChannel
    • workspacesChannel
    • windowsChannel
    • menubarChannel
    • urlChannel
    • storageChannel
    • logLevelChannel
  1. private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise<Client<string>>): ICodeWindow[] {
  2. // Register more Main IPC services
  3. const launchService = accessor.get(ILaunchService);
  4. const launchChannel = new LaunchChannel(launchService);
  5. this.mainIpcServer.registerChannel('launch', launchChannel);
  6. // Register more Electron IPC services
  7. const updateService = accessor.get(IUpdateService);
  8. const updateChannel = new UpdateChannel(updateService);
  9. electronIpcServer.registerChannel('update', updateChannel);
  10. const issueService = accessor.get(IIssueService);
  11. const issueChannel = new IssueChannel(issueService);
  12. electronIpcServer.registerChannel('issue', issueChannel);
  13. const workspacesService = accessor.get(IWorkspacesMainService);
  14. const workspacesChannel = new WorkspacesChannel(workspacesService);
  15. electronIpcServer.registerChannel('workspaces', workspacesChannel);
  16. const windowsService = accessor.get(IWindowsService);
  17. const windowsChannel = new WindowsChannel(windowsService);
  18. electronIpcServer.registerChannel('windows', windowsChannel);
  19. sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel));
  20. const menubarService = accessor.get(IMenubarService);
  21. const menubarChannel = new MenubarChannel(menubarService);
  22. electronIpcServer.registerChannel('menubar', menubarChannel);
  23. const urlService = accessor.get(IURLService);
  24. const urlChannel = new URLServiceChannel(urlService);
  25. electronIpcServer.registerChannel('url', urlChannel);
  26. const storageMainService = accessor.get(IStorageMainService);
  27. const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService));
  28. electronIpcServer.registerChannel('storage', storageChannel);
  29. // Log level management
  30. const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
  31. electronIpcServer.registerChannel('loglevel', logLevelChannel);
  32. sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel));
  33. ...
  34. // default: read paths from cli
  35. return windowsMainService.open({
  36. context,
  37. cli: args,
  38. forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']),
  39. diffMode: args.diff,
  40. noRecentEntry,
  41. waitMarkerFileURI,
  42. gotoLineMode: args.goto,
  43. initialStartup: true
  44. });
  45. }

每一个信道,内部实现两个方法 listen和call

例如:src/vs/platform/localizations/node/localizationsIpc.ts

构造函数绑定事件

  1. export class LocalizationsChannel implements IServerChannel {
  2. onDidLanguagesChange: Event<void>;
  3. constructor(private service: ILocalizationsService) {
  4. this.onDidLanguagesChange = Event.buffer(service.onDidLanguagesChange, true);
  5. }
  6. listen(_: unknown, event: string): Event<any> {
  7. switch (event) {
  8. case 'onDidLanguagesChange': return this.onDidLanguagesChange;
  9. }
  10. throw new Error(`Event not found: ${event}`);
  11. }
  12. call(_: unknown, command: string, arg?: any): Promise<any> {
  13. switch (command) {
  14. case 'getLanguageIds': return this.service.getLanguageIds(arg);
  15. }
  16. throw new Error(`Call not found: ${command}`);
  17. }
  18. }