开发者

Calling stateful unmanaged C++ class from ASP.NET webservice

开发者 https://www.devze.com 2023-01-29 04:00 出处:网络
I\'ve inherited a half completed application that seems to use a model that I\'m not sure can reliably work.

I've inherited a half completed application that seems to use a model that I'm not sure can reliably work.

It is a ASP.NET webservice that on each call loads a unmanaged C++ .DLL using

[DllImport ( "kernel32.dll" , EntryPoint = "LoadLibraryA" )]
public static extern int LoadLibrary( string lpLibFileName );

and then makes a number of calls to it e.g.

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

In the unmanaged C++ .dll it is using a singleton to hold state开发者_高级运维 between calls. This is so that it only has to initialise once and load a bunch of slow stuff from disk and database rather than on each webservice call as this is too slow.

So each call into the unmanaged .dll first calls Getinstance() if the instance is null it initialises it and reloads everything, if not it assumes its ready to go.

FreeLibrary is not being called each time in the webservice as I assume this would cause the unmanaged class to have to be re-initialised each time.

Is this model at all reliable? Can you ensure that if the webservice is shut down that the unmanaged state is cleaned up properly? Can you ensure you will relaible get a valid singleton instance between loadlibrary calls?


Is this model at all reliable? Can you ensure that if the webservice is shut down that the unmanaged state is cleaned up properly?

The answer depends on a few things; first and foremost: what kind of state? If you're looking at the kind of things that the kernel is fundamentally responsible for -- memory, file handles, HWNDs -- then you can expect the kernel to clean up when the library is unloaded, whenever that is ("[I]n the gold linker I often deliberately omitted destructors, because many of the data structures live for the life the program; in such a case, destructors serve only to slow down program exit"; yes, I know the gold linker doesn't run on Windows, but the principle still applies).

If you're talking about something that's not guaranteed by the kernel, then I would recommend providing a DllMain function that handles the PROCESS_DETACH message to handle any needed unloading.

Can you ensure you will reliably get a valid singleton instance between loadlibrary calls?

The easy cases are:

  • singleton does not exist
  • Process A needs singleton, creates and uses it
  • Process B needs singleton, sees it already exists, uses it
  • Processes A and B no longer need singleton, it's cleaned up
  • singleton does not exist
  • Process C needs singleton, creates and uses it
  • ...

The more difficult case would involve either race conditions on creation or cleanup:

  • singleton does not exist
  • Process A needs singleton, starts to create it
  • Before Process A finishes, Process B needs singleton, creates it; this is a problem
  • Processes A and B no longer need singleton, begin cleaning it up
  • Before singleton is cleaned up, Process C needs it, sees it exists and tries to use it; this is a problem

These are classic race conditions, and the solution is to make sure the check/create step (and check/cleanup) are atomic. Don't get fancy. Use atomic reference counting or Mutexes


For the record; I'm not a fan of this kind of architecture (singletons in the library). I would instead recommend a library where the state in question is stored in objects. The library API can be C-like (exported functions, CreateXxxObject()/DestroyXxxObject() functions that return pointers to opaque structs, similar to the APR), or C++-like. There will come a time when the singleton model doesn't cut it.

However, I'm only answering the question asked, not saying "first, throw out your plans, ..."


I don't see why you need to load stuff via unmanaged dlls. If you don't know what it does and how it does it I understand, however you always have to doubt if the objects inside the unmanaged dlls have destructors and proper memory management.

If you do know what the dll achieves and it is not too much effort to write it in C# you can use ASP.NET's session state to store data in between calls for each session.

In .NET 2.0 the global.asax is not added by default to web service applictions, however that doesn't prevent you from adding it manually. After all a web service application is still a web application... Without the global.asax each service call will result in a unique sessionid for the duration of the service call, adding it will allow you to persist stuff in between.

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

In the Session_Start method of the global.asax you can add the code that does the slow data retrieval, and so forth (the equivalent of the unmanaged dll...). You can also store that data either on the session itself or create a static variables in the global.asax that will hold the data (just like the singleton in the unmanaged dll)...


I faced a similar problem a few years back working with a legacy unmanaged DLL which could not be upgraded. The issue is that using P/Invoke inside the web server doesn't want play, (I never quite figured out why; my best guess was that it was a security issue). What I had to do was wrap the calls to the DLL in a Windows Service and then use a WCF remoting call to fake the interoperability. Unfortunately, I no longer have access to that code or I would offer you a better sample. I hope this can point you in the right direction.


I recently developed a very similar project. In my case there were several architectures available to me, one using COM+ and the other using p/Invoke. I chose the p/Invoke method because due to simplicity. I had many people tell me outright that this was an incorrect approach, including Microsoft! In the end, I was able to use p/Invoke architecture without a lot of headache, but getting there was not easy. I was forced to address the differences between managed and unmanaged data structures, passing on the heap versus the stack, memory allocation, overwriting buffer/stack issues, ASP.Net security issues, and above all, the architectural differences between managed and unmanaged code. In the end the brain ache left me a much better programmer.

In answer to the question of can an unmanaged, cross language DLL be called re-entrantly from managed code using p/Invoke? Yes it can. BUT; it must be architected to maintain its state in a reentrant manner. This means that the unmanaged DLL must keep track of the memory it allocates, and release that memory each time it’s called from your web service. It must not release or allocate memory that belongs to another web service call.

0

精彩评论

暂无评论...
验证码 换一张
取 消