第一篇: vscode 源码分析 [一] 从源码运行 vscode
第二篇: vscode 源码分析 [二] 程序的启动逻辑, 第一个窗口是如何创建的
第三篇: vscode 源码分析 [三] 程序的启动逻辑, 性能问题的追踪
第四篇: vscode 源码分析 [四] 程序启动的逻辑, 最初创建的服务
第五篇: vscode 源码分析 [五] 事件分发机制
第六篇: vscode 源码分析 [六] 服务实例化和单例的实现
在 mian.ts 中的 doStartup 方法里, 创建了一个命名管道服务
- (src\vs\code\electron-main\main.ts)
- server = await serve(environmentService.mainIPCHandle);
- once(lifecycleService.onWillShutdown)(() => server.dispose());
传入的 environmentService.mainIPCHandle 是命名管道的识别路径,
一个有规则的字符串, 规则如下:
- function getWin32IPCHandle(userDataPath: string, type: string): string {
- const scope = crypto.createHash('md5').update(userDataPath).digest('hex');
- return `\\\\.\\pipe\\${scope}-${pkg.version}-${type}-sock`;
- }
注意: 每次启动程序, 取这个字符串的时候, 都会获得同样的值 (而且这个值是会被缓存起来的);
以后监听消息, 发送消息, 都根据这个字符串来;
创建服务的代码 (serve):
- export function serve(hook: any): Promise<Server> {
- return new Promise<Server>((c, e) => {
- const server = createServer();
- server.on('error', e);
- server.listen(hook, () => {
- server.removeListener('error', e);
- c(new Server(server));
- });
- });
- }
这个方法返回了一个 Promise 的对象,
c 和 e 是 Promise 的参数, c 代表成功时的回调, e 代表失败时的回调 (有点类似 es6 的 Promise)
匿名函数内 createServer 就是 Node.JS 里的原生接口,
Server 类绑定了连接和断开的事件, 暂时不细说;
回头看看 main.ts startup 方法里有这么一句:
instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup();
这句显然是创建了 CodeApplication 的实例, 然后执行了实例的 startup 方法
注意: 创建这个实例的时候, 把我们前面创建的 mainIpcServer 传递进去了;
CodeApplication(src\vs\code\electron-main\App.ts) 的 startup 方法, 还启动了 Electron 的 IPCServer
const electronIpcServer = new ElectronIPCServer();
vscode 把 electron 默认的通信机制也接入到了自己的事件体系内, 有消息过来, 会触发事件;
具体先不细说, 后面再讲.
接着就跳转到同类型里的 openFirstWindow 方法 (是不是很熟悉, 我们在第一篇文章中讲到过这里)
在这里, 给这两个服务 (mainIpcServer 和 electronIpcServer ), 创建了一堆信道:
- const launchService = accessor.get(ILaunchService);
- const launchChannel = new LaunchChannel(launchService);
- this.mainIpcServer.registerChannel('launch', launchChannel);
- const updateService = accessor.get(IUpdateService);
- const updateChannel = new UpdateChannel(updateService);
- electronIpcServer.registerChannel('update', updateChannel);
- const issueService = accessor.get(IIssueService);
- const issueChannel = new IssueChannel(issueService);
- electronIpcServer.registerChannel('issue', issueChannel);
- const workspacesService = accessor.get(IWorkspacesMainService);
- const workspacesChannel = new WorkspacesChannel(workspacesService);
- electronIpcServer.registerChannel('workspaces', workspacesChannel);
- const windowsService = accessor.get(IWindowsService);
- const windowsChannel = new WindowsChannel(windowsService);
- electronIpcServer.registerChannel('windows', windowsChannel);
- sharedProcessClient.then(client => client.registerChannel('windows', windowsChannel));
- const menubarService = accessor.get(IMenubarService);
- const menubarChannel = new MenubarChannel(menubarService);
- electronIpcServer.registerChannel('menubar', menubarChannel);
- const urlService = accessor.get(IURLService);
- const urlChannel = new URLServiceChannel(urlService);
- electronIpcServer.registerChannel('url', urlChannel);
- const storageMainService = accessor.get(IStorageMainService);
- const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService as StorageMainService));
- electronIpcServer.registerChannel('storage', storageChannel);
- const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
- electronIpcServer.registerChannel('loglevel', logLevelChannel);
有存储, 日志, 菜单栏, 工作台, 升级..... 等等
主要的通信还是用 electronIpcServer 来干的, mainIpcServer 只有一个 launch 信道;
下面我们看看消息是怎么传递的
我们随便打开一个信道的类型 (src\vs\platform\Windows\node\windowsIpc.ts)
它有两个主要的函数, listen 和 call,
- listen(_: unknown, event: string): Event<any> {
- switch (event) {
- case 'onWindowOpen': return this.onWindowOpen;
- case 'onWindowFocus': return this.onWindowFocus;
- case 'onWindowBlur': return this.onWindowBlur;
- case 'onWindowMaximize': return this.onWindowMaximize;
- case 'onWindowUnmaximize': return this.onWindowUnmaximize;
- case 'onRecentlyOpenedChange': return this.onRecentlyOpenedChange;
- }
- throw new Error(`Event not found: ${event}`);
- }
- call(_: unknown, command: string, arg?: any): Promise<any> {
- switch (command) {
- case 'pickFileFolderAndOpen': return this.service.pickFileFolderAndOpen(arg);
- case 'pickFileAndOpen': return this.service.pickFileAndOpen(arg);
- case 'pickFolderAndOpen': return this.service.pickFolderAndOpen(arg);
- case 'pickWorkspaceAndOpen': return this.service.pickWorkspaceAndOpen(arg);
- case 'showMessageBox': return this.service.showMessageBox(arg[0], arg[1]);
- case 'showSaveDialog': return this.service.showSaveDialog(arg[0], arg[1]);
- case 'showOpenDialog': return this.service.showOpenDialog(arg[0], arg[1]);
- //......
消息来了, 进入 listen 函数, 发送消息, 进入 call 函数;
注意, 消息来了, 触发的也不是他自己的方法, 我们看看它的构造函数:
- constructor(private service: IWindowsService) {
- this.onWindowOpen = Event.buffer(service.onWindowOpen, true);
- this.onWindowFocus = Event.buffer(service.onWindowFocus, true);
- this.onWindowBlur = Event.buffer(service.onWindowBlur, true);
- this.onWindowMaximize = Event.buffer(service.onWindowMaximize, true);
- this.onWindowUnmaximize = Event.buffer(service.onWindowUnmaximize, true);
- this.onRecentlyOpenedChange = Event.buffer(service.onRecentlyOpenedChange, true);
- }
看到没, 触发的其实是一个事件, 事件是关联到 service 实例的;
这个实例是这样创建的:
const windowsService = accessor.get(IWindowsService);
具体的代码在: src\vs\platform\Windows\electron-browser\windowsService.ts
来源: https://www.cnblogs.com/liulun/p/11050725.html