I l @ ve RuBoard |
It might surprise you to learn that two different runtimes are available to all .NET applications. The most commonly used version (and the default) is the workstation build of the runtime (which is contained in mscorwks.dll). The second version, which is optimized for server applications, is contained in mscorsvr.dll. These builds are designed to provide optimal performance for client applications and multiprocessor server applications. The server build of the runtime makes better use of multiple processors, enabling garbage collection to be done on all processors concurrently (in parallel). Which runtime will your application use? All applications generated by Visual Basic .NET will use the workstation runtime, regardless of what system it is running on or what type of application (Windows Forms, Windows service, console application, and so forth). Only ASP.NET applications run under the server runtime (provided that the machine has four or more processors). Unfortunately, there is currently no way to tell Visual Basic .NET to use the server runtime instead of the default workstation runtime. The only way to tell it to use the server runtime is to host the CLR in your own wrapper. Unfortunately, this means you need to build a native Visual C++ skeleton application that does the work of hosting the CLR and launching your application with the setting you want.
There are distinct advantages to creating your own CLR host other than just loading the server version of the runtime. You can tweak various properties of the CLR, including the number of worker threads (as mentioned in Chapter 3) and completion threads. Among other things, you can:
The CLR is available via a set of COM interfaces, which means you can also host the CLR in a Visual Basic 6.0 application. But I don't recommend doing that. The most efficient way to host the CLR is with a native application. Hosting it in Visual Basic 6.0 would mean you'd be hosting a runtime within a runtime. That would be ugly, and your performance would probably be awful . To help illustrate how to host the CLR with a native application, I built a sample application called SvrLoader (one of the sample files for this chapter) in Visual C++ ”it's the only C++ example in this entire book. It's pretty simple and does not support passing command-line parameters to the .NET executable, but you can add that at your leisure. //SvrLoader.cpp:Loadsanapplicationusingtheserverruntime // #include "stdafx.h" #include "mscoree.h" #include "stdio.h" #import "mscorlib.tlb" named_guidsno_namespaceraw_interfaces_only no_implementationexclude("IID_IObjectHandle", "IObjectHandle") constWCHARAPPLICATION_NAME[]=L"test.exe"; constDWORDMAX_WORKER_THREADS=20; constDWORDMAX_COMPLETION_THREADS=30; ICorRuntimeHost*Init() { //InitializeCOM HRESULThrCOM=::CoInitialize(NULL); if(FAILED(hrCOM)) { printf("UnabletoinitializeCOM.\n"); returnNULL; } LPWSTRpszFlavor=L"svr";//usemscorsvr.dll //PointerfortheRuntimeInterface ICorRuntimeHost*pCorHost=NULL; //Weattempttoloadthe'svr'Runtimehere.Ifitisnotavailable, //inthecaseofaworkstationOS,thenthe'wks'Runtimewillbe //loadedbydefault. HRESULThr=CorBindToRuntimeEx(NULL, pszFlavor, NULL, CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (void**)&pCorHost); if(FAILED(hr)) { printf("UnabletocreatetheHostinginterface.\n"); returnNULL; } //TrytostarttheRuntime hr=pCorHost->Start(); if(FAILED(hr)) { pCorHost->Release();//ReleasetheRuntimeinterface printf("UnabletoinstantiateCOM+Runtime.\n"); returnNULL; } returnpCorHost; } voidShutdown(ICorRuntimeHost*pCorHost) { if(pCorHost!=NULL) pCorHost->Release(); //CleanupCOM ::CoUninitialize(); } BOOLSetMaxThreads() { //setmaxthreads ICorThreadpool*pTpool; HRESULThr; hr=CoCreateInstance(CLSID_CorRuntimeHost, NULL, CLSCTX_INPROC_SERVER, IID_ICorThreadpool, (void**)&pTpool); if(FAILED(hr)) { printf("Unabletoacquirethreadpoolinterface.\n"); returnFALSE; } hr=pTpool->CorSetMaxThreads(MAX_WORKER_THREADS, MAX_COMPLETION_THREADS); pTpool->Release(); if(FAILED(hr)) { printf("Unabletosetmaxthreadsforthreadpool.\n"); returnFALSE; } returnTRUE; } LONGExecuteAssembly(ICorRuntimeHost*pCorHost) { LONGretVal=0; //getthedefaultAppDomain IUnknown*pUnkAppDomain=NULL; HRESULThr=pCorHost->GetDefaultDomain(&pUnkAppDomain); if(FAILED(hr)) { printf("UnabletoacquireIUnknowninterfacefordefaultdomain.\n"); returnfalse; } //getthe_AppDomaininterface _AppDomain*pDomain=NULL; hr=pUnkAppDomain->QueryInterface(IID__AppDomain,(void**)&pDomain); if(FAILED(hr)) { printf("UnabletoacquireAppDomaininterface.\n"); pUnkAppDomain->Release(); return1; } //wenolongerneedpUnkAppDomain pUnkAppDomain->Release(); //executethespecifiedassembly BSTRbstrAssemblyName=SysAllocString(APPLICATION_NAME); hr=pDomain->ExecuteAssembly_2(bstrAssemblyName,&retVal); pDomain->Release(); //freeassemblyBSTR SysFreeString(bstrAssemblyName); if(FAILED(hr)) { if(hr==0x80070002) { printf("Unabletofindmanagedassembly:%s",APPLICATION_NAME); } else { printf("Errorexecutingassembly(hresult=0x%x).\n",hr); } returnretVal; } returnretVal; } // int_tmain(intargc,_TCHAR*argv[]) { ICorRuntimeHost*pCorHost=NULL; pCorHost=Init(); longretVal=1; if(pCorHost!=NULL) { if(SetMaxThreads()) { //Executetheassembly retVal=ExecuteAssembly(pCorHost); } //StoptheRuntime pCorHost->Stop(); } //Cleanupandexit Shutdown(pCorHost); returnretVal; } You can enhance this code to your heart's content. For example, you can
If this is too much for you, don't worry about it. For most systems, the workstation runtime will always be loaded, regardless of which runtime is requested . Essentially, the workstation runtime will always outperform the server runtime on systems with one or two processors, so it won't let you choose otherwise . Loading the server runtime is most critical for server applications on systems with four or more processors. I've included this discussion simply for the sake of completeness. After all, this is a book about enterprise development. |
I l @ ve RuBoard |