diff --git a/Cargo.toml b/Cargo.toml
index faf9be5..4267262 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,3 +16,6 @@ crate-type = ["rlib"]
 
 [dependencies]
 libc = "0.2"
+
+[target.'cfg(target_os = "windows")'.build-dependencies]
+vswhom = "0.1.0"
diff --git a/README.md b/README.md
index 3188204..31f2f7c 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,13 @@
 # vlc-rs [![Build Status](https://travis-ci.org/garkimasera/vlc-rs.svg?branch=master)](https://travis-ci.org/garkimasera/vlc-rs)
+
 Rust bindings for libVLC media framework.
 
 ## Status
+
 Many missing functions and wrappers.
 
 ## Use
+
 Please add the following dependencies to your Cargo.toml.
 
 ```Toml
@@ -20,7 +23,9 @@ git = "https://github.com/garkimasera/vlc-rs.git"
 ```
 
 ## Example
+
 Play for 10 seconds from a media file.
+
 ```Rust
 extern crate vlc;
 use vlc::{Instance, Media, MediaPlayer};
@@ -45,5 +50,27 @@ fn main() {
 
 Other examples are in the examples directory.
 
+## Building
+
+### Windows
+
+To build `vlc-rs`, you must either build VLC from source or grab one of the pre-built packages from [videolan.org](https://www.videolan.org/vlc/download-windows.html).
+
+If you're building for `x86_64`, then you should grab the download labelled "Installer for 64bit version".
+That installer is actually a self-extracting ZIP archive, so we can extract the contents without installing VLC itself.
+
+If you're building for `x86`, then you should either download labelled "7zip package" or the one labelled "Zip package".
+
+Once you've downloaded your chosen package, you should extract it some place such that its path contains no spaces.
+To point `vlc-rs` at your VLC package, you should set an appropriate environment variable:
+
+- `VLC_LIB_DIR`: Directory of the VLC pacakge, any architecture
+- `VLC_LIB_DIR_X86` : Directory of the VLC pacakge, `x86`-only
+- `VLC_LIB_DIR_X86_64` : Directory of the VLC pacakge, `x86_64`-only
+
+You should also add the package to your `PATH` variable if you intend to run the program.
+For distribution of an executable program, you should probably copy over the neccessary DLLs, as well as the `plugins` directory.
+
 ## License
+
 MIT (Examples are licensed under CC0)
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..02c7834
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,113 @@
+fn main() {
+    #[cfg(target_os = "windows")]
+    windows::link_vlc();
+}
+
+#[cfg(target_os = "windows")]
+mod windows {
+    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+    compile_error!("Only x86 and x86_64 are supported at the moment. Adding support for other architectures should be trivial.");
+
+    use std::env;
+    use std::ffi::OsString;
+    use std::fs;
+    use std::path::{Path, PathBuf};
+    use std::process::Command;
+
+    use vswhom::VsFindResult;
+
+    pub fn link_vlc() {
+        let vlc_path = vlc_path();
+
+        let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
+
+        let vs = VsFindResult::search().expect("Could not locate Visual Studio");
+        let vs_exe_path = PathBuf::from(
+            vs.vs_exe_path
+                .expect("Could not retrieve executable path for Visual Studio"),
+        );
+
+        generate_lib_from_dll(&out_dir, &vs_exe_path, &vlc_path);
+        println!("cargo:rustc-link-search=native={}", out_dir.display());
+        // NOTE: Without this directive, linking fails with:
+        //       ```
+        //       error LNK2019: unresolved external symbol vsnprintf referenced in function _{MangledSymbolName}
+        //          msvcrt.lib(vsnprintf.obj) : error LNK2001: unresolved external symbol vsnprintf
+        //          msvcrt.lib(vsnprintf.obj) : error LNK2001: unresolved external symbol _vsnprintf
+        //       ```
+        //       https://stackoverflow.com/a/34230122
+        println!("cargo:rustc-link-lib=dylib=legacy_stdio_definitions");
+    }
+
+    fn generate_lib_from_dll(out_dir: &Path, vs_exe_path: &Path, vlc_path: &Path) {
+        // https://wiki.videolan.org/GenerateLibFromDll/
+
+        let vs_dumpbin = vs_exe_path.join("dumpbin.exe");
+        let vs_lib = vs_exe_path.join("lib.exe");
+        let vlc_def_path = out_dir.join("libvlc.def");
+        let vlc_import_lib = out_dir.join("vlc.lib");
+
+        let libvlc = vlc_path.join("libvlc.dll");
+        let exports = Command::new(vs_dumpbin)
+            .current_dir(out_dir)
+            .arg("/EXPORTS")
+            .arg(libvlc.display().to_string().trim_end_matches(r"\"))
+            .output()
+            .unwrap();
+        let exports = String::from_utf8(exports.stdout).unwrap();
+
+        let mut vlc_def = String::from("EXPORTS\n");
+        for line in exports.lines() {
+            if let Some(line) = line.get(26..) {
+                if line.starts_with("libvlc_") {
+                    vlc_def.push_str(line);
+                    vlc_def.push_str("\r\n");
+                }
+            }
+        }
+        fs::write(&vlc_def_path, vlc_def.into_bytes()).unwrap();
+
+        // FIXME: Handle paths with spaces in them.
+        Command::new(vs_lib)
+            .current_dir(out_dir)
+            .arg("/NOLOGO")
+            .args(&[
+                format!(
+                    r#"/DEF:{}"#,
+                    vlc_def_path.display().to_string().trim_end_matches(r"\")
+                ),
+                format!(
+                    r#"/OUT:{}"#,
+                    vlc_import_lib.display().to_string().trim_end_matches(r"\")
+                ),
+                format!(
+                    "/MACHINE:{}",
+                    match target_arch().as_str() {
+                        "x86" => "x86",
+                        "x86_64" => "x64",
+                        _ => unreachable!(),
+                    }
+                ),
+            ])
+            .spawn()
+            .unwrap();
+    }
+
+    fn vlc_path() -> PathBuf {
+        #[allow(unused_assignments)]
+        let arch_path: Option<OsString> = match target_arch().as_str() {
+            "x86" => env::var_os("VLC_LIB_DIR_X86"),
+            "x86_64" => env::var_os("VLC_LIB_DIR_X86_64"),
+            _ => unreachable!(),
+        };
+
+        arch_path
+            .or_else(|| env::var_os("VLC_LIB_DIR"))
+            .map(PathBuf::from)
+            .expect("VLC_LIB_DIR not set")
+    }
+
+    fn target_arch() -> String {
+        env::var("CARGO_CFG_TARGET_ARCH").unwrap()
+    }
+}