共用方式為


將 32 位 Managed 程式碼移轉至 64 位

 

Microsoft Corporation

更新日期:2005 年 5 月

適用於:
   Microsoft .NET
   Microsoft .NET Framework 2.0

總結: 瞭解將 32 位受控應用程式移轉至 64 位、可能會影響移轉的問題,以及可用來協助您的工具。 (17 個列印的頁面)

目錄

簡介
32 位環境中的 Managed 程式碼
輸入 64 位環境的 CLR
移轉和平臺叫用
移轉和 COM 互通性
移轉和不安全的程式碼
移轉和封送處理
移轉和序列化
總結

簡介

本白皮書討論:

  • 將受控應用程式從 32 位移轉至 64 位時牽涉到什麼
  • 可能會影響移轉的問題
  • 有哪些工具可協助您

這項資訊並非規定性;而是想要讓您熟悉在移轉至 64 位的過程中容易發生問題的不同區域。 此時,您可以遵循並確保程式碼在 64 位上運作的步驟沒有特定的「操作手冊」。 本白皮書中包含的資訊會讓您熟悉不同的問題,以及應該檢閱的內容。

如您所見,如果您的 Managed 元件不是 100% 類型的安全程式碼,您必須檢閱您的應用程式及其相依性,以判斷移轉至 64 位的問題。 您可以在下一節中瞭解的許多專案,都可以透過程式設計變更來解決。 在許多情況下,如果您想要同時在兩個環境中執行,您也必須保留一些時間來更新程式碼,才能在 32 位和 64 位環境中正確執行。

Microsoft .NET 是一組用來連接資訊、人員、系統和裝置的軟體技術。 自 2002 年 1.0 版起,組織已成功部署 。不論內部、獨立軟體廠商 (ISV) 或某種組合,都是以 NET 為基礎的解決方案。 有數種類型的 .NET 應用程式會推送 32 位環境的限制。 這些挑戰包括但不限於更實際可定址記憶體的需求,以及增加浮點效能的需求。 x64 和 Itanium 提供比您可以在 x86 上取得的浮點運算更好的效能。 不過,您取得的 x64 或 Itanium 結果也可能與您在 x86 上取得的結果不同。 64 位平臺旨在協助解決這些問題。

隨著 .NET Framework 2.0 版的發行,Microsoft 支援在 x64 和 Itanium 64 位平臺上執行的 Managed 程式碼。

Managed 程式碼只是「程式碼」,可提供足夠的資訊,以允許 .NET Common Language Runtime (CLR) 提供一組核心服務,包括:

  • 透過中繼資料自我描述程式碼和資料
  • 堆疊走動
  • 安全性
  • 記憶體回收
  • Just-In-Time 編譯

除了 Managed 程式碼之外,還有數個其他定義,當您調查移轉問題時,請務必瞭解這些定義。

受控資料- 配置在 Managed 堆積上的資料,並透過垃圾收集進行收集。

元件-可讓 CLR 完整瞭解應用程式內容的部署單位,以及強制執行應用程式所定義的版本設定和相依性規則。

型別安全程式碼— 只使用 Managed 資料的程式碼,而且沒有無法驗證的資料類型或不支援的資料類型轉換/強制轉換作業 (亦即非區分聯集或結構/介面指標) 。 使用 /clr:safe 編譯的 C#、Visual Basic .NET 和 Visual C++ 程式碼會產生型別安全程式碼。

Unsafe Code— 允許執行這類較低層級作業的程式碼,例如宣告和操作指標、在指標和整數類型之間執行轉換,以及取得變數的位址。 這類作業允許與基礎作業系統互動、存取記憶體對應裝置,或實作時間關鍵演算法。 原生程式碼不安全。

32 位環境中的 Managed 程式碼

若要瞭解將 Managed 程式碼移轉至 64 位環境所涉及的複雜度,讓我們來檢閱 Managed 程式碼在 32 位環境中執行的方式。

選取要執行的應用程式受控或 Unmanaged 時,會叫用 Windows 載入器,並負責決定如何載入及執行應用程式。 此程式的一部分涉及查看可執行檔的可攜式執行 (PE) 標頭,以判斷是否需要 CLR。 您可能已經猜到,PE 中有指出 Managed 程式碼的旗標。 在此情況下,Windows 載入器會啟動 CLR,然後負責載入和執行受控應用程式。 (這是一個簡化的程式描述,因為涉及許多步驟,包括判斷要執行的 CLR 版本、設定 AppDomain 'sandbox' 等)

互通性

當受控應用程式執行時,它可以 (假設適當的安全性許可權) 透過 CLR 互通性功能與原生 API 互動, (包括 WIN32 API) 和 COM 物件。 不論在 32 位環境中完全執行時,呼叫原生平臺 API、提出 COM 要求或封送處理結構,開發人員都與必須考慮資料類型大小和資料對齊隔離。

考慮移轉至 64 位時,請務必研究應用程式擁有的相依性。

輸入 64 位環境的 CLR

為了讓 Managed 程式碼在與 32 位環境一致的 64 位環境中執行,.NET 小組為 Itanium 和 x64 64 位系統開發 Common Language Runtime (CLR) 。 CLR 必須嚴格遵守 Common Language Infrastructure (CLI) 和 Common Language Type System 的規則,以確保以任何 .NET 語言撰寫的程式碼都能在 32 位環境中互通。 此外,以下是一些其他部分的清單,這些部分也必須移植和/或針對 64 位環境進行開發:

  • System.* (基類庫)
  • Just-In-Time 編譯器
  • 偵錯支援
  • .NET Framework SDK

64 位 Managed 程式碼支援

.NET Framework 2.0 版支援執行下列專案的 Itanium 和 x64 64 位處理器:

  • Windows Server 2003 SP1
  • 未來的 Windows 64 位用戶端版本

(您無法在 Windows 2000 上安裝 .NET Framework 2.0 版。使用 .NET Framework 1.0 和 1.1 版所產生的輸出檔案將會在 64 位作業系統的 WOW64 下執行。)

在 64 位平臺上安裝 .NET Framework 2.0 版時,您不只會安裝所有必要的基礎結構,以在 64 位模式中執行 Managed 程式碼,而是要安裝受控程式碼在 Windows-on-Windows 子系統中執行的必要基礎結構,或 WoW64 (32 位模式) 。

簡單的 64 位移轉

請考慮 100% 型別安全程式碼的 .NET 應用程式。 在此案例中,您可以採用您在 32 位電腦上執行的 .NET 可執行檔,並將其移至 64 位系統,並成功執行。 為什麼這可以運作? 由於元件是 100% 型別安全,因此我們知道原生程式碼或 COM 物件沒有相依性,而且沒有「不安全」程式碼,這表示應用程式完全在 CLR 的控制下執行。 CLR 保證當 Just-In-Time (JIT) 編譯所產生的二進位程式碼在 32 位和 64 位之間會有所不同,但執行的程式碼會以語意方式相同。 (您無法在 Windows 2000 上安裝 .NET Framework 2.0 版。使用 1.0 和 1.1 版.NET Framework產生的輸出檔案將會在 64 位作業系統的 WOW64 下執行。)

事實上,從載入受控應用程式的觀點來看,上一個案例比較複雜。 如上一節所述,Windows 載入器負責決定如何載入和執行應用程式。 不過,不同于 32 位環境,在 64 位 Windows 平臺上執行表示有兩個 (2 個) 環境可在原生 64 位模式或 WoW64 中執行應用程式。

Windows 載入器現在必須根據其在 PE 標頭中發現的內容做出決策。 您可能已經猜到 Managed 程式碼中有可設定的旗標,可協助進行此程式。 (請參閱corflags.exe,以顯示 PE.) 下列清單代表 PE 中找到的資訊,可協助決策制定程式。

  • 64 位 - 表示開發人員已建置以 64 位進程為目標的元件。
  • 32 位 - 表示開發人員已建置特別以 32 位進程為目標的元件。 在此實例中,元件會在 WoW64 中執行。
  • 無從驗證-表示開發人員使用 Visual Studio 2005 建置元件,並命名為 「Whidbey」 的程式碼。 或更新版本的工具和元件可以在 64 位或 32 位模式中執行。 在此情況下,64 位 Windows 載入器會在 64 位中執行元件。
  • 舊版 - 表示建置元件的工具是「pre-Whidbey」。 在此情況下,元件將會在 WoW64 中執行。

注意 PE 中也有資訊,告知 Windows 載入器元件是否以特定架構為目標。 這項額外資訊可確保以特定架構為目標的元件不會在不同的架構中載入。

C#、Visual Basic .NET 和 C++ Whidbey 編譯器可讓您在 PE 標頭中設定適當的旗標。 例如,C# 和 THIRD 有 /platform:{anycpu、x86、Itanium、x64} 編譯器選項。

注意 雖然在編譯元件之後,技術上可以修改元件的 PE 標頭中的旗標,但 Microsoft 不建議這麼做。

如果您想知道如何在 Managed 元件上設定這些旗標,您可以執行 .NET Framework SDK 中提供的 ILDASM 公用程式。 下圖顯示「舊版」應用程式。

請記住,將元件標示為 Win64 的開發人員判斷應用程式的所有相依性都會以 64 位模式執行。 64 位進程無法在進程 (中使用 32 位元件,而 32 位進程無法在進程) 載入 64 位元件。 請記住,系統將元件載入 64 位進程的能力並不會自動表示其會正確執行。

因此,我們現在知道由 100% 型別安全型別的 Managed 程式碼所組成的應用程式可以複製 (或 xcopy 將) 它部署至 64 位平臺,並讓它 JIT 並在 64 位模式下順利執行 .NET。

不過,我們通常會看到不理想的情況,並讓我們成為這份檔的主要焦點,也就是提高移轉相關問題的認知。

您可以擁有非 100% 型別安全的應用程式,而且仍然能夠在 .NET 下的 64 位中順利執行。 請務必仔細查看應用程式,請記住下列各節中討論的潛在問題,並判斷您是否可以在 64 位中成功執行。

移轉和平台叫用

利用 .NET 的平台叫用 (或 p/invoke) 功能是指對非受控或原生程式碼進行呼叫的 Managed 程式碼。 在典型的案例中,此機器碼是動態連結程式庫 (DLL) ,屬於系統 (Windows API 等) 、應用程式的一部分或協力廠商程式庫。

使用非 Managed 程式碼並不表示移轉至 64 位時會有問題;而是應該視為需要其他調查的指標。

Windows 中的資料類型

每個應用程式和每個作業系統都有抽象資料模型。 許多應用程式不會明確公開此資料模型,但模型會引導撰寫應用程式程式碼的方式。 在 32 位程式設計模型中, (稱為 ILP32 模型) 、整數、long 和指標資料類型的長度為 32 位。 大部分的開發人員都已使用此模型,而不需要察覺。

在 64 位 Microsoft Windows 中,資料類型大小的同位假設無效。 讓所有資料類型的長度為 64 位會浪費空間,因為大部分的應用程式不需要增加的大小。 不過,應用程式確實需要 64 位資料的指標,而且需要在選取的案例中具有 64 位資料類型的能力。 這些考慮導致 Windows 小組選取名為 LLP64 (或 P64) 的抽象資料模型。 在 LLP64 資料模型中,只有指標會展開至 64 位;所有其他基本資料類型 (整數和長) 長度維持 32 位。

適用于 64 位平臺的 .NET CLR 使用相同的 LLP64 抽象資料模型。 在 .NET 中,有一種不廣為人知的整數資料類型,特別指定用來保存「指標」資訊:大小相依于平臺的 IntPtr (例如 32 位或 64 位) 正在執行。 請考慮下列程式碼片段:

[C#]
public void SizeOfIntPtr() {
Console.WriteLine( "SizeOf IntPtr is: {0}", IntPtr.Size );
}

在 32 位平臺上執行時,您會在主控台上取得下列輸出:

SizeOf IntPtr is: 4

在 64 位平臺上,您會在主控台上取得下列輸出:

SizeOf IntPtr is: 8

注意 如果您想要在執行時間檢查您是否在 64 位環境中執行,您可以使用 IntPtr.Size 做為一種方式來進行此判斷。

移轉考量

移轉使用 p/invoke 的受控應用程式時,請考慮下列專案:

  • 64 位版本的 DLL 可用性
  • 使用資料類型

可用性

需要判斷的第一件事之一,是您的應用程式是否具有相依性的非 Managed 程式碼可供 64 位使用。

如果此程式碼是在內部開發,則您的成功能力就會增加。 當然,您仍然需要配置資源,以將非 Managed 程式碼移植到 64 位,以及適當的資源以進行測試、品質保證等。 (此白皮書並未針對開發程式提出建議;而是嘗試指出資源可能需要配置給工作以移植 code.)

如果此程式碼來自協力廠商,您必須調查此協力廠商是否已經有 64 位可用的程式碼,以及協力廠商是否願意讓它可供使用。

如果協力廠商不再提供此程式碼的支援,或協力廠商不願意執行工作,則會發生較高的風險問題。 這些案例需要對執行類似功能之可用程式庫進行其他研究,協力廠商是否會讓客戶自行執行埠等等。

請務必記住,相依程式碼的 64 位版本可能會有改變的介面簽章,這表示其他開發工作,並解決 32 位和 64 位版本的應用程式之間的差異。

資料類型

使用 p/invoke 需要 .NET 中開發的程式碼宣告 Managed 程式碼的目標方法原型。 指定下列 C 宣告:

[C++]
typedef void * HANDLE
HANDLE GetData();

原型方法的範例如下所示:

[C#]

[DllImport( "sampleDLL", CallingConvention=CallingConvention.Cdecl )]
      public static extern int DoWork( int x, int y );

[DllImport( "sampleDLL", CallingConvention=CallingConvention.Cdecl )]
      public unsafe static extern int GetData();

讓我們來檢閱這些範例,並留意 64 位移轉問題:

第一個範例會呼叫傳遞兩個 (2) 32 位整數的方法 DoWork ,我們預期會傳回 32 位整數。 雖然我們在 64 位平臺上執行,但整數仍是 32 位。 此特定範例中沒有任何內容會阻礙我們的移轉工作。

第二個範例需要對程式碼進行一些變更,才能在 64 位中成功執行。 我們在這裡執行的動作是呼叫 GetData 方法,並宣告我們預期傳回整數,但函式實際上會傳回 int 指標。 以下是我們的問題:請記住,整數是 32 位,但在 64 位指標中為 8 個位元組。 如此一來,在 32 位世界中撰寫相當多的程式碼,假設指標和整數的長度相同,4 個位元組。 在 64 位世界中,這已不再成立。

在此最後一種情況下,您可以將方法宣告變更為使用 IntPtr 來取代 int來解決問題。

public unsafe static extern IntPtr GetData();

進行這項變更可以在 32 位和 64 位環境中運作。 請記住,IntPtr 是平臺特定的。

在受控應用程式中使用 p/invoke 並不表示無法移轉至 64 位平臺。 也不表示會有問題。 這表示您必須檢閱受控應用程式所擁有非 Managed 程式碼的相依性,並判斷是否有任何問題。

移轉和 COM 互通性

COM 互通性是 .NET 平臺的假設功能。 如同先前有關平台叫用的討論,使用 COM 互通性表示 Managed 程式碼正在呼叫非 Managed 程式碼。 不過,不同于平臺叫用,COM 互通性也表示能夠讓非 Managed 程式碼呼叫 Managed 程式碼,就像是 COM 元件一樣。

同樣地,使用非 Managed COM 程式碼並不表示移轉至 64 位時會有問題;而是應該視為需要其他調查的指標。

移轉考量

請務必瞭解,在 .NET Framework 2.0 版版本中,不支援架構間互通性。 若要更簡潔,您無法在同一個進程中使用 32 位與 64 位之間的 COM 互通性。 但是,如果您有跨進程 COM 伺服器,則可以使用 32 位與 64 位之間的 COM 互通性。 如果您無法使用跨進程 COM 伺服器,您會想要將受控元件標示為 Win32,而不是 Win64 或無從驗證,以便讓程式在 WoW64 中執行,使其可以與 32 位 COM 物件交互操作。

以下是在 64 位環境中呼叫 COM 時,必須使用 COM 互通性的不同考慮討論。 具體而言,

  • 64 位版本的 DLL 可用性
  • 使用資料類型
  • 型別程式庫

可用性

p/invoke 一節中有關相依程式碼 64 位版本可用性的討論也與本節有關。

資料類型

p/invoke 一節中有關相依程式碼 64 位版本資料類型的討論也與本節有關。

型別程式庫

不同于元件,類型程式庫無法標示為「中性」;它們必須標示為 Win32 或 Win64。 此外,必須針對 COM 執行所在的每個環境註冊類型程式庫。 使用tlbimp.exe從型別程式庫產生 32 位或 64 位元件。

在受控應用程式中使用 COM 互通性並不表示無法移轉至 64 位平臺。 也不表示會有問題。 這表示您必須檢閱受控應用程式擁有的相依性,並判斷是否有任何問題。

移轉和不安全的程式碼

核心 C# 語言在省略指標做為資料類型時,與 C 和 C++ 不同。 相反地,C# 會提供參考和建立垃圾收集行程所管理的物件的能力。 在核心 C# 語言中,不可能有未初始化的變數、「懸置」指標或索引超出其界限之陣列的運算式。 因此,會消除例行叫用 C 和 C++ 程式的整個 Bug 類別。

雖然 C 或 C++ 中的每個指標類型建構在 C# 中都有對應的參考型別,但在某些情況下,存取指標類型會變得必要。 例如,與基礎作業系統互動、存取記憶體對應裝置,或實作時間關鍵演算法可能無法或實際執行,而不需要存取指標。 為了解決此需求,C# 提供撰寫不安全程式碼的能力。

在不安全的程式碼中,可以宣告及操作指標、在指標和整數類型之間執行轉換、取得變數的位址等等。 就某種意義而言,撰寫不安全的程式碼就像在 C# 程式內撰寫 C 程式碼一樣。

不安全的程式碼實際上是從開發人員和使用者的觀點來看的「安全」功能。 不安全的程式碼必須明確標示為 不安全的修飾詞,因此開發人員不小心使用不安全的功能。

移轉考量

為了討論不安全程式碼的潛在問題,讓我們來探索下列範例。 我們的 Managed 程式碼會呼叫 Unmanaged DLL。 特別是,有一個稱為 GetDataBuffer 的方法會傳回 100 個專案 (,在此範例中,我們會傳回固定數目的專案) 。 這些專案都包含整數和指標。 下列範例程式碼是 Managed 程式碼的摘錄,其中顯示負責處理此傳回資料的不安全函式。

[C#]

public unsafe int UnsafeFn() {
   IntPtr * inputBuffer = sampleDLL.GetDataBuffer();
   IntPtr * ptr = inputBuffer;
   int   result = 0;

   for ( int idx = 0; idx < 100; idx ++ ) {
      // Add 'int' from DLL to our result
      result = result + ((int) *ptr);

// Increment pointer over int (
      ptr = (IntPtr*)( ( (byte *) ptr ) + sizeof( int ) );

      // Increment pointer over pointer (
      ptr = (IntPtr*)( ( (byte *) ptr ) + sizeof( int ) );
   }
   return result;
}

注意 此特定範例可能已經完成,而不需要使用不安全的程式碼。 更具體來說,還有其他技術,例如可能已使用的封送處理。 但基於此目的,我們使用不安全的程式碼。

UnsafeFn會迴圈查看 100 個專案,並加總整數資料。 當我們逐步執行資料的緩衝區時,程式碼必須逐步執行整數和指標。 在 32 位環境中,此程式碼可正常運作。 不過,如先前所討論,指標在 64 位環境中為 8 個位元組,因此程式碼區段 (如下所示) 將無法正常運作,因為它會使用常見的程式設計技術,例如,將指標視為相當於整數。

// Increment pointer over pointer (
ptr = (IntPtr*)( ( (byte *) ptr ) + sizeof( int ) );

為了讓此程式碼同時在 32 位和 64 位環境中運作,您必須將程式碼變更為下列專案。

// Increment pointer over pointer (
ptr = (IntPtr*)( ( (byte *) ptr ) + sizeof( IntPtr ) );

如我們剛才所見,有一個需要使用不安全程式碼的實例。 在大部分情況下,由於 Managed 程式碼相依于某些其他介面,因此需要它。 不論不安全程式碼存在的原因為何,都必須在移轉程式中檢閱。

我們先前使用的範例相當簡單,而且修正讓程式在 64 位中運作相當簡單。 顯然有許多不安全的程式碼範例較複雜。 有些需要深入檢閱,或許需要逐步回顧並重新思考 Managed 程式碼所使用的方法。

若要重複您已閱讀的內容,在受控應用程式中使用不安全的程式碼並不表示無法移轉至 64 位平臺。 也不表示會有問題。 這表示您必須檢閱受控應用程式擁有的所有不安全程式碼,並判斷是否有任何問題。

移轉和封送處理

封送處理提供一組方法,用於配置 Unmanaged 記憶體、複製 Unmanaged 記憶體區塊,以及將 Managed 轉換成 Unmanaged 類型,以及其他與 Unmanaged 程式碼互動時所使用的其他方法。

封送處理會透過 .NET Marshal 類別來顯示。 在 Visual Basic 中,封送處理類別上定義的 靜態共用 方法對於使用 Unmanaged 資料而言非常重要。 建置自訂封送處理器的進階開發人員,這些開發人員需要提供 Managed 和 Unmanaged 程式設計模型之間的橋樑通常會使用大部分定義的方法。

移轉考量

封送處理會造成與應用程式移轉至 64 位相關的一些更複雜的挑戰。 由於開發人員嘗試使用封送處理完成的本質,也就是將結構化資訊傳送至 Managed 和 Unmanaged 程式碼,或從 Managed 和 Unmanaged 程式碼來回傳輸結構化資訊,我們會看到我們提供資訊,有時是低階,以協助系統。

就版面配置而言,開發人員可以進行兩個特定的宣告;這些宣告通常是透過使用程式碼屬性來建立。

LayoutKind.Sequential

讓我們檢閱.NET Framework SDK 說明中提供的定義:

「物件的成員會依序排列,以匯出至 Unmanaged 記憶體時出現的順序排列。 成員會根據 StructLayoutAttribute.Pack中指定的封裝來配置,而且可能不連續。」

我們會告知配置是其定義順序的特有。 然後,我們只需要確定 Managed 和 Unmanaged 宣告類似。 但是,我們也知道封裝也是一個重要要素。 此時,您不會驚奇地瞭解,如果沒有開發人員明確介入,就會有預設套件值。 您可能已經猜到,預設套件值在 32 位和 64 位系統之間並不相同。

定義中關於非連續成員的 語句是指,因為有預設套件大小,記憶體中配置的資料可能不是位元組 0、位元組 1、位元組2 等。相反地,第一個 成員會位於位元組 0,但第二個成員可能位於位元組 4。 系統會執行此預設封裝,以允許電腦存取成員,而不需要處理不對齊的問題。

以下是我們必須密切注意封裝的區域,同時嘗試讓系統以慣用模式運作。

以下是 Managed 程式碼中定義的結構範例,以及 Unmanaged 程式碼中定義的對應結構。 您應該仔細注意此範例示範如何在這兩個環境中設定套件值。

[C#]
[StructLayout(LayoutKind.Sequential, Pack=1)]
public class XYZ {
      public byte arraysize = unchecked((byte)-1);
      [MarshalAs(UnmanagedType.ByValArray, SizeConst=52)]
      public int[] padding = new int[13];
};
[unmanaged c++]
#pragma pack(1)
typedef struct{
      BYTE arraysize;      // = (byte)-1;
      int      padding[13];
} XYZ;

LayoutKind.Explicit

讓我們檢閱 .NET FrameworkSDK 說明中提供的定義:

「明確控制 Unmanaged 記憶體中物件之每個成員的精確位置。 每個成員都必須使用 FieldOffsetAttribute 來指出該欄位在類型內的位置。」

我們在此被告知,開發人員會提供確切的位移來協助封送處理資訊。 因此,開發人員必須正確地指定 FieldOffset 屬性中的資訊。

那麼,潛在問題在哪裡? 請記住,欄位位移的定義是瞭解繼續資料成員大小的大小,請務必記住,並非所有資料類型大小都等於 32 位和 64 位。 具體而言,指標長度為 4 或 8 個位元組。

我們現在有一個案例,可能必須更新受控原始程式碼以以特定環境為目標。 下列範例顯示包含指標的結構。 雖然我們已將指標設為 IntPtr,但移至 64 位時仍會有差異。

[C#]
[StructLayout(LayoutKind.Explicit)]
    internal struct FooValue {
        [FieldOffset(0)] public int dwType;
        [FieldOffset(4)] public IntPtr pType;
        [FieldOffset(8)] public int typeValue;
    }

針對 64 位,我們必須調整結構中最後一個資料成員的欄位位移,因為它真的從位移 12 開始,而不是 8。

[C#]
[StructLayout(LayoutKind.Explicit)]
    internal struct FooValue {
        [FieldOffset(0)] public int dwType;
        [FieldOffset(4)] public IntPtr pType;
        [FieldOffset(12)] public int typeValue;
    }

當需要 Managed 和 Unmanaged 程式碼之間的複雜互通性時,封送處理的使用是實境。 利用這個功能強大的功能不是您可以將 32 位應用程式移轉至 64 位環境的指標。 不過,由於與使用封送處理相關聯的複雜度,因此這是需要仔細注意詳細資料的區域。

程式碼的分析會指出每個平臺是否需要個別的二進位檔,以及您是否也必須修改 Unmanaged 程式碼來解決封裝之類的問題。

移轉和序列化

序列化是將物件的狀態轉換成可保存或傳輸之形式的程序。 序列化的互補方法是還原序列化,它將資料流轉換成為物件。 將這些程序搭配在一起,可讓資料輕鬆地儲存與傳輸。

.NET Framework 具有兩項序列化技術:

  • 二進位序列化保留型別精確度,這對於在應用程式不同的引動過程之間,保留物件狀態相當實用。 例如,藉由將物件序列化至剪貼簿,就可在不同應用程式之間共用該物件。 您可以將物件序列化為資料流、序列化至磁碟、記憶體、在網路上序列化等等。 .NET 遠端處理使用序列化,將物件「依值」從一部電腦或應用程式域傳遞至另一部電腦。
  • XML 序列化程序僅對公用屬性與欄位進行序列化,並不保留型別精確度。 當您不想限制使用資料的應用程式,而能提供或使用資料時,這種做法就很有用。 因為 XML 為開放標準,因此是在 Web 上共用資料的很好選擇。 同樣是開放標準的 SOAP,也是一項很好的選擇。

移轉考量

當我們考慮序列化時,我們必須記住我們嘗試達成的目標。 當您移轉至 64 位時要記住的一個問題是,您是否想要在不同平臺之間共用序列化資訊。 換句話說,64 位受控應用程式會讀取 (或還原序列化 32 位受控應用程式所儲存) 資訊。

您的答案有助於推動解決方案的複雜度。

  • 您可能想要撰寫自己的序列化常式來考慮平臺。
  • 您可能想要限制資訊的共用,同時仍允許每個平臺讀取和寫入自己的資料。
  • 您可能想要重新流覽序列化的內容,並進行變更,以協助避免某些問題。

那麼,在之後,序列化有哪些考慮?

  • 根據平臺而定,IntPtr 的長度為 4 或 8 個位元組。 如果您序列化資訊,則會將平臺特定資料寫入輸出。 這表示如果您嘗試共用這項資訊,您可能會遇到問題。

如果您在上一節中考慮有關封送處理和位移的討論,您可能會有關于序列化如何處理封裝資訊的問題或兩個問題。 針對二進位序列化 .NET,內部會使用位元組型讀取和正確處理資料,使用正確的未對齊存取序列化資料流程。

如我們所見,使用序列化並不會防止移轉至 64 位。 如果您使用 XML 序列化,則必須在序列化程式期間從 和 轉換成原生 Managed 類型,以隔離您與平臺之間的差異。 使用二進位序列化可提供您更豐富的解決方案,但會建立需要針對不同平臺如何共用序列化資訊做出決策的情況。

總結

移轉至 64 位即將推出,Microsoft 正努力盡可能簡單地從 32 位受控應用程式轉換至 64 位。

不過,假設使用者只能在 64 位環境中執行 32 位程式碼,並讓它執行而不查看您要移轉的內容,這是不切實際的事。

如先前所述,如果您有 100% 的型別安全 Managed 程式碼,您實際上可以直接將它複製到 64 位平臺,並在 64 位 CLR 下成功執行。

但比起可能,受控應用程式將涉及下列任一項或所有專案:

  • 透過 p/invoke 叫用平臺 API
  • 叫用 COM 物件
  • 使用不安全的程式碼
  • 使用封送處理作為共用資訊的機制
  • 使用序列化作為保存狀態的方式

無論您的應用程式執行哪一件事,都必須執行您的工作,並調查程式碼的作用,以及您擁有的相依性。 完成此工作之後,您必須查看您的選擇,以執行下列任一項或所有動作:

  • 移轉沒有變更的程式碼。
  • 對程式碼進行變更,以正確處理 64 位指標。
  • 與其他廠商合作,以提供 64 位版本的產品。
  • 對邏輯進行變更,以處理封送處理和/或序列化。

在某些情況下,您可能會決定不要將 Managed 程式碼移轉至 64 位,在此情況下,您可以選擇標記元件,讓 Windows 載入器可以在啟動時執行正確的動作。 請記住,下游相依性對整體應用程式有直接的影響。

FxCop

您也應該注意可用來協助您進行移轉的工具。

現今 Microsoft 有一個稱為 FxCop 的工具,這是一種程式碼分析工具,可檢查 .NET Managed 程式碼元件是否符合 Microsoft .NET Framework 設計指導方針。 它會使用反映、MSIL 剖析和呼叫圖表分析,檢查下欄區域中超過 200 個瑕疵的元件:命名慣例、程式庫設計、當地語系化、安全性和效能。 FxCop 包含工具的 GUI 和命令列版本,以及用來建立您自己的規則的 SDK。 如需詳細資訊,請參閱 FxCop 網站。 Microsoft 正在開發其他 FxCop 規則,以提供您資訊來協助您進行移轉工作。

還有一個區域管理程式庫函式,可協助您在執行時間判斷您執行的環境。

  • System.IntPtr.Size — 判斷您是否以 32 位或 64 位模式執行
  • System.Reflection.Module.GetPEKind — 以程式設計方式查詢.exe或.dll,以查看它是否只要在特定平臺上或 WOW64 下執行

沒有一組特定的程式可解決您可能會遇到的所有挑戰。 本白皮書旨在提高您對這些挑戰的認知,並為您提供可能的替代方案。