在.NET 4中调用GDAL库时遇到的问题及解决方法
最近需要在 .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 中没有显式声明的静态构造。
把代码简化一下的话,大概是这样的:
1 | class OsrPINVOKE { |
如果有代码调用 DoSomething ,我对这段代码执行顺序的估计是这样的:
OsrPINVOKE 的静态构造方法(里面初始化 helper 这个静态字段);
SWIGStringHelper 的静态构造方法(输出字符串);
SWIGStringHelper 的实例构造方法(里面啥也没有做);
DoSomething 方法(输出字符串)。
所以应该是先输出 SWIGStringHelper static constructor 而后输出 static method of OsrPINVOKE 。
试着这样调用一下:
1 | static void Main(string[] args) { |
却发现如果用的 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 时遇到了类似的问题,不妨试一下。
参考:
感谢: