Re: mkbundle --machine-configが2.0のconfigで機能しない件

id:atsushieno:20070417:p1

ありがとうございます。あげていただいたdll入れ替えで試してみたのですがどうもやはり読まれてない雰囲気でした。

それでソースにパッチを当てて自分で調べようと思いつつ、チェックアウトしてたら寝てたり(謎)、他の事をやったりで数日がたち追えておりません。すみません。

[Mono] mkbundle

Mac OS Xは諦めて、とりあえずLinuxで。

まあ --deps --static --machine-config で実行してごっそり固めるところまでは簡単で、そのままそのマシン上であれば動きます。
ですがほかのマシンに持ってゆくといい感じで動いてくれません。

たとえばWebRequest.Createの内部のGetCreatorでprefixに対応するIWebRequestCreateを取得できなかったり、そこを無理やり登録してもGetResponseメソッドの中でリクエストを送信する際以下のような感じで死んでみたり。

Unhandled Exception: System.TypeInitializationException: An exception was thrown by the type initializer for System.Net.ServicePointManager ---> System.InvalidCastException: Cannot cast from source type to destination type.
  at System.Net.ServicePointManager..cctor () [0x00000] --- End of inner exception stack trace ---

行数が出ていないのでアレなのですが

		static ServicePointManager ()
		{
#if NET_2_0 && CONFIGURATION_DEP
			object cfg = ConfigurationManager.GetSection (configKey);
			ConnectionManagementSection s = cfg as ConnectionManagementSection;
			if (s != null) {
				manager = new ConnectionManagementData (null);
				foreach (ConnectionManagementElement e in s.ConnectionManagement)
					manager.Add (e.Address, e.MaxConnection);

				return;
			}
#endif
			manager = (ConnectionManagementData) ConfigurationSettings.GetConfig (configKey);
		}

の最後の行で落ちてるようですね。これはWebRequest.GetCreatorとも同じでConfigurationManager.GetSectionが空っぽになっているせい(なのでWebRequestはprefixが何も登録されていない)。

試しに

using System;
using System.Reflection;
using System.Configuration;
using System.Net.Configuration;
using System.IO;
class Test
{
        public static void Main(String[] args)
        {
                Console.WriteLine(ConfigurationManager.GetSection("system.net/connectionManagement"));
                object cfg = ConfigurationManager.GetSection ("system.net/connectionManagement");
                ConnectionManagementSection s = cfg as ConnectionManagementSection;
                if (s != null) {
                 //manager = new ConnectionManagementData (null);
                 foreach (ConnectionManagementElement e in s.ConnectionManagement)
                        Console.WriteLine ("{0} / {1}", e.Address, e.MaxConnection);
                 return;
                }
        }
}

というのを実行してもbundleしたものを持っていった先では何もでません。

で、さっきのコンストラクタの例だとそのままreturnにたどり着かないで最後の行にたどり着いて、GetConfig で ConnectionManagementSection が帰ってきて死亡。

ということはmachine.configが読み込まれてないんじゃ?と思って、どういう風になっているのかと思ったのですが

  1. bundleのCソース
  2. mono/mono/metadata/mono-config.c 511:mono_register_machine_config (const char *config_xml)
  3. mono/mono/metadata/mono-config.c 517:mono_get_machine_config (void)
  4. mono/mono/metadata/icall.c 6055: ves_icall_System_Configuration_DefaultConfig_get_bundled_machine_config (void)
  5. mcs/class/System/System.Configuration/ConfigurationSettings.cs 205: extern private static string get_bundled_machine_config ();
  6. mcs/class/System/System.Configuration/ConfigurationSettings.cs 206: internal static string GetBundledMachineConfig ()
  7. mcs/class/System/System.Configuration/ConfigurationSettings.cs 174: if (data.LoadString (GetBundledMachineConfig ())) {

らしいのでSystem.Configuration.DefaultConfig.GetBundledMachineConfig メソッドでちゃんと返ってくるかどうかみたのですが、一応中身は返ってきてる風。というかConfigurationSettings.GetConfig メソッドでは返ってきますし。

で、ConfigurationManager.GetSection を見てみるわけですが

http://anonsvn.mono-project.com/viewcvs/trunk/mcs/class/System.Configuration/System.Configuration/ConfigurationManager.cs?rev=62342&view=auto
http://anonsvn.mono-project.com/viewcvs/trunk/mcs/class/System.Configuration/System.Configuration/ClientConfigurationSystem.cs?rev=60483&view=auto
http://anonsvn.mono-project.com/viewcvs/trunk/mcs/class/System.Configuration/System.Configuration/InternalConfigurationFactory.cs?rev=46444&view=auto

まあ、System.Configuration.DefaultConfig.GetBundledMachineConfig が呼ばれていなさそうということはわかりました。

そういえばこういうinternalなコアライブラリをデバッグするときはデバッグコードを入れてビルドして、ということをやるのだとしてもビルドの時間とかライブラリの配置とかめんどくさそう……(よくわからないので内部を見たいときはActivatorとMethodInfoでがんばってた)。

そしてその場しのぎで無理やり解決する方法を思いついた

strace でファイルの位置を確認して、

$ mkdir -p mono/2.0/
$ cp machine.config mono/2.0/
$ export MONO_CFG_DIR=./ && ./test
System.Net.Configuration.ConnectionManagementSection
* / 2

ktkr!

ということはmkbundle2のオプションで --config-dir ./ とかやっておけばよさそうと思ったのでやってみたらだいじょぶっぽい。

おー。WebRequest.Createのprefixが登録されない問題もコレでクリア。根本的な解決にはなってないのですが。