Visual Studioが何度も無関係のプロジェクトをビルドするのは、奴のせいかもしれない – NuGet編

NuGetまるで続きがあるかのようなラノベタイトルですが、一話完結です (;´Д`)

ものすごーく長い間、頭を悩ませてきた問題の一つが、先日ようやく判明したので共有しようと思います。
Visual Studio 2013(より前のバージョンも多分)で、比較的中規模~大規模なソリューション(沢山プロジェクトが含まれている)において、クリーンビルド後にも再びビルドが走って「イラっと」する問題です。

何度やっても発生する

GitなどのSCMからソースコードだけを取得して、完全なクリーンビルドを行っても起きます。これが起きると、以後のビルドは「リビルド」の如く、依存関係上ビルド不要なものを次々とビルドしてしまうので、開発効率が極めて悪くなります。

「お前はバッチファイルでビルドしてるだろ」

と言いたくなります。

何故か、自分一人で開発している場合には発生しません。また、使用しているPCによって、起きたり起きなかったりします。PCによって挙動が大体決まるものの、同じPCで試行しても現象が変わる事があります。規模の問題なのか、プロジェクトに関わっている誰かが何かをしたのか、PCが腐っているのか、Visual Studioの問題なのか、ずっと謎のままでした。

原因

謎な要因は、得てして複雑な背景があるものですが、今回もそうでした。

  • プロジェクトには「NuGet」でパッケージ導入しているものが含まれます。とあるパッケージ「ABC」のバージョンが「1.0.0」とします。
  • ビルドされたバイナリは、共通のフォルダに格納されます。例えば、「$(SolutionDir)$(PlatformName)\$(ConfigurationName)」です。今回のシステムでは、アセンブリを動的にロードする要件があるので、プロジェクトのサブフォルダに出力されるとデバッグがやりにくいのです(というか、デフォルトでこうして欲しいんだけどなぁ)。

チーム内の誰かが、新たにプロジェクトを追加する場合に、自分が関わったプロジェクトの構成をひな形に、新しいプロジェクトを追加します。この時、手動でアセンブリ参照を追加します。その中には「ABC.1.0.0.dll」が含まれます。手動で追加しているので、当然ファイルダイアログから「packages」フォルダ内を参照して追加するわけですが、これによって「NuGetとは何の関係もない事になっているアセンブリを追加した」事になります。

さて、コアメンバーが新しいNuGetパッケージ群に対応させるため、NuGetのパッケージを更新したとします。ここで、SafeUpdateをしないと、「ABC」パッケージには「1.0.0」と「1.0.3」のような、二つのバージョンが混在する可能性があります(SafeUpdateは、Update-Packageに-Safeを付ける)。packagesフォルダ内には、この二種類のパッケージが配置され、プロジェクトファイルも新しいバージョンを使うように修正されます、

プロジェクトがNuGetで構成されていれば。

手動でアセンブリ参照を追加したプロジェクトは、当然packages.configファイルを持っていません。そのため、NuGetのUpdateで自動的に参照が更新されません。しかし、SafeUpdateしなかったためにpackagesフォルダには「1.0.0」のパッケージが残っており、ビルド自体には成功します。ここに問題があります。

複数のプロジェクトがビルドされると、これらの混在したバージョンのアセンブリが、何度も「共通の」出力先フォルダにコピーされます。その結果、1.0.0と1.0.3のアセンブリが、まるでロシアンルーレットのように上書きコピーされ、ビルド完了時にはどちらのバージョンが配置されたのか分かりません。すると、次回ビルド時に、何の変更をしていなくても、Visual Studioが「あ、ビルドしなきゃ」と思っていそいそとビルドを始めます。

この問題は、マルチコアによる平行ビルドを実行すると顕著に表われます。環境によって発生したりしなかったりし、再現性が無いのでかなりイライラします orz

解決方法

  • NuGetで複数のバージョンを同時に使わない事。確認するには、ビルド後にpackagesフォルダ内を見て、複数のバージョンのパッケージが配置されていない事を確認する。もし、複数のバージョンが含まれていたら、どのプロジェクトが犯人かをチェックして修正する。csproj内のパスを修正し、packages.config内のバージョンも修正する。一旦、古いバージョンに合わせた後、SafeUpdateで一括更新すると楽かもしれない。
  • プロジェクトに、直接アセンブリ参照している構成が無い事を確認する。これはプロジェクトが多いと地味に苦痛かもしれない。
  • NuGet絡みで書きましたが、「共通の出力先フォルダ」を使い、異なるバージョンの同一のアセンブリ名のファイルを配置していると、同様の問題が発生する可能性があります。心当たりある場合は、確認してみましょう。

感想

久しぶりに一発ビルド完了した状態を見た。感慨深かった。
NuGet、ALMの一部としてみると脆弱で辛い。PSで構成とか、悪夢だ…

Roslyn for Scriptingで、あなたのアプリケーションにもC#スクリプトを!!

dotnet_logoいよいよ、Visual Studio 2015リリースが近づいてきました。今回はC#的にはあまり大がかりな拡張がありませんが、内情としてはC#コンパイラのインフラが「Roslyn」に正式対応するという事で、地味に大きな変更となっています。

Roslynは、MSのオープンソース戦略としては早い段階で公開され、それ以来、パブリックな場で将来のC#コンパイラの仕様検討などが行われています。勿論、ソースコードも「オープンソース」として公開されており、自分でいじりたければフォークも可能です。そろそろ概要を掴んでおこうと考えている方向けに、いくつかリンクを張っておきます。

なお、この記事には続編があります:「真・Roslyn for Scripting! あなたのアプリケーションにもC#スクリプトを!!」


ちょっとだけ背景

csc今まで、C#のコンパイラは「csc.exe」で、.NET Frameworkに付属していました。このプログラムはアンマネージコード(多分、C++)で書かれており、メンテナンスが大変そうだなと思います。また、Visual Studioが、ソースコードエディタ内でインテリセンスを表示させるために、csc.exeとは「別に」、独自にソースコードのパースを行っており、言語仕様の変化に合わせて二重にメンテナンスしなければならないという問題もありました。更に、Resharperなどのサードパーティプラグインも、この内部実装にはアクセスできないため、またもや同じようなパーサーを独自に書かなければならず、どう考えても不健全です。

そこで、C#のソースコードを解析し、いわゆる「構文木」をオブジェクト表現可能な形でパースする、ニュートラルな独立したライブラリを実装する事にした… というのがRoslynです。当初は「Compiler as a service」 (InfoQ)なワードだけが取りざたされて、何か大きなサービス的なもののように誤解されたこともありましたが、要するにコンパイラのための(低レベルな)インフラをライブラリ化して、様々な環境から共通利用出来るようにしたものです。

# なお、C#, C#と言ってますが、VB.netもRoslyn化されています。

さて、Roslynのパーサーの説明をしようとすると大変そうなので (;´Д`) この記事では「スクリプティング」に焦点を置きたいと思います。

所で、実はこの分野は、.NETのマルチプラットフォーム展開を行う「Mono」の方が先行していて、MonoのC#コンパイラは C#で書かれているので、既に同じようなインフラが存在する事になります。Monoのインフラを使ったスクリプティングインフラとして、「scriptcs」が存在します。このプロジェクトは、スクリプティングのモジュール化に必要な「スクリプトライブラリ」を作る事が出来たり、スクリプトから参照するライブラリをNuGetから取得(実行時に配置、即使用可能)出来たりと、スクリプト環境としての熟成を図っているようです。また、Roslynがリリースされたことで、scriptcsのインフラを、MonoかRoslynかで選択可能にしようとしています。

Monoプロジェクトは今後、.NET Coreプロジェクトと相互に情報交換して、.NET純正のソースコードのMonoへの取り込みや、Monoの相互運用性(Linux対応など)の.NETへの取り込みを行う事を表明しています。また、C#コンパイラのインフラについては、Roslynベースに統一する方針のようです。


スクリプティング・ホスティング

自分で書いた何らかのアプリケーションに、スクリプティング機能を載せたいと思ったことはありませんか? 例えば、何らかのゲームを作っているとして、このゲームにC#のスクリプティング機能を搭載出来たら、アドベンチャーゲームやロールプレイングゲームのシナリオ進行、シミュレーションゲームの敵AI調整に C# のスクリプトが使えるようになるのです。ゲームの世界ではluaやpython辺りをよく聞きますね。でも、本体をC#で書いているなら、スクリプトもC#で書きたいものです。

別の例として、Officeを挙げます。Officeには「Visual Basic for Application」という、VBチックなスクリプト環境があります。が、VBA、書きたくないです… VBAは徐々にフェードアウトの方向になっていると思いますが、代わりにC#でスクリプトが書けたら、もうちょっとOfficeに前向きになりそうです(Excel向けであれば、商用プラグインが既にあります:FCell)。MicrosoftはOfficeでC#を使えるようにするか? は、まだまだ分かりませんが、Roslynが公開されたことで、その可能性は高くなったと思います。

Roslynのコアは、C#のパーサーライブラリです。そして、極めて依存性が低く設計されています(Portable class library化されています)。が、パースしても構文木が得られるだけで、これが実行可能になるわけではありません。この構文木を食わせて、スクリプトとして実行可能にするインフラが「Roslyn for scripting」です。

流れは以下のようになります。

Roslyn Roslyn for scripting
スクリプト(文字列) → 構文木 → IL変換(コンパイル) → 実行

Roslyn for scriptingは、構文木をコンパイルしてIL命令に変換し、更に実行まで行います。IL変換は、System.Reflection.Emitを使用して動的にILを生成します。Emitは環境によって使用出来ない(例:ストアアプリ環境であるWinRTではEmitは使えない)ため、Roslyn本体には含めなかったのだと推察しています。現在のところ、Roslyn for scriptingは.NET4.5以上の環境でのみ使用可能です。

話が複雑になってきましたか? いやいや、Roslyn for scriptingは、ものすごく簡単に使えます!


Hello! C# scripting!!

roslyn1Roslyn for scriptingを使ってスクリプト環境を作る場合、殆どRoslynを意識する必要はありません。とりあえずHello worldをやってみましょう。.NET 4.5以上のコンソールアプリケーションのプロジェクトを作ってください。次に、NuGetで以下のパッケージを導入します。まだ正式版ではないので、「リリース前のパッケージを含める」を選択して下さい。

# もうRCでGoLiveなので、インターフェイスに大きな変更は無いと思われます。
# 最近、NuGetの調子が悪いようなので、上記パッケージが一覧に表示されない場合は、パッケージマネージャコンソールから “Install-Package” コマンドでインストールすると確実です。

導入出来たら、Mainメソッドに以下のようにRoslyn for scriptingの実装を書きます。

using Microsoft.CodeAnalysis.Scripting.CSharp;

namespace HelloRoslynForScripting
{
	class Program
	{
		static void Main(string[] args)
		{
			// Console.WriteLine("Hellow C# scripting!");
			var script = CSharpScript.Create(
				"Console.WriteLine(\"Hello C# scripting!\");");
			script.Run();
		}
	}
}

roslyn2これだけです! さぁ、実行してみましょう!! 簡単ですね! 簡単過ぎて、応用性が無いのでは? と思うかもしれませんが、大丈夫です。追々分かると思います。

ところで、ソースコードの先頭にコメントを入れておきましたが、興味深い事に気が付きましたか?

Consoleクラスは、本来はSystem名前空間に存在しますが、指定していません。これは、事前に「using System;」相当の定義が組み込まれているからです。
また、このコード自体、名前空間の宣言も、クラス宣言も、メソッド宣言もありません。スクリプト環境でこれらを書くことももちろん出来ますが、スクリプト環境ではこれらを省略可能にしているのです。そのため、いきなり実装本体が来ても、全く問題ありません。


スクリプト環境固有の課題

上記のように、スクリプティングの世界では、単にソースコードをリアルタイムで解析・実行するだけではなく、普通のソースコードとは異なる状況に対処する必要があります。以下にそのような課題を挙げます。

  • 記述の省略を可能にする
  • 逐次処理を可能にする
  • ホスト環境と通信する

記述の省略を可能にする

前述のように、スクリプト環境では色々省略して記述出来た方が便利です。ホストする側で、事前にusingを成立させておくことが出来ます。「System」名前空間は既定で定義されていますが、独自の名前空間も定義しておくことが出来ます。そのためには、事前にScriptOptionsクラスを用意して、オプションとして指定します。

using Microsoft.CodeAnalysis.Scripting;
using Microsoft.CodeAnalysis.Scripting.CSharp;

