diff --git a/build/Aegisub/Aegisub.vcxproj b/build/Aegisub/Aegisub.vcxproj
index ebd159711..86b816e76 100644
--- a/build/Aegisub/Aegisub.vcxproj
+++ b/build/Aegisub/Aegisub.vcxproj
@@ -348,7 +348,10 @@
     <ClCompile Include="$(SrcDir)command\video.cpp" />
     <ClCompile Include="$(SrcDir)command\vis_tool.cpp" />
     <ClCompile Include="$(SrcDir)compat.cpp" />
-    <ClCompile Include="$(SrcDir)crash_writer.cpp" />
+    <ClCompile Include="$(SrcDir)crash_writer.cpp">
+      <ExcludedFromBuild>true</ExcludedFromBuild>
+    </ClCompile>
+    <ClCompile Include="$(SrcDir)crash_writer_minidump.cpp" />
     <ClCompile Include="$(SrcDir)dialog_about.cpp" />
     <ClCompile Include="$(SrcDir)dialog_attachments.cpp" />
     <ClCompile Include="$(SrcDir)dialog_automation.cpp" />
diff --git a/build/Aegisub/Aegisub.vcxproj.filters b/build/Aegisub/Aegisub.vcxproj.filters
index bb8fba4db..29ce46093 100644
--- a/build/Aegisub/Aegisub.vcxproj.filters
+++ b/build/Aegisub/Aegisub.vcxproj.filters
@@ -1238,6 +1238,9 @@
     <ClCompile Include="$(SrcDir)crash_writer.cpp">
       <Filter>Utilities\Logging</Filter>
     </ClCompile>
+    <ClCompile Include="$(SrcDir)crash_writer_minidump.cpp">
+      <Filter>Utilities\Logging</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="$(SrcDir)res/res.rc">
diff --git a/src/crash_writer_minidump.cpp b/src/crash_writer_minidump.cpp
new file mode 100644
index 000000000..da07e5a01
--- /dev/null
+++ b/src/crash_writer_minidump.cpp
@@ -0,0 +1,153 @@
+// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include "config.h"
+
+#include "crash_writer.h"
+
+#include "version.h"
+
+#include <libaegisub/fs.h>
+#include <libaegisub/util.h>
+
+#include <atomic>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/format.hpp>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#include <DbgHelp.h>
+#include <Windows.h>
+
+extern EXCEPTION_POINTERS *wxGlobalSEInformation;
+
+namespace {
+wchar_t crash_dump_path[MAX_PATH];
+agi::fs::path crashlog_path;
+
+using MiniDumpWriteDump = BOOL(WINAPI *)(
+	HANDLE hProcess,
+	DWORD dwPid,
+	HANDLE hFile,
+	MINIDUMP_TYPE DumpType,
+	CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+	CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+	CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
+
+struct dump_thread_state {
+	std::mutex start_mutex;
+	std::condition_variable start_cv;
+
+	std::atomic<bool> exit = false;
+	EXCEPTION_POINTERS *ep = nullptr;
+	DWORD thread_id = 0;
+
+	// Must be last so everything else is initialized before it
+	std::thread thread;
+
+	dump_thread_state() : thread([&] { main(); }) { }
+
+	void main() {
+		auto module = LoadLibrary(L"dbghelp.dll");
+		if (!module) return;
+
+		auto fn = reinterpret_cast<MiniDumpWriteDump>(GetProcAddress(module, "MiniDumpWriteDump"));
+		if (!fn) {
+			FreeLibrary(module);
+			return;
+		}
+
+		std::unique_lock<std::mutex> lock(start_mutex);
+		start_cv.wait(lock, [&] { return ep || exit; });
+		if (ep)
+			write_dump(fn);
+		FreeLibrary(module);
+	}
+
+	void write_dump(MiniDumpWriteDump fn) {
+		auto file = CreateFile(crash_dump_path,
+			GENERIC_WRITE,
+			0,  // no sharing
+			nullptr,
+			CREATE_NEW,
+			FILE_ATTRIBUTE_NORMAL,
+			nullptr);
+		if (file == INVALID_HANDLE_VALUE) return;
+
+		MINIDUMP_EXCEPTION_INFORMATION info;
+		info.ThreadId = thread_id;
+		info.ExceptionPointers = ep;
+		info.ClientPointers = FALSE;
+
+		fn(GetCurrentProcess(), GetCurrentProcessId(), file, MiniDumpNormal, &info, nullptr, nullptr);
+
+		CloseHandle(file);
+	}
+};
+
+std::unique_ptr<dump_thread_state> dump_thread;
+}
+
+namespace crash_writer {
+void Initialize(agi::fs::path const& path) {
+	crashlog_path = path / "crashlog.txt";
+
+	auto dump_path = path / "crashdumps";
+	agi::fs::CreateDirectory(dump_path);
+
+	const auto path_str = (dump_path / GetVersionNumber()).wstring();
+	wcscpy_s(crash_dump_path, path_str.c_str());
+	auto len = path_str.size();
+
+	const auto t = time(nullptr);
+	struct tm tm;
+	localtime_s(&tm, &t);
+
+	len += wcsftime(crash_dump_path + len, MAX_PATH - len, L"-%Y-%m-%d-%H-%M-%S-", &tm);
+	len += swprintf_s(crash_dump_path + len, MAX_PATH - len, L"%d", GetCurrentProcessId());
+	wcscpy_s(crash_dump_path + len, MAX_PATH - len, L".dmp");
+
+	if (!dump_thread)
+		dump_thread = agi::util::make_unique<dump_thread_state>();
+}
+
+void Cleanup() {
+	dump_thread->exit = true;
+	dump_thread->start_cv.notify_all();
+	dump_thread->thread.join();
+	dump_thread.reset();
+}
+
+void Write() {
+	dump_thread->ep = wxGlobalSEInformation;
+	dump_thread->thread_id = GetCurrentThreadId();
+	dump_thread->start_cv.notify_all();
+	dump_thread->thread.join();
+	dump_thread.reset();
+}
+
+void Write(std::string const& error) {
+	boost::filesystem::ofstream file(crashlog_path, std::ios::app);
+	if (file.is_open()) {
+		file << agi::util::strftime("--- %y-%m-%d %H:%M:%S ------------------\n");
+		file << boost::format("VER - %s\n") % GetAegisubLongVersionString();
+		file << boost::format("EXC - Aegisub has crashed with unhandled exception \"%s\".\n") % error;
+		file << "----------------------------------------\n\n";
+		file.close();
+	}
+}
+}
diff --git a/src/main.cpp b/src/main.cpp
index 05a7672b9..3437f1d7b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -311,6 +311,7 @@ int AegisubApp::OnExit() {
 
 	// Keep this last!
 	delete agi::log::log;
+	crash_writer::Cleanup();
 
 	return wxApp::OnExit();
 }