• Vui lòng đọc nội qui diễn đàn để tránh bị xóa bài viết
  • Tìm kiếm trước khi đặt câu hỏi

Tạo Shell Context Menu copy đường dẫn items đang được chọn

Các bài viết hướng dẫn về Visual Basic .NET và C#

Điều hành viên: tungcan5diop, QUANITGROBEST

Hình đại diện của người dùng
Kỳ Nam
Guru
Guru
Bài viết: 510
Ngày tham gia: CN 12/08/2007 8:47 pm
Đến từ: Qui Nhơn
Been thanked: 1 time
Liên hệ:

Tạo Shell Context Menu copy đường dẫn items đang được chọn

Gửi bàigửi bởi Kỳ Nam » T.Tư 09/07/2008 12:49 pm

Tên bài viết: Tạo Shell Context Menu đơn giản để copy đường dẫn các item đang được chọn trong windows explorer
Tác giả: kỳ nam
Cấp độ bài viết: dễ hiểu dễ làm
Tóm tắt: Tạo Shell Context Menu đơn giản để copy đường dẫn các item đang được chọn trong windows explorer . Dùng VB . Nếu dùng C# thì vài chỗ sẽ khác


Để hiểu , bạn cần MSDN , luôn luôn cần MSDN để viết các ct liên quan đến windows

Shell là phần giao diện trong windows , tựa như GNOME hay KDE của linux , gồm có :

+ bạn mở cửa sổ explorer lên , cho hiện cây thư mục bên trái , toàn bộ cấu trúc mà bạn đang nhìn thấy là 1 phần của Shell

+ desktop , start menu , taskbar , tray icon

Trước tiên , vì sau khi tạo context menu và đăng ký nó , nó sẽ được load bởi explorer.exe ( và cả các chương trình khác ) và 0 thể xóa nếu ct đó đang chạy , bạn nên chọn "mở cửa sổ thư mục trong tiến trình riêng" trong Folder options -> mỗi lần kiểm tra Dll của mình , mình sẽ mở 1 cửa sổ riêng trên 1 process explorer.exe riêng , sau khi kiểm tra xong , đóng process đó lại và 0 ảnh hưởng tới explorer.exe đầu tiên

MSDN index : Creating Context Menu Handlers

Khi người dùng bấm chuột phải lên 1 đối tượng trong Shell , Shell hiện menu của nó , menu đó gồm những item chuẩn ( như Cut , Copy ... ) , những item được lưu trong registry cho từng loại đối tượng Shell ( VD : Open , Edit , Print ... ) , và cuối cùng những item được tạo bởi các Context menu handler ( giờ mình sẽ tạo cái đây nè )

Để tạo context menu handler :

1/ tạo 1 Dll chứa COM class đã Implement IContextMenu & IShellExtInit
1.1/ khai báo IContextMenu & IShellExtInit
1.2/ tạo COM class
1.3/ Implement IContextMenu & IShellExtInit cho class đó

2/ đăng ký Dll đó trong registry để Shell biết tới



1.0/ Tạo 1 project Class Library bằng VB , dùng .NET 2.0

1.1/khai báo những thứ dùng đến , đặt trong 1 file Interop.vb