namespace HelloRoslynForScripting
{
	class Program
	{
		static void Main(string[] args)
		{
			// using System.Collection.Generic; 相当
			var options = new ScriptOptions().
				WithNamespaces(
					"System.Collections.Generic").
				WithReferences(
					typeof(object).Assembly);

			// var list = new List<string> { "ABC", "DEF" }; System.Console.WriteLine(string.Join(",", list));
			var script1 = CSharpScript.Create(
				"var list = new List<string> { \"ABC\", \"DEF\" }; System.Console.WriteLine(string.Join(\",\", list));",
				options);
			script1.Run();
		}
	}
}

ScriptOptionsや他のクラスもそうですが、Roslynでは「イミュータブルオブジェクト」を全面的に採用しています。イミュータブルオブジェクトとは、文字列(System.String)や日付構造体(System.DateTime)のように、インスタンスの内容が不変である事を保証した実装の事です。一度生成されたインスタンスは、中身が変更されることはありません。

上記の例では「WithNamespaces」メソッドを呼び出していますが、これによって、最初に生成したScriptOptionsクラスのインスタンスを変更するのではなく、新たなScriptOptionsクラスのインスタンスを生成しています。以下がWithNamespacesの定義です。

/// <summary>
/// Creates a new <see cref="T:Microsoft.CodeAnalysis.Scripting.ScriptOptions"/> with the namespaces changed.
/// </summary>
public ScriptOptions WithNamespaces(IEnumerable<string> namespaces)
{
	// (内部実装)
}

このように、WithNamespacesを呼び出すと、名前空間をオプションとして追加した「新しいScriptOptions」を生成し、戻り値として返却します。そのため、単にWithNamespacesを呼び出しただけでは、元のインスタンスには何も変更が加わっていない事になるので注意が必要です。

roslyn3さて、名前空間の定義は「WithNamespaces」メソッドで行いますが、ScriptOptionsの定義を空のインスタンスから始めた(new ScriptOptions())ので、mscorlibへの参照を追加しておく必要があります。それが「WithReferences」メソッドです。

なお、最初の例でオプションを省略しましたが、省略時はmscorlibへの参照と、System名前空間の定義だけが行われた状態になっています。

こうして必要な定義を追加したオプションを生成出来れば、あとは、CSharpScript.Createの引数にオプションを渡すことで、これらの定義が解釈されます。一般的には、「System」「System.Collections.Generic」「System.Linq」「System.Threading.Tasks」などが定義されていた方が、利便性が向上するでしょう。あるいは、これらの定義をホスト側のApp.configや設定ファイルから読み込むようにすれば、更に柔軟性が高くなります。


逐次処理を可能にする

スクリプト環境では、ユーザーがコンソールからインタラクティブにコードを記述する可能性があります。例えば、コマンドプロンプト(cmd.exe)やPowerShellでは、ユーザーがコマンドラインから入力した命令やコードが「Enter」キーの押下で、即実行されます。と言う事は、コンパイル実行の場合は、一度にすべてのコードを評価しなければならないのが、ちょっとづつ、断片的にソースコードが渡される可能性がある事を意味します。この状況を、とりあえず簡単に記述したのが以下の例です。

using Microsoft.CodeAnalysis.Scripting.CSharp;

namespace HelloRoslynForScripting
{
	class Program
	{
		static void Main(string[] args)
		{
			var script1 = CSharpScript.Create(
				"Console.WriteLine(\"Hello C# scripting for first line, and continue...\");");
			script1.Run();

			var script2 = CSharpScript.Create(
				"Console.WriteLine(\"Hello C# scripting for next line!\");");
			script2.Run();
		}
	}
}

単に、CSharpScriptのインスタンスを2回生成して実行しているだけです。これは問題ないでしょう。しかし、スクリプトコードの入力をユーザーがインタラクティブに実行している場合、もっと複雑な問題が発生する可能性があります:

using Microsoft.CodeAnalysis.Scripting.CSharp;

namespace HelloRoslynForScripting
{
	class Program
	{
		static void Main(string[] args)
		{
			var script1 = CSharpScript.Create(
				"var data = 123;");
			script1.Run();

			var script2 = CSharpScript.Create(
				"Console.WriteLine(\"Hello C# scripting with data: {0}\", data);");

			// "(1, 56): error CS0103: The name 'data' does not exist in the current context"
			script2.Run();
		}
	}
}

roslyn4コードは名前空間・クラス・メソッドなどの定義が無くても解釈されるのでしたよね? いきなり変数「data」が定義されていますが、スクリプトとしては問題ありません。問題はscript2です。このスクリプトで、data変数を参照しようとしていますが、コンパイルエラーが発生しています。

これは、script1とscript2が、相互に全く関係のないスクリプトとして実行しているためです。勿論、そのように意図している場合は問題ありませんが、インタラクティブに実行する場合は、このようにコードが分断されてしまう可能性は十分考えられます。

では、どうすれば良いでしょうか? script1で定義されたdata変数をscript2で参照可能にするには、「ScriptState」を使います。

using Microsoft.CodeAnalysis.Scripting.CSharp;

namespace HelloRoslynForScripting
{
	class Program
	{
		static void Main(string[] args)
		{
			var script1 = CSharpScript.Create(
				"var data = 123;");
			var script1State = script1.Run();

			var script2 = CSharpScript.Create(
				"Console.WriteLine(\"Hello C# scripting with data: {0}\", data);");
			script2.Run(script1State);
		}
	}
}

CSharpScript.Runメソッドの戻り値は、ScriptStateです。この中には、スクリプトを実行した結果や、スクリプトとして記憶しておくべき様々な情報が格納されています。これを、次のスクリプト実行時のRunメソッドの引数に渡すと、スクリプトはScriptStateの記憶を前提とした環境で動作します。この中にはdata変数の結果も格納されているので、script2も問題無く実行出来るようになるのです。

さぁ、もう、かなり自分のアプリケーションに組み込める気がしてきましたよね?! 次の問題が片付けば、大抵の環境に組み込んで、すぐに応用が可能ですよ!


ホスト環境と通信する

ここまでで、独立したスクリプトエンジンとして問題なく操作できるようになったはずですが、現実には、スクリプト側のコードから、ホスト環境のオブジェクトを操作したりしたい訳です。でなければ、スクリプト環境を組み込む意味がありませんよね。

前述のCSharpScript.Runメソッドの引数の定義を見て、疑問に思ったかもしれません。

/// <summary>
/// Runs this script.
/// </summary>
/// <param name="globals">An object instance whose members can be accessed by the script as global variables, or a <see cref="T:Microsoft.CodeAnalysis.Scripting.ScriptState"/> instance that was the output from a previously run script.</param>
/// <returns>
/// A <see cref="T:Microsoft.CodeAnalysis.Scripting.ScriptState"/> that represents the state after running the script, including all declared variables and return value.
/// </returns>
public ScriptState Run(object globals = null)
{
	// (内部実装)
}

Runメソッドの引数は「object」です。ここに渡していたのはScriptStateクラスのインスタンスなので、わざわざobjectと定義する意味が分からないかもしれません。実は、この引数にはScriptStateだけではなく、任意のクラスのインスタンスが渡せます。以下に例を示します:

using System;
using System.Linq;

using Microsoft.CodeAnalysis.Scripting.CSharp;

namespace HelloRoslynForScripting
{
	public sealed class HostObject
	{
		public int Value;

		public string CreateValues(int count)
		{
			var r = new Random();
			return string.Join(",", Enumerable.Range(0, count).Select(index => r.Next()));
		}
	}

	class Program
	{
		static void Main(string[] args)
		{
			var hostObject = new HostObject { Value = 123 };

			var script1 = CSharpScript.Create(
				"Console.WriteLine(\"Hello C# scripting with host object: Value={0}, Values=[{1}]\", Value, CreateValues(10));");
			script1.Run(hostObject);
		}
	}
}

ホスト側で用意した「HostObject」クラスのインスタンスを渡し、スクリプト側からアクセスしています。面白い事に、スクリプトからはグローバルな無名の要素としてアクセス出来ます。そのため、「Value」フィールドや「CreateValues」メソッドに、インスタンスやクラスの指定がありません。

roslyn5Runの引数に渡せるインスタンスは、クラスである必要があります(構造体はダメです。勿論、クラスのメンバーに構造体があっても問題ありません)。また、クラスはパブリックでなければなりません。恐らく、スクリプトがコンパイルされた際に、クラスのメタデータにアクセスする必要があるからでしょう。


残る課題

ここまで分かれば、後は応用だけです。スクリプト環境として考えられる他の課題は、scriptcsを見てみると分かるかもしれません。

  • インタラクティブな指令で、アセンブリ参照を追加する
  • インタラクティブな指令で、スクリプトをファイルとしてロードする
  • インタラクティブな指令で、NuGetなどのパッケージシステムをサポートする

