jump to navigation

Cách viết thư viện DLL 19/11/2007

Posted by Hung Nguyen in Lập trình C++.
Tags:
trackback

Gọi hàm trong file .DLL với C#
Giới thiệuDLL (Dynamic Link Library) là một dạng mô-đun để chứa các hàm (function) và dữ liệu phục vụ cho các ứng dụng hoặc cho DLL khác. Bằng cách này, DLL giúp tiết kiệm bộ nhớ khi chia sẻ các hàm của nó cho nhiều chương trình cùng truy xuất tại một thời điểm.

Bài viết này sẽ giúp bạn “tái sử dụng” các DLL ngày xửa ngày xưa trong môi trường C#, bằng cách gọi các hàm export chứa trong nó.

Lưu ý:
– Khi .NET ra đời, Microsoft đã phát triển thêm một dạng khác của DLL là Class Library khá tiện lợi. Tuy nhiên, bài viết này chỉ đề cập đến dạng DLL “cổ điển”.
– Để thử nghiệm, bạn chỉ cần có Visual Studio 7.1 (VS 2003) trở lên.

Tạo file DLL phục vụ thử nghiệm

Trước hết, chúng ta cần một file DLL phục vụ thử nghiệm. Bạn tạo một project Visual C++ mới, chọn Configuration type là “Dynamic Library (.dll)”. Sau đó tạo mới 2 file như sau:

– File “TestAPI.cpp”: chứa nội dung chính của DLL, với 2 hàm export. Hàm GetAge “bình dân” với kiểu trả về và tham số đều là kiểu dữ liệu nguyên thủy (integer). Hàm GetUser() phức tạp hơn một chút với một tham chiếu dạng struct “UserInfo”.

– File “TestAPI.def”: khai báo các hàm được export.

File “TestAPI
Code:

//khai báo 1 struct thử nghiệm
struct UserInfo
{
int id;
int age;
char name[30];
};

//hàm có giá trị trả về và 1 tham số là kiểu int
__declspec(dllexport) DWORD _stdcall GetAge(int id)
{
if (id == 1)
{
return 10;
}
return -1;
}

//hàm có tham số là một biến tham chiếu struct
__declspec(dllexport) DWORD _stdcall GetUser(int id, UserInfo &user)
{
if (id == 1)
{
user.id = 1;
user.age = 10;
strcpy(user.name, “sonhn”);
return 0;
}
return -1;
}

//entry point cho DLL
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
return TRUE;
}

File “TestAPI.def
Code:

LIBRARY “TestDLL”
DESCRIPTION ‘sample dll Interface Windows DLL

EXPORTS
; Explicit exports can go here
GetAge
GetUser

Biên dịch project này để sinh ra file “TestDLL.dll”.

Tạo project C# thử nghiệm

Trước hết, tạo một project C# dạng Console Application (cho đơn giản!). Sau đó, hãy copy file “TestDLL.dll” vào folder “\bin\Debug” của project này.

Bạn sẽ thấy nội dung của file .cs chính có dạng như bên dưới. Lưu ý, để import và gọi các hàm trong DLL, bạn cần khai báo thêm gói InteropServices như dòng số 2.
Code:

Using System;
Using System.Runtime.InteropServices; //cần để thao tác với DLL

namespace CSharp
{
class TestCS
{
static void Main(string[] args)
{
//nội dung đặt ở đây
}
}
}

Gọi hàm cơ bản trong DLL từ C#

Để gọi một hàm trong DLL từ C#, chúng ta phải làm 3 bước:
– Import file DLL
– Khai báo một “nguyên mẫu” hàm sẽ gọi
– Gọi hàm

Ví dụ:
– Để import và khai báo nguyên mẫu cho hàm GetAge(int id), chúng ta chỉ cần dùng 2 dòng code sau:
Code:

[DllImport(“TestDLL.DLL”, EntryPoint=”GetAge”)] //import hàm
public static extern int GetAge(int id); //khai báo nguyên mẫu

