Winforms:在Winforms中应用Inproc-SxS

1.     Inproc-SxS简介

InProc-SxS.Net 4.0新推出的功能。在.Net 2.0中,一个进程中只能运行一个版本的.Net的运行时(Runtime)。在.Net 4.0中,我们可以在一个进程中运行多个不同版本的.Net运行时。也就是说在.Net 4.0,我们可以把进程的一部分的运行在.Net 4.0上,而另一部分运行在.Net 2.0上。我们可以把进程、运行时和程序域(AppDomain)用一个图来表示:

 

Winforms:在Winforms中应用Inproc-SxS_第1张图片 

引入Inproc-SxS,给我们构建应用程序带来了很大的灵活性。在很多大型软件中,都允许第三方开发商添加插件(Add-in)。有了Inproc-SxS之后,不同的插件就可以运行在不同的.Net运行时上了。

比如一个软件开发商推出了一个基于.Net 4.0的软件,另一第三方软件开发商开发了该软件的一个插件。该插件的开发商还没有升级到.Net 4.0,仍然基于.Net 2.0开发软件,因此他们的插件还没有在4.0上做过任何测试。如果没有Inproc-SxS,该插件和它的宿主(Host)软件一样,都运行在.Net 4.0。由于插件并没有在4.0下测试过,运行在4.0运行时中就可能会有问题。现在有了Inproc-SxS,我们可以把宿主软件运行在.Net 4.0上,而插件运行在.Net 2.0上。

可能有人会问:.Net应该做到向下兼容,那基于.Net 2.0开发的插件在4.0上运行怎么会有问题呢?的确,微软的.Net 相应的开发团队在向下兼容性方面做了大量的工作。但100%的向下兼容只可能是一个理论上的目标。实际上在推出一个新版本的时候,总会对上一个版本会有若干不兼容的改动。如果用户在某一特定功能上仍然需要上一个版本,他可以利用Inproc-SxS把该功能单独运行在上一个版本的.Net中。

2.     Inproc-SxS实例

下面通过一个实例来讨论如何在Winforms中应用Inproc-SxS。这个实例分为三部分:第一部分是Winforms插件,第二部分负责把插件在某一特定版本的.Net中运行,第三部分是一个宿主应用程序。

2.1 Winforms插件

如下图所示,我们在一个UserControlclass名为CLRVersionControl)里添加一个GroupBox,一个Label和一个PropertyGrid

Winforms:在Winforms中应用Inproc-SxS_第2张图片

 

为了演示需要,我们在Load的事件处理器(Event Handler)中,把GroupBoxText设为当前程序域的名字,而把LabelText设为当前.Net运行时的版本:

        private void CLRVersionControl_Load(object sender, EventArgs e)

        {

            groupBox1.Text = AppDomain.CurrentDomain.FriendlyName;

 

            label1.Text = "CLR version: " + Environment.Version.ToString();

        }

 

同时,我们添加一个函数,用来生成该插件的一个实例,并返回该插件的句柄(Handle)。它的宿主应用程序可以通过该函数创建一个插件,并把该插件添加到宿主中。

        public static IntPtr CreateCLRVersionControl()

        {

            Control control = new CLRVersionControl();

            return control.Handle;

        }

2.2 运行某版本的.Net

这一部分的功能分为两部分:首先我们要创建某一版本的.Net运行时;接着我们在.Net运行时中创建一个程序域的实例,并在该程序域中创建一个插件的实例。

.Net提供了接口ICorRuntimeHostICLRMetaHost ICLRRuntimeInfo来运行某一版本的.Net。下面是一段代码范例。读者可以查阅MSDN对应的帮助文档,我在此不再详细介绍这些API。

CComPtr<ICorRuntimeHost> LoadRunTime(LPCWSTR strVersion)

{

      CComPtr<ICorRuntimeHost> runtimeHost = NULL;

 

      CComPtr<ICLRMetaHost> metahost;

      CComPtr<ICLRRuntimeInfo> rtInfo;

 

      HMODULE hmscoree = LoadLibrary(L"mscoree.dll");

 

      if(hmscoree == NULL) return NULL;

 

      PGETCLRMETAHOST getClrMetaHost = (PGETCLRMETAHOST)GetProcAddress(hmscoree,

            "GetCLRMetaHost");

 

      if(getClrMetaHost == NULL) return NULL;

 

      HRESULT hr = getClrMetaHost(__uuidof(ICLRMetaHost), (LPVOID*)&metahost);

 

      if(SUCCEEDED(hr))

      {

            hr = metahost->GetRuntime(strVersion,

                  IID_ICLRRuntimeInfo,

                  (LPVOID*)&rtInfo);

            if(SUCCEEDED(hr))

            {

                  hr = rtInfo->GetInterface(CLSID_CorRuntimeHost,

                        IID_ICorRuntimeHost,

                        (LPVOID*)&runtimeHost);

                  if(SUCCEEDED(hr))

                  {

                        hr =  runtimeHost->Start();

                        if(FAILED(hr)) runtimeHost.Release();

                  }

            }

      }

 

      if(hmscoree) FreeLibrary(hmscoree);

 

      if(FAILED(hr))

      {

            MessageBox(NULL, L"CLR Load Failed", L"WinFormsSxSTest", MB_OK);

      }

      return runtimeHost;

}

我们在上述代码中输入某一个.Net的版本号,就能得到该版本号对应的.Net运行时。接下来我们在一个.Net运行时里创建一个程序域,并创建一个Winforms插件的实例:

int CreateControl(CComPtr<ICorRuntimeHost> runtimeHost,

      LPCWSTR strAppDomainName,

      LPCWSTR strAssemblyName,

      LPCWSTR strTypeName,

      LPCWSTR strMethodName)

{

      CComPtr<IUnknown> punkAD;

      HRESULT hr = runtimeHost->CreateDomain(strAppDomainName, NULL, &punkAD);

 

      int retValue = 0;

 

      if(SUCCEEDED(hr))

      {

            CComPtr<mscorlib::_AppDomain> srpAD;

            hr = punkAD.QueryInterface(&srpAD);

 

            if(SUCCEEDED(hr))

            {

                  long hashCode = 0;

                  srpAD->raw_GetHashCode(&hashCode);

            }

 

            if(SUCCEEDED(hr))

            {

                  CComPtr<mscorlib::_Assembly> srpAsm;

                  CComBSTR asmName = strAssemblyName;

                  hr = srpAD->raw_Load_2(asmName, &srpAsm);

 

                  if(SUCCEEDED(hr))

                  {

                        CComPtr<mscorlib::_Type> srpType;

                        CComBSTR typeName = strTypeName;

                        hr = srpAsm->raw_GetType_2(typeName, &srpType);

 

                        if(SUCCEEDED(hr) && srpType != NULL)

                        {

                              CComBSTR methodName = strMethodName;

                              CComVariant varEmpty, varRet;

                              hr = srpType->raw_InvokeMember(methodName,

                                    (mscorlib::BindingFlags)

                                    (mscorlib::BindingFlags_InvokeMethod |

                                    mscorlib::BindingFlags_Public |

                                    mscorlib::BindingFlags_Static),

                                    NULL, varEmpty, NULL, NULL, NULL, NULL, &varRet);

 

                              retValue = varRet.llVal;

                        }

                  }

            }

      }

 

      if(FAILED(hr))

      {

            MessageBox(NULL, L"WinForm Load Failed", L"Error", MB_OK);

      }

 

      return retValue;

}

上述代码用调用ICorRuntimeHost::CreateDomain在某版本的.Net运行时中创建一个程序域,让后在该该程序域中调用CLRVersionControl.CreateCLRVersionControl创建Winforms插件CLRVersionControl的一个实例。

有了前面的准备,我们就可以在一个特定的运行时里创建运行CLRVersionControl的实例:

extern "C" __declspec (dllexport) int __stdcall CreateCLRVersionControl

(

      LPCWSTR strVersion,

      LPCWSTR strAppDomainName,

      LPCWSTR strAssemblyName,

      LPCWSTR strTypeName,

      LPCWSTR strMethodName

)

{

      CComPtr<ICorRuntimeHost> runtimeHost = LoadRunTime(strVersion);

 

      return CreateControl(runtimeHost,

            strAppDomainName,

            strAssemblyName,

            strTypeName,

            strMethodName);

}

2.3宿主应用程序

创建一个基于对话框的MFC项目,在该对话框上添加两个Button,如下图所示:

 

为第一个buttonclick添加响应函数:

CWnd* pWndNET2Controls;

 

void CApplicationHostDlg::OnBnClickedButton1()

{

      if(pWndNET2Controls != NULL)

            return;

 

      int handle = CreateCLRVersionControl(L"v2.0.50727",

            L"AppDomain A",

            L"WindowsFormsControlLibrary1",

            L"InProcSxSDemo.CLRVersionControl",

            L"CreateCLRVersionControl");

     

      pWndNET2Controls = new CWnd();

      pWndNET2Controls->Attach((HWND)handle);

      pWndNET2Controls->SetParent(this);

 

      // set size and position

      ……

}

 

在上述代码中,v2.0.50727.Net 2.0的版本号。我们根据返回的插件句柄创建一个窗口,并将该插件的窗口作为子窗口添加到对话框中。

同样我们也为第二个button添加click的响应函数:

CWnd* pWndNET4Controls;

 

void CApplicationHostDlg::OnBnClickedButton2()

{

      if(pWndNET4Controls != NULL)

            return;

 

      int handle = CreateCLRVersionControl(L"v4.0.21002",

            L"AppDomain B",

            L"WindowsFormsControlLibrary1",

            L"InProcSxSDemo.CLRVersionControl",

            L"CreateCLRVersionControl");

 

     

      pWndNET4Controls = new CWnd();

      pWndNET4Controls->Attach((HWND)handle);

      pWndNET4Controls->SetParent(this);

 

      // set size and position

      ……

}

 

V4.0.21002.Net 4.0 Beta2的版本号。读者安装了的.Net 4.0的版本号可能会有所不同,请做相应修改。和前面一样,我们也将插件的窗口作为子窗口添加到对话框中。

现在我们运行我们宿主软件,并先后点击两个button,得到如下效果:

 

Winforms:在Winforms中应用Inproc-SxS_第3张图片

通过上面的截图,我们可以看出来,左边的插件时运行在.Net 2.0中,而右边插件运行在.Net 4.0中。如果仔细观察,我们还能发现在两个插件的PropertyGrid中略有不同:左边的PropertyGrid+或者-来表示树状结点的打开或者收拢的状态,而右边的PropertyGrid用风格的三角形来表述树状结点的状态。这是因为在.Net 4.0中,WinformsPropertyGrid做了一点改动,使PropertyGrid的树状结构和Vista、Windows 7的风格保持一致。

你可能感兴趣的:(.net,assembly,null,button,WinForm,WinForms)