none
如何以VB6執行具有Unicode字元的路徑 RRS feed

  • 問題

    1. 編譯器或直譯器: VB6 SP5
    2. 作業系統:WinXP x32 Pro And Windows77600 x64 Ultimate
    3. 作業系統語系:繁體中文

    首先我的程式是要由外部程式呼叫, 並傳遞執行參數
    像這樣
    "D:\program\02\Project1.exe" "D:\ソ\0.txt"
    "D:\program\02\Project1.exe"是VB6程式的位置, "D:\ソ\0.txt"是參數, 想要讓程式呼叫執行的部分, 故意加上"ソ"字元, 為BIG5不包含的字元

    一般的情況是使用Shell Command 即可, 但若執行參數Command中出現當前字元表不含的字元會變成"?"(無法辨識)
    於是我用比較複雜的方式調用了幾個API
    GetCommandLineW
    ShellExecuteW
    CopyMemory
    實現方法為用GetCommandLineW取得Unicode碼的執行字串後, 用CopyMemory傳遞給Integer陣列作拆解,
    分離出取得執行參數後, 再以CopyMemory給定長字串, 而後以ShellExecuteW執行該定長字串
    來處理這個問題, 程式碼如下



    Private Declare Function ShellExecuteW Lib "shell32.dll" ( _
      ByVal hwnd As Long, ByVal lpOperation As String, _
      ByVal lpFile As String, ByVal lpParameters As String, _
      ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
      Destination As Any, Source As Any, ByVal Length As Integer)
    Private Declare Function GetCommandLine Lib "kernel32" Alias "GetCommandLineW" () As Long
    Private Declare Function lstrlen Lib "kernel32" Alias "lstrlenW" ( _
      ByVal lpString As Long) As Long
    
    Function GetComm() As String
      Dim RetStr As Long
      Dim SLen As Long
      Dim int_Search_code As Integer
      Dim indexOfStart As Integer
      Dim Buffer() As Integer
      Dim i As Integer
      RetStr = GetCommandLine
      SLen = lstrlen(RetStr)
      If SLen > 0 Then
        ReDim Buffer(SLen - 1)
        CopyMemory Buffer(0), ByVal RetStr, SLen * 2
        
        '僅擷取執行參數部分, 以"或 (空白)分隔
        If Buffer(0) = 34 Then
          '若第一個字元為", 尋找下一個"作為分隔
          int_Search_code = 34
        Else
          '否則, 尋找空白字元作為分隔
          int_Search_code = 32
        End If
        
        For i = 1 To SLen - 1
          If Buffer(i) = int_Search_code Then
            i = i + 1
            '去空格
            Do Until Buffer(i) <> 32
              i = i + 1
            Loop
            indexOfStart = i
            Exit For
          End If
        Next
        
        '將執行參數複製給GetComm
        GetComm = Space$((SLen - indexOfStart) * 2)
        CopyMemory ByVal GetComm, Buffer(indexOfStart), (SLen - indexOfStart) * 2
        
        '執行
        r = ShellExecuteW(0, vbNullString, GetComm, "", "", 1)
        MsgBox r
        
        '檢查用
        Dim ss As String
        For i = indexOfStart To (SLen - 1)
          ss = ss & Hex(Buffer(i)) & " "
        Next
        MsgBox ss
      End If
    End Function



    若使用"D:\program\02\Project1.exe" "D:\ソ\0.txt"執行字串, r會傳回2 (執行失敗 找不到檔案)
    但以"D:\program\02\Project1.exe" "D:\0.txt" 去掉BIG5無法分析的字元, 則會執行成功
    以檢查用的ss來看記憶體內容
    GetCommandLineW與CopyMemory的確取得了正確的Unicode碼
    但ShellExecuteW卻無法正確執行
    我猜測是ShellExecuteW仍然把Unicode轉成ANSI才做執行的動作

    我想請教大家, 有沒有辦法在VB6上順利執行含Unicode的檔案路徑?

    ( .Net完全不用管這些東西 )
    • 已編輯 Litfal 2009年8月21日 下午 04:36 程式碼格式重設
    2009年8月21日 下午 01:31

解答

  • 用 Byte() 來處理 WChar 的呼叫,做了一個簡例:

    Private Declare Function apiGetCommandLine Lib "kernel32" Alias "GetCommandLineW" () As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, ByVal Source As Long, ByVal Length As Long)
    Private Declare Function MessageBox Lib "user32" Alias "MessageBoxW" (ByVal hWnd As Long, byteText As Any, ByVal lpCaption As String, ByVal Buttons As vbMsgboxStyle) As VbMsgBoxResult
    
    Function GetCommandLine() As String
       Dim Buffer(511) As Byte
       lpCL = apiGetCommandLine()
       CopyMemory Buffer(0), ByVal lpCL, 512
       rtnString = CStr(Buffer)
       rtnString = Left(rtnString, InStr(rtnString, Chr(0) & Chr(0)) - 1)
       GetCommandLine = rtnString
    End Function
    
    Private Sub Form_Load()
       Dim byteText() As Byte
       strCommand = Command
       byteText = strCommand & Chr(0) & Chr(0)
       MessageBox Me.hWnd, byteText(0), strCommand, vbOKOnly
       
       strCommand = GetCommandLine
       byteText = strCommand & Chr(0) & Chr(0)
       MessageBox Me.hWnd, byteText(0), strCommand, vbOKOnly
    End Sub
    

    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    2009年8月21日 下午 03:19

所有回覆

  • 用 Byte() 來處理 WChar 的呼叫,做了一個簡例:

    Private Declare Function apiGetCommandLine Lib "kernel32" Alias "GetCommandLineW" () As Long
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, ByVal Source As Long, ByVal Length As Long)
    Private Declare Function MessageBox Lib "user32" Alias "MessageBoxW" (ByVal hWnd As Long, byteText As Any, ByVal lpCaption As String, ByVal Buttons As vbMsgboxStyle) As VbMsgBoxResult
    
    Function GetCommandLine() As String
       Dim Buffer(511) As Byte
       lpCL = apiGetCommandLine()
       CopyMemory Buffer(0), ByVal lpCL, 512
       rtnString = CStr(Buffer)
       rtnString = Left(rtnString, InStr(rtnString, Chr(0) & Chr(0)) - 1)
       GetCommandLine = rtnString
    End Function
    
    Private Sub Form_Load()
       Dim byteText() As Byte
       strCommand = Command
       byteText = strCommand & Chr(0) & Chr(0)
       MessageBox Me.hWnd, byteText(0), strCommand, vbOKOnly
       
       strCommand = GetCommandLine
       byteText = strCommand & Chr(0) & Chr(0)
       MessageBox Me.hWnd, byteText(0), strCommand, vbOKOnly
    End Sub
    

    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    2009年8月21日 下午 03:19
  • 我想您的做法跟我差不多, 都是用API來接收執行參數
    但仍沒解決"執行"上的問題

    也就是執行殼層VB內建的Shell或API的ShellExecuteW
    都無法解析Unicode路徑

    可能是我的問題描述得不夠清楚, 抱歉
    2009年8月21日 下午 03:49
  • 你可以用處理 MessageBox 的方法處理 ShellExecute
    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    2009年8月22日 上午 08:59
  • 我想您的做法跟我差不多, 都是用API來接收執行參數
    但仍沒解決"執行"上的問題

    也就是執行殼層VB內建的Shell或API的ShellExecuteW
    都無法解析Unicode路徑

    可能是我的問題描述得不夠清楚, 抱歉


    可以解析unicode路径,你的函数没写对,需要全部为Long,错一个成string都不能成功执行W。原先不能执行的韩文路径和韩文文件名,都被我测试通过了。

    Private Declare Function ShellExecuteW Lib "shell32.dll" (ByVal hwnd As Long, ByVal lpOperation As Long, byteText As Any, ByVal lpParameters As Long, ByVal lpDirectory As Long, ByVal nShowCmd As Long) As Long

    ShellExecuteW 0, StrPtr("open"), byteText(0), 0, 0, 1

    -------------------------------

    非常感谢上面大大的代码,对我帮助极大。同时提醒Lital也要注意观察。不知道大大的代码下,为什么messagebox Unicode会连带把程序本体的路径也给带出来,文件名我执行了三次才挖出真正的路径,请看我改的大大的代码。

       rtnstring = Left(rtnstring, InStr(rtnstring, Chr(0) & Chr(0)) - 1)
       rtnstring = Replace(rtnstring, Chr(34), "")
       rtnstring = Replace(rtnstring, Left(rtnstring, InStr(rtnstring, "exe") + 3), "")
       GetCommandLine = rtnstring

     

     

    2010年12月4日 下午 06:19
  • API 宣告是依據 MSDN 的函數說明與你呼叫時所需的技巧而定,並沒有一定要怎樣宣告,這部分要精熟的話,只能多測試、多看相關資料。

     

    CommandLine 函數本來就會帶出完整執行程序的路徑,只是 VB6 幫你自動略掉。

    你用 Vista 以後的工作管理員、處理程序 勾選 CommandLine 後,你就會看到每個程序都是含完整的執行檔。

    所以上面的範例是標準完整的 CommandLine ,再依需求做字串切割即可。


    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    2010年12月5日 上午 03:49