– Sau đó, sử dụng hàm GetAge() này như thể nó đang nằm trong project C#:
Code:

int age = GetAge(1);
Console.WriteLine(“age = ” + age);

Khi tham số hàm là các kiểu dữ liệu phức tạp

Với tham số hàm là kiểu string, struct… thì mọi chuyện phức tạp hơn một chút, ví dụ:
– Để chứa chuỗi ký tự, C# có kiểu string, nhưng ANSI C++ lại là một mảng kiểu char kết thúc bằng giá trị ‘0’.
– Với dữ liệu dạng struct, hãy nhìn vào cấu trúc UserInfo ở ví dụ trên: data struct này được khai báo bên DLL thuộc project VC++. Khi sang bên project C#, chỉ có… trời mới hiểu UserInfo là cái quái gì!

Giải quyết?

Với biến string, ta dùng những “chỉ dẫn” để bắt C# “hiểu” và thao tác với dữ liệu của C++. Với biến struct, ta khai báo lại một data struct tương tự bên project C#, kèm theo một số chỉ dẫn của C#.

Ví dụ: khai báo cấu trúc UserInfo tương tự bên C#
Code:

[StructLayout(LayoutKind.Sequential)]
public struct UserInfo
{
public int id;
public int age;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)]
public string name; //tương đương char name [30] trong C++
};

Chú ý dòng thứ 1 và 6, đây là những “chỉ dẫn” giúp C# quản lý các kiểu dữ liệu không thuộc phạm vi của nó.

Và sau đó là sử dụng “vô tư”:
Code:

UserInfo user = new UserInfo();
int errCode = GetUser(1, ref user);
Console.WriteLine(“id = ” + user.id + “, name = ” + user.name + “, age = ” + user.age);

>> [zxc ] hơi nghi ngờ cái chỗ: UnmanagedType.ByValTStr, yếu tố nào quyết định nó tương úng với char mà không phải là WCHAR.

>> [sonhn ]
Thanks zxc. Quả thật đó là 1 thiếu sót nhỏ. Nhưng ByValTStr sẽ được tự động chuyển kiểu để tương thích với cả ANSI lẫn Unicode. Trong Tut trên, khi không khai báo ChatSet, VS sẽ hiểu:

 

Code:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.None)]

tức tương đương với chuỗi ANSI.Nếu muốn rõ ràng hơn, chúng ta có thể bổ sung thêm khai báo CharSet:
– Cho chuỗi ANSI:

 

Code:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]

– Cho Unicode:

 

Code:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]

– Và tự động theo hệ điều hành: Unicode (Win NT, 2K, XP) và ANSI (Win98, Me)

 

Code:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]

>> [TQN ]
Các cậu hiểu gì về DLL chưa, đã tự tay viết 1 DLL nào chưa, hỏi đơn giản thôi: tại sao lại cần file .DEF, file .def để làm gì ?
Cứ bê nguyên sê ri mấy bài đó thì càng chết, biểu A thì các cậu cứ làm A, chả biết tại sao, đúng hay sai. Tai hại, tai hại, đó là hậu quả của .NET đó.>> [Dreaminess ]
Theo Dr thì:
File .DEF trước hết có thể hiểu đơn giản nó là file định nghĩa (Define file), dùng để định nghĩa các tùy chọn cho việc linking (cho linker) với các thông tin liên quan đến exports, attributes và các thông tin liên quan khác của ứng dụng. File .def phần lớn được sử dụng khi building một thư việc .dll.
File .def là không nhất thiết phải có, mình có thể sử dụng __declspec(dllexport)_ để thay thế khi cần thiết.

Bác TQN bổ sung thêm xem có thiếu phần nào không? Dr chỉ có biết từng đó!

(Resource by http://forums.congdongcviet.com/)

Comments»

1. Sử dụng Visual Studio để lập trình C/C++ - 24/09/2014

I like the helpful information you provide for your articles.
I will bookmark your blog and check again right here
regularly. I’m moderately certain I will be informed many new stuff proper here!
Best of luck for the next!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: