PDA

View Full Version : Plug-In Programming - Season 1: Simple && Basic Pluggable SDK



OWickedFox
06-04-2009, 09:50 PM
Hi Y(:D ,

Sau một thời gian ngâm cứu khá lâu, hôm nay, Fox sẽ trình bày cho các bạn cách lập trình Plug-In trong C#. Hy vọng những bài

viết về đề tài này có thể giúp các bạn có cái nhìn rộng hơn về công nghệ lập trình.

Bài viết này xuất phát từ CỘNG ĐỒNG C VIỆT; các bạn có thể tùy ý sao chép, tham khảo; nhưng xin ghi rõ nguồn. CÁM ƠN

SEASON 1: BASIC PLUGGABLE APPLICATION

5137 5138 51395140 5141

I. GIỚI THIỆU

Plug-In cưng là ai :D ? Plug-In là cách lập trình viên có thể mở rộng thêm một vài chức năng của một ứng dụng nào đó, mà

không cần phải re-compile lại toàn bộ ứng dụng; Nói cách khác lập trình viên có thể mở rộng ứng dụng mà không cần phải thao

tác bất cứ gì vào source code nguyên thủy.

Một số ứng dụng plug-in phổ biến hiện nay: Mozilla FireFox, Microsoft Visual Studio, SharpDevelop, ứng dụng của Fox nữa :D…

II. Ý TƯỞNG

_ Thông qua bài viết của anh Zcoder87

Kỹ thuật lập trình Plugin trong lập trình VC++ (http://forums.congdongcviet.com/showthread.php?t=9916) . Do không rành lắm về C++, Fox bắt đầu tìm hiểu những bài viết về Plugin trên C#.

_ Bài viết này sẽ được chia làm nhiều kỳ (từ cách xây dựng một ứng dụng Plug-In đơn giản đến những cách mới phức tạp hơn -

Fox hy vọng sẽ có những bạn khác cùng tham gia chia sẽ :) )

III. CODING

1. Lên kế hoạch
_ Xây dựng một thư viện BasicArrayProcessorSDK để làm nền plug-in cho ứng dụng.
_ Xây dựng một chuơng trình thao tác với một mảng các số nguyên.
_ Xây dựng 2 thư viện sẽ lf 2 default plug-ins để sắp xếp và tìm kiếm trên mảng số nguyên này.

2. Coding
_ Trong kỳ 1 này chúng ta sẽ xây dựng một nền plug-in theo style Interfaces, Assembly và AppDomain vì đây là cách được ưa

dùng nhất.

_ Hệ thống thư mục output dành cho ứng dụng



+ Output Folder // thư mục output của ứng dụng.
+ PlugIns Folder // thư mục chứa các plug-in.
_ HostApplication.exe // file chạy chương trình chính.
_ PluggableLibrary.dll // thư viện tạo nền plug-in.


a. BasicArrayProcessorSDK
_ Mở C# IDE của bạn lên, tạo một Project Class Library, đặt tên tùy ý... (ở đây Fox đặt tên là BasicArrayProcessorSDK). Chỉnh

đường dẫn output theo hệ thống thư mục.
_ Chúng ta sẽ xây dựng 2 Interface, một Interface để sau này nếu ứng dụng của bạn cần một chức năng nào khác để thao tác

với một mảng số nguyên thì chỉ cần implements Interface này.



using System;

namespace BasicArrayProcessorSDK
{
/// <summary>
/// Interface to build an Array-Processor plug-in.
/// </summary>
public interface IArrayProcessor
{
/// <summary>
/// Do what you want to an integer array.
/// </summary>
///
/// <param name="array">given integer array.</param>
///
/// <returns>an integer array after having some fun.</returns>
int[] Process(int[] array);

/// <summary>
/// Chooses a manager.
/// </summary>
///
/// <param name="host">an object to manage this plug-in.</param>
void Initialize(IHostProcessor host);

/// <summary>
/// Releases all resources.
/// </summary>
void Dispose();

/// <summary>
/// Gets or sets plug-in attributes.
/// </summary>
ArrayProcessorPlugInAttribute Attributes
{
get;
set;
}
}
}


_ Một Interface khác tượng trưng cho một host quản lý việc thực thi các plug-in(s).



using System;

namespace BasicArrayProcessorSDK
{
/// <summary>
/// Interface to build a plug-in manager.
/// </summary>
public interface IHostProcessor
{
/// <summary>
/// Report to client a message.
/// </summary>
///
/// <param name="message">sending message.</param>
void Report(string message);

/// <summary>
/// Releases all resources.
/// </summary>
void Dispose();
}
}


Để có thể đảm bảo rằng host sẽ load đúng những plug-in(s) hợp lệ, chúng ta cần một Attribute để đánh dấu đồng thời cũng

quản lý những thông tin liên quan đến mỗi plug-in.



using System;

namespace BasicArrayProcessorSDK
{
/// <summary>
/// Object represents a plug-in's attributes.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class ArrayProcessorPlugInAttribute : Attribute
{
#region - field(s) -

/// <summary>
/// Name of plug-in.
/// </summary>
string mName;

/// <summary>
/// publisher of plug-in.
/// </summary>
string mPublisher;

/// <summary>
/// Description of plug-in.
/// </summary>
string mDescription;

/// <summary>
/// Version of plug-in.
/// </summary>
string mVersion;

#endregion

#region - constructor(s) -

/// <summary>
/// Initialize a plug-in attribute with given name, publisher, description and version.
/// </summary>
///
/// <param name="name">given name.</param>
/// <param name="publisher">given publisher.</param>
/// <param name="description">given description.</param>
/// <param name="version">given version.</param>
public ArrayProcessorPlugInAttribute(string name, string publisher, string description, string version)
{
mName = name;
mPublisher = publisher;
mDescription = description;
mVersion = version;
}

#endregion

#region - property(ies) -

/// <summary>
/// Gets the name of plug-in;
/// </summary>
public string Name
{
get { return mName; }
}

/// <summary>
/// Gets the publisher of plug-in;
/// </summary>
public string Publisher
{
get { return mPublisher; }
}

/// <summary>
/// Gets the description of plug-in;
/// </summary>
public string Description
{
get { return mDescription; }
}

/// <summary>
/// Gets the version of plug-in;
/// </summary>
public string Version
{
get { return mVersion; }
}

#endregion

#region - method(s) -

public override string ToString()
{
return "Name: " + mName + "; Publisher: " + mPublisher + "; Description: " + mDescription + "; Version: " + mVersion

+ ".";
}

#endregion
}
}


b. Xây dựng một ứng dụng đảm đưong trách nhiệm là một host quản lý plug-in.
_ Tạo mới một Project Windows Application trong cùng Solution (Fox đặt tên là HostViewer). Project này sẽ được Add

References đến thư viện nền plug-in là BasicArrayProcessorSDK.dll; Các bạn chọn thuộc tính Copy Local cho thư viện

BasicArrayProcessorSDK.dll thành false, vì chúng ta đã set output path cho thư viện này ở phần "a" rồi. Chúng ta cũng không

quên set output path cho ứng dụng host này theo hệ thống thư mục.

_ Chúng ta tạo một đối tượng implements từ IHostProcessor có nhiệm vụ quản lý các plug-in(s)



using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Windows.Forms;

using BasicArrayProcessorSDK;

namespace HostViewer
{
/// <summary>
/// Object represents a plug-in manager.
/// </summary>
public class HostService : IHostProcessor
{
#region - field(s) -

/// <summary>
/// Initialize a collection to store all available plug-in.
/// </summary>
private Types.ArrayProcessorCollection mPlugInCollection = new Types.ArrayProcessorCollection();

/// <summary>
/// Control to display message from this host.
/// </summary>
private Label mLabel;

#endregion

#region - constructor(s) -

/// <summary>
/// Initialize this host with a message-sender.
/// </summary>
///
/// <param name="control">host's message-sender.</param>
public HostService(Label control)
{
mLabel = control;
}

#endregion

#region - method(s) -

#region - public member(s) -

/// <summary>
/// Finds all library(ies) in the plug-in folder.
/// </summary>
///
/// <param name="folderPath">folder contains all plug-in library.</param>
public void FindPlugIns(string folderPath)
{
mPlugInCollection.Clear(); // reloads them all.

// goes through all library-file(s) in the plug-in folder...
DirectoryInfo container = new DirectoryInfo(folderPath);

foreach (FileInfo file in container.GetFiles("*.dll"))
this.LoadPlugIns(file.FullName);
}

/// <summary>
/// Loads available plug-in(s) to this host.
/// </summary>
///
/// <param name="filePath">path of plug-in library.</param>
private void LoadPlugIns(string filePath)
{
Assembly pluginAssembly = Assembly.LoadFrom(filePath);

object[] pluginAttributes;

// loops through all the Types found in the assembly...
foreach (Type pluginType in pluginAssembly.GetTypes())
{
pluginAttributes = pluginType.GetCustomAttributes(typeof(ArrayProcess orPlugInAttribute), true);

// only looks for a public, non-abstract and having attribute type(s).
if (pluginType.IsPublic && !pluginType.IsAbstract && pluginAttributes.Length != 0)
{
Type interfaceType = pluginType.GetInterface("BasicArrayProcessorSDK.IArrayProcessor", true);
if (null != interfaceType) // gets the right interface-type.
{
Types.AvailableArrayProcessor plugin = new Types.AvailableArrayProcessor();
plugin.AssemblyPath = filePath;
plugin.Instance =

(IArrayProcessor)Activator.CreateInstance(pluginAs sembly.GetType(pluginType.ToString()));
plugin.Instance.Attributes = pluginAttributes[0] as ArrayProcessorPlugInAttribute;
plugin.Instance.Initialize(this);

mPlugInCollection.Add(plugin); // loaded done.

plugin = null; // clean up...
}

interfaceType = null; // clean up...
}
}

pluginAssembly = null; // clean up...
}

#endregion

#region - implements IHostProcessor member(s) -

public void Report(string message)
{
mLabel.Text = message;
}

public void Dispose()
{
foreach (Types.AvailableArrayProcessor plugin in mPlugInCollection)
{
plugin.Instance.Dispose();
plugin.Instance = null;
}

mPlugInCollection.Clear();
}

#endregion

#endregion

#region - property(ies) -

/// <summary>
/// Gets the plug-in collection.
/// </summary>
public Types.ArrayProcessorCollection PlugIns
{
get { return mPlugInCollection; }
}

#endregion
}

namespace Types
{
/// <summary>
/// Represents a plug-in collection.
/// </summary>
public class ArrayProcessorCollection : CollectionBase
{
#region - method(s) -

/// <summary>
/// Adds new plug-in to the collection.
/// </summary>
///
/// <param name="plugin">new plug-in.</param>
public void Add(AvailableArrayProcessor plugin)
{
this.List.Add(plugin);
}

/// <summary>
/// Removes a plug-in from collection.
/// </summary>
///
/// <param name="plugin">removing plug-in.</param>
public void Remove(AvailableArrayProcessor plugin)
{
this.List.Remove(plugin);
}

/// <summary>
/// Removes all plug-in from collection.
/// </summary>
public void Clear()
{
this.List.Clear();
}

#endregion

#region - property(ies) -

/// <summary>
/// Gets numbre of available plug-in(s).
/// </summary>
public int PlugInCounter
{
get { return base.List.Count; }
}

#endregion
}

/// <summary>
/// Represents an available plug-in.
/// </summary>
public class AvailableArrayProcessor
{
#region - field(s) -

/// <summary>
/// Plug-in instance.
/// </summary>
private IArrayProcessor mInstance;

/// <summary>
/// Assembly-path of plug-in.
/// </summary>
private string mAssemblyPath;

#endregion

#region - constructor(s) -

/// <summary>
/// Initialize a default instance.
/// </summary>
public AvailableArrayProcessor()
: this(null, "")
{ }

/// <summary>
/// Initialize a instance with given value.
/// </summary>
///
/// <param name="plugin">instance of a plug-in.</param>
/// <param name="path">the path of plug-in assembly.</param>
public AvailableArrayProcessor(IArrayProcessor plugin, string path)
{
mInstance = plugin;

mAssemblyPath = path;
}

#endregion

#region - property(ies) -

/// <summary>
/// Gets, sets the instance of plug-in.
/// </summary>
public IArrayProcessor Instance
{
get { return mInstance; }
set { mInstance = value; }
}

/// <summary>
/// Gets, sets the path of plug-in assembly.
/// </summary>
public string AssemblyPath
{
get { return mAssemblyPath; }
set { mAssemblyPath = value; }
}

#endregion

#region - method(s) -

public override int GetHashCode()
{
int hashcode = 0;

unchecked
{
hashcode += 1000034 * mInstance.GetHashCode();
}

return hashcode;
}

public override bool Equals(object other)
{
if (null == other) return false;

AvailableArrayProcessor anotherOne = other as AvailableArrayProcessor;

return this.mInstance.Equals(anotherOne.mInstance);
}

public override string ToString()
{
return mInstance.ToString();
}

#endregion
}
}
}


Sau khi đã chuẩn bị nền móng đầy đủ, chúng ta sẽ xây dựng một FormMain để chạy chương trình.

Trong MainForm này chúng ta sẽ khai báo đối tượng HostService vừa tạo ở trên và sẽ cấp vùng nhớ new cho host này cùng với MainForm.



...
/// <summary>
/// Adapter the host uses to talk back to this.
/// </summary>
private HostService mHostAdapter;
...


Khai báo một mảng số nguyên Random khoãng 10 phần từ để các plug-in có chỗ mà làm việc :D



...
/// <summary>
/// Array to process.
/// </summary>
private int[] mArray = new int[10];
...


Đăng ký sự kiện FormLoad cho MainForm để load các plug-in thao tác với mảng (các plug-in sẽ được load dưới dạng các button;

khi click các button sẽ làm việc với mãng ngẫu nhiên đã tạo với chức năng riêng của mỗi plug-in).



/// <summary>
/// Loads all available plug-in(s) and disguises them as a <c>Button</c>.
/// </summary>
private void MainFormOnLoad(object sender, EventArgs e)
{
mHostAdapter.FindPlugIns(Application.StartupPath + @"\PlugIns");

foreach (Types.AvailableArrayProcessor plugin in mHostAdapter.PlugIns)
{
// disguising...
Button button = new Button();
button.Text = plugin.Instance.Attributes.Name;
button.Tag = plugin;
button.Padding = new Padding(6, 6, 6, 6);
button.Width = pluginsFlowLayoutPanel.Width - 10;
button.Height = pluginsFlowLayoutPanel.Height / 5;
button.TextAlign = ContentAlignment.MiddleCenter;
button.Click += new EventHandler(ArrayProcessorsPicking); // registers a click event...

this.mainToolTip.SetToolTip(button, plugin.Instance.Attributes.Description);

pluginsFlowLayoutPanel.Controls.Add(button);
}

mHostAdapter.Report(mHostAdapter.PlugIns.Count + " Plug-In(s) Loaded."); // reports to clients...
}


Sự kiện click trên các plug-in button(s)



/// <summary>
/// Clicking on a plug-in button will process the array by the way the plug-in is for.
/// </summary>
private void ArrayProcessorsPicking(object sender, EventArgs e)
{
resultFlowLayoutPanel.Controls.Clear();

Label elementResult = null;

Types.AvailableArrayProcessor processor = ((Button)sender).Tag as Types.AvailableArrayProcessor;

int[] result = processor.Instance.Process(mArray); // processing...

for (int i = 0; i < result.Length; i++)
{
elementResult = new Label();

resultFlowLayoutPanel.Controls.Add(elementResult);

this.StyleLabel(elementResult, Color.Red, result.ToString());
}

processor = null; // release plugin...
}


Chúng ta cũng nên đăng ký sự kiện FormClosing để có thể release những Resource có thể làm leak memory... :D



/// <summary>
/// When application is about to close, release the host's resources.
/// </summary>
private void MainFormOnClosing(object sender, FormClosingEventArgs e)
{
mHostAdapter.Dispose();
}


c. Xây dựng các plug-in(s)
_ Để xây dựng một plug-in chúng ta chỉ cần tạo từng Project Class Library, Add Reference đến BasicArrayProcessorSDK.dll

(BasicArrayProcessorSDK.dll này sẽ được set CopyLocal = false). Nhớ set ouput path cho plug-in theo "Hệ thống thư mục" nhé :)

_ Mỗi một đối tượng được tạo trong Plug-In Library chỉ cần implements đến IArrayProcessor là có thể trở thành một Plug-In để

Host có thể load lên MainForm (dưới dạng một button).

Chúng ta sẽ trở lại thời "cấu trúc dữ liệu và giải thuật" để xây dựng các plug-in sắp xếp và tìm kiếm nhé :D .

+ Plug-in sắp xếp
Ở đây Fox dùng thuật toán InsertionSort :D



using System;

using BasicArrayProcessorSDK;

namespace ArraySorting
{
[ArrayProcessorPlugIn("Ascending Insertion Sort Plug-In", "O'Wicked Fox", "Sorting an integer array with ascending
insertion-sort style", "1.0.0")] // Khai báo Attribute để Host nhận ra các thông tin của Plug-in, các plug-in xài giấy tờ giả là không được load đâu :D
public class InsertionSortAscending : IArrayProcessor
{
private IHostProcessor mHostViewer;

private ArrayProcessorPlugInAttribute mAttributes;

#region IArrayProcessor Members

public int[] Process(int[] array)
{
// Sắp xếp InsertionSort theo thứ tự tăng dần
int k, postition, temp;

for (k = 1; k < array.Length; k++)
{
temp = array[k];

postition = k;
while ((postition > 0) && (array[postition - 1] > temp))
{
array[postition] = array[postition - 1];

postition--;
}

array[postition] = temp;
}

mHostViewer.Report("Ascending Array Sorted.");

return ArrayUtils.CopyArray(array);
}

public void Initialize(IHostProcessor host)
{
mHostViewer = host;
}

public void Dispose()
{
mAttributes = null;
}

public ArrayProcessorPlugInAttribute Attributes
{
get { return mAttributes; }
set { mAttributes = value; }
}

#endregion
}


+ Để tìm kiếm, Fox sẽ dùng cách kiểm tra trong mãng ngẫu nhiên có số nào là số nguyên tố hay không...



using System;
using System.Collections.Generic;

using BasicArrayProcessorSDK;

namespace PrimeNumberFinder
{
[ArrayProcessorPlugIn("Prime-Number Finder Plug-In", "O'Wicked Fox", "Looking through a given integer array and collects all
element(s) which has value a prime-number.", "1.0.0")]
public class PrimeNumberProcessor : IArrayProcessor
{
#region - field(s) -

IHostProcessor mHostSideAdapter;

private ArrayProcessorPlugInAttribute mAttributes;

#endregion

#region IArrayProcessor Members

public int[] Process(int[] array)
{
List<int> storage = new List<int>();

bool flag;

for (int i = 0; i < array.Length; i++)
{
flag = this.CheckPrimeNumber(array[i]);
if (flag)
storage.Add(array[i]);
}

int count = storage.Count;
if (count == 0)
{
mHostSideAdapter.Report("No Prime Number Found.");

return array;
}
else
{
mHostSideAdapter.Report(count.ToString() + " Prime-Number(s) Found.");

int[] result = new int[count];

storage.CopyTo(result);

return result;
}
}

public void Initialize(IHostProcessor host)
{
mHostSideAdapter = host;
}

public void Dispose()
{
mAttributes = null;
}

public ArrayProcessorPlugInAttribute Attributes
{
get { return mAttributes; }
set { mAttributes = value; }
}

#endregion

private bool CheckPrimeNumber(int input)
{
if (input == 2) return true;

bool result = false;

int count = 0;

for (int j = 1; j <= input; j++)
{
if (input % j == 0)
{
count++;

if (count > 2) // số có nhìu hơn 2 thừa số nguyên tố thì không phải là số nguyên tố
{
result = false;
break;
}
else
result = true;
}
}

return result;
}
}
}


Nhấn Build nào :D , sau khi build, các bạn mở thư mục output ra, nếu nó có cấu trúc sau là đúng chuẩn :)
5142

Vậy là xong rồi :D . Nếu sau này các bạn muốn tạo một vài plug-in để chương trình có những tính năng mới, chỉ cần thực hiện
theo cách tạo plug-in của Fox, biên dịch plug-in ra file dll; copy vào thư mục "Plugins" của ứng dụng; restart ứng dụng thì chúng ta sẽ có một nút plug-in mới với chức năng do chúng ta tự tạo. :)

IV. KẾT

Kỳ 1 đã hết, Để đảm bảo các bạn có thể hiểu được chút ít về bài viết của Fox, Fox sẽ yêu cầu một bài tập sau cho các bạn :D :
Viết một plug-in để sắp xếp các phần tử trong mảng số tự nhiên theo thuật toán BubbleSort hoặc MergeSort.

[I]Fox có yêu cầu thế này, nếu các bạn muốn viết bài hồi đáp sau bài hướng dẫn của Fox (Đặt câu hỏi hoặc góp ý kiến thì Fox rất hoan nghênh :) ), xin các bạn hãy đính kèm theo một file .ZIP bài tập đã làm, hoặc gửi mail tới địa chỉ mail owickedfox@gmail.com.

Mời các bạn download sourcecode cho kỳ 1 và đón xem kỳ 2 về lập trình plug-in trên một ứng dụng MDI :)

Tạm biệt :)

