
おそらく注目されるのは、ソフトウェアの配布部分でしょう。
🌟マークのところですよ。
Web制作の技術は幅広く使えるんですよね。
このページの主旨は「HTML・CSS・JavaScriptでパソコンソフトを作ろう」というものです。
その気になればいくらでも機能拡張できますし、ちょっとした便利ソフトくらいなら、わりと簡単に作れるもんですよ?
それでは、まず私が作った「フローティング・ランチャー」をご紹介した後、実際の手順とソースコードへ話題を移します。
私は整理されたデスクトップが好きです。
ある日、壁紙を邪魔しないランチャーソフトが欲しくなったのですが、フリーソフトで「これだ!」と思うソフトを見つけられませんでした😵💫
ずっとLinuxを使っていて、Windowsが久しぶりだったこともあってね…
そこで、自分でこういうソフトを作っちゃったのですよ。
これはWindows11のデスクトップです。
壁紙はアナザーエデンというゲームのものですよ。
綺麗でしょう?
ちょっとランチャーソフトだけを拡大しましょうか。
こういう感じの、テキストベースのフローティング・ランチャーです。
でも最前面には表示させず、あくまでも自分が使っているソフトが主体となるようにしてます。
また、Windows・Mac・Linuxを問わず動作します。
元がWeb技術だからさ。
新しくLinuxをインストールしても、このソフトは活躍してくれるでしょう。
自作ソフトをスタートアップに登録する
何も自作ソフトに限ったことではありませんが、Windows11の起動時にソフトを起動するには、次のようにします。
これで次回Windowsを起動すると、fein_launcher.exe によってフローティング・ランチャーが自動的に起動するようになります。
では、そろそろ実際の作り方へ話題を移しましょう。
いろんな方法があるのですが、私は次のようなやり方をしています。
上記3つを使ってソフトを作る手順は、次の通りです。
初めてやる時にありがちなつまづきポイントは「環境構築」です。
勉強すれば、ある程度のコードを書くくらいなら難しくないんですよね。
だからレンタルサーバーにちょっとしたホームページを作るくらいなら、わりと手軽にできちゃいます。
でも環境構築まで必要な開発になってくると、なかなか厳しい状況もあるでしょう。
このページではそういう部分にも解説を添えつつ、簡単なフローティング・ランチャーを作っていきます。
ではいよいよソースコードを書いていきますが、その前に一言形式的なご挨拶をしておきますね。
当Webサイト作成者は、例示を目的としてマークアップ及びプログラミング例を提供しており、明示または黙示にかかわらず、いかなる責任も負わないものとします。
このページは、説明されているマークアップ及びプログラミング言語、手順を作成およびデバッグするために使用される各種ツールに読者が精通していることを前提にしています。
このページは、特定の機能を説明するのに役立つ可能性がありますが、当Webサイト作成者がこれらの例を変更した上で、特定の要件を満たすために追加の機能を提供したり、システムを構築したりすることはできません。
加えて、この例の手順に従う場合は、読者の各種ファイルを事前にバックアップすることを推奨いたします。
まずはNode.jsをインストールします。
次はnpmの確認です。
node -v
npm -v
これでNode.jsとnpmが正しくインストールされたことを確認できます。
このページでは、Node.jsとElectronを使ってデスクトップアプリを作ろうとしています。
Node.jsとnpmの関係性、そしてそれがなぜパソコンソフトを作れることに繋がるのか、ちょっと説明しましょう。
Node.jsとnpmを使うことで、JavaScriptを使って様々な種類のソフトウェアを作ることができます。
Webアプリケーションはもちろんのこと、デスクトップアプリケーションや、サーバー上で動作するプログラムも作成可能です。
ちょっと紛らわしいように思えるけど、なにもパソコンがサーバーになるわけではありません。
Node.jsで作成したプログラムは、様々な場所で実行することができるのです。
このページでは、上記で言うところのデスクトップアプリケーションを作ろうとしているんですよね。
プロジェクトなんて言うと大袈裟に聞こえますが、実際にこう呼ばれます。
ここでやるのは、要するにソフトを作る場所を用意しましょうという、それだけですよ。
きちんと言うなら、ここでやるのは新しいNode.jsプロジェクトを始めるための初期設定を行う手順です。
引き続き、コマンドプロンプトでの作業です。
mkdir fein_launcher
cd fein_launcher
npmプロジェクトの初期化
npm init -y
npm init -yは、Node.jsのプロジェクトを初期化するためのコマンドです。
このコマンドを実行すると、新しいプロジェクト用の package.json ファイルが自動的に作成されます。
このファイルはプロジェクトの設定情報や依存関係を管理するために使用されます。
ひとまず、これでプロジェクトを始めるための基盤が整いました。
次は、このプロジェクトに必要なライブラリやフレームワークをインストールして、実際の開発を進めることができます。
npmプロジェクトの初期化Node.jsプロジェクトを整理し、管理するための重要なステップです。
初期化(init)という言葉は、これらの設定を最初に行うことを意味しています。
このステップをスキップすると、依存関係やプロジェクトの設定を正しく管理することが難しくなってしまうんです。
久しぶりにやったんだけど、やけに時間がかかりましたねー。
実際にチャレンジされる方は、このコマンドを入れてからお風呂に入ると良いかも?
npm install electron --save-dev
このElectronがあることで、HTML、CSS、JavaScriptでデスクトップアプリを作れるのです。
個人サイトをそのまんまアプリにする方法というページでは、普通のホームページをプログレッシブウェブアプリとして構築し、いろんなパソコンやスマホにインストールできるようにしました。
似たような雰囲気で、─ だいぶ違うけど。雰囲気を掴んでもらえれば… ─ 普通のパソコンの普通のソフトウェアだって作れてしまいます。
こうしてみると、HTML、CSS、JavaScriptはとっても汎用性が高いと感じますね。
Electron は GitHub(Electron
GitHub
リポジトリ)が開発したオープンソースのフレームワークです。
Electron を使うことで、Web 技術 (HTML, CSS, JavaScript) を使って Windows, macOS, Linux 向けのデスクトップアプリケーションを開発することができます。
Electron 製のアプリケーションとして有名なのは、このあたりでしょうかね。
ElectronはWeb開発の知識があればデスクトップアプリケーション開発を始められるという点で、多くの開発者に利用されています。
私も学生時代にサイト制作とエレクトロンを行き来したもんですわ。
現在のWindowsでプリインストールされているのはMicrosoft Edgeなので、このブラウザについても多少は理解を深める必要があります。
Microsoft Edgeは、Chromiumベースのバージョン(Chromium
Edge)と、独自のEdgeHTMLエンジンを使ったバージョンがあります。最新のEdgeはChromiumベースであり、基本的にはChromiumと同じ技術を使用しています。
Electron自体は、特定のChromiumバージョンを使って最適化されているため、Node.jsとEdge(特にChromiumベースのEdge)を使うというよりは、Electronが内部で適切にChromiumを扱う形で使用するのです。
fein_launcherというフォルダの下に、次のようにファイルとフォルダを作ります。
それぞれのファイルに、次のような役割を持たせていきます。
ちなみに、ここで紹介しているフローティング・ランチャーくらいであればpreload.jsの記述までは必要ありません。
ローカルで使用する単純なランチャーアプリですからね。
ただし、後々、アプリに追加機能を追加したくなった場合や、他のプロジェクトに拡張したい場合には、preload.js
を使ってセキュリティや機能性を向上させることができますよ?
では、実際のコードを書いていきます。
このサイトではhtmlの意味までは掲載していません。
次のコードをご覧いただければ分かるように、さほど難しいコードではないです。
少し余計な記述もあるんだけど、これは私が後日ランチャーを拡張したくなったときのために書いてあるだけです。
ユーザーインターフェースを定義するindex.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Start Menu</title>
<link rel="stylesheet" href="style.css">
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self'">
</head>
<body>
<div id="start-menu">
<button id="start-button">fein menu</button>
<div id="menu-content" class="hidden">
<ul>
<li>Visual Atelier Code
<ul>
<li><a href="#" onclick="openVSCode()">VS code</a></li>
<li><a href="#" onclick="openFeinsApp()">fein's app</a></li>
<li><a href="#" onclick="openFeinsPortal()">fein's portal</a></li>
</ul>
</li>
<li><a href="#" onclick="openEdge()">Microsoft Edge</a></li>
<li>Another Eden
<ul>
<li><a href="#"
onclick="openFile('C:\\Users\\ユーザー名\\OneDrive\\_fein\\google cloud\\web tools\\visual_studio_code\\anothereden.txt')">anothereden.txt</a>
</li>
<li><a href="#"
onclick="openFile('C:\\Users\\ユーザー名\\OneDrive\\_fein\\google cloud\\web tools\\visual_studio_code\\document.txt')">document.txt</a>
</li>
<li><a href="#"
onclick="openFile('C:\\Users\\ユーザー名\\OneDrive\\_fein\\google cloud\\web tools\\visual_studio_code\\sourcecode.txt')">sourcecode.txt</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
<script src="renderer.js"></script>
</body>
</html>
ただ、onclick="openFeinsApp()"とか、JavaScriptと繋がっている部分についてはけっこう大切な部分なので、見ておいていただけると。
上述していますが、ここでご紹介しているソフトウェアの基礎構造は、ファイルの整理とプロジェクトの構造を明確に保つためによく行われる方法です。
renderer.js、index.html、style.css などのファイルはアプリのUI(レンダラー)に関連するものであり、これらを src フォルダにまとめることで、プロジェクトの構造が整理され、理解しやすくなります。
main.js はElectronアプリのメインプロセスを担当するファイルです。アプリ起動時に最初に実行されるため、ルートディレクトリに置かれます。
package.json はプロジェクトの設定ファイルであり、依存関係やスクリプト、メタデータを含んでいます。
npmや他のツールによる管理が容易になるため、ルートディレクトリに配置されます。
preload.js はレンダラープロセスとメインプロセスの間でデータをやり取りするためのスクリプトです。
セキュリティの理由からも、ルートディレクトリに置かれることが多いです。
ユーザーの操作や表示を制御するrenderer.js
const { exec } = require('child_process');
function openVSCode() {
exec('"C:\\Users\\ユーザー名\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe"', (error, stdout, stderr) => {
if (error) {
console.error(`Error opening VS Code: ${error.message}`);
return;
}
console.log(`VS Code output: ${stdout}`);
});
}
function openEdge() {
exec('"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"', (error, stdout, stderr) => {
if (error) {
console.error(`Error opening Microsoft Edge: ${error.message}`);
return;
}
console.log(`Microsoft Edge output: ${stdout}`);
});
}
function openFile(filePath) {
const command = `start "" "${filePath}"`;
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error opening file ${filePath}: ${error.message}`);
return;
}
console.log(`File output: ${stdout}`);
});
}
function openFeinsApp() {
window.open('https://feinatelier.org/', '_blank');
}
function openFeinsPortal() {
const command = '"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe" https://portal.feinatelier.org/';
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error opening Fein's Portal: ${error.message}`);
return;
}
console.log(`Fein's Portal output: ${stdout}`);
});
}
console.log('Script loaded');
const startButton = document.getElementById('start-button');
const menuContent = document.getElementById('menu-content');
console.log(startButton);
startButton.style.position = 'fixed';
startButton.style.right = '10px';
startButton.style.top = '10px';
startButton.addEventListener('click', () => {
console.log('Start button clicked');
if (menuContent.classList.contains('hidden')) {
menuContent.classList.remove('hidden');
menuContent.style.display = 'block';
} else {
menuContent.classList.add('hidden');
menuContent.style.display = 'none';
}
});
document.querySelectorAll('#menu-content > ul > li').forEach((item) => {
const subMenu = item.querySelector('ul');
if (subMenu) {
item.addEventListener('click', (event) => {
event.stopPropagation();
if (subMenu.style.display === 'none' || !subMenu.style.display) {
subMenu.style.display = 'block';
} else {
subMenu.style.display = 'none';
}
});
}
});
このコードは、いくつかのアプリケーションを開いたり、ボタンの動作を制御するためのJavaScriptです。
1. モジュールのインポート
const { exec } = require('child_process');
ここでは、child_processモジュールのexec関数をインポートしています。
child_processモジュールは、Node.jsの標準モジュールの一つで、外部プログラムを実行するためのものです。
このモジュールを使うことで、Node.jsからシェルコマンドや他のスクリプト、実行可能ファイルを呼び出すことができます。
その中でも、exec関数は指定されたコマンドを実行し、その結果をコールバック関数に渡すために使われます。
例えば、他のプログラムやスクリプトを実行したり、OSのコマンドを実行する際に利用されます。
ここでは、次のような役割を持っています。
ランチャーらしいでしょ?
child_processモジュールを使うことで、Node.jsアプリケーションからさまざまなシステム操作を簡単に行うことができるのです。
2. VS Codeを開く関数
この関数はVisual Studio Codeを開きます。
エラーが発生した場合はエラーメッセージを表示します。
Ⅰ. 関数の定義
function openVSCode() {
openVSCodeという名前の関数を定義しています。
この関数はVisual Studio Codeを開くために使います。
Ⅱ. exec関数の呼び出し
exec('"C:\\Users\\ユーザー名\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe"', (error, stdout, stderr) => {
Ⅲ. コールバック関数
exec関数の第2引数としてコールバック関数を渡しています。
このコールバック関数は、コマンドの実行が終了したときに呼び出されます。コールバック関数は3つの引数を受け取ります。
Ⅳ. エラーチェック
if (error) {
console.error(`Error opening VS Code: ${error.message}`);
return;
}
エラーが発生した場合、そのエラーメッセージをコンソールに出力し、関数の実行を終了します(return)。
Ⅴ. 標準出力のログ出力
console.log(`VS Code output: ${stdout}`);
エラーが発生しなかった場合、Visual Studio Codeの実行結果(標準出力)をコンソールに出力します。
この関数を実行すると、指定されたパスにあるVisual Studio Codeが起動されます。
エラーが発生した場合はエラーメッセージが表示され、エラーが発生しなかった場合はVS Codeの標準出力がコンソールに表示されます。
3. Edgeを開く関数
function openEdge() {
exec('"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"', (error, stdout, stderr) => {
if (error) {
console.error(`Error opening Microsoft Edge: ${error.message}`);
return;
}
console.log(`Microsoft Edge output: ${stdout}`);
});
}
この関数はMicrosoft Edgeを開きます。
動作は上記のVS Codeの関数と同様です。
4. ファイルを開く関数
function openFile(filePath) {
const command = `start "" "${filePath}"`;
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error opening file ${filePath}: ${error.message}`);
return;
}
console.log(`File output: ${stdout}`);
});
}
この関数は指定されたファイルを開きます。
filePathには開きたいファイルのパスを入力します。
5. fein's appを新しいタブで開く関数
function openFeinsApp() {
window.open('https://feinatelier.org/', '_blank');
}
この関数は、指定されたURLを新しいタブで開きます。
6. fein's PortalをEdgeで開く関数
function openFeinsPortal() {
const command = '"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe" https://portal.feinatelier.org/';
exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Error opening Fein's Portal: ${error.message}`);
return;
}
console.log(`Fein's Portal output: ${stdout}`);
});
}
この関数はfein's PortalをMicrosoft Edgeで開きます。
7. ボタンとメニューの動作
ここがちょっと長いですね。
主にいくつかのアプリケーションを開いたり、Webサイトを表示したりするための関数と、ボタンをクリックしたときの動作を設定するものです。
概要としては、次の通りです。
Ⅰ. HTML要素の取得
const startButton = document.getElementById('start-button');
const menuContent = document.getElementById('menu-content');
ここでは、HTMLドキュメントからstart-buttonとmenu-contentというIDを持つ要素を取得しています。
Ⅱ. ボタンの位置の設定
startButton.style.position = 'fixed';
startButton.style.right = '10px';
startButton.style.top = '10px';
取得したボタン(startButton)の位置を画面の右上に固定しています。
Ⅲ. ボタンのクリックイベントの設定
startButton.addEventListener('click', () => {
if (menuContent.classList.contains('hidden')) {
menuContent.classList.remove('hidden');
menuContent.style.display = 'block';
} else {
menuContent.classList.add('hidden');
menuContent.style.display = 'none';
}
});
Ⅳ. サブメニューのクリックイベントの設定
document.querySelectorAll('#menu-content > ul > li').forEach((item) => {
const subMenu = item.querySelector('ul');
if (subMenu) {
item.addEventListener('click', (event) => {
event.stopPropagation();
if (subMenu.style.display === 'none' || !subMenu.style.display) {
subMenu.style.display = 'block';
} else {
subMenu.style.display = 'none';
}
});
}
});
このようにして、ボタンのクリックに応じてメニューの表示/非表示を制御し、さらにリスト項目をクリックするとそのサブメニューの表示/非表示も切り替えることができるようになります。
こんな感じかなー
あんまり長々と説明しててもね。
このフローティング・ランチャーもバージョンアップを重ねていくでしょうから、その折にまた再度書きましょうか。
あるいはこのWebサイトのように、軽いアニメーションでも付けようかと思っています。
でもWindows11のスタートボタンもアニメーションはほとんど付いていないし、悩ましいところです。
ランチャーソフトの見た目を設定するstyle.css
/* 全体の幅と高さを100%に設定し、余白とオーバーフローをリセット */
body,
html {
width: 100%; /* 要素の幅を100%に設定 */
height: 100%; /* 要素の高さを100%に設定 */
margin: 0; /* 余白を0に設定 */
overflow: hidden; /* 要素のコンテンツがはみ出さないようにする */
background: transparent; /* 背景を透明に設定 */
font-size: 12px; /* フォントサイズを12pxに設定 */
font-family: 'Lucida Handwriting', cursive; /* フォントファミリーを設定 */
color: #0078D4; /* 文字色を設定 */
}
/* body要素のスタイル設定 */
body {
border-radius: 15px; /* 角を15pxの半径で丸くする */
overflow: hidden; /* コンテンツがはみ出さないようにする */
border: 1px solid rgba(0, 120, 215, 0.5); /* 半透明の1pxの境界線を設定 */
box-sizing: border-box; /* ボーダーとパディングを含めたボックスサイズを設定 */
}
/* #start-button要素のスタイル設定 */
#start-button {
width: 120px; /* ボタンの幅を120pxに設定 */
height: 30px; /* ボタンの高さを30pxに設定 */
background-color: rgba(144, 238, 144, 0.4); /* 半透明の背景色を設定 */
border-radius: 30px / 15px; /* 楕円形の角を設定 */
border: none; /* 境界線をなしに設定 */
color: white; /* 文字色を白に設定 */
font-size: 12px; /* フォントサイズを12pxに設定 */
font-family: 'Lucida Handwriting', cursive; /* フォントファミリーを設定 */
cursor: pointer; /* マウスオーバー時のカーソルを指ポインターに設定 */
display: flex; /* フレックスボックスで配置 */
align-items: center; /* 垂直方向に中央揃え */
justify-content: center; /* 水平方向に中央揃え */
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); /* 半透明の影を設定 */
position: fixed; /* スクロールしても固定位置 */
right: 10px; /* 右から10pxの位置に設定 */
top: 10px; /* 上から10pxの位置に設定 */
}
/* #start-buttonのホバー時のスタイル設定 */
#start-button:hover {
background-color: rgba(102, 205, 170, 0.4); /* ホバー時の背景色を設定 */
}
/* #menu-content要素のスタイル設定 */
#menu-content {
width: calc(100% - 40px); /* 幅を計算して設定 */
background-color: rgba(173, 216, 230, 0.2); /* 半透明の背景色を設定 */
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1); /* 半透明の影を設定 */
padding: 10px; /* 内側の余白を10pxに設定 */
position: absolute; /* 絶対位置に設定 */
top: 60px; /* 上から60pxの位置に設定 */
right: 10px; /* 右から10pxの位置に設定 */
display: none; /* 非表示に設定 */
border-radius: 15px; /* 角を15pxの半径で丸くする */
border: none; /* 境界線をなしに設定 */
font-family: 'Lucida Handwriting', cursive; /* フォントファミリーを設定 */
}
/* 非表示要素の設定 */
.hidden {
display: none; /* 非表示に設定 */
}
/* #menu-content内のul要素のスタイル設定 */
#menu-content ul {
list-style: none; /* リストスタイルをなしに設定 */
padding: 0; /* 内側の余白を0に設定 */
margin: 0; /* 外側の余白を0に設定 */
font-family: 'Lucida Handwriting', cursive; /* フォントファミリーを設定 */
}
/* #menu-content内のli要素のスタイル設定 */
#menu-content li {
padding: 5px; /* 内側の余白を5pxに設定 */
cursor: pointer; /* マウスオーバー時のカーソルを指ポインターに設定 */
}
/* 入れ子になったul要素のスタイル設定 */
#menu-content ul ul {
padding-left: 20px; /* 左側の余白を20pxに設定 */
display: none; /* 非表示に設定 */
}
/* a要素のスタイル設定 */
a {
color: #4285F4; /* リンクの文字色を設定 */
text-decoration: none; /* 下線をなしに設定 */
font-family: 'Lucida Handwriting', cursive; /* フォントファミリーを設定 */
}
/* a要素のホバー時のスタイル設定 */
a:hover {
color: #0078D4; /* ホバー時の文字色を設定 */
text-decoration: underline; /* 下線を表示 */
font-family: 'Lucida Handwriting', cursive; /* フォントファミリーを設定 */
}
特別なことはやっていないです。
フォントはちょっと気分で筆記体のものを入れてみたんだけど、あるいはまた変えようかしら?
まだバージョン1ですから、これからいろいろ追記できる余地も欲しかったんですよね。
このコードはElectronを使用してシンプルなデスクトップアプリケーションを作成し、そのウィンドウの動作を制御します。
エレクトロンアプリ全体の動作を制御するmain.js
const { app, BrowserWindow } = require('electron');
const path = require('path');
function createWindow() {
const { screen } = require('electron');
const display = screen.getPrimaryDisplay();
const { width, height } = display.workAreaSize;
let win = new BrowserWindow({
width: width * 0.2,
height: height * 0.5,
x: width - width * 0.2,
y: 0,
frame: false,
transparent: true,
hasShadow: false,
alwaysOnTop: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false,
}
});
win.loadFile('src/index.html');
win.setBackgroundColor('#00000000');
win.on('closed', () => {
win = null;
app.quit();
});
}
app.on('window-all-closed', () => {
app.quit();
});
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
Ⅰ. モジュールのインポート
const { app, BrowserWindow } = require('electron');
const path = require('path');
pathモジュールはファイルやディレクトリのパスを操作するためのものです。
Ⅱ. ウィンドウを作成する関数の定義
function createWindow() {
const { screen } = require('electron');
const display = screen.getPrimaryDisplay();
const { width, height } = display.workAreaSize;
let win = new BrowserWindow({
width: width * 0.2,
height: height * 0.5,
x: width - width * 0.2,
y: 0,
frame: false,
transparent: true,
hasShadow: false,
alwaysOnTop: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false,
}
});
win.loadFile('src/index.html');
win.setBackgroundColor('#00000000');
win.on('closed', () => {
win = null;
app.quit();
});
}
Ⅲ. 全てのウィンドウが閉じられたときの処理
app.on('window-all-closed', () => {
app.quit();
});
全てのウィンドウが閉じられたら、アプリケーションを終了します。
Ⅳ. アプリケーションの準備ができたときの処理
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
main.js はElectronアプリケーションのメインプロセス (main process) を制御するファイルです。
Electronアプリでは、アプリケーションの全体的なライフサイクル管理やウィンドウの作成などの重要な部分がメインプロセスで行われます。
一方、renderer.js はレンダラープロセス (renderer process)
のスクリプトであり、各ウィンドウ(またはウェブページ)の動作を制御します。
レンダラープロセスは基本的にブラウザ環境で動作し、ユーザーインターフェイスやDOM操作などを担当します。
ここで、それぞれの役割と違いについて詳しく説明しておきますね。
コード本体より、この考え方のほうが重要と思うから。
main.jsが必要な理由があるんですよね。
これは普通の、巷で言うところの「ホームページ」にあるJavaScriptと違う側面でしょう。
この役割分担により、Electronアプリは効率的かつ安全に動作します。
renderer.jsだけではアプリケーションの全体的な管理ができないため、main.jsが必要になります。
ノード統合(Node
Integration)とは、ElectronアプリケーションのレンダラープロセスでNode.jsの機能を使用できるようにする設定です。
通常、ブラウザ環境ではNode.jsの機能を直接使用することはできませんが、Electronではノード統合を有効にすることで、HTMLやJavaScriptを使っているレンダラープロセス内でもNode.jsのAPIやモジュールを利用できるようになります。
ノード統合を有効にすることで、次のようなことが可能になるのです。
しかし、ノード統合を有効にすることにはリスクも伴います。
特に、レンダラープロセス内のWebコンテンツが外部の悪意あるスクリプトによって操作されると、Node.jsのAPIが悪用される可能性があります。
そのため、セキュリティ対策としては、信頼できるコンテンツのみを読み込む、またはコンテキスト分離(Context
Isolation)を有効にするなどの方法があります。
上述のmain.jsではノード統合が有効になっています。
次のコードをご覧ください。
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true, // ここでノード統合を有効にしている
contextIsolation: false,
}
webPreferencesオブジェクト内のnodeIntegration:
trueという設定がノード統合を有効にしています。
これはレンダラープロセス内でNode.jsの機能を使用することができる状態です。
同時に、contextIsolation: falseが設定されているため、レンダラープロセス内でのコンテキストの分離も無効になっています。
これはセキュリティの観点から注意が必要な設定です。
ここまで見てきたように、ノード統合はElectronアプリケーションの柔軟性を高めるための機能ですが、使用する際にはセキュリティに十分注意する必要があります。
renderer.jsを見ると、特定のアプリケーションやファイルを開くためにNode.jsのchild_processモジュールを使用していることが分かります。
セキュリティを強化する設定に変更すると動作に影響を与える可能性が高いです。
ノード統合を無効にすると、レンダラープロセス内でNode.jsの機能(requireやexecなど)にアクセスできなくなります。
このため、現状のスクリプトでは以下の部分が動作しなくなるでしょう。
コンテキスト分離を有効にすると、メインプロセスとレンダラープロセスが異なるJavaScriptコンテキストで動作するため、セキュリティが強化されますが、同様にNode.jsの機能に直接アクセスできなくなります。
ノード統合を無効にしたり、コンテキスト分離を有効にする場合、次のようにプリロードスクリプトを使用して、メインプロセスとレンダラープロセス間で安全にデータをやり取りする方法があります。
上述したように、preload.jsはレンダラープロセスとメインプロセス間で安全に通信するためのスクリプトを記述するファイルであり、必要なAPIをレンダラープロセスに公開します。
このプリロードスクリプト(preload.js)で、contextBridgeとipcRendererを使用して、メインプロセスの機能をレンダラープロセスから利用できるようにします。
そうすればセキュリティを利かせつつ、このページで掲載している renderer.js も動作します。
const { contextBridge, ipcRenderer } = require('electron');
const { exec } = require('child_process');
contextBridge.exposeInMainWorld('api', {
openVSCode: () => ipcRenderer.invoke('open-vscode'),
openEdge: () => ipcRenderer.invoke('open-edge'),
openFile: (filePath) => ipcRenderer.invoke('open-file', filePath),
openFeinsPortal: () => ipcRenderer.invoke('open-feins-portal')
});
上記のpreload.jsを書いてから、メインプロセスの main.js でIPC通信を設定しますよね。
次に、レンダラープロセスの renderer.js を用いてプリロードスクリプトで公開されたAPIを使用します。
こうやってやれば、セキュリティを強化しつつ、現在のスクリプトの機能を維持することができます。
まぁでも…すでにお話しましたが、ここで作っているのはローカルで稼働するシンプルなフローティング・ランチャーです。
デザイン面や機能などをさらに拡張したくなった時に改めて考えれば良いわけで。
現段階ではここまでやらずとも良いと判断しているってことですね。
ここまで、あまりにも長くなり過ぎました。
このpackage.jsonも重要なコードであり、ちゃんと書かないとソフトウェアとしてまともに機能しません。
プロジェクトの設定情報を管理するpackage.json
{
"name": "fein_launcher",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron .",
"package-win": "electron-packager . fein_launcher --platform=win32 --arch=x64 --out=dist --overwrite",
"package-linux": "electron-packager . fein_launcher --platform=linux --arch=x64 --out=dist --overwrite",
"package-mac": "electron-packager . fein_launcher --platform=darwin --arch=x64 --out=dist --overwrite"
},
"devDependencies": {
"electron": "^xx.x.x",
"electron-packager": "^xx.x.x"
}
}
package.jsonは、Node.jsプロジェクトの「設定ファイル」として機能し、プロジェクトの基本情報、依存パッケージ、実行可能なスクリプトなどを管理します。
また、プロジェクトのセットアップやビルド、実行が簡単になります。
このフローティング・ランチャーで使われているコードの説明を致しましょう。
electronとelectron-packagerのバージョン記述はサボっちゃいました😆ww
いや…だって、この手の設定ファイルって凝り出すとキリないんだもん…
バージョンアップしていくにつれて、少しずつ完璧な形へ近付けていきましょう。
これも戸惑いを感じるファイルじゃないかな?
プロジェクトのルートディレクトリに作られるもので、何かがおかしいわけじゃないです。
ルートディレクトリに生成されるpackage-lock.jsonは、Node.jsのプロジェクトにおける依存関係を厳密に管理するためのファイルです。
このファイルには、次のようにプロジェクトで使用される正確なパッケージバージョンと依存関係のツリーが記録されています。
package.jsonとpackage-lock.jsonは、Node.jsプロジェクトの依存関係管理において大切な役割を持っているんですよね。
加えて、package-lock.jsonは手動で作成するのではなく、次のような状況で自動的に生成されます。
package-lock.json
は、プロジェクト内の依存関係を厳密に管理し、他の開発者が同じ依存関係でプロジェクトを再現できるようにするための重要なファイルです。
なので、自分で手動で作成する必要はなく、npmが自動的に管理してくれます。
次のコマンドでElectronアプリが起動します。
開発を進めるには、上記のファイルにソースコードを記載しつつ、機能を追加していきます。
npm start
ソフトウェアを終了するには、コマンドプロンプト内部でctrl+cを押してからyesで終了できます。
ここで、ほっとんどの場合バグが出ます。
想定通りにソフトウェアが動いてくれないので、デバッグする必要がある。
デバッグとは、プログラミングにおいて、プログラムの欠陥や誤り(バグ)を見つけ出し、取り除く作業のことです。
いろんなやり方があるのですが、ここでは基本的なものを紹介します。
次の画像をご覧ください。
まず、自分が作っているソフト上で Shift+Ctrl+I を押します。
大きいソフトだったらいいけど、そうでない場合は上の画像のように大変操作しにくい開発者ツールが開いてしまうのです。
そしたらソフトのサイズそのものを広げても良いし、右上にある縦に並んだ・を押していただきます。
またいろんな選択肢が出てきますが、ここでは「Undock into separate window」を押してみましょう。
これで、広々とした画面で開発者ツールを閲覧できるようになりました。
こうしたデバッグのツールはいろんな種類があり、非常に多機能です。
とてもとても、全ての機能を網羅するのは難しいでしょう。
でもとりあえず、ここのConsoleタブだけは見ておくと良いですよ?
プログラムにバグがあれば、ここにエラーメッセージが出ます。
そのメッセージについて調べながら、ソースコードを修正していくわけです。
node.jsのエレクトロンでソフトウェアを作っていると、下の画像のように「node_modules」というフォルダができていることに気付くでしょう。
バグではありませんよ?
「node_modules」フォルダは、node.jsのプロジェクトにおいて、外部のライブラリやパッケージをインストールした際に生成されるフォルダです。
node.jsのプロジェクトでは、様々な外部のライブラリやパッケージを利用することができます。
これらのライブラリやパッケージは、上述したnpm(Node Package
Manager)などのパッケージマネージャーを使ってインストールします。
npmを使ってライブラリやパッケージをインストールすると、それらのファイルは「node_modules」フォルダ内に保存されます。
つまり、「node_modules」フォルダは、プロジェクトが依存する外部のライブラリやパッケージをまとめて管理する場所なのです。
「node_modules」フォルダは、サイズが大きくなりやすいという特徴があります。
このページでやってるランチャーソフトくらいならいいけど、多くのライブラリやパッケージをインストールしているプロジェクトでは、「node_modules」フォルダが数GBになることもあります。
そのため、Gitなどで「node_modules」フォルダを管理することは推奨されていません。
代わりに、package.jsonファイルを使って、プロジェクトの依存関係を管理します。
「ビルド」は、ソースコードから実行可能な形式に変換する全体的なプロセスを指し、「パッケージング」はそのビルドされたファイルをインストーラなどの形で配布可能な形式にまとめるプロセスを指すことが一般的です。
ソフトを作ったら、それを手軽に実行できるようにしなければ意味がありません。これがビルドです。
そして、ビルドされたものを一般ユーザーが使えるようにすると良いですね。これがパッケージングです。
では、実際のやり方を見ていきましょう。
package.jsonのコード紹介にて、ビルドコマンドを記述していましたね?
しかし、package.jsonにビルドコマンドを記述しても、electron-packager自体がインストールされていなければ実行できません。
まずは、electron-packagerをプロジェクトにインストールしましょう。
ターミナルで、プロジェクトのルートディレクトリに移動し、次のコマンドを実行します。
npm install electron-packager --save-dev
これでelectron-packagerがプロジェクトの devDependencies としてインストールされます。
electron-packagerのインストールが完了したら、いよいよビルドコマンドを実行します。
package.jsonに記述したコマンドは npm scripts として登録されているので、次のように簡単に実行できます。
npm run <script名>
例えば、Windows版をビルドする場合は、次のコマンドです。
npm run package-win
このコマンドを実行すると、ソフトウェアを作っているフォルダに「dist」というフォルダが生成されるはずです。
このフォルダの中に、exeファイルが入っているんだよね。
このランチャーソフトの場合は、次の画像のようになります。
distフォルダの下にfein_launcher-win32-x64フォルダがあります。
その中に「fein_launcher.exe」があるんだよね。
これで、ソフトウェアの完成となります!
さらにresurcesというフォルダを見てみましょう。
中にはappフォルダがあり、その中に私が作ったJavaScriptなどが収まっている。
加えて、srcフォルダの中を見れば、ちゃんとhtmlやcssも入っている。
これが、私が作ったフローティング・ランチャーの仕組みです。
同様に、Linux版、Mac版をビルドする場合は、それぞれ次のコマンドを実行します。
npm run package-linux
npm run package-mac
ちなみにpackage.jsonへ次のように記述しておくと、npm run
buildコマンドを実行するだけで、全てのプラットフォーム向けの実行ファイルを一度に生成することができます。
私はやらないんだけどねー。
"build": "npm run package-win && npm run package-linux && npm run package-mac"
さて、こうしてビルドが完了すると、package.jsonで指定した出力先--out=distに、プラットフォームごとの実行ファイルが生成されます。
distディレクトリの中身を確認し、目的の実行ファイルが存在することを確認しましょう。
electron-packagerは、Electronフレームワークを使って開発したアプリケーションを、Windows、macOS、Linuxといった各プラットフォームで配布可能な実行ファイルにパッケージングするためのツールです。
Electronアプリを開発したら、それをエンドユーザーに配布する必要があります。
しかし、ElectronアプリはWeb技術(HTML, CSS,
JavaScript)をベースにしているため、そのままでは各OSで実行することができません。
そこで、electron-packagerのようなツールを使って、Electronアプリを各OSに対応した実行ファイルに変換(パッケージング)する必要があるのです。
electron-packagerは、次のようなことをしてくれるのです。
electron-packagerは、コマンドラインツールとして提供されています。
通常は、Node.jsのnpmパッケージとしてインストールし、package.jsonにビルドコマンドを定義して使用します。
上述したビルドとパッケージングの作業が、まさにそれです。
基本的な使い方をおさらいしておきましょう。
npm install electron-packager --save-devでインストールするのは同じです。
ビルドコマンドの意味を押さえておくと良いかもね?
例えば、Windows向け実行ファイルの生成は次のようにコマンドします。
electron-packager . myapp --platform=win32 --arch=x64 --out=dist
このコマンドを実行すると、現在のディレクトリ(.)にあるmyappというアプリを、Windows 64bit向けにパッケージングし、distディレクトリに出力します。
electron-packagerは、Electronアプリを各プラットフォーム向けに配布するために必要不可欠なツールですよね。
これを使うことで、開発したアプリをより多くの人に利用してもらうことができるってこと。
一応、devDependencies という言葉についても書いておこうかな。
Node.jsプロジェクトにおいて、外部のライブラリやツールを利用する場合、それらをプロジェクトにインストールする必要があります。
これらの外部ライブラリやツールは、プロジェクトの「依存関係」と呼ばれることが多いです。
この依存関係には、大きく分けてdependencies(依存)とdevDependencies(開発依存)の2種類があります。
electron-packagerはdevDependenciesなんですよ?
electron-packagerは、Electronアプリをパッケージングするためのツールであり、アプリケーションの実行時には必要ありません。
electron-packagerは、あくまで開発者がアプリをビルド・パッケージングする際に使用するツールです。
そのため、electron-packagerはdevDependenciesとしてインストールされます。
devDependenciesとしてインストールされたパッケージは、本番環境にデプロイされる際には含まれないということです。
Electron で作成したアプリを配布する方法はいくつかありますが、dist フォルダを zip 形式で圧縮することもその一つです。
electron-packager や electron-builder などのツールを使ってアプリをビルドすると、通常、dist フォルダに実行ファイルや関連ファイルが生成されます。
この dist フォルダを zip 形式で圧縮することで、配布しやすい形にまとめることができます。
ただ、zip形式には次のようなメリットとデメリットがあることを押さえておきましょう。
このように、Electron アプリの配布方法として dist フォルダを zip 形式で圧縮することは一つの選択肢です。
しかしよりユーザーフレンドリーな方法としては、インストーラー形式での配布が推奨されます。
インストーラー形式では、受け取った側はインストーラーを実行するだけでアプリが適切な場所にインストールされ、必要な設定も自動的に行われます。
また、自動更新機能も実装しやすくなります。
Electron アプリならelectron-builderを使うのがやりやすいでしょうね。
パッケージングするためのツールですが、インストーラーの作成機能も備わっています。
個人発のソフトウェアをむやみにインストールしてはならない理由…
無数にあるけど、次のようなことはどこの教科書にも書いてあるのでは?
私も子供の頃からもう…イヤってほど叩きこまれています。
1. マルウェアのリスク
個人サイトに公開されているexeファイルには、マルウェアやウイルスが含まれている可能性があります。
これらの有害なプログラムは、コンピュータやネットワークに損害を与えたり、個人情報を盗み取ったりすることがあります。
2. 信頼性の欠如
公式のソースや信頼できるダウンロードサイトで提供されていないexeファイルは、その動作や品質が保証されていません。
予期しない動作やシステムのクラッシュなどの問題が発生する可能性があります。
3. プライバシーの侵害
不正なexeファイルは、個人情報や機密情報を不正に収集する可能性があります。
プライバシーが侵害されるリスクがあります。
4. ライセンス違反
正規のライセンスを持たないソフトウェアをダウンロードして使用することは、法的な問題を引き起こす可能性があります。
違法なソフトウェアの使用は、ソフトウェアの開発者や著作権者に損害を与えるだけでなく、自身も法的なトラブルに巻き込まれるリスクがあります。
5. セキュリティ更新の欠如
公式のソースから提供されているソフトウェアは、セキュリティ更新やパッチが定期的に提供されますが、個人サイトで公開されているexeファイルはそのような更新が行われないことが多いため、セキュリティホールが放置されるリスクがあります。
exeなどソフトウェアの実行ファイルをインストールする際には、必ず公式サイトや信頼できるダウンロードサイトから入手することを強くお勧めします。
人間には好奇心があり、それは大いに尊重されるべきものです。
お勉強の原動力になるからね。
GitHubなどを見ると、おもしろそうなソフトもいっぱいありますよ!
とは言え、上述している背景から、私のフローティング・ランチャーのexeファイルは提供しません。
でもソースコードはこうして全文公開して解説まで付けています。
言い変えると、このページに習って頂ければ、御自身で「マルチプラットフォーム対応のフローティング・ランチャー」を作れてしまいます😆ww
それなら安全であることはもちろん、次のようなメリットが期待できるでしょう。
今回は私の思い付きでフローティング・ランチャーでしたが、数か月後にはまた何かネタを思い付くかもしれません。
そしたらこのランチャーに機能追加してバージョン2として、改めて公開しましょう。
最後に、要は「調子こいちゃう」から、トラブルになるのですよ。
人からパクったソフトウェアを再配布してしまったりね。
意外にも、技術的なことより人間の原始的な欲求がトラブルの原因であることが多いものです。
節度を守りつつ、インターネットで遊べると良いですね🎵
全ページをリスト化したサイトマップも用意していますが、けっこうなページ数があります。
下記の「カテゴリー分けサイトマップ」のほうが使いやすいでしょう。
アナザーエデンの強敵戦やストーリーコンテンツのリスト、お勧めバッジなどを掲載したコーナーです。
期間限定のない普通のRPGですので、初心者でも安心して続けていけるゲームとなっています。
もっとも重要なグラスタについては、場所別に網羅した表があります。
個人でウェブサイトを作るにはどうすればいいか。
HTML・CSS・JavaScriptの書き方はもちろん、無料かつ広告なしでホームページを作る方法を掲載したコーナーです。
Webデザインやレイアウトについても書いてあります。
ゲームとパソコンだけじゃなく、アウトドアも趣味なんです。
このコーナーでは魚釣りの記録とか、魚料理のレシピ、はたまたサイクリングなどなど。
アウトドアに関連するコンテンツが詰め込まれています。