Mã: Chọn hết

  1. Imports System.Runtime.InteropServices
  2.  
  3. Namespace Interop
  4.  
  5.     <ComImport(), Guid("000214e8-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
  6.     Public Interface IShellExtInit
  7.         <PreserveSig()> Function Initialize(ByVal pidlFolder As IntPtr, ByVal lpdobj As IntPtr, ByVal hKeyProgID As IntPtr) As Integer
  8.     End Interface
  9.  
  10.     <ComImport(), Guid("000214e4-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
  11.     Public Interface IContextMenu
  12.         <PreserveSig()> Function QueryContextMenu(ByVal hMenu As IntPtr, ByVal indexMenu As UInteger, ByVal idCmdFirst As UInteger, ByVal idCmdLast As UInteger, ByVal uFlags As CMF) As Integer
  13.         <PreserveSig()> Function InvokeCommand(ByVal pici As CMInvokeCommandInfo) As Integer
  14.         <PreserveSig()> Function GetCommandString(ByVal idCmd As Integer, ByVal uType As GCS, ByRef pwReserved As Integer, ByVal pszName As IntPtr, ByVal cchMax As Integer) As Integer
  15.     End Interface
  16.  
  17.     <StructLayout(LayoutKind.Sequential)> Public NotInheritable Class CMInvokeCommandInfo
  18.         Public Size As Integer = Marshal.SizeOf(GetType(CMInvokeCommandInfo))
  19.         Public fMask As CMICMask
  20.         Public hwnd As IntPtr
  21.         Public lpVerb As IntPtr
  22.         Public lpParameters As IntPtr
  23.         Public lpDirectory As IntPtr
  24.         Public nShow As Integer
  25.         Public dwHotKey As Integer
  26.         Public hIcon As IntPtr
  27.         Public lpTitle As IntPtr
  28.         Public lpVerbW As IntPtr
  29.         Public lpParametersW As IntPtr
  30.         Public lpDirectoryW As IntPtr
  31.         Public lpTitleW As IntPtr
  32.         Public ptInvoke As Win32Point
  33.     End Class
  34.  
  35.     Public Enum CMICMask
  36.         Hotkey = &H20
  37.         Unicode = &H4000
  38.         ShiftDown = &H10000000
  39.         ControlDown = &H40000000
  40.     End Enum
  41.  
  42.     <Flags()> Public Enum CMF
  43.         Normal = 0
  44.         DefaultOnly = 1
  45.         VerbsOnly = &H2
  46.         Explore = &H4
  47.         NoVerbs = &H8
  48.         CanRename = &H10
  49.         NoDefault = &H20
  50.         IncludeStatic = &H40
  51.         ExtendedVerbs = &H100
  52.     End Enum
  53.  
  54.     <Flags()> Public Enum GCS
  55.         COMPATTR = &H10
  56.         COMPCLAUSE = &H20
  57.         COMPREADATTR = &H2
  58.         COMPREADCLAUSE = &H4
  59.         COMPREADSTR = &H1
  60.         COMPSTR = &H8
  61.         CURSORPOS = &H80
  62.         DELTASTART = &H100
  63.         HELPTEXTA = &H1
  64.         HELPTEXTW = &H5
  65.         RESULTCLAUSE = &H1000
  66.         RESULTREADSTR = &H200
  67.         RESULTSTR = &H800
  68.         UNICODE = &H4
  69.         VALIDATEA = &H2
  70.         VALIDATEW = &H6
  71.         VERBA = &H0
  72.         VERBW = &H4
  73.     End Enum
  74.  
  75.     <StructLayout(LayoutKind.Sequential)> Public Structure Win32Point
  76.  
  77.         Public X As Integer
  78.         Public Y As Integer
  79.  
  80.         Public Sub New(ByVal x As Integer, ByVal y As Integer)
  81.             Me.X = x
  82.             Me.Y = y
  83.         End Sub
  84.  
  85.     End Structure
  86.  
  87.     <Flags()> Public Enum ClipboardFormat
  88.         ACCEPT = &H0
  89.         ANSIONLY = &H400I
  90.         APPLY = &H200I
  91.         BITMAP = 2
  92.         BOTH = (SCREENFONTS Or PRINTERFONTS)
  93.         SCREENFONTS = &H1
  94.         PRINTERFONTS = &H2
  95.         BOTTOMUP_DIB = DIB
  96.         DIB = 8
  97.         CONVERTONLY = &H100I
  98.         DEFER = &H2
  99.         DIBV5 = 17
  100.         DIF = 5
  101.         DISABLEACTIVATEAS = &H40I
  102.         DISABLEDISPLAYASICON = &H20I
  103.         DSPBITMAP = &H82
  104.         DSPENHMETAFILE = &H8E
  105.         DSPMETAFILEPICT = &H83
  106.         DSPTEXT = &H81
  107.         EFFECTS = &H100I
  108.         ENABLEHOOK = &H8I
  109.         ENABLETEMPLATE = &H10I
  110.         ENABLETEMPLATEHANDLE = &H20I
  111.         ENHMETAFILE = 14
  112.         FIXEDPITCHONLY = &H4000I
  113.         FORCEFONTEXIST = &H10000
  114.         GDIOBJFIRST = &H300
  115.         GDIOBJLAST = &H3FF
  116.         HDROP = 15
  117.         HIDECHANGEICON = &H80I
  118.         INITTOLOGFONTSTRUCT = &H40I
  119.         JPEG = 19
  120.         LIMITSIZE = &H2000I
  121.         LOCALE = 16
  122.         MAX = 17
  123.         METAFILEPICT = 3
  124.         MULTI_TIFF = 22
  125.         NOFACESEL = &H80000
  126.         NOOEMFONTS = NOVECTORFONTS
  127.         NOVECTORFONTS = &H800I
  128.         NOSCRIPTSEL = &H800000
  129.         NOSIMULATIONS = &H1000I
  130.         NOSIZESEL = &H200000
  131.         NOSTYLESEL = &H100000
  132.         NOVERTFONTS = &H1000000
  133.         NULL = 0
  134.         OEMTEXT = 7
  135.         OWNERDISPLAY = &H80
  136.         PALETTE = 9
  137.         PENDATA = 10
  138.         PRIVATEFIRST = &H200
  139.         PRIVATELAST = &H2FF
  140.         REJECT = &H1
  141.         RIFF = 11
  142.         SCALABLEONLY = &H20000
  143.         SCRIPTSONLY = ANSIONLY
  144.         SELECTACTIVATEAS = &H10I
  145.         SELECTCONVERTTO = &H8I
  146.         SELECTSCRIPT = &H400000
  147.         SETACTIVATEDEFAULT = &H4I
  148.         SETCONVERTDEFAULT = &H2I
  149.         SHOWHELPBUTTON = &H1I
  150.         SYLK = 4
  151.         TEXT = 1
  152.         TIFF = 6
  153.         TOPDOWN_DIB = 20
  154.         TTONLY = &H40000
  155.         UNICODETEXT = 13
  156.         USESTYLE = &H80I
  157.         WAVE = 12
  158.         WYSIWYG = &H8000
  159.     End Enum
  160.  
  161.     <Flags()> Public Enum MF
  162.         INSERT = &H0
  163.         CHANGE = &H80
  164.         APPEND = &H100
  165.         DELETE = &H200
  166.         REMOVE = &H1000
  167.  
  168.         BYCOMMAND = &H0
  169.         BYPOSITION = &H400
  170.  
  171.         ENABLED = &H0
  172.         GRAYED = &H1
  173.         DISABLED = &H2
  174.  
  175.         UNCHECKED = &H0
  176.         CHECKED = &H8
  177.         USECHECKBITMAPS = &H200
  178.  
  179.         UNHILITE = &H0
  180.         HILITE = &H80
  181.  
  182.         [STRING] = &H0
  183.         [BITMAP] = &H4
  184.         POPUP = &H10
  185.         MENUBARBREAK = &H20
  186.         MENUBREAK = &H40
  187.         OWNERDRAW = &H100
  188.         SEPARATOR = &H800
  189.  
  190.         [DEFAULT] = &H1000
  191.         SYSMENU = &H2000
  192.         HELP = &H4000
  193.         RIGHTJUSTIFY = &H4000
  194.         MOUSESELECT = &H8000
  195.     End Enum
  196.  
  197.     Public Enum CF
  198.         Text = 1
  199.         Bitmap = 2
  200.         MetaFilePict = 3
  201.         Sylk = 4
  202.         Dif = 5
  203.         Tiff = 6
  204.         OemText = 7
  205.         Dib = 8
  206.         Palette = 9
  207.         Pendata = 10
  208.         Riff = 11
  209.         Wave = 12
  210.         UnicodeText = 13
  211.         EnhMetaFile = 14
  212.         HDrop = 15
  213.         Locale = 16
  214.         Dibv5 = 17
  215.  
  216.         OwnerDisplay = &H80
  217.         DspText = &H81
  218.         DspBitmap = &H82
  219.         DspMetaFilePict = &H83
  220.         DspEnhMetaFile = &H8E
  221.  
  222.         PrivateFirst = &H200
  223.         PrivateLast = &H2FF
  224.  
  225.         GdiObjFirst = &H300
  226.         GdiObjLast = &H3FF
  227.     End Enum
  228.  
  229.     Public NotInheritable Class User32
  230.  
  231.         Public Declare Auto Function AppendMenu Lib "User32.dll" (ByVal hMenu As IntPtr, ByVal wFlags As MF, ByVal wIDNewItem As IntPtr, ByVal lpNewItem As String) As Boolean
  232.  
  233.         Public Declare Function OpenClipboard Lib "User32.dll" (ByVal hwnd As IntPtr) As Boolean
  234.         Public Declare Function SetClipboardData Lib "User32.dll" (ByVal Format As CF, ByVal hMem As IntPtr) As IntPtr
  235.         Public Declare Function EmptyClipboard Lib "User32.dll" () As Boolean
  236.         Public Declare Function CloseClipboard Lib "User32.dll" () As Boolean
  237.  
  238.     End Class
  239.  
  240.     Public NotInheritable Class Shell32
  241.  
  242.         Public Declare Auto Function DragQueryFile Lib "Shell32.dll" (ByVal HDROP As IntPtr, ByVal UINT As Integer, ByVal lpStr As String, ByVal ch As Integer) As Integer
  243.  
  244.     End Class
  245.  
  246. End Namespace

1.2/ tạo COM class

+ Xóa class Class1.vb lúc tạo project

+ Add new item -> chọn COM class , đặt tên là CopyPathContextMenuHandler -> file đó sẽ có sẵn code dạng như ở dưới , bạn bỏ đi các interfaceID & eventID , và Imports CopyPath.Interop

Mã: Chọn hết

  1. Imports CopyPath.Interop
  2. Imports System.Runtime.InteropServices
  3.  
  4. <ComClass(CopyPathContextMenuHandler.ClassId)> _
  5. Public NotInheritable Class CopyPathContextMenuHandler
  6.  
  7. #Region "COM GUIDs"
  8.     ' These  GUIDs provide the COM identity for this class
  9.     ' and its COM interfaces. If you change them, existing
  10.     ' clients will no longer be able to access the class.
  11.     Public Const ClassId As String = "4403e053-9671-4482-91ad-3c88af3feb7c"
  12. #End Region
  13.  
  14.     ' A creatable COM class must have a Public Sub New()
  15.     ' with no parameters, otherwise, the class will not be
  16.     ' registered in the COM registry and cannot be created
  17.     ' via CreateObject.
  18.     Public Sub New()
  19.         MyBase.New()
  20.     End Sub
  21.  
  22. End Class

tốt nhất copy luôn code ở trên , khỏi Add new item COM class , để cố định dùng GUID "4403e053-9671-4482-91ad-3c88af3feb7c" . Nếu bạn copy kiểu này , dô project properties chọn "Register COM ..."

1.3/ implement các interface

Implements IShellExtInit, IContextMenu dưới dòng Public NotInheritable Class CopyPathContextMenuHandler , các method sẽ được tự tạo

Các biến sẽ dùng

Mã: Chọn hết

  1.    Dim hDrop As IntPtr

Function Initialize giúp mình biết được hDrop chứa các file đang được chọn

Mã: Chọn hết

  1.    Public Function Initialize(ByVal pidlFolder As System.IntPtr, ByVal lpdobj As System.IntPtr, ByVal hKeyProgID As System.IntPtr) As Integer Implements Interop.IShellExtInit.Initialize
  2.         If Not lpdobj = IntPtr.Zero Then
  3.             Dim dataObject As ComTypes.IDataObject = Marshal.GetObjectForIUnknown(lpdobj)
  4.             Dim fmt As New ComTypes.FORMATETC
  5.             fmt.cfFormat = ClipboardFormat.HDROP
  6.             fmt.ptd = 0
  7.             fmt.dwAspect = ComTypes.DVASPECT.DVASPECT_CONTENT
  8.             fmt.lindex = -1
  9.             fmt.tymed = ComTypes.TYMED.TYMED_HGLOBAL
  10.             Dim medium As New ComTypes.STGMEDIUM
  11.             dataObject.GetData(fmt, medium)
  12.             hDrop = medium.unionmember
  13.         End If
  14.     End Function

Function QueryContextMenu để đưa menu item của mình vào trong context menu

Mã: Chọn hết

  1.    Public Function QueryContextMenu(ByVal hMenu As System.IntPtr, ByVal indexMenu As UInteger, ByVal idCmdFirst As UInteger, ByVal idCmdLast As UInteger, ByVal uFlags As Interop.CMF) As Integer Implements Interop.IContextMenu.QueryContextMenu
  2.         'idCmdFirst là ID nhỏ nhất mình được phép sử dụng cho các Menu item mà mình sẽ thêm vào Menu có handle hMenu
  3.         User32.AppendMenu(hMenu, MF.STRING, idCmdFirst, "Copy đường dẫn")
  4.         Return 1 'để Shell biết được là mình đã thêm 1 menu item vào context menu
  5.     End Function

Function InvokeCommand để copy đường dẫn khi menu item của mình được người dùng Click
Kèm theo hàm để lấy các file đang được chọn , và hàm copy unicode text vào clipboard

Mã: Chọn hết

  1.    Public Function InvokeCommand(ByVal pici As Interop.CMInvokeCommandInfo) As Integer Implements Interop.IContextMenu.InvokeCommand
  2.         ClipboardSetText(String.Join(Convert.ToChar(13) & Convert.ToChar(10), SelectedItems))
  3.     End Function
  4.    
  5.     Private Function SelectedItems() As String()
  6.         Dim Count As Integer = Shell32.DragQueryFile(hDrop, &HFFFFFFFF, Nothing, 0)
  7.         Dim A(Count - 1) As String
  8.         Dim s As String
  9.         For i As Integer = 0 To Count - 1
  10.             s = New String("", 260)
  11.             Shell32.DragQueryFile(hDrop, i, s, s.Length)
  12.             A(i) = s.Substring(0, s.IndexOf(Convert.ToChar(0)))
  13.         Next
  14.         Return A
  15.     End Function
  16.  
  17.     Private Shared Sub ClipboardSetText(ByVal Text As String)
  18.         User32.OpenClipboard(Nothing)
  19.         User32.EmptyClipboard()
  20.         User32.SetClipboardData(CF.UnicodeText, Marshal.StringToCoTaskMemUni(Text))
  21.         User32.CloseClipboard()
  22.     End Sub
  23.  
  24.  

2/ Đăng ký Dll

2 thủ tục đây sẽ được chạy khi mình đăng ký Dll , lúc đăng ký , mình ghi các giá trị cần thiết vào registry , để COM class của mình được gọi khi nhấp phải vào thư mục hay tập tin , lúc hủy đăng ký , xóa mấy giá trị đó đi

Mã: Chọn hết

  1. #Region "Register COM"
  2.  
  3.     <ComRegisterFunction()> Private Shared Sub Register(ByVal t As Type)
  4.         Registry.SetValue("HKEY_CLASSES_ROOT\Folder\shellex\ContextMenuHandlers\CopyPath", "", "{" & ClassId & "}")
  5.         Registry.SetValue("HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\CopyPath", "", "{" & ClassId & "}")
  6.     End Sub
  7.  
  8.     <ComUnregisterFunction()> Private Shared Sub UnRegister(ByVal t As Type)
  9.         Dim CR As RegistryKey = Registry.ClassesRoot
  10.         CR.DeleteSubKey("Folder\shellex\ContextMenuHandlers\CopyPath", False)
  11.         CR.DeleteSubKey("*\shellex\ContextMenuHandlers\CopyPath", False)
  12.     End Sub
  13.  
  14. #End Region


nếu bạn chọn "Register COM ..." trong project properties , khi build Dll sẽ được tự động đănk ký

để đăng ký bình thường , dùng RegAsm.exe :

"C:\Windows\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe" /codebase "đường dẫn đến tập tin Dll của mình"

bỏ đăng ký

"C:\Windows\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe" /u "đường dẫn đến tập tin Dll của mình"



Release.zip
(8.44 KiB) Đã tải 784 lần


Toàn bộ code có đủ ở trên

Đây là ví dụ đơn giản để tạo shell context menu extension , muốn tạo nhiều Menu item , tạo submenu , dùng hình , ... , coi MSDN



Hình đại diện của người dùng
clarkkent
Mạnh Thường Quân
Mạnh Thường Quân
Bài viết: 1641
Ngày tham gia: T.Tư 16/04/2008 11:25 am
Đến từ: Chợ Lách - Bến Tre
Been thanked: 31 time
Liên hệ:

Re: Tạo Shell Context Menu copy đường dẫn items đang được chọn

Gửi bàigửi bởi clarkkent » T.Tư 09/07/2008 3:19 pm

Đây là ví dụ đơn giản để tạo shell context menu extension , muốn tạo nhiều Menu item , tạo submenu , dùng hình , ... , coi MSDN

Mình đang rất quan tâm đến cái này: cho nó cái Icon, tạo submenu... :)
Kỳ Nam hướng dẫn luôn đi, chứ không phải ai cũng có điều kiện coi MSDN và coi cũng chưa chắc hiểu. :D
• Hôm bây: www.tinsoftware.com ^ ^
Cố gắng lên...

nta189
Bài viết: 3
Ngày tham gia: T.Tư 04/06/2008 8:45 am

Re: Tạo Shell Context Menu copy đường dẫn items đang được chọn

Gửi bàigửi bởi nta189 » T.Sáu 16/01/2009 5:43 pm

Nam ơi, sao mình thêm vào lệnh đơn giản như Msgbox ("a") vào Function InvokeCommand sao không thấy chạy nhỉ, mình có làm sai ở chỗ nào không mà nó vẫn copy được path nhưng tuyệt nhiên không msgbox, thế là sao nhỉ??? Mình cũng thử với gán giá trị nào đó vào registry nhưng cũng vô ích...

Hình đại diện của người dùng
Kỳ Nam
Guru
Guru
Bài viết: 510
Ngày tham gia: CN 12/08/2007 8:47 pm
Đến từ: Qui Nhơn
Been thanked: 1 time
Liên hệ:

Re: Tạo Shell Context Menu copy đường dẫn items đang được chọn

Gửi bàigửi bởi Kỳ Nam » T.Sáu 16/01/2009 6:29 pm

được mà , cậu thử tắt mở lại explorer.exe

nta189
Bài viết: 3
Ngày tham gia: T.Tư 04/06/2008 8:45 am

Re: Tạo Shell Context Menu copy đường dẫn items đang được chọn

Gửi bàigửi bởi nta189 » T.Hai 19/01/2009 2:23 pm

Kỳ Nam đã viết:được mà , cậu thử tắt mở lại explorer.exe


Hóa ra là vậy, mình làm được rồi, nhưng mình cũng muốn hỏi thêm, là mình không muốn menu của mình ở dưới cùng mà mình muốn cho nó lên ở chỗ khác thì có thể sửa được ở tham số nào không?

Cảm ơn Nam nhé!

Hình đại diện của người dùng
Kỳ Nam
Guru
Guru
Bài viết: 510
Ngày tham gia: CN 12/08/2007 8:47 pm
Đến từ: Qui Nhơn
Been thanked: 1 time
Liên hệ:

Re: Tạo Shell Context Menu copy đường dẫn items đang được chọn

Gửi bàigửi bởi Kỳ Nam » T.Hai 19/01/2009 7:34 pm

nếu chỉ cần thêm 1 string đơn giản , cậu dùng cái này

Public Declare Auto Function InsertMenu Lib "User32.dll" (ByVal hMenu As IntPtr, ByVal uPosition As Integer, ByVal wFlags As MF, ByVal wIDNewItem As IntPtr, ByVal lpNewItem As String) As Boolean

Mã: Chọn hết

  1.    Public Function QueryContextMenu(ByVal hMenu As System.IntPtr, ByVal indexMenu As UInteger, ByVal idCmdFirst As UInteger, ByVal idCmdLast As UInteger, ByVal uFlags As Interop.CMF) As Integer Implements Interop.IContextMenu.QueryContextMenu
  2.         InsertMenu(hMenu, 0, MF.BYPOSITION Or MF.STRING, idCmdFirst, "Copy đường dẫn") '0 là vị trí cao nhất ( nhưng chưa chắc là đầu tiên )
  3.         Return 1
  4.     End Function

0 dùng InsertMenu để thêm menu , nên chưa kiểm tra

nta189
Bài viết: 3
Ngày tham gia: T.Tư 04/06/2008 8:45 am

Re: Tạo Shell Context Menu copy đường dẫn items đang được chọn

Gửi bàigửi bởi nta189 » T.Ba 20/01/2009 8:36 am

Ok, mình làm được rồi, thank Nam.


Quay về “[.NET] Bài viết hướng dẫn”

Đang trực tuyến

Đang xem chuyên mục này: Không có thành viên nào trực tuyến.1 khách