Windowsでiconv/dynなVimに読んで欲しいiconvのdllを探せない問題


## はじめに

しばらくアップデートしていなかったVimを新しいバージョンにしたのだが、自前でビルドしている`iconv.dll`を読まなくなってしまい、一部ファイルが編集できなくなってしまった。
このファイルを編集できなければ困るので結局前のバージョンに戻してしまったのだが、本日ようやく原因が分かったので共有する。

## `iconv.dll`の読み込み順序

[vim-jpのこのIssue](https://github.com/vim-jp/issues/issues/778)によると、7.4.892時点では下記の順序で読むようになっているようだ。

1. iconv.dll
2. libiconv.dll
3. libiconv2.dll
4. libiconv-2.dll

[香り屋版Vim](http://www.kaoriya.net/software/vim/)には`iconv.dll`として同梱されていたため、自前でビルドしたiconvも同様に`iconv.dll`という名前にしてVimに読ませていた。

この読み込み順序は[src/mbyte.c](https://github.com/vim/vim/blob/master/src/mbyte.c)に書いており、7.4.892のパッチを見るとこのように実装されている。

```diff
diff --git a/src/mbyte.c b/src/mbyte.c
index 75559b0..6046d7d 100644
--- a/src/mbyte.c
+++ b/src/mbyte.c
@@ -4400,7 +4400,9 @@ static HINSTANCE hMsvcrtDLL = 0;
 
 #  ifndef DYNAMIC_ICONV_DLL
 #   define DYNAMIC_ICONV_DLL "iconv.dll"
-#   define DYNAMIC_ICONV_DLL_ALT "libiconv.dll"
+#   define DYNAMIC_ICONV_DLL_ALT1 "libiconv.dll"
+#   define DYNAMIC_ICONV_DLL_ALT2 "libiconv2.dll"
+#   define DYNAMIC_ICONV_DLL_ALT3 "libiconv-2.dll"
 #  endif
 #  ifndef DYNAMIC_MSVCRT_DLL
 #   define DYNAMIC_MSVCRT_DLL "msvcrt.dll"
@@ -4456,9 +4458,16 @@ iconv_enabled(verbose)
 {
     if (hIconvDLL != 0 && hMsvcrtDLL != 0)
 	return TRUE;
+
+    /* The iconv DLL file goes under different names, try them all. */
     hIconvDLL = vimLoadLib(DYNAMIC_ICONV_DLL);
-    if (hIconvDLL == 0)		/* sometimes it's called libiconv.dll */
-	hIconvDLL = vimLoadLib(DYNAMIC_ICONV_DLL_ALT);
+    if (hIconvDLL == 0)
+	hIconvDLL = vimLoadLib(DYNAMIC_ICONV_DLL_ALT1);
+    if (hIconvDLL == 0)
+	hIconvDLL = vimLoadLib(DYNAMIC_ICONV_DLL_ALT2);
+    if (hIconvDLL == 0)
+	hIconvDLL = vimLoadLib(DYNAMIC_ICONV_DLL_ALT3);
+
     if (hIconvDLL != 0)
 	hMsvcrtDLL = vimLoadLib(DYNAMIC_MSVCRT_DLL);
     if (hIconvDLL == 0 || hMsvcrtDLL == 0)
```

ではどこかでこの順序が変わってしまったのだろうとパッチを順番に見ていくと、7.4.1165で以下のように変更されているのを見つけた。

```diff
diff --git a/src/mbyte.c b/src/mbyte.c
index edca9a7..0e4b7e4 100644
--- a/src/mbyte.c
+++ b/src/mbyte.c
@@ -4392,21 +4392,21 @@ iconv_string(vcp, str, slen, unconvlenp, resultlenp)
  * Dynamically load the "iconv.dll" on Win32.
  */
 
-#ifndef DYNAMIC_ICONV	    /* just generating prototypes */
-# define HINSTANCE int
-#endif
+#   ifndef DYNAMIC_ICONV	    /* must be generating prototypes */
+#    define HINSTANCE int
+#   endif
 static HINSTANCE hIconvDLL = 0;
 static HINSTANCE hMsvcrtDLL = 0;
 
-#  ifndef DYNAMIC_ICONV_DLL
-#   define DYNAMIC_ICONV_DLL "iconv.dll"
-#   define DYNAMIC_ICONV_DLL_ALT1 "libiconv.dll"
-#   define DYNAMIC_ICONV_DLL_ALT2 "libiconv2.dll"
-#   define DYNAMIC_ICONV_DLL_ALT3 "libiconv-2.dll"
-#  endif
-#  ifndef DYNAMIC_MSVCRT_DLL
-#   define DYNAMIC_MSVCRT_DLL "msvcrt.dll"
-#  endif
+#   ifndef DYNAMIC_ICONV_DLL
+#    define DYNAMIC_ICONV_DLL "iconv.dll"
+#    define DYNAMIC_ICONV_DLL_ALT1 "libiconv.dll"
+#    define DYNAMIC_ICONV_DLL_ALT2 "libiconv2.dll"
+#    define DYNAMIC_ICONV_DLL_ALT3 "libiconv-2.dll"
+#   endif
+#   ifndef DYNAMIC_MSVCRT_DLL
+#    define DYNAMIC_MSVCRT_DLL "msvcrt.dll"
+#   endif
 
 /*
  * Get the address of 'funcname' which is imported by 'hInst' DLL.
@@ -4459,14 +4459,22 @@ iconv_enabled(verbose)
     if (hIconvDLL != 0 && hMsvcrtDLL != 0)
 	return TRUE;
 
-    /* The iconv DLL file goes under different names, try them all. */
-    hIconvDLL = vimLoadLib(DYNAMIC_ICONV_DLL);
-    if (hIconvDLL == 0)
-	hIconvDLL = vimLoadLib(DYNAMIC_ICONV_DLL_ALT1);
+    /* The iconv DLL file goes under different names, try them all.
+     * Do the "2" version first, it's newer. */
+#ifdef DYNAMIC_ICONV_DLL_ALT2
     if (hIconvDLL == 0)
 	hIconvDLL = vimLoadLib(DYNAMIC_ICONV_DLL_ALT2);
+#endif
+#ifdef DYNAMIC_ICONV_DLL_ALT3
     if (hIconvDLL == 0)
 	hIconvDLL = vimLoadLib(DYNAMIC_ICONV_DLL_ALT3);
+#endif
+    if (hIconvDLL == 0)
+	hIconvDLL = vimLoadLib(DYNAMIC_ICONV_DLL);
+#ifdef DYNAMIC_ICONV_DLL_ALT1
+    if (hIconvDLL == 0)
+	hIconvDLL = vimLoadLib(DYNAMIC_ICONV_DLL_ALT1);
+#endif
 
     if (hIconvDLL != 0)
 	hMsvcrtDLL = vimLoadLib(DYNAMIC_MSVCRT_DLL);
```

パッチにもあるように7.4.1165から

1. libiconv2.dll
2. libiconv-2.dll
3. iconv.dll
4. libiconv.dll

の順序で読み込むように変更されたようだ。
調べてみると昔にインストールした何か由来の`libiconv2.dll`を真っ先に見つけて読むようになっていた。
自前ビルド版のiconvのdllを`libiconv2.dll`として`vim.exe`と同一ディレクトリに配置してやることで本来読んで欲しいiconvのdllを読ませることに成功した。

**追記**

古い`iconv.dll`を削除すると

> コンピューターに iconv.dll がないため、プログラムを開始できません。この問題を解決するには、プログラムを再インストールしてみてください

というエラーが出るようになった。
どうも`intl.dll`等が`iconv.dll`がある前提となっており、`libiconv2.dll`だけでは立ち上がらないようだ。
仕方がないので`libiconv2.dll`のハードリンクを`iconv.dll`という名前で作成すること解決とした。

## 蛇足

香り屋版Vimの配布方法が[githubのreleasesを使う方法に変更](http://www.kaoriya.net/blog/2016/04/09/)されるようだ。
こちらのProxyではAmazon AWSとの通信を問答無用で遮断されてしまうので、今後新バージョンをダウンロードできなくなってしまった。
仕方がないのでMinGW-w64によるビルド環境を整え、自前でビルドするようにした。