Chào mừng đến với Diễn đàn lập trình - Cộng đồng lập trình.
Trang 1 của 2 12 CuốiCuối
Kết quả 1 đến 10 của 11
  1. #1
    Ngày tham gia
    Sep 2015
    Bài viết
    0

    Gọi hàm export của file .DLL trong lập trình C#

    Giới thiệu

    DLL (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”

    Mã:
    //khai báo 1 struct thử nghiệmstruct 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 DLLBOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved){    return TRUE;}
    File “TestAPI.def”

    Mã:
    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.

    Mã:
    Using System;Using System.Runtime.InteropServices;   //cần để thao tác với DLL namespace CSharp{    class TestCS    {        static void Main(string[] args)        {            /ộ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:

    Mã:
    [DllImport("TestDLL.DLL", EntryPoint="GetAge")]  //import hàmpublic 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#:

    Mã:
    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#

    Mã:
    [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ư”:

    Mã:
    UserInfo user = new UserInfo();int errCode = GetUser(1, ref user);Console.WriteLine("id = " + user.id + ", name = " + user.name + ", age = " +  user.age);

  2. #2
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    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

  3. #3
    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:
    Mã:
    [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:
    Mã:
    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
    - Cho Unicode:
    Mã:
    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
    - Và tự động theo hệ điều hành: Unicode (Win NT, 2K, XP) và ANSI (Win98, Me)
    Mã:
    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]

  4. #4
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    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 đó!

  5. #5
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    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 đó.

  6. #6
    Ngày tham gia
    Sep 2015
    Bài viết
    0

    Sao lại không cần thiết, cậu thử bỏ file .def đi xem code C# của cậu có get address các hàm export trong file .def được hay không ? Làm thử xem sao !
    Câu trả lời của tôi đã nằm trong poscount #7. Còn từ "không cần thiết" của tôi ám chỉ trường hợp sử dụng file .DLL trong VC++ có kèm theo file Library và Header (vì trong file Header đã có sẵn mẫu hàm). Cảm ơn sự nhiệt tình của cậu.

  7. #7
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Trích dẫn Gửi bởi 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 đó.
    Theo tôi được biết, file .DEF còn có tác dụng giúp các hàm export có được cái tên tường minh (đúng với tên của hàm) trong Export Table của file DLL. Bước này quả thật là không cần thiết, nhưng tôi vẫn dùng nó như một thói quen.

  8. #8
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Sao lại không cần thiết, cậu thử bỏ file .def đi xem code C# của cậu có get address các hàm export trong file .def được hay không ? Làm thử xem sao !

  9. #9
    Đọc tut này rất hay ,cảm ơn mọi người đã thảo luận.Mình có một thắc mắc nhỏ nhử sau mong mọi người giúp đỡ:
    Mình sử dụng hàm CryptAcquireContext,CryptGetUserKey của thư viện advapi32.dll ,tuy nhiên các tham số của hàm sử dụng các tham số mà khai báo dạng
    HCRYPTPROV hCryptProv;
    HCRYPTKEY hKey;
    HCRYPTKEY hXchgKey;
    HCRYPTHASH hHash;

    PBYTE pbKeyBlob;
    DWORD dwKeyBlobLen;
    Đó là khai báo trên C++ vậy khi sử dụng trên C# thì thế nào ,mong mọi người giúp đỡ.
    Mình muốn sử dụng các hàm Crypto API viết bằng C# đó mà
    Chân thành cảm ơn !

  10. #10
    Bài viết rất bổ ích, mình cũng đang khắp vấn đề với *.DLL thanks pác sonhn

 

 
Trang 1 của 2 12 CuốiCuối

Quyền viết bài

  • Bạn Không thể gửi Chủ đề mới
  • Bạn Không thể Gửi trả lời
  • Bạn Không thể Gửi file đính kèm
  • Bạn Không thể Sửa bài viết của mình
  •