最近需要在.NET 4的环境中调用GDAL库。GDAL本身是一套非托管类库,不过还好提供了托管的Wrapper。
这些托管的程序集被包含在了FWTools的安装包中,FWTools中带的版本依赖于gdal_fw.dll,gdal_fw.dll是GDAL核心类库的修改版,而它依赖的其他非托管程序集太多了,加起来有18M左右。所以还是自己下载代码编译的好。
这篇文章介绍了1.4版本的编译方法,该方法同样适用于现在的1.7版本。
编译好之后引用、调用、Debug都没问题,一切正常,但是如果用Release编译并在VS之外运行的话则会报出AccessViolationException,异常信息提示说访问了受保护的内存。我的第一反应就是托管的Wrapper中用P/Invoke调用了非托管程序集,而非托管程序集导致了这个问题。但是这个猜测并不能解释为什么只有在.NET 4+Release+IDE外运行的情况下才会出错的现象。
猜来猜去,找来找去找到了问题的所在:
GDAL的托管Wrapper中有一个叫做SWIGStringHelper的类型,该类型的静态构造方法中执行了一些比较重要的初始化操作。另外一个叫做OsrPINVOKE的类中声明了一个SWIGStringHelper类型的私有静态字段,并在声明时就初始化了该字段,而且OsrPINVOKE中没有显式声明的静态构造。
把代码简化一下的话,大概是这样的:
如果有代码调用DoSomething,我对这段代码执行顺序的估计是这样的:
OsrPINVOKE的静态构造方法(里面初始化helper这个静态字段);
SWIGStringHelper的静态构造方法(输出字符串);
SWIGStringHelper的实例构造方法(里面啥也没有做);
DoSomething方法(输出字符串)。
所以应该是先输出SWIGStringHelper static constructor而后输出static method of OsrPINVOKE。
试着这样调用一下:
却发现如果用的target framework是.net 4,用release编译并且在VS外运行的话,就会只输出static method of OsrPINVOKE,感觉好像SWIGStringHelper的静态构造方法没有执行。而如果用的是.net 2.0、3.5,或者是用Debug编译或是在VS里面运行的话都不会有问题。
难道是静态字段的初始化在.NET 4中变成Lazy的了?
事实证明真的是这样。所以要解决这个问题的话只要在OsrPINVOKE里面显示声明一个静态构造方法,把new SWIGStringHelper();这一句放到里执行就OK了。
如果您在.NET 4中调用GDAL时遇到了类似的问题,不妨试一下。
参考:
感谢: