从ASP.NET webservice调用有状态的非托管C ++类

时间:2010-12-07 23:44:11

标签: c# asp.net c++ web-services unmanaged

我继承了一半已完成的应用程序,似乎使用的模型我不确定是否可以正常工作。

这是一个ASP.NET Web服务,在每次调用时都使用

加载非托管C ++ .DLL
[DllImport ( "kernel32.dll" , EntryPoint = "LoadLibraryA" )]
public static extern int LoadLibrary( string lpLibFileName );

然后对它进行多次调用,例如

 [DllImport(@"MyUnamanagedDLL.dll")]
 public static extern string DoStuff( );

在非托管C ++ .dll中,它使用单例来保持调用之间的状态。这样只需要初始化一次并从磁盘和数据库加载一堆缓慢的东西而不是每个webservice调用,因为这太慢了。

因此,对非托管.dll的每次调用首先调用Getinstance(),如果实例为null,则初始化它并重新加载所有内容,如果没有,则假定它已准备就绪。

每次在webservice中都没有调用FreeLibrary,因为我认为这会导致每次都必须重新初始化非托管类。

这个型号是否完全可靠?您是否可以确保在关闭Web服务时是否正确清理了非托管状态?你能确保你可以在loadlibrary调用之间获得有效的单例实例吗?

4 个答案:

答案 0 :(得分:1)

  

这个型号是否完全可靠?如果Web服务关闭,可以确保正常清理非托管状态吗?

答案取决于一些事情;首要的是:什么样的国家?如果您正在查看内核从根本上负责的事情 - 内存,文件句柄,HWND - 那么you can expect the kernel to clean up when the library is unloaded,无论何时(“[我]黄金链接器我经常故意省略析构函数,因为许多数据结构一直存在于程序中;在这种情况下,析构函数仅用于减慢程序退出“;是的,我知道黄金链接器不能在Windows上运行,但原理仍然适用)

如果你在谈论内核无法保证的事情,那么我建议提供一个DllMain函数来处理PROCESS_DETACH消息以处理任何所需的卸载。

  

您能确保在loadlibrary调用之间可靠地获得有效的单例实例吗?

简单的案例是:

  • 单身人士不存在
  • 进程A需要单例,创建和使用它
  • 流程B需要单身,看它已经存在,使用它
  • 进程A和B不再需要单例,它已被清理
  • 单身人士不存在
  • 进程C需要单例,创建和使用它
  • ...

更难的案例涉及创建或清理的竞争条件:

  • 单身人士不存在
  • 进程A需要单例,开始创建它
  • 在流程A完成之前,流程B需要单例,创建它;这是一个问题
  • 进程A和B不再需要单例,开始清理它
  • 在清理单例之前,进程C需要它,看到它存在并尝试使用它;这是一个问题

这些是经典竞争条件,解决方案是确保检查/创建步骤(和检查/清理)是原子的。不要花哨。使用atomic引用计数或Mutexes


记录;我不喜欢这种架构(图书馆里的单身人士)。我建议使用一个库,其中有问题的状态存储在对象中。库API可以是类C(导出函数,CreateXxxObject()/ DestroyXxxObject()函数,返回指向不透明struct的指针,类似于APR)或类似C ++。单身人士模型不会削减它。

但是,我只回答问题,而不是说“首先,抛出你的计划,......”

答案 1 :(得分:0)

我不明白为什么你需要通过非托管dll加载东西。如果您不知道它的作用以及它是如何做的,我理解,但是您总是不得不怀疑非托管dll中的对象是否具有析构函数和适当的内存管理。

如果您确实知道dll实现了什么,并且在C#中编写它并不太费力,您可以使用ASP.NET的会话状态在每个会话的调用之间存储数据。

在.NET 2.0中,默认情况下不会将global.asax添加到Web服务应用程序,但这并不妨碍您手动添加它。毕竟一个Web服务应用程序仍然是一个Web应用程序...如果没有global.asax,每个服务调用将在服务调用期间产生一个唯一的sessionid,添加它将允许您在两者之间保留内容。

protected void Session_Start( object sender, EventArgs e ) 
{ 
  Trace.WriteLine( "Session_Start" ); 
}

在global.asax的Session_Start方法中,您可以添加执行慢速数据检索的代码,依此类推(相当于非托管dll ...)。您还可以将这些数据存储在会话本身上,或者在global.asax中创建一个静态变量来保存数据(就像非托管dll中的单例一样)......

答案 2 :(得分:0)

几年前,我遇到了类似的问题,使用了无法升级的旧版非托管DLL。问题是在Web服务器中使用P / Invoke不想玩,(我从来没有弄清楚为什么;我最好的猜测是它是一个安全问题)。我必须做的是将调用包装在Windows服务中,然后使用WCF远程调用来伪造互操作性。不幸的是,我不再能够访问该代码,或者我会为您提供更好的示例。我希望这可以指出你正确的方向。

答案 3 :(得分:0)

我最近开发了一个非常相似的项目。在我的例子中,我可以使用几种架构,一种使用COM +,另一种使用p / Invoke。我选择了p / Invoke方法,因为简单。我有很多人直截了当地告诉我这是一个不正确的方法,包括微软!最后,我能够使用p / Invoke架构而没有太多头痛,但到那里并不容易。我被迫解决托管和非托管数据结构之间的差异,传递堆与堆栈,内存分配,覆盖缓冲区/堆栈问题,ASP.Net安全问题,以及最重要的是托管代码和非托管代码之间的架构差异。最后,大脑疼痛给我留下了更好的程序员。

在回答问题时,可以使用p / Invoke从托管代码中重新调用非托管的跨语言DLL吗?是的,它可以。但;必须将其架构为以可重入的方式维持其状态。这意味着非托管DLL必须跟踪它分配的内存,并在每次从Web服务调用时释放该内存。它不得释放或分配属于另一个Web服务调用的内存。