V. VERSION

1. Ngày 06 tháng 04
_ Viết bài kỳ 1.

2. Ngày 07 tháng 04
_ Fix hàm LoadPlugIns(), check nếu mảng attribute lấy được có chiều dài khác = 0 khi đó plug-in hợp lệ mới cho load. (check = null chương trình sẽ báo lỗi Null Exception).

ZCoder87
27-04-2009, 11:52 AM
Bài viết hay lắm đó.

- Ở C++ khi mình muốn call 1 phương thức trên 1 file DLL/EXE thì bắc buộc phải export functions đó ra. Lấy con trỏ hàm và call nó.
- Nhưng ở C# dù lâu quá chưa code, giờ đọc lại code hơi mệt nhưng vẫn nhận ra đối tượng Assembly. Công nhận nó hay thật. Có thể call 1 function trên DLL khá tiện lợi. (plugin thì ko bao giờ add references rồi :D và nó là giải pháp thay thế hay)



/// <summary>
/// Loads available plug-in(s) to this host.
/// </summary>
///
/// <param name="filePath">path of plug-in library.</param>

private void LoadPlugIns(string filePath)
{
Assembly pluginAssembly = Assembly.LoadFrom(filePath);

object[] pluginAttributes;

// loops through all the Types found in the assembly...
foreach (Type pluginType in pluginAssembly.GetTypes())
{
pluginAttributes = pluginType.GetCustomAttributes(typeof(ArrayProcess orPlugInAttribute), true);

// only looks for a public, non-abstract and having attribute type(s).
if (pluginType.IsPublic && !pluginType.IsAbstract && pluginAttributes.Length != 0)
{
Type interfaceType = pluginType.GetInterface("BasicArrayProcessorSDK.IArrayProcessor", true);
if (null != interfaceType) // gets the right interface-type.
{
Types.AvailableArrayProcessor plugin = new Types.AvailableArrayProcessor();
plugin.AssemblyPath = filePath;
plugin.Instance =

(IArrayProcessor)Activator.CreateInstance(pluginAs sembly.GetType(pluginType.ToString()));
plugin.Instance.Attributes = pluginAttributes[0] as ArrayProcessorPlugInAttribute;
plugin.Instance.Initialize(this);

mPlugInCollection.Add(plugin); // loaded done.

plugin = null; // clean up...
}

interfaceType = null; // clean up...
}
}

pluginAssembly = null; // clean up...
}


Thank fox nhé!

OWickedFox
05-05-2009, 11:03 PM
Hi anh ZCoder87,
Cám ơn anh đã góp ý cho bài viết của em. Xem ra bài này ế quá rồi, may có anh kéo nó lại bờ :D . Mà cũng hên, không biết ai gắn cho nó cái neo ngay đầu Box luôn nhỉ :-? .

PS: Em sẽ cảm kích hơn, nếu anh ZCoder87 làm bài tập và cõ vài hình mình họa :D

vinhson
10-05-2009, 12:00 AM
bài viết này cũng hay đó. Mới nghiên cứu ra (cũng làm trong C# luôn).
Hơi khác một tí nhưng cũng dùng đối tượng Assembly luôn.
Chức năng mới thêm vào (cả form và các xử lý đều nằm trong 1 file DLL luôn)

http://rapidshare.com/files/231042702/menu_plugin.rar

huynguyen
12-05-2009, 09:59 AM
Lúc trước huynguyen nghĩ rằng plugin được viết trên dotNet mà load ở trên C++, VB hay Delphi là điều không thể nhưng nay thì đã biết giải pháp.
Ở đây huynguyen ví dụ bằng code trực tiếp chứ ko tạo project.
Đầu tiên là file AssemblyInfo.cs:

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("dotNETPlugin")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Kobekara")]
[assembly: AssemblyProduct("dotNETPlugin")]
[assembly: AssemblyCopyright("Copyright © Kobekara 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("8656e98c-a589-417f-b478-14df2084ebcc")]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Chú ý dòng màu đỏ, đây là dòng khai báo cho biết có thể build dll này ra dạng COM object.
Tiếp theo là lớp nền:

//using System;
//using System.Collections.Generic;
//using System.Text;

public interface IMyDotNetPlugin
{
#region ----- Plugin Information -----
string GetName();
string GetVersion();
string GetCompany();
string GetAuthor();
string GetDescription();
#endregion

#region ----- Communication with app. -----
void SendMessage();
void ReceiveMessage();
#endregion

#region ----- Main -----
void Init(int ParentHandle, int MyMessage);
//void ShowPlugin(HostApplication: TApplication; ParentControl: TWinControl);
//function GetPlugin: TComponent;
#endregion
}

Cuối cùng là lớp triển khai:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;

[ClassInterface(ClassInterfaceType.AutoDual)]
public class MyDotNetPlugin : IMyDotNetPlugin
{
const string CS_NAME = "My Plugin"; // your plugin name
const string CS_VERSION = "1.001"; // your current version
const string CS_COMPANY = "Kobekara"; // your company
const string CS_AUTHOR = "Nguyen Trong Huy"; // your name
const string CS_DESCRIPTION = "This is my first test plugin"; // your description

#region ----- Properties -----
private UserControl _plugin;
public UserControl myPlugin
{
get { return _plugin; }
set { _plugin = value; }
}
#endregion

[DllImport("user32.dll", EntryPoint = "SetParent")]
private static extern IntPtr SetParent(IntPtr child, IntPtr newParent);

#region ----- Plugin Information -----
public string GetName()
{
return CS_NAME;
}
public string GetVersion()
{
return CS_VERSION;
}
public string GetCompany()
{
return CS_COMPANY;
}
public string GetAuthor()
{
return CS_AUTHOR;
}
public string GetDescription()
{
return CS_DESCRIPTION;
}
#endregion

#region ----- Communication with app. -----
public void SendMessage()
{
}
public void ReceiveMessage()
{
}
#endregion

#region ----- Main -----
public void Init(int ParentHandle, int MyMessage)
{
myPlugin = new UserControl();
myPlugin.Top = 0;
myPlugin.Left = 0;
myPlugin.Width = 256;
myPlugin.Height = 256;
myPlugin.BorderStyle = BorderStyle.FixedSingle;
Panel p = new Panel();
p.Dock = DockStyle.Fill;
p.BackColor = Color.Blue;
myPlugin.Controls.Add(p);
IntPtr hwndParentPtr = new IntPtr(ParentHandle);
SetParent(myPlugin.Handle, hwndParentPtr);
}
//void ShowPlugin(HostApplication: TApplication; ParentControl: TWinControl);
//function GetPlugin: TComponent;
#endregion
}

Sau đó tạo file build.bat như sau:

c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc. exe /t:library /out:dotNETPlugin.dll *.cs
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\rega sm dotNETPlugin.dll codebase /tlb

@pause
Cuối cùng là thành quả: ta sẽ có 1 file dll và 1 file tlb tương ứng. File tlb này sẽ được import vào các project được viết trên Delphi, VB, C++ để sử dụng.

OWickedFox
12-05-2009, 02:11 PM
Khổ nổi em chỉ rành mỗi dotNET, Java thôi :( , việc tìm hiểu vấn đề của anh Huy chắc phải cần thời gian lâu dài. À hay thử với Java được không ta :-?

AdminPro
14-05-2009, 10:44 AM
Cuối cùng là thành quả: ta sẽ có 1 file dll và 1 file tlb tương ứng. File tlb này sẽ được import vào các project được viết trên Delphi, VB, C++ để sử dụng.
__________________

import bằng cách nào bạn,thử cho 1 vd đi

huynguyen
14-05-2009, 08:05 PM
import dạng type library trong delphi, trong c++ và vb hình như cũng là add reference. bạn có thể tìm hiểu thêm đối tượng com và file tlb. sau khi tìm hiểu thì có thể post lên cho mọi người tham khảo thêm.

vinaghost
16-09-2009, 05:00 PM
Mình cũng xây dựng một hệ thống nền chung để phát triển các ứng dụng quản lý cũng dựa theo những tác dụng gần thế này và có thể hơn thế, các bạn xem qua ứng dụng của nó ở một phần mêm nhỏ minh gửi kèm (show hướng dẫn tí thôi xem cho vui (:X) )

OWickedFox
20-09-2009, 12:05 PM
@vinaghost:
_ Document này thuộc bản quyền của bạn à? Theo mình thấy thì nó của PSoft! Mình chỉ xin góp ý kiến rằng nên hạn chế công khai tài liệu của một công ty lên mạng kẻo vi phạm bản quyền thì không hay...
_ Project này rất là đồ sộ, nhưng trong document mình không đọc thấy hướng dẫn về cách lập trình plug-in của cậu? Nếu có thể xin bạn vui lòng cho mình xem hình về cấu trúc của Folder trương chình sau khi cài đặt được không?
_ "Phần mềm nhỏ" hay "ứng dụng gửi kèm" của bạn nói, mình tải về không thấy (chỉ có tài liệu thiết kế thôi)!
_ Xem cho vui thì xem truyện tranh hay hơn bạn! Bạn hiểu ý mình chứ!

Cheer :D

tieutin2009
20-09-2009, 12:10 PM
hix, các bạn chỉ húp mình để học tốt c++ mình nên bắt đầu học từ đâu vậy:L

vinaghost
28-09-2009, 09:18 AM
Phần mềm cũng như nền tảng của nó do mình làm một mình từ đầu (con người quan trọng hơn bản quyền). Với framework mình phát triển thì làm 1 project trong vài ngày, còn lại thời gian làm báo cáo với chỉnh sửa thêm. Framework được thiết kế riêng biệt cho phép liên kết các project đơn lẻ dù đã được biên dịch (lấy chức năng gần giống chương trình Reflector) và phân quyền chi tiết đến từng chức năng, controls,...Mọi người tham gia vào project có thể tạo mới riêng project con của mình mà không cần quan tâm đến hệ thống tổng thể cũng như phân quyền bảo mật, chỉ cần quan tâm đến các thông số chung,... và còn nhiều cái hay ho kế thừa từ nhiều project mà mình học hỏi được. (Tất cả được cấu hình động trên database ko cần addreference, không cần sự gắn kết trực tiếp nào). Project trong hướng dẫn sử dụng là nhỏ so với những cái mình đã làm thôi. Vì mình đang làm cho công ty nên ko muốn chi tiết hóa nhiều.

mouseover
18-12-2009, 08:52 AM
ủa sao ko thấy phần tiếp theo thế Fox

OWickedFox
27-04-2010, 12:52 AM
Cám ơn những lời đóng góp của các bạn :) , lý do Fox không tiếp tục series này nữa do dotnet 3.5 có System.Addins và dotNET 4.0 có MEF hỗ trợ MVVM quá tốt (dễ dùng).

nguyenvu_101
29-04-2010, 11:20 AM
quả thật bài của hai anh rất hay, nhưng vì chủ đề này ít có người học nên chủ đề bị "ế", em cũng đang nghiên cứu về đề tài này, hai anh có tài liệu gì hay share cho em với nhe. Thank hai anh nhiều :)

shinichiqn
06-05-2010, 06:51 PM
Mình ủng hộ topic :D ..


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BasicArrayProcessorSDK;

namespace SoNguyenToTrongMang
{
[ArrayProcessorPlugIn("Tìm số nguyên tố Plug-In", "O'Wicked Fox", "Looking through a given integer array and collects all element(s) which has value a prime-number.", "1.0.0")]
public class TimSoNguyenTo : IArrayProcessor
{
IHostProcessor mHostSideAdapter;
private ArrayProcessorPlugInAttribute mAttributes;

int[] IArrayProcessor.Process(int[] array)
{
List<int> n = new List<int>();
foreach (int a in array) {
if (a != 0 && a!= 1 && a%2 != 0 && a%3 != 0) {
n.Add(a);
}
if (a == 2) {
n.Add(a);
}
}
if (n.Count== 0)
{
mHostSideAdapter.Report("Khong co so nguyen to nao ");
}
else {
mHostSideAdapter.Report("co' " + n.Count + " so nguyen to trong mang ");
}
int[] result = new int[n.Count];
n.CopyTo(result);
return result;
}

void IArrayProcessor.Initialize(IHostProcessor host)
{
mHostSideAdapter = host;
}

void IArrayProcessor.Dispose()
{
mAttributes = null;
}

ArrayProcessorPlugInAttribute IArrayProcessor.Attributes
{
get { return mAttributes; }
set { mAttributes = value; }
}
}
}

http://i287.photobucket.com/albums/ll132/shinichiqn/Plugin.png

shinichiqn
06-05-2010, 06:52 PM
Chủ topic cho mình xin nick yahoo được ko ..
hoặc có thể add nick mình .. yahoo : 190590@yahoo.com
mong được làm quen với bạn :D . mình rất hứng thú với lập trình kiểu như vầy .

shinichiqn
07-05-2010, 01:57 AM
Cách của vingaghost mình thấy không hay lắm :D đó là ý kiến của mình . vì khi update bạn phải đăng ký lại chức năng ví dụ chức nằng sử dụng dll nào . ko biết có phải như thế ko nhỉ ?

OWickedFox
08-05-2010, 10:56 PM
Ồ, bài này viết đã tròn 1 năm 1 tháng 1 ngày, giờ chỉ mới có bạn shinichiqn là người đầu tiên làm một plug-in vào chương trình của Fox. Cám ơn bạn rất nhiều; điều này khiến Fox cảm thấy rất vui vì có một bạn đã hiểu và làm được nội dung Fox đưa ra :) . Một lần nữa xin cám ơn bạn.

PS: về việc add nick yahoo thì bạn cứ add nick nightwish_1987 , nhưng nhớ giới thiệu trước khi add nhé :D , với lại dạo này Fox bận lắm, khi nào rãnh thì sẵn sàng trao đổi với bạn :)

OWickedFox
24-05-2010, 02:30 PM
Fox xin thông báo rằng, hiện tại Fox đã hoàn thành bước xây dựng nền cho Season 2 trong chuyên mục Plug-in Programming mang tên là Devious Movements.

Trong Season 2 này chúng ta sẽ tim hiểu cách xây dựng một mô hình Plug-in cho ứng dụng thông qua MAF (Managed Addin Framework - dotNET 3.0, 3.5). Cụ thể ở đây chúng ta sẽ xây dựng một chuơng trình mô phỏng các thuật toán sắp xếp đã được học trong môn Analyse and Design Algorythm.

Chuơng trình chính sẽ quản lý việc sắp xếp đồ họa. Trong khi các Plug-in sẽ là những thuật toán sắp xếp. Khi đó chúng ta sẽ xây dựng các cầu nối (delegate, event) để 2 đối tượng này có thể nói chuyện với nhau. Sau đây là hình ảnh cho bản build beta của chuơng trình.

http://i390.photobucket.com/albums/oo348/OWicked_Fox/Capture-1-2.png

Do trong tuần này Fox phải thi, cho nên Fox chỉ báo tin như vậy, bản chính thức dự kiến hoàn thành vào Chủ Nhật tuần này, mong các bạn đón xem và ủng hộ để Fox tiếp tục thực hiện tốt loạt bài viết này.

Cám ơn, chúc các bạn một tuần làm việc mới vui vẽ :)

Fox

lemanh2006
27-06-2010, 05:11 PM
O'Wicked Fox có thể post lên toàn bộ source code cùa session 2 cho mọi người cùng tham khảo được không. thanks

phuong111
14-10-2010, 08:10 AM
Bài viết thật hay . Thanks!!!

conanhero
20-10-2011, 12:20 AM
Sao không thấy anh Fox tiếp tục topic này nữa. Đang mong tin anh.

Naughty_boy9x
30-11-2011, 10:44 AM
Hay quá, em đang tìm hiểu về vấn đề này, tks bài viết rất rất nhiều, đợi phần 2 của anh :)

frankie123
18-01-2014, 08:15 AM
bài viết hay quá, cũng đang đợi phần 2

akiko
20-06-2015, 09:27 PM
anh Fox tiếp tục phần 2 lên đi hj ! nay đang ngâm cứu về vấn đề này nên tìm được bài của anh nhưng lại không thấy phần 2 tiếc quá