つまり、インタラクティブなコンソールがコードの入力となる場合は、帯域外の指令で、ホストがスクリプティングのサポートをする必要があります。scriptcsでは、このような独自コマンドを定義(先頭が”:”で始まるコマンドが入力された場合は、スクリプトコードではなく独自に処理を行う。例えば”:load”でスクリプトライブラリをロードする)して対処しています。

さあ、これでC#スクリプトで何でも出来るでしょう!

Final LINQ Extensions II – 第三回 Center CLR 勉強会

finallinqextensions2第三回Center CLR勉強会で、Final LINQ Extensions IIのタイトルで登壇してきました。

ご静聴ありがとうございます。
前回網羅できなかった、細かい話の落穂広いと、標準の演算子を一通り説明しました。
標準演算子が使いこなせるようになると、LINQでの実装がはかどる事間違いなしですよ。

にしても、あーあ、やっちまいました (;´Д`) 次回もよろしくお願いします!!


オリジナルスライドはこちら: Final LINQ Extensions II.pptx

旧環境で、もはやDVDを使わない – Windows 10をISOイメージからインストールする

DVD小ネタです。最近のUEFI対応BIOSではなく、少し前のPCに対して、Windowsを素からインストールしたい場合、一番安全なのは USB DVDドライブから起動する事でした。しかし、DVDはメディアをDVD-Rに焼く必要があって面倒です。今回、Windows 10 Insider Previewを旧式のPCにインストールする際に、いい加減USBメモリからインストールしたいという欲求で、調べなおしたのでメモしておきます。

今頃か!という気も、自分でもするんですが (;´Д`)、以前はUSBメモリからブートさせてインストールをするには面倒な手順が必要で、DVD-Rメディアもそれほど高くなかったこともあり、必要に応じて焼き直して使っていたからです。


UNetbootinはダメ

unetbootinサクッと検索すると、最初に見つかったのがコレ。

UNetbootin

UNetbootin allows you to create bootable Live USB drives for Ubuntu, Fedora, and other Linux distributions without burning a CD. It runs on Windows, Linux, and Mac OS X.

これは発想がちょっと面白く、Linux等のディストリビューションをダウンロードしてUSBにコピーするまでを、全部やってくれます。オプションとしてISOイメージをUSBにコピーする事も出来ます。残念ながら、サイトにはっきり書いてない通り、これを使ってWindowsのISOイメージからブート可能なUSBメモリを作る事は出来ませんでした(正常終了しますが、ブートしません)。


本命 Windows 7 USB/DVDダウンロードツール

isoimager1マイクロソフト純正にこのようなツールがありました。

Windows USB/DVD Download Tool

CodePlexでソースコードも公開しています。また、日本語版もありますね。インストールして起動すると、ISOイメージを選択できます。今回はWindows 10 Insider Previewのx64版を選択。ちなみに、こちらで登録する事で、ダウンロードできます。

isoimager2次に、インストール先のUSBメモリを選択します。まあ、スクリーンショットを見ての通り、何も難しい事はありません。

isoimager3USBメモリはフォーマットされます。中身が必要な場合は、事前にバックアップしておきましょう。

isoimager4と、このように、非常に簡単にUSBメモリでブートするWindowsメディアを用意出来ます。このツール、改めて紹介するほどの物でもないのですが、知らなければ一度使ってみると良いと思います。とにかく、たまにしか再インストールをしない場合など、色々準備事項を忘れてしまう場合に重宝するかと思います。


酷いファーストインプレッション

fiwin10さて、Windows 10を手持ちの実機で試したかったのですが、すぐに入れられるのが放置されているAcer AspireOne AO751-Bk26だったので、これに入れてみました。第一世代Atom Z520で、元はWindows XPのマシンです。シングルコアなマシンで非常に非力。Windows 8.1も相当にきつい(主に描画がもたつく)のですが、Windows 10もその点はあまり変わらない感じです。但し、GMA500ドライバ(Vista)を入れれば、Huluの動画は問題なくSpartanで見る事が出来ました。

正直、先日の中華タブの方が数倍良いです(;´Д`)。まさかいないと思いますが、アップグレードを考えている人は、諦めて新しいマシンを買った方が良いですよ!