第一篇: 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就是nodejs裡的原生介面,
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