Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 12 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
12
Dung lượng
201,7 KB
Nội dung
using System;
using System.Runtime.InteropServices;
public class CallWithStructure {
// (Bỏ qua lớp OSVersionInfo.)
[DllImport("kernel32.dll")]
public static extern bool GetVersionEx([In, Out] OSVersionInfo osvi);
private static void Main() {
OSVersionInfo osvi = new OSVersionInfo();
osvi.dwOSVersionInfoSize = Marshal.SizeOf(osvi);
GetVersionEx(osvi);
Console.WriteLine("Class size: " + osvi.dwOSVersionInfoSize);
Console.WriteLine("Major Version: " + osvi.dwMajorVersion);
Console.WriteLine("Minor Version: " + osvi.dwMinorVersion);
Console.WriteLine("Build Number: " + osvi.dwBuildNumber);
Console.WriteLine("Platform Id: " + osvi.dwPlatformId);
Console.WriteLine("CSD Version: " + osvi.szCSDVersion);
Console.WriteLine("Platform: " + Environment.OSVersion.Platform);
Console.WriteLine( "Version: " + Environment.OSVersion.Version);
Console.ReadLine();
}
}
Nếu chạy ứng dụng này trên hệ thống Windows XP, bạn sẽ thấy thông tin như sau:
Class size: 148
Major Version: 5
Minor Version: 1
Build Number: 2600
Platform Id: 2
CSD Version: Service Pack 1
Platform: Win32NT
Version: 5.1.2600.0
1.1 Gọi một hàm không-được-quản-lý có sử dụng callback
V
V
Bạn cần gọi một hàm không-được-quản-lý và cho phép nó gọi một hàm khác.
#
#
Tạo một ủy nhiệm cho callback. Sử dụng ủy nhiệm này khi định nghĩa và sử
dụng hàm không-được-quản-lý.
Nhiều hàm của Win32 API sử dụng callback. Ví dụ, nếu muốn lấy tên của tất cả các cửa
sổ đang mở, bạn có thể sử dụng hàm EnumWindows trong thư viện user32.dll . Khi gọi
EnumWindows, bạn cần truyền cho nó một con trỏ chỉ đến một hàm khác trong mãlệnh
củ
a bạn. Hệ điều hành Windows sau đó sẽ gọi hàm này mỗi khi tìm thấy một cửa sổ đang
mở, và truyền handle của cửa sổ cho nó.
.NET Framework cho phép bạn quảnlý việc sử dụng callback màkhông cần các con trỏ
và các khối mãkhông an toàn. Thay vào đó, bạn có thể định nghĩa và sử dụng một ủy
nhiệm chỉ đến hàm callback. Khi bạn truyền ủy nhiệm cho hàm EnumWindows, CLR sẽ
tự độ
ng marshal ủy nhiệm thành con trỏ hàm không-được-quản-lý như mong muốn.
Ví dụ dưới đây sử dụng EnumWindows cùng với một callback để hiển thị tên của tất cả
các cửa sổ đang mở.
using System;
using System.Text;
using System.Runtime.InteropServices;
public class GetWindows {
// Chữ ký cho hàm callback.
public delegate bool CallBack(int hwnd, int lParam);
// Hàm không-được-quản-lý sẽ kích hoạt callback
// khi duyệt qua các cửa sổ đang mở.
[DllImport("user32.dll")]
public static extern int EnumWindows(CallBack callback, int param);
[DllImport("user32.dll")]
public static extern int GetWindowText(int hWnd,
StringBuilder lpString, int nMaxCount);
private static void Main() {
CallBack callBack = new CallBack(DisplayWindowInfo);
// Yêu cầu hệ điều hành duyệt qua các cửa sổ đang mở,
// kích hoạt callback với handle của mỗi cửa sổ.
EnumWindows(callBack, 0);
Console.ReadLine();
}
// Hàm sẽ nhận callback. Thông số thứ hai
// khôngđược sử dụng nhưng phải được khai báo để
// tương thích với chữ ký của callback.
public static bool DisplayWindowInfo(int hWnd, int lParam) {
int chars = 100;
StringBuilder buf = new StringBuilder(chars);
if (GetWindowText(hWnd, buf, chars) != 0) {
Console.WriteLine(buf);
}
return true;
}
}
1.2 Lấy thông tin lỗi không-được-quản-lý
V
V
Bạn cần truy xuất thông tin lỗi (mã lỗi hoặc thông điệp mô tả lỗi) giải thích tại
sao một lời gọi Win32 API thất bại.
#
#
Trong phần khai báo của hàm không-được-quản-lý, thiết lập trường
SetLastError của đặc tính DllImportAttribute là true. Nếu có lỗi khi thực thi,
gọi phương thức tĩnh Marshal.GetLastWin32Error để truy xuất mã lỗi. Để lấy
thông điệp mô tả một mã lỗi cụ thể, sử dụng hàm không-được-quản-lý
FormatMessage.
Bạn không thể trực tiếp lấy thông tin lỗi bằng hàm không-được-quản-lý GetLastError.
Vấn đề là, mã lỗi do GetLastError trả về có thể không ph
ản ánh lỗi do hàm không-được-
quản-lý gây ra. Thay vào đó, nó có thể được thiết lập bởi các lớp .NET Framework khác
hoặc CLR. Bạn có thể lấy thông tin lỗi một cách an toàn bằng phương thức tĩnh
Marshal.GetLastWin32Error. Phương thức này cần được gọi ngay sau lời gọi hàm không-
được-quản-lý, và nó sẽ trả về thông tin lỗi chỉ một lần (các lần gọi GetLastWin32Error
sau đó sẽ trả về mã lỗi 127). Ngoài ra, bạn phả
i thiết lập trường SetLastError của đặc tính
DllImportAttribute là true, cho biết những lỗi do hàm này sinh ra sẽ được ghi nhận.
[DllImport("user32.dll", SetLastError=true)]
Sau đó, bạn có thể sử dụng hàm FormatMessage trong thư viện kernel32.dll để lấy thông
điệp mô tả lỗi từ mã lỗi Win32.
Ví dụ, ứng dụng dưới đây muốn hiển thị một MessageBox, nhưng lại sử dụng handle
không đúng. Mã lỗi được lấy bằng Marshal.GetLastWin32Error, và thông điệp mô tả lỗi
được lấy bằng FormatMessage.
using System;
using System.Runtime.InteropServices;
public class TestError {
[DllImport("kernel32.dll")]
private unsafe static extern int FormatMessage(int dwFlags,
int lpSource, int dwMessageId, int dwLanguageId,
ref String lpBuffer, int nSize, int Arguments);
[DllImport("user32.dll", SetLastError=true)]
public static extern int MessageBox(int hWnd, string pText,
string pCaption, int uType);
private static void Main() {
int badWindowHandle = 453;
MessageBox(badWindowHandle, "Message", "Caption", 0);
int errorCode = Marshal.GetLastWin32Error();
Console.WriteLine(errorCode);
Console.WriteLine(GetErrorMessage(errorCode));
Console.ReadLine();
}
// GetErrorMessage trả về thông điệp lỗi
// tương ứng với mã lỗi errorCode.
public static string GetErrorMessage(int errorCode) {
int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
int messageSize = 255;
string lpMsgBuf = "";
int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS;
int retVal = FormatMessage(dwFlags, 0, errorCode, 0,
ref lpMsgBuf, messageSize, 0);
if (0 == retVal) {
return null;
} else {
return lpMsgBuf;
}
}
}
Sau đây là kết xuất của ứng dụng:
1400
Invalid window handle.
1.3 Sử dụng thành phần COM trong .NET-client
V
V
Bạn cần sử dụng thành phần COM trong một .NET-client.
#
#
Sử dụng một Primary Interop Assembly, nếu có. Nếu không, tạo một Runtime
Callable Wrapper bằng Type Library Importer (Tlbimp.exe), hoặc tính năng Add
Reference trong Visual Studio .NET.
.NET Framework hỗ trợ khảnăngliêntác với COM. Để cho phép .NET-client tương tác
với một thành phần COM, .NET sử dụng một Runtime Callable Wrapper (RCW)—một
lớp proxy đặc biệt đóng vai trò trung gian giữa mã.NETvà thành phần COM. RCW sẽ
xử
lý tất cả chi tiết, bao gồm việc marshal các kiểu dữ liệu, sử dụng các giao diện COM
truyền thống, và xử lý các sự kiện COM.
Bạn có ba tùy chọn khi sử dụng một RCW:
• Lấy RCW từ tác giả của thành phần COM. Trong trường hợp này, RCW được gọi là
một Primary Interop Assembly (PIA).
• Tạo RCW bằng tiện ích dòng lệnh
Tlbimp.exe hoặc bằng Visual Studio .NET.
• Tự tạo RCW thông qua các kiểu trong không gian tên System.Runtime.
InteropServices (cách này cực kỳ dài dòng và phức tạp).
Nếu muốn sử dụng Visual Studio .NET để tạo RCW, bạn chỉ cần chọn Project | Add
Reference và sau đó chọn thành phần thích hợp trong thẻ COM. Interop Assembly sẽ
được tạo ra vàđược thêm vào các tham chiếu của dự án. Sau đó, bạn có thể sử dụng
Object Browser để khảo sát các không gian tên và các lớp có trong thành phần này.
[
Hình 15.2 Sử dụng Object Browser
để xem các không gian tên và các lớp có trong thành phần COM
Nếu không sử dụng Visual Studio .NET, bạn có thể tạo một Wrapper Assembly bằng công
cụ Tlbimp.exe, chỉ cần truyền cho nó tên file chứa thành phần COM. Ví dụ, dòng lệnh sau
đây sẽ tạo một RCW với tên file vàkhông gian tên m
ặc định, giả sử file
MyCOMComponent.dll đang nằm trong thư mục hiện hành.
tlbimp MyCOMComponent.dll
Giả sử MyCOMComponent chứa một thư viện kiểu có tên là MyClasses, file RCW sẽ có
tên là MyClasses.dll và sẽ trưng các lớp của nó thông qua một không gian tên có tên là
MyClasses. Bạn có thể cấu hình các tùy chọn này bằng các đối số dòng lệnh (được mô tả
chi tiết trong tàiliệu MSDN). Ví dụ, bạn có thể sử dụng /out:[Filename] để
chỉ định tên
file assemby, sử dụng /namespace:[Namespace] để chỉ định không gian tên. Bạn cũng
có thể sử dụng /keyfile[keyfilename] để tạo tên mạnh cho assembly, và đặt nó vào
Global Assembly Cache (GAC). Sử dụng /primary để tạo một PIA.
Nếu có thể, bạn nên sử dụng PIA thay vì tạo RCW. PIA hoạt động đáng tin cậy hơn vì
được tạo ra bởi nhà phát hành thành phần, và cũng có thể
chứa một số tính năngnâng
cao. Nếu một PIA được đăng ký với hệ thống cho một thành phần COM thì Visual Studio
.NET sẽ tự động sử dụng PIA đó khi bạn thêm một tham chiếu đến thành phần COM. Ví
dụ, adodb.dll (có trong .NET Framework) cho phép bạn sử dụng các đối tượng ADO
truyền thống. Nếu bạn thêm một tham chiếu đến thành phần Microsoft ActiveX Data
Objects, Interop Assembly
này sẽ tự động được sử dụng, màkhông sinh ra một RCW mới.
Tương tự, Microsoft Office XP cung cấp một PIA để nâng cao sự hỗ trợ .NET cho Office
The image part with relationsh ip ID rId 5 was not fo und in the file.
Automation (bạn có thể tải PIA này tại địa chỉ
[http://msdn.microsoft.com/downloads/list/office.asp]).
Ví dụ sau minh họa cách sử dụng COM Interop để truy xuất các đối tượng ADO truyền
thống trong ứng dụng .NET Framework.
using System;
public class ADOClassic {
private static void Main() {
ADODB.Connection con = new ADODB.Connection();
string connectionString = "Provider=SQLOLEDB.1;" +
"Data Source=localhost;" +
"Initial Catalog=Northwind;Integrated Security=SSPI";
con.Open(connectionString, null, null, 0);
object recordsAffected;
ADODB.Recordset rs = con.Execute("SELECT * From Customers",
out recordsAffected, 0);
while (rs.EOF != true) {
Console.WriteLine(rs.Fields["CustomerID"].Value);
rs.MoveNext();
}
Console.ReadLine();
}
}
1.4 Giải phóng nhanh thành phần COM
V
V
Bạn cần bảo đảm một thành phần COM được xóa khỏi bộ nhớ ngay tức thì,
không phải chờ bộ thu gom rác (Garbage Collector) làm việc. Hoặc bạn muốn
bảo đảm các đối tượng COM được giải phóng theo một thứ tự xác định.
#
#
Sử dụng phương thức tĩnh Marshal.ReleaseComObject và truyền RCW thích
hợp để giải phóng tham chiếu đến đối tượng COM nằm dưới.
COM sẽ đếm các tham chiếu đến đối tượng để xác định khi nào đối tượng sẽ được giải
phóng. Khi bạn sử dụng một RCW, tham chiếu sẽ được giữ cả khi biến đối tượng vượt
khỏi tầm vực. Tham chiếu này chỉ được giải phóng khi bộ thu gom rác giải phóng đối
tượng RCW. Do đó, bạn không thể kiểm soát việc các đối tượng COM sẽ được giải phóng
khi nào hoặc theo thứ tự nào.
Để vượt qua hạn chế này, bạn có thể sử dụng phương thức Marshal.ReleaseComObject.
Trong ví dụ ở mục 15.6, bạn hãy thêm hai dòng sau vào cuối mãlệnh để giải phóng đối
tượng Recordset và Connection nằm dưới.
Marshal.ReleaseComObject(rs);
Marshal.ReleaseComObject(con);
# Về mặt kỹ thuật, phương thức ReleaseComObject không thực sự giải phóng
đối tượng COM, nó chỉ giảm số lượng tham chiếu đến đối tượng. Nếu số tham
chiếu là 0, đối tượng COM sẽ được giải phóng. Tuy nhiên, nếu cùng thể hiện
của một đối tượng COM được sử dụng tại nhiều mẩu mã lệnh, nó phải được
giải phóng ở
các nơi đó trước khi được giải phóng khỏi bộ nhớ.
1.5 Sử dụng thông số tùy chọn
V
V
Bạn cần gọi một phương thức trong thành phần COM màkhông phải truyền
tất cả các thông số cần thiết.
#
#
Sử dụng trường Type.Missing.
Hầu hết các phương thức trong .NET Framework đều được nạp chồng nhiều lần để bạn
có thể gọi phiên bản yêu cầu chỉ những thông số do bạn cung cấp. Mặt khác, COM không
hỗ trợ việc nạp chồng phương thức. Thay vào đó, các thành phần COM thường sử dụng
các phương thức với một danh sách dài các thông số tùy chọn. Không may là, C# không
hỗ
trợ thông số tùy chọn, nghĩa là người phát triển phải cung cấp thêm các giá trị không
cần thiết khi truy xuất một thành phần COM. Và vì các thông số COM thường được
truyền bằng tham chiếu nên mãlệnh của bạn không thể truyền một tham chiếu null, mà
phải khai báo một biến đối tượng và rồi truyền biến đó.
Bạn có thể giảm nhẹ vấn đề đến một chừng mực nào
đó bằng cách cung cấp trường
Type.Missing bất cứ khi nào muốn bỏ qua một thông số tùy chọn. Nếu cần truyền một
thông số bằng tham chiếu, bạn chỉ cần khai báo một biến đối tượng, thiết lập nó là
Type.Missing, và sử dụng nó trong mọi trường hợp:
private static object n = Type.Missing;
Ví dụ dưới đây sử dụng đối tượng Word để tạo và hiển thị một tài liệu. Trong đó, có
nhiều ph
ương thức yêu cầu các thông số tùy chọn (được truyền bằng tham chiếu). Việc
sử dụng trường Type.Missing đơn giản hóa mãlệnh rất nhiều.
using System;
public class OptionalParameters {
private static object n = Type.Missing;
private static void Main() {
// Chạy Word phía nền.
Word.ApplicationClass app = new Word.ApplicationClass();
app.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;
// Tạo một tàiliệu mới (không khả kiến đối với người dùng).
Word.Document doc = app.Documents.Add(
ref n
,
ref n
,
ref n
,
ref n
);
Console.WriteLine();
Console.WriteLine("Creating new document.");
Console.WriteLine();
// Thêm một tiêu đề và hai hàng text.
Word.Range range = doc.Paragraphs.Add(
ref n
).Range;
range.InsertBefore("Test Document");
string style = "Heading 1";
object objStyle = style;
range.set_Style(ref objStyle);
range = doc.Paragraphs.Add(
ref n
).Range;
range.InsertBefore("Line one.\nLine two.");
range.Font.Bold = 1;
// Hiển thị Print Preview, làm cho Word trở nên khả kiến.
doc.PrintPreview();
app.Visible = true;
Console.ReadLine();
}
}
1.6 Sử dụng điều kiểm ActiveX trong .NET-client
V
V
Bạn cần đặt một điều kiểm ActiveX trên một cửa sổ ứng dụng .NET
Framework.
#
#
Sử dụng một RCW (cũng giống như với một thành phần COM bình thường). Để
làm việc với điều kiểm ActiveX khi thiết kế, thêm nó vào hộp công cụ của Visual
Studio .NET.
.NET Framework hỗ trợ như nhau đối với tất cả các thành phần COM, bao gồm điều
kiểm ActiveX. Điều khác nhau cơ bản là lớp RCW (cho điều ki
ểm ActiveX) dẫn xuất từ
kiểu .NET đặc biệt System.Windows.Forms.AxHost. Về mặt kỹ thuật, bạn thêm AxHost
vào form, và nó sẽ giao tiếp với điều kiểm ActiveX phía hậu trường. Vì dẫn xuất từ
System.Windows.Forms.Control, nên AxHost cũng có các thuộc tính, phương thức, và sự
kiện chuẩn như Location, Size, Anchor, Nếu RCW được sinh tự động, các lớp AxHost
luôn bắt đầu bằng Ax.
Bạn có thể tạo một RCW
cho một điều kiểm ActiveX cũng giống như cho bất cứ thành
phần COM nào khác bằng công cụ Tlbimp.exe hoặc tính năng Add Reference trong Visual
Studio .NET, sau đó lập trình để tạo điều kiểm. Tuy nhiên, một cách tiếp cận dễ hơn trong
Visual Studio .NET là thêm điều kiểm ActiveX vào hộp công cụ (xem mục 11.4 để biết
thêm chi tiết).
Chẳng có gì xảy ra khi bạn thêm một điều ki
ểm ActiveX vào hộp công cụ. Tuy nhiên, bạn
có thể sử dụng biểu tượng trong hộp công cụ để thêm một thể hiện của điều kiểm vào
form. Lần đầu bạn làm việc này, Visual Studio .NET sẽ tạo một Interop Assembly và thêm
nó vào dự án của bạn. Ví dụ, nếu bạn thêm điều kiểm Microsoft Masked Edit (không có
điều kiểm .NET tương đương), Visual Studio .NET sẽ tạo m
ột RCW Assembly có tên là
AxInterop.MSMask.dll. Dưới đây là đoạn mã trong vùng designer dùng để tạo một thể
hiện của điều kiểm này và thêm nó vào form:
this.axMaskEdBox1 = new AxMSMask.AxMaskEdBox();
((System.ComponentModel.ISupportInitialize)(this.axMaskEdBox1)).
BeginInit();
//
// axMaskEdBox1
//
this.axMaskEdBox1.Location = new System.Drawing.Point(16, 12);
this.axMaskEdBox1.Name = "axMaskEdBox1";
this.axMaskEdBox1.OcxState = ((System.Windows.Forms.AxHost.State)
(resources.GetObject("axMaskEdBox1.OcxState")));
this.axMaskEdBox1.Size = new System.Drawing.Size(112, 20);
this.axMaskEdBox1.TabIndex = 0;
this.Controls.Add(this.axMaskEdBox1);
[...]... dòng lệnh Type Library Exporter (Tlbexp.exe) .NET Framework hỗ trợ việc COM-client sử dụng thành phần NET Khi COM-client tạo một đối tượng NET, CLR sẽ tạo một đối tượng được- quản- lývà một COM Callable Wrapper (CCW) bọc lấy đối tượng này COM-client sẽ tương tác với đối tượng thông qua CCW CLR chỉ tạo một CCW cho một đối tượng được- quản- lý, bất chấp có bao nhiêu COM-client đang sử dụng nó Các kiểu cần được. .. COM-client phải thỏa mãn các yêu cầu sau: • Các kiểu được- quản- lý (lớp, giao diện, cấu trúc, hoặc kiểu liệt kê) phải được khai báo là public • Nếu COM-client cần tạo đối tượng, nó phải có một phương thức khởi dựng mặc định public COM không hỗ trợ các phương thức khởi dựng có chứa thông số • Các thành viên của kiểu cần được truy xuất phải là các thành viên public COMclient không truy xuất được các thành viên...Chú ý rằng, các thuộc tính tùy biến của điều kiểm ActiveX khôngđược áp dụng trực tiếp thông qua các lệnh thiết lập thuộc tính Thay vào đó, chúng sẽ được thiết lập theo nhóm khi thuộc tính OcxState đã được thiết lập Tuy nhiên, mãlệnh của bạn có thể sử dụng các thuộc tính này một cách trực tiếp 1.7 Tạo thành phần NET dùng cho COM-client Bạn cần... sẽ được gán cho giao diện • Nên tạo tên mạnh cho assembly để nó có thể được cài đặt vào GAC vàđược dùng chung cho nhiều client Để tạo một đối tượng NET, COM-client cần một thư viện kiểu (file tlb) File thư viện kiểu có thể được tạo từ một assembly bằng tiện ích dòng lệnh Tlbexp.exe Ví dụ: tlbexp ManagedLibrary.dll Một khi đã tạo ra thư viện kiểu, bạn có thể tham chiếu nó từ một công cụ phát triển không- được- quản- lý. .. của kiểu cần được truy xuất phải là các thành viên public COMclient không truy xuất được các thành viên private, protected, internal, và static Ngoài ra, bạn nên tuân theo các kinh nghiệm sau: • Không nên tạo các quan hệ thừa kế giữa các lớp, vì các quan hệ này sẽ khôngkhả kiến đối với COM-client (mặc dù NET giả lập quan hệ này bằng cách khai báo một giao diện lớp cơ sở dùng chung) • Các lớp mà bạn... lệnh Tlbexp.exe Ví dụ: tlbexp ManagedLibrary.dll Một khi đã tạo ra thư viện kiểu, bạn có thể tham chiếu nó từ một công cụ phát triển không- được- quản- lý Với Visual Basic 6, bạn tham chiếu file tlb từ hộp thoại Project | Reference Trong Visual C++ 6, bạn có thể sử dụng lệnh #import để nhập các định nghĩa kiểu từ thư viện kiểu . hàm không- được- quản- lý GetLastError.
Vấn đề là, mã lỗi do GetLastError trả về có thể không ph
ản ánh lỗi do hàm không- được-
quản- lý gây ra. Thay vào đó,.
Version: 5.1.2600.0
1.1 Gọi một hàm không- được- quản- lý có sử dụng callback
V
V
Bạn cần gọi một hàm không- được- quản- lý và cho phép nó gọi một hàm khác.