免註冊啟用 .NET 元件:逐步解說
Steve White
Microsoft UK 開發人員的頂級支援
Leslie Muller
全球 IT 研究 & 開發,Credit Suisse First Boston
2005 年 7 月
總結: Microsoft Platform SDK 會執行記錄 隔離應用程式和並存元件主題的絕佳工作。 不過,並非所有人都會將本主題與無註冊的 COM 元件啟用相等。 無註冊 COM 是企業在共用基礎結構上隔離鎖定的伺服器和應用程式的絕佳興趣平臺功能。 本文將逐步解說原生用戶端透過 COM Interop 註冊免費啟用 .NET Framework 元件的工作範例。 (11 個列印頁面)
適用於:
Microsoft Windows Server 2003
Microsoft Windows XP Service Pack 2
Microsoft .NET Framework 1.1 版
Microsoft Visual Studio .NET 2003
Microsoft Visual Basic 6.0
下載本文隨附的範例, MSDNRegFreeNet.msi。
Contents
簡介
Registration-Free COM 術語
執行範例
將 .NET 元件建置為 COM 伺服器
建置用戶端
Registration-Free啟用
疑難排解
結論
深入閱讀
簡介
無註冊 COM 是 Microsoft Windows XP (SP2 上適用于) 和 Microsoft Windows Server 2003 平臺.NET Framework型元件的機制。 如其名所示,機制可讓您輕鬆 (,例如 XCOPY) 將 COM 元件部署至電腦,而不需要註冊它們。
在目標平臺上,初始化進程及其相依模組的其中一個階段是將任何相關聯的 資訊清單檔 載入至稱為 啟用內容的記憶體結構。 如果沒有對應的登錄專案,這是提供 COM 執行時間所需之系結和啟用資訊的啟用內容。 COM 伺服器或用戶端中不需要任何特殊程式碼,除非您選擇使用 啟用內容 API自行建置啟用內容,以略過檔案的使用。
在本逐步解說中,我將建置簡單的 .NET 元件,並從以 Visual C++ 和 Visual Basic 6.0 撰寫的原生 COM 用戶端註冊和取消註冊。 您可以立即下載原始程式碼和範例,並立即查看它們,也可以遵循逐步解說並自行建置。
Registration-Free COM 術語
熟悉 .NET 技術的任何人都習慣使用一詞 元件 ,這表示一組已部署的一或多個模組,命名和設定為單位版本,其中一個模組包含定義集合的 資訊清單 。 在無註冊 COM 中, 元件 和 資訊清單 是針對概念類似的概念所借用,但與 .NET 對應專案不同。
無註冊 COM 會使用 元件 來表示一組一或多個 PE 模組 (,也就是原生 或 受控) 部署、命名和設定為單位的版本設定。 無註冊 COM 會使用 資訊清單 來參考副檔名為 .manifest 副檔名的文字檔,其會定義 元件 (資訊清單的身分識別) 與其類別的系結和啟用詳細資料,或定義 應用程式 (應用程式資訊清單) 的身分識別,以及一或多個元件識別參考。 元件資訊清單檔會針對元件命名,而應用程式資訊清單檔則為應用程式命名。
並行 (SxS) 元件 一詞是指透過資訊清單檔案來設定相同 COM 元件的不同版本,以便不同執行緒可以同時載入它們,而不需要註冊。 SxS 啟用,且與 無註冊 COM的鬆散同義字。
執行範例
下載並擷取範例程式碼之後,您會發現名為 \deployed的資料夾。 以下是用戶端應用程式的 Visual C++ 版本 (client.exe) 、其資訊清單 (client.exe.manifest) ,以及 COM 伺服器 C# 版本的 (SideBySide.dll) 。 繼續執行 client.exe。 預期的結果是,client.exe會啟動在SideBySide.dll) 中實作的SideBySideClass (實例,並顯示呼叫其Version方法的結果,看起來應該像 「1.0.0-C#」。
將 .NET 元件建置為 COM 伺服器
步驟 1
在 Visual Studio .NET 2003 中,建立新的 C# 或 Visual Basic .NET 類別庫專案 ,並將其命名為 SideBySide。 移除 AssemblyInfo.[來自專案的 cs/vb] 檔案,並實作 類別,如下所示:
C# 程式碼
using System;
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: Guid("[LIBID_SideBySide]")]
namespace SideBySide
{
[Guid("[IID_ISideBySideClass]")]
public interface ISideBySideClass
{
string Version();
}
[Guid("[CLSID_SideBySideClass]")]
public class SideBySideClass : ISideBySideClass
{
public string Version()
{
return "1.0.0-C#";
}
}
}
Visual Basic .NET 程式碼
Imports System
Imports System.Reflection
Imports System.Runtime.InteropServices
<Assembly: AssemblyVersion("1.0.0.0")>
<Assembly: Guid("[LIBID_SideBySide]")>
<Guid("[IID_ISideBySideClass]")> _
Public Interface ISideBySideClass
Function Version() As String
End Interface
<Guid("[CLSID_SideBySideClass]")> _
Public Class SideBySideClass
Implements ISideBySideClass
Function Version() As String Implements ISideBySideClass.Version
Version = "1.0.0-VB.NET"
End Function
End Class
我撰寫了 GUID 值,其會以預留位置的形式為專案特有。 您必須使用 guidgen 工具來產生唯一的 GUID,這會是我後續使用預留位置時所想要的個別值。
步驟 2
因此,在建置階段會產生並註冊型別程式庫,請將專案的 [COM Interop 註冊 ] 設定設為 true。
步驟 3
產生發行組建,並將 SideBySide.dll 複製到 \deployed。
建置用戶端
下一個步驟是建置用戶端,並針對此逐步解說,您可以選擇建置 Visual C++ 或 Visual Basic 6.0 用戶端。
步驟 4 (選項 A:Visual C++)
在與 SideBySide專案資料夾相對的同層級資料夾中,建立名為client的新Visual C++ Win32 主控台專案。 在 [Win32 應用程式精靈] 的 [ 應用程式設定 ] 索引標籤上,核取 [ 新增 ATL 支援] 核取方塊。
編輯 stdafx.h ,並在 檔案頂端緊接在 之後 #pragma once
新增下列這一行:
#define _WIN32_DCOM
此外, 在 stdafx.h 中,于檔案底部新增下列這一行:
import "[path]\SideBySide.tlb" no_namespace
在這裡, [path] 應該是建置 SideBySide 元件時所產生的類型程式庫相對路徑。 此路徑通常會因您在步驟 1 中選擇 C# 或 Visual Basic .NET 專案而有所不同。
以下列程式碼取代 client.cpp 的內容:
#include "stdafx.h"
#include <iostream>
using namespace std;
void ErrorDescription(HRESULT hr)
{
TCHAR* szErrMsg;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&szErrMsg, 0, NULL) != 0)
{
cout << szErrMsg << endl;
LocalFree(szErrMsg);
}
else
cout << "Could not find a description for error 0x"
<< hex << hr << dec << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
CoInitializeEx(0, COINIT_MULTITHREADED);
{
ISideBySideClassPtr ptr;
HRESULT hr =
ptr.CreateInstance(__uuidof(SideBySideClass));
if (SUCCEEDED(hr))
{
cout << ptr->Version() << endl;
}
ErrorDescription(hr);
char c;
cin >> c;
}
CoUninitialize();
return 0;
}
產生發行組建,並將 \release\client.exe 複製到 \deployed。
步驟 4 (選項 B:Visual Basic 6.0)
建立新的 Visual Basic 6.0 Standard EXE 專案。 在 [專案總管] 中 選取 [Project1 ] 節點,然後在 [ 屬性] 視窗中,將其名稱變更為 用戶端。 選擇 檔案 |另存新檔 ,並將表單檔案和專案檔儲存在 與 SideBySide 專案資料夾相對的同層級資料夾中。 選擇 專案 |參考,勾選 SideBySide 旁的核取方塊,然後選擇 [ 確定]。
按兩下表單設計工具中的主表單,並在 Sub Form_Load () 中貼上下列程式碼:
Dim obj As New SideBySideClass
Dim isxs As SideBySide.ISideBySideClass
Set isxs = obj
MsgBox isxs.Version()
選擇 檔案 |製作client.exe... 並流覽至 \deployed 資料夾,然後選擇 [ 確定]。
步驟 5
目前,除了某些中繼檔案之外, \deployed 資料夾應該包含 ,只有client.exe 和 SideBySide.dll;後者將會由其建置程式註冊。 若要在這些正常情況下檢查您的伺服器和用戶端是否一起運作,請執行 \deployed\client.exe 並記下預期的輸出 「1.0.0-C#」 或 「1.0.0-VB.NET」。
步驟 6
本逐步解說是 關於無註冊 COM,因此我們現在需要取消註冊 SideBySide 元件。 在 Visual Studio 2003 命令提示字元中,流覽至 \deployed 資料夾並執行命令: regasm /u SideBySide.dll
。
步驟 7
若要查看上一個步驟的效果,請再次執行 \deployed\client.exe ,您會看到訊息「類別未註冊」或「執行階段錯誤 '429':ActiveX 元件無法建立物件」訊息。 在這個階段,我們感到挫折 COM 執行時間在登錄中尋找所需的資訊,但我們尚未透過替代方式提供資訊。 我們將在下列步驟中解決此問題。
Registration-Free啟用
步驟 8
在 \deployed 資料夾中,建立應用程式資訊清單檔, (client.exe 應用程式的文字檔) ,並將它呼叫 client.exe.manifest。 將下列內容貼到 檔案中:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<assemblyIdentity
type = "win32"
name = "client"
version = "1.0.0.0" />
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="SideBySide"
version="1.0.0.0" />
</dependentAssembly>
</dependency>
</assembly>
步驟 9
在 SideBySide 專案的 資料夾中, (文字檔) 建立私用組件資訊清單檔案,並呼叫 SideBySide.manifest。 將下列內容貼到 檔案中:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<assemblyIdentity
type="win32"
name=" SideBySide"
version="1.0.0.0" />
<clrClass
clsid="{[CLSID_SideBySideClass]}"
progid="SideBySide.SideBySide"
threadingModel="Both"
name="SideBySide.SideBySideClass" >
</clrClass>
</assembly>
下一個工作是將上述元件資訊清單檔內嵌至 SideBySide 元件作為 Win32 資源。 在撰寫本文時,這是 Windows XP 的必要專案,但不適用於 Windows Server 2003。 在 Windows Server 2003 上, 您可以 直接在元件旁邊部署元件資訊清單檔案即可。 不過,我鼓勵您不要依賴此行為,因為這可能會在即將推出的 Windows Server 2003 Service Pack 中變更。 為了確保您在未來繼續支援這兩個平臺,請遵循後續幾個步驟,並將元件資訊清單檔案內嵌至 .NET 元件作為 Win32 資源。 這僅適用于 的無註冊啟用 。NET 型 元件,並非 原生 COM 元件的無註冊啟用需求。
步驟 10
在 SideBySide 專案的 資料夾中,建立資源定義腳本檔案 (文字檔) ,並呼叫 SideBySide.rc。 將下列內容貼到 檔案中:
#include <windows.h>
#define MANIFEST_RESOURCE_ID 1
MANIFEST_RESOURCE_ID RT_MANIFEST SideBySide.manifest
當您安裝 Platform SDK (Core SDK 一節) 或 Visual C++ 時,可以使用 windows.h 檔案及其相依性。 以下是定義所需的 windows.h 部分:
#define RT_MANIFEST 24
因此, SideBySide.rc 的內容會解析為:
1 24 SideBySide.manifest
不過,更清楚且更一般的方式是依照指示使用巨集定義。
步驟 11
在 SideBySide 專案的 資料夾中,建立建置命令檔 (文字檔) ,並呼叫 它 build.cmd。 將下列內容貼到 檔案中:
若要建置 C#:
rc SideBySide.rc
csc /t:library /out:..\deployed\SideBySide.dll
/win32res:SideBySide.res Class1.cs
若要建置 Visual Basic .NET:
rc SideBySide.rc
vbc /t:library /out:..\deployed\SideBySide.dll
/win32resource:SideBySide.res /rootnamespace:SideBySide Class1.vb
這些命令先從 Platform SDK (rc.exe 叫用 Microsoft Windows 資源編譯器工具,) 將步驟 10 的資源定義腳本編譯成名為 SideBySide.res的已編譯資源檔。接下來,它會叫用 C# 或 Visual Basic .NET 編譯器,將原始程式碼檔案建置到元件中,並將編譯的資源檔內嵌到其中做為 Win32 資源。 編譯的元件會寫入 \deployed 資料夾,但 未 註冊 COM Interop。
步驟 12
在 Visual Studio 2003 命令提示字元中,流覽至 SideBySide 專案的資料夾並執行命令: build
。
步驟 13
若要驗證資訊清單檔案,您的用戶端再次能夠透過 COM Interop 啟用 SideBySideClass 類別、執行 \deployed\client.exe ,並記下預期的輸出 「1.0.0-C#」 或 「1.0.0-VB.NET」。
疑難排解
如我們所見,.NET Framework型元件的無註冊啟用不需要在伺服器或用戶端中使用特殊程式碼。 所有必要專案都是相符的資訊清單檔組,其中一個會內嵌到 .NET 元件中,做為類型為 RT_MANIFEST 的 Win32 資源。
我建議您以本逐步解說的方式,採用您自己的無註冊開發方式。 具體來說:請先查看用戶端使用已註冊的伺服器,以取得已知狀態;然後取消註冊伺服器,並確認您的錯誤訊息是您預期的內容;最後,藉由製作和部署資訊清單檔案來補救這種情況。 如此一來,有關無註冊啟用的疑難排解工作將僅限於資訊清單檔案的結構,以及正確的元件資訊清單內嵌。
針對無註冊的 COM 問題進行疑難排解時,Windows Server 2003 上的事件檢視器是您朋友。 當 Windows XP 或 Windows Server 2003 偵測到組態錯誤時,通常會顯示標題為已啟動之應用程式的錯誤訊息框,並包含「此應用程式因為應用程式設定不正確而無法啟動」訊息。 重新安裝應用程式可能會修正此問題。」建議您每當看到此訊息時,您會在 Windows Server 2003 上重現問題,請參閱系統事件記錄檔,並從 SideBySide 來源尋找事件。 我不建議在這些情況下查看 XP 的事件記錄檔,原因是它不一定會包含「產生啟用內容失敗的 [路徑]\[應用程式檔名] 之類的訊息。清單。 參考錯誤訊息:作業已順利完成」,這無法協助識別問題。
在移至資訊清單檔案的結構之前,讓我們來討論 Win32 資源。 如上所述, windows.h 會將RT_MANIFEST符號定義為值 24,也就是作業系統將辨識為內嵌資訊清單檔案的值。 如果您忘記在資源定義腳本中包含 windows.h (.rc 檔案) ,您的組建仍會成功,而且您的資訊清單檔仍會內嵌為資源,但不是正確的類型。 若要確認您已正確內嵌其資訊清單,請在 Visual Studio (檔案中開啟 您的SideBySide.dll|開啟 |檔。。。) 。您會看到一個樹狀檢視,其中顯示模組內的資源。 根節點下應該是名為 RT_MANIFEST 的節點,其下方應該是另一個節點,其中顯示逐步解說) 中資訊清單資源的資源編號 (1。 按兩下這個最後一個節點,即可在二進位檢視中查看資料,並快速檢查它是否與 XML 資訊清單檔案類似。 雖然它是二進位的,但 ASCII 範圍的字元會很明顯。 如果二進位資料遺失或看起來不正確,請確認您的資源定義腳本 (.rc 檔案) 參考您的資訊清單檔。 如果RT_MANIFEST節點的文字是以引號括住,您可能忘記在資源定義腳本中包含 windows.h (.rc 檔案) 。
每個剛才提及的錯誤都會在 Windows Server 2003 系統事件記錄檔中回報,訊息為:「找不到相依元件 [名稱],且上次錯誤是未在您的系統上安裝參考的元件。」
各種資訊清單檔案的架構記載于平臺 SDK 標題的 [資訊清單檔案參考] 底下,而架構驗證工具 Manifestchk.vbs 可供使用,因此在這裡,我只會呼叫幾個與逐步解說相關的點。 首先,讓我們檢查元件資訊清單檔。 如需範例,請回到步驟 9。
您記得,在 無註冊的 COM 意義中, 元件 是一個抽象概念,您可以利用 元件資訊清單 檔案的內容來建立一或多個實體檔案的關聯。
assemblyIdentity元素會定義元件的識別。 針對 。NET 型 元件的名稱屬性 必須符合 .NET 元件的名稱,因此其檔案名,否則您會在 Windows Server 2003 系統事件記錄檔中看到下列訊息:「找不到相依元件 [ name 屬性的值],且上次錯誤是未安裝在系統上的參考元件。」不過, 版本 屬性不需要符合 .NET 元件的 AssemblyVersion或其 AssemblyFileVersion,雖然最好套用某種一致性。
clrClass元素只有兩個必要屬性:name和clsid。 name屬性必須符合要啟動之 CLR 類別的合併命名空間和類別名稱。 如果沒有,CoCreateInstance 會傳回具有值COR_E_TYPELOAD (0x80131522) 的 HRESULT。 當類型載入器在 .NET 元件中找不到要求的 CLR 類型時,就會擲回 System.TypeLoadException。 如果您的 .NET 元件是以 Visual Basic .NET 撰寫,要watch一件事,就是您要在命令列上提供 /rootnamespace 參數給 Visual Basic .NET 編譯器 (vbc.exe) 。 clsid屬性必須符合指派給透過其 GuidAttribute 啟動之 CLR 類別的 CLSID。 如果沒有,CoCreateInstance 會傳回具有值REGDB_E_CLASSNOTREG (0x80040154) 的 HRESULT,也就是「類別未註冊」的訊息文字。
現在讓我們將注意力轉向應用程式資訊清單檔案。 如需範例,請回到步驟 8。 應用程式資訊清單 的格式必須是[application filename].manifest。 因此,在逐步解說中,它會命名 為client.exe.manifest ,讓它清楚顯示每當 載入進程client.exe 時應該讀取。 如果未正確完成,CoCreateInstance 會傳回具有值REGDB_E_CLASSNOTREG (0x80040154) 的 HRESULT,其訊息文字為「類別未註冊」。
應用程式資訊清單中最重要的元素是dependentAssembly/assemblyIdentity元素。 這個專案是元件資訊清單中對等專案的參考,而兩者必須 完全相符。 確保這麼做的好方法是從元件資訊清單複製元素,並將其貼到此處。 如果有任何差異,您會在 Windows Server 2003 系統事件記錄檔中看到下列訊息:「在資訊清單中找到的元件身分識別不符合要求的元件身分識別」。
結論
無註冊 COM 是一種技術,可從相依于 Windows 登錄的 COM 元件,進而使使用這些元件的應用程式不需要專用伺服器。 它可讓相依于相同 COM 元件的不同版本的應用程式共用基礎結構,並在 .NET 版本設定和部署機制的回應中並排載入這些各種 COM 元件版本。
本文將逐步引導您示範以 Visual C++ 和 Visual Basic 6.0 撰寫的原生用戶端應用程式,以無註冊方式啟用以.NET Framework為基礎的元件。 其中說明機制的運作方式,並加上一些可能的設定錯誤底線,以及如何進行疑難排解。
深入閱讀
關於作者
Steve White 是一位應用程式開發顧問,在 Microsoft UK 的開發人員頂級支援小組中工作。 他支援使用 Visual C# 進行開發的客戶,Windows Forms和 ASP.NET。 他的 部落格 提供有關音樂、視覺效果和程式設計興趣的詳細資訊。
Leslie Muller 是一位技術工作者,與 Credit Suisse First Boston 的 Research & Development 小組合作。 Leslie 有 12 年的開發人員和技術架構師經驗,在金融服務、技術啟動、產業自動化和工業自動化等環境中工作。 當他不是程式設計或進行研究時,他喜歡雪球、雪球,以及盡可能在極極端的環境中,對馬達車輛執行一些有趣的事,例如愛爾蘭或岩石。