[HỎI] Làm sao để convert text trong clipboard?

Liên hệ QC

Hoàng Trọng Nghĩa

Chuyên gia GPE
Thành viên BQT
Moderator
Tham gia
17/8/08
Bài viết
8,590
Được thích
16,653
Giới tính
Nam
Tôi có hàm để lấy chuỗi trong clipboard, tuy nhiên khi chạy hàm, những chữ có dấu tiếng Việt (unicode) bị mã hóa thành các dấu chấm hỏi (?) hoặc mất luôn dấu.

Ví dụ: Trong chuỗi gốc tôi copy là: HOÀNG TRỌNG NGHĨA thì chạy hàm ClipBoard_GetData nó ra kết quả: HOÀNG TR?NG NGHIA

Xin vui lòng hướng dẫn cách khắc phục ạ?

Mã:
Declare Function OpenClipboard Lib "User32" (ByVal hwnd As Long) As Long
Declare Function CloseClipboard Lib "User32" () As Long
Declare Function GetClipboardData Lib "User32" (ByVal wFormat As Long) As Long
Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags, ByVal dwBytes As Long) As Long
Declare Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Declare Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long
Declare Function GlobalSize Lib "kernel32" (ByVal hMem As Long) As Long
Declare Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, ByVal lpString2 As Any) As Long

Public Const GHND = &H42
Public Const CF_TEXT = 1
Public Const MAXSIZE = 4096

Function ClipBoard_GetData() As String
    On Error GoTo ErrorHandler
    Dim hClipMemory As Long
    Dim lpClipMemory As Long
    Dim MyString As String
    Dim RetVal As Long
    If OpenClipboard(0) = 0 Then
        MsgBox "Cannot open Clipboard. Another app. may have it open"
        Exit Function
    End If
    ' Obtain the handle to the global memory
    ' block that is referencing the text.
    hClipMemory = GetClipboardData(CF_TEXT)
    If IsNull(hClipMemory) Then
        MsgBox "Could not allocate memory"
        GoTo OutOfHere
    End If
    ' Lock Clipboard memory so we can reference
    ' the actual data string.
    lpClipMemory = GlobalLock(hClipMemory)
    If Not IsNull(lpClipMemory) Then
        MyString = Space$(MAXSIZE)
        RetVal = lstrcpy(MyString, lpClipMemory)
        RetVal = GlobalUnlock(hClipMemory)
        ' Peel off the null terminating character.
        MyString = Mid(MyString, 1, InStr(1, MyString, Chr$(0), 0) - 1)
    Else
        MsgBox "Could not lock memory to copy string from."
    End If
OutOfHere:
    RetVal = CloseClipboard()
    ClipBoard_GetData = MyString
    Exit Function
ErrorHandler:
    RetVal = CloseClipboard()
    ClipBoard_GetData = ""
End Function
 
Mấu chốt là dùng CF_UNICODETEXT thay cho CF_TEXT. Nếu tôi không lầm thì thường có cả 2 loại.

vd.
Tôi thích dùng CopyMemory
Mã:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
    (Destination As Any, Source As Any, ByVal Length As Long)
Private Const CF_UNICODETEXT = 13
...
Function ClipBoard_GetData() As String
    On Error GoTo ErrorHandler
    Dim hClipMemory As Long
    Dim lpClipMemory As Long
    Dim MyString As String
    Dim RetVal As Long
    Dim size As Long, m() As Byte
    If OpenClipboard(0) = 0 Then
        MsgBox "Cannot open Clipboard. Another app. may have it open"
        Exit Function
    End If
    
    hClipMemory = GetClipboardData(CF_UNICODETEXT)
'    If hClipMemory = 0 Then hClipMemory = GetClipboardData(CF_TEXT)
    If hClipMemory = 0 Then
        MsgBox "Could not allocate memory"
        CloseClipboard
        Exit Function
    End If
    lpClipMemory = GlobalLock(hClipMemory)
    size = GlobalSize(hClipMemory)
    ReDim m(0 To size - 1)
    CopyMemory m(0), ByVal lpClipMemory, size
    GlobalUnlock hClipMemory
    MyString = m
    CloseClipboard
    ClipBoard_GetData = Left(MyString, Len(MyString) - 1)
    Exit Function
ErrorHandler:
    CloseClipboard
    ClipBoard_GetData = ""
End Function
 
Upvote 0
Mấu chốt là dùng CF_UNICODETEXT thay cho CF_TEXT. Nếu tôi không lầm thì thường có cả 2 loại.

vd.
Tôi thích dùng CopyMemory
Mã:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
    (Destination As Any, Source As Any, ByVal Length As Long)
Private Const CF_UNICODETEXT = 13
...
Function ClipBoard_GetData() As String
    On Error GoTo ErrorHandler
    Dim hClipMemory As Long
    Dim lpClipMemory As Long
    Dim MyString As String
    Dim RetVal As Long
    Dim size As Long, m() As Byte
    If OpenClipboard(0) = 0 Then
        MsgBox "Cannot open Clipboard. Another app. may have it open"
        Exit Function
    End If
   
    hClipMemory = GetClipboardData(CF_UNICODETEXT)
'    If hClipMemory = 0 Then hClipMemory = GetClipboardData(CF_TEXT)
    If hClipMemory = 0 Then
        MsgBox "Could not allocate memory"
        CloseClipboard
        Exit Function
    End If
    lpClipMemory = GlobalLock(hClipMemory)
    size = GlobalSize(hClipMemory)
    ReDim m(0 To size - 1)
    CopyMemory m(0), ByVal lpClipMemory, size
    GlobalUnlock hClipMemory
    MyString = m
    CloseClipboard
    ClipBoard_GetData = Left(MyString, Len(MyString) - 1)
    Exit Function
ErrorHandler:
    CloseClipboard
    ClipBoard_GetData = ""
End Function
Dạ, nhờ anh em đã giải quyết xong vấn đề rồi ạ! :eek::);)
 
Upvote 0
Tôi có hàm để lấy chuỗi trong clipboard, tuy nhiên khi chạy hàm, những chữ có dấu tiếng Việt (unicode) bị mã hóa thành các dấu chấm hỏi (?) hoặc mất luôn dấu.

Ví dụ: Trong chuỗi gốc tôi copy là: HOÀNG TRỌNG NGHĨA thì chạy hàm ClipBoard_GetData nó ra kết quả: HOÀNG TR?NG NGHIA

Xin vui lòng hướng dẫn cách khắc phục ạ?

Mã:
Declare Function OpenClipboard Lib "User32" (ByVal hwnd As Long) As Long
Declare Function CloseClipboard Lib "User32" () As Long
Declare Function GetClipboardData Lib "User32" (ByVal wFormat As Long) As Long
Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags, ByVal dwBytes As Long) As Long
Declare Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Declare Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long
Declare Function GlobalSize Lib "kernel32" (ByVal hMem As Long) As Long
Declare Function lstrcpy Lib "kernel32" (ByVal lpString1 As Any, ByVal lpString2 As Any) As Long

Public Const GHND = &H42
Public Const CF_TEXT = 1
Public Const MAXSIZE = 4096

Function ClipBoard_GetData() As String
    On Error GoTo ErrorHandler
    Dim hClipMemory As Long
    Dim lpClipMemory As Long
    Dim MyString As String
    Dim RetVal As Long
    If OpenClipboard(0) = 0 Then
        MsgBox "Cannot open Clipboard. Another app. may have it open"
        Exit Function
    End If
    ' Obtain the handle to the global memory
    ' block that is referencing the text.
    hClipMemory = GetClipboardData(CF_TEXT)
    If IsNull(hClipMemory) Then
        MsgBox "Could not allocate memory"
        GoTo OutOfHere
    End If
    ' Lock Clipboard memory so we can reference
    ' the actual data string.
    lpClipMemory = GlobalLock(hClipMemory)
    If Not IsNull(lpClipMemory) Then
        MyString = Space$(MAXSIZE)
        RetVal = lstrcpy(MyString, lpClipMemory)
        RetVal = GlobalUnlock(hClipMemory)
        ' Peel off the null terminating character.
        MyString = Mid(MyString, 1, InStr(1, MyString, Chr$(0), 0) - 1)
    Else
        MsgBox "Could not lock memory to copy string from."
    End If
OutOfHere:
    RetVal = CloseClipboard()
    ClipBoard_GetData = MyString
    Exit Function
ErrorHandler:
    RetVal = CloseClipboard()
    ClipBoard_GetData = ""
End Function
Dùng DataObject thì có vấn đề gì với Nghĩa?
Mã:
Function GetFormClipboard()
  On Error Resume Next
  With CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
    .GetFromClipboard
    GetFormClipboard = .GetText
  End With
End Function
 
Upvote 0
Dùng DataObject thì có vấn đề gì với Nghĩa?
Mã:
Function GetFormClipboard()
  On Error Resume Next
  With CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
    .GetFromClipboard
    GetFormClipboard = .GetText
  End With
End Function
Cho em hỏi nha, cái này mình dùng được cho WinXP và Win7 không ạ? Em chưa thử với WinXP nhưng người dùng lại dùng với cả 2 mới ghê.
 
Upvote 0
Cho em hỏi nha, cái này mình dùng được cho WinXP và Win7 không ạ? Em chưa thử với WinXP nhưng người dùng lại dùng với cả 2 mới ghê.
Đối tượng này là 1 thành phần của FM20.dll. Vậy chỗ nào Nghĩa chèn được UseForm thì có thể dùng được. Cũng có nghĩa là nếu chương trình của Nghĩa có dùng đến UserForm thì có thể viết tường minh hàm trên như sau:
Mã:
Function GetFormClipboard()
  On Error Resume Next
  With New DataObject
    .GetFromClipboard
    GetFormClipboard = .GetText
  End With
End Function
 
Upvote 0
Đối tượng này là 1 thành phần của FM20.dll. Vậy chỗ nào Nghĩa chèn được UseForm thì có thể dùng được. Cũng có nghĩa là nếu chương trình của Nghĩa có dùng đến UserForm thì có thể viết tường minh hàm trên như sau:
Mã:
Function GetFormClipboard()
  On Error Resume Next
  With New DataObject
    .GetFromClipboard
    GetFormClipboard = .GetText
  End With
End Function
Thầy, cho hỏi, clear clipboar bằng cái này được chứ nhỉ? Em có cái clear bằng API rồi, giờ mới thấy cái này.

Hình như nó là cái này "Microsoft Forms 2.0 Object Library" trong Tools > References phải không ta?
 
Upvote 0
Thầy, cho hỏi, clear clipboar bằng cái này được chứ nhỉ? Em có cái clear bằng API rồi, giờ mới thấy cái này.

Hình như nó là cái này "Microsoft Forms 2.0 Object Library" trong Tools > References phải không ta?
Tự hỏi, tự nghiên cứu luôn thầy ơi:

Mã:
Sub ClearClipboard()
    With New DataObject
        .SetText ""
        .PutInClipboard
        .Clear
    End With
End Sub

Nhưng sự khác biệt ở cái trên và API là gì nhỉ?
Mã:
Private Declare Function EmptyClipboard Lib "User32" () As Long
Private Declare Function OpenClipboard& Lib "User32" (ByVal hwnd As Long)
Private Declare Function CloseClipboard& Lib "User32" ()

Public Sub ClearClipboard()
    OpenClipboard 0&
    EmptyClipboard
    CloseClipboard
End Sub

Trước mắt, thấy dùng API thì nút Paste của Window nó Not Enabled. Còn cái của DataObject thì nó vẫn còn nguyên, ta click vào thì báo không có gì để Paste.
 
Lần chỉnh sửa cuối:
Upvote 0
Thầy, cho hỏi, clear clipboar bằng cái này được chứ nhỉ? Em có cái clear bằng API rồi, giờ mới thấy cái này.
Hình như nó là cái này "Microsoft Forms 2.0 Object Library" trong Tools > References phải không ta?
Vụ làm việc với Clipboard tôi từng thử nhiều lần và thấy DataObject không clear clipboard ngon bằng API
----------------------------------------
Tự hỏi, tự nghiên cứu luôn thầy ơi:

Mã:
Sub ClearClipboard()
    With New DataObject
        .SetText ""
        .PutInClipboard
        .Clear
    End With
End Sub

Nhưng sự khác biệt ở cái trên và API là gì nhỉ?
Mã:
Private Declare Function EmptyClipboard Lib "User32" () As Long
Private Declare Function OpenClipboard& Lib "User32" (ByVal hwnd As Long)
Private Declare Function CloseClipboard& Lib "User32" ()

Public Sub ClearClipboard()
    OpenClipboard 0&
    EmptyClipboard
    CloseClipboard
End Sub

Trước mắt, thấy dùng API thì nút Paste của Window nó Not Enabled. Còn cái của DataObject thì nó vẫn còn nguyên, ta click vào thì báo không có gì để Paste.
API đương nhiên là rất mạnh nhưng cũng khá rắc rối với người chưa từng trải. Nội cái vụ tương thích cho từng version office + windows cũng đủ mệt rồi. Trong khi những đối tượng viết sẵn dễ dùng hơn (nhưng đằng nào thì tôi nghĩ các đối tượng ấy cũng sẽ dùng đến API bên trong "ruột" của nó mà thôi)
Nếu ta tôi thì tôi chọn:
- Lấy clipboard bằng DataObject
- Clear Clipboard bằng API
 
Upvote 0
Mọi người thử nghĩ xem tại sao mình lại muốn lấy giá trị (chuỗi) trong clipboard để mần chi không? Trong khi copy và paste ra là được rồi?

P/s: Ngoài lề, nhưng đố vui thôi vì tôi muốn chia sẻ một kinh nghiệm.
 
Upvote 0
Upvote 0
Ai mà biết được. Lúc đầu tôi chỉ nghĩ bạn muốn vọc, muốn luyện API thôi.

Chứ nếu biết bạn chỉ cần lấy text thì gửi link sau cho khỏe.

https://www.giaiphapexcel.com/diendan/threads/có-đoạn-code-nào-biến-mọi-thao-tác-dán-thành-paste-values-không.48811/post-453062
API cũng là môn học rất hay cho phần giao diện và các can thiệp trực tiếp vào hệ điều hành, nên em cũng luôn chú tâm nghiên cứu và học hỏi. Riêng trường hợp này em cần có chuỗi sau khi copy và xử lý chúng.

'---------------------------------------------------------------------------------------------------------------

Tôi muốn chia sẻ cho mọi người một kinh nghiệm khi làm việc với TextBox và ComboBox.

Với một chuỗi nguồn mà có dấu ngắt xuống hàng, ví dụ:

Hoàng
Trọng Nghĩa


Khi copy vào TextBox hoặc ComboBox, ngay lập tức nó chỉ còn mỗi chữ Hoàng, còn các chữ khác biến mất tiêu! Nếu TextBox Ta đặt thuộc tính MultiLine=True thì mới còn đầy đủ.

Giả sử ta muốn copy một chuỗi mà chuỗi đó nằm rải rác ở các ô như trong hình:

TestChar.jpg

Và tôi muốn có giá trị là: Mr. Hoàng Trọng Nghĩa đẹp trai quá thì tôi phải xử lý trước khi paste vào ComboBox (nếu không xử lý thì xin thưa sau khi paste nó được cái chuỗi "Mr. là cùng).

Theo nguyên tắc xử lý thì chuỗi đó được copy từ một cell hoặc nhiều cell thì từ trái sang phải, từ trên xuống dưới (mặc định, nếu có cài đặt thì khác) thì chuỗi trên ráp lại cũng đầy đủ nghĩa, không bị hoán đổi vị trí về nghĩa. Vì thế tôi phải làm gì đó để xử lý, chắc các bạn đã biết mục đích của tôi rồi nhỉ?

Đó là kinh nghiệm khi thao tác trên ComboBox và TextBox.
 
Upvote 0
Mọi người thử nghĩ xem tại sao mình lại muốn lấy giá trị (chuỗi) trong clipboard để mần chi không? Trong khi copy và paste ra là được rồi?

P/s: Ngoài lề, nhưng đố vui thôi vì tôi muốn chia sẻ một kinh nghiệm.
Có cả đống ứng dụng liên quan.
Hình như chú này đi nghỉ mát lâu quá quên hay sao chứ chuyện này đã bàn trên diễn đàn biết bao nhiêu lần rồi
 
Upvote 0
Có cả đống ứng dụng liên quan.
Hình như chú này đi nghỉ mát lâu quá quên hay sao chứ chuyện này đã bàn trên diễn đàn biết bao nhiêu lần rồi
Cái mục đích cuối cùng của em là xử lý chuỗi khi paste nó sẽ thế này nè. Chỉ Ctrl+V là OK.

Paste ở TextBox với Multi Line nó ra gần như nguyên mẫu.

Paste ở ComboBox nó xử lý ngay hàng thẳng lối, không bị mất chuỗi.

 
Lần chỉnh sửa cuối:
Upvote 0
Quá nhọc công với dữ liệu không chuẩn, đúng là VN ta hay xử lý kiểu mẹo mực
 
Upvote 0
Web KT
Back
Top Bottom