vscode源碼分析【七】主進程啟動消息通信服務


第一篇: 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


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM