2011年7月22日 星期五

取出目錄下所有子目錄的檔名,使用 VB6

最近為了整理文件,需要將某資料夾裡所有檔案各別列出說明,
雖然使用 DIR /s/o >> FileName.txt 的指令可以直接存成檔案,但輸出格式無法很方便使用,
所以昨天花了點時間,用 VB6 寫了個程式,可以列出指定目錄下的所有檔案為我自己要的格式。
主要是呼叫 Windows API 並用遞回的寫法,把檔名全都列出來

Call API 的寫法如下

'=================================
'=== Call API 的設定
Private Const BIF_RETURNONLYFSDIRS = 1
Private Const BIF_DONTGOBELOWDOMAIN = 2
Private Const MAX_PATH = 260

Private Declare Function SHBrowseForFolder Lib "shell32" _
(lpbi As BrowseInfo) As Long
Private Declare Function SHGetPathFromIDList Lib "shell32" _
(ByVal pidList As Long, ByVal lpBuffer As String) As Long
Private Declare Function lstrcat Lib "kernel32" Alias "lstrcatA" _
(ByVal lpString1 As String, ByVal lpString2 As String) As Long

Private Type BrowseInfo
   hWndOwner As Long
   pIDLRoot As Long
   pszDisplayName As Long
   lpszTitle As Long
   ulFlags As Long
   lpfnCallback As Long
   lParam As Long
   iImage As Long
End Type
'=================================

Function OpenFolderPath() As String
    '=======================================
    '用 VB 呼叫出在【尋找:所有檔案】中的【瀏覽資料夾】問話框
    Dim lpIDList As Long
    Dim sBuffer As String
    Dim szTitle As String
    Dim tBrowseInfo As BrowseInfo
    szTitle = "請選擇要開始搜尋的資料夾...."    '<-- 此標題可根據需要自行更改
    
    With tBrowseInfo
        .hWndOwner = Me.hWnd
        .lpszTitle = lstrcat(szTitle, "")
        .ulFlags = BIF_RETURNONLYFSDIRS + BIF_DONTGOBELOWDOMAIN
    End With
    
    lpIDList = SHBrowseForFolder(tBrowseInfo)
    
    If (lpIDList) Then
        sBuffer = Space(MAX_PATH)
        SHGetPathFromIDList lpIDList, sBuffer
        sBuffer = Left(sBuffer, InStr(sBuffer, vbNullChar) - 1)
        OpenFolderPath = sBuffer    '傳回路徑字串
    End If
End Function

遞回的寫法如下:
''=== 取該目錄下所有檔名
Private Function GetFileName(ByVal strFilePath As String, ByVal strInsertString As String) As String
    Dim retString As String
    Dim F1, fc
    Dim objF As Object  '定義開檔物件
    Dim objFs2 As Scripting.FileSystemObject

    strInsertString = gstrFileInsertString & strInsertString
    
    Set objFs2 = CreateObject("Scripting.FileSystemObject")
    Set objF = objFs2.GetFolder(strFilePath)
    Set fc = objF.Files
    '抓取要讀入的檔名資料
    For Each F1 In fc
        retString = retString & strInsertString & F1.Name & vbCrLf
    Next

    Set objFs2 = Nothing '釋放FileSystemObject 物件
    Set objF = Nothing
    
    GetFileName = retString
End Function

''=== 用遞回方式取出子目錄下所有檔名
Private Function GetSubFolder(ByVal strFolderPath As String, ByVal strFolderInsertString As String, ByVal strFileInsertString As String) As String
    '==================================按下儲存資料夾名稱時
    Dim F1, fc
    Dim objF As Object  '定義開檔物件
    Dim strFoldersName As String    '定義資料夾名稱為字串型態
    Dim objFs2 As Scripting.FileSystemObject
    Dim strInsertString As String
    Dim strLogString As String
    
    strFolderInsertString = gstrFolderInsertString & strFolderInsertString
    strInsertString = gstrFileInsertString & strFileInsertString


    Set objFs2 = CreateObject("Scripting.FileSystemObject")
    Set objF = objFs2.GetFolder(strFolderPath)
    Set fc = objF.SubFolders
    '抓取要讀入的資料夾名稱資料
    For Each F1 In fc
        strLogString = strLogString & vbCrLf & strFolderInsertString & "[" & F1.Name & "]" & vbCrLf
        strLogString = strLogString & GetFileName(strFolderPath & "\" & F1.Name, strInsertString)

        strLogString = strLogString & GetSubFolder(strFolderPath & "\" & F1.Name, strFolderInsertString, strInsertString)
        
        DoEvents
    Next
    
    Set objF = Nothing
    Set objFs2 = Nothing '釋放FileSystemObject 物件
    
    GetSubFolder = strLogString
End Function
由於這個程式只是臨時要用花個半天時間寫的,
很多細節沒有去 Handle,能用就好,
有需要的人自行下載程式碼回去修改。
執行檔下載(7.9K)
原始程式碼下載(3.3K)
程式畫面如下

Becky Mail 無法加入附件

昨天要寄 Mail 時突然無法加入附件,
一開始以為電腦出問題,所以重開機,
但重開完後還是不行。

後來發現是檔名或路徑帶有特殊字元造成的
這次是遇到 "®" 這個字,
上網查詢一下,發現還有 "羿" 這個字
我猜想應該不只,應該是該字元的組合包含了 "\" 這個符號造成的
只是猜測,我還沒有時間去證實,
反正改個路徑及檔名就可以了,
就先醬吧!!

2011年7月15日 星期五

如何使用 VB6 下載網路檔案

今天一個朋友打電話,說需要用 VB6 寫一個可以下載網路檔案的功能
相關功能很久以前寫過,翻了一下記錄發現很簡單,幾行就解決了。

建立一個 Module

'由網路下載檔案
Public Declare Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" (ByVal Caller As Long, ByVal URL As String, ByVal FileName As String, ByVal Reserved As Long, ByVal fnCB As Long) As Long
'由網路下載檔案
Public Declare Function DoFileDownload Lib "shdocvw.dll" _
(ByVal lpszFile As String) As Long

在主程式中直接呼叫
'不會跳出下載框
    URLDownloadToFile 0, "http://xxx.xxx.xxx.xxx/test.txt", "c:\test.txt", 0, 0

    '會跳出下載框
    Dim sFileUrl As String
    sFileUrl = StrConv("http://xxx.xxx.xxx.xxx/test.txt", vbUnicode)
    DoFileDownload sFileUrl

就這麼簡單。

2011年7月13日 星期三

開發 Android 程式(環境準備)

最近因為需要一個手機功能,
但是在 Android 市場上都找不到,
所以打算自己寫一個。

一開始當然是開發環境的準備囉~
參考環境安裝說明
好不容易裝好,不過卻也花了半天的時間了,
然後就是開始始撰寫軟體,最後就是看看如何發佈軟體,
先看了一下如何發佈,才發現,
註冊開發人員帳戶需要美金 25元,
蝦會~~還以為不用錢咧~~
再考慮看看吧,先寫個好用的程式再說!

2011年7月12日 星期二

謠言止於智者!!

今天同事請假,聽說住院了,
於是大家議論紛紛...

A: ㄟ...聽說同事S請假
B: 哦~~剛才J主管好像有去看他
C: 咦,S他老婆生了嗎?
D: 喔~他老婆生了住院,所以S請假嗎?
B: 不是,是S住院,請假
C: 怎了,要不要緊
B: 聽說是肚子痛檢查是盲腸炎,結果開刀
D: 蝦會~他老婆怎麼會割包皮!!

從頭到尾都沒人說割包皮,不知道怎麼聽的,
唉~~ 謠言止於智者!!

2011年7月6日 星期三

終於搞定了,在 Blog 中秀程式碼範例框prettifier(高亮顏色字體)

以前就看過有人在網頁上秀出程式碼的範例框,
一直不以為意,直到前天在寫 Blog 時發現,要貼的程式碼一多,
會造成困擾,因為要不停的調色及排版.....
後來才想到可以試試看用 Java Script的方式整理出 Code
找了幾篇文章,終於搞懂就是那麼簡單。

參考資料
google-code-prettify
Javascript code prettifier
介紹九種實用的秀程式碼方式(9 Useful Javascript Syntax Highlighting Scripts)
這是我選用的 SyntaxHighlighter 官網
針對 Blog 該如何使用的說明(Adding a Syntax Highlighter to your Blogger blog)

基本上照著使用說明操作一次就完成了,非常簡單
首先在 Blog 中進入 設計 -> 修改 HTML

找到 <head> </head>
這兩段的區間,
針對 Blog 該如何使用的說明中的程式碼貼到 </head> 的前面

<!--SYNTAX HIGHLIGHTER BEGINS-->
<link href= 略...
...
<!--SYNTAX HIGHLIGHTER ENDS-->

接下來在網頁中加入
<pre class="brush:vb; toolbar: false;">
程式碼放在這裡面
</pre>

或是使用以下的方式
<script type="syntaxhighlighter" class="brush:html"><![CDATA[
程式碼放在這裡面
]]></script>

效果就會像這樣

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        '我是註解
        Dim intI As Integer
        Dim strSum As String = ""

        For intI = 0 To 9
            strSum = strSum & intI.ToString
        Next
        MsgBox(strSum)
    End Sub

當然不只 VB 的 Code
還有很多,請參考官網支援高亮語言項目
還可以有多種風格自行選用,
更多用法也請自行參考官網。

2011年7月4日 星期一

將非 COM 元件的外部 DLL ,包入 .NET 開發的執行檔中 (PART 2)

接續上一篇文章的程式碼實作部份

假設某一個 DLL (AddNumber.dll)
提供一個功能叫做 Add2Number(Num1,Num2) as Integer
首先將這個 AddNumber.dll 加入資源檔中

然後新增一個 Class (clsTest.vb)內容如下,新增三個會用到的 API

    Declare Function LoadLibrary Lib "kernel32" (ByVal LibFilePath As String) As Integer
    Declare Function GetProcAddress Lib "kernel32" (ByVal ModuleHandle As Integer, ByVal ProcName As String) As Integer
    Declare Function FreeLibrary Lib "kernel32" (ByVal ModuleHandle As Integer) As Integer

先前提到要載入的 DLL 有個 Function 叫 Add2Number 所以也要加進來讓主程式呼叫用
  Delegate Function TestAdd2Number(ByVal Num1As Integer,ByVal Num1As Integer) As Integer
這裡使用委派 (Delegate) 的 Function Name 為了區別,我特地加了 Test 字樣

再來是主程式的部份
主程式裡要引用 System.Runtime.InteropServices ,以及剛才建立的 clsTest 檔案
  Imports System.Runtime.InteropServices
  Imports clsTest

在主程式的 Class下,順便把要用的變數設定好
Private _dllFuctionHandle As IntPtr
Private _loadDLLPath As String = "C:\TestAdd.DLL"   '解出的DLL要存放的路徑

程式執行時要先由資源檔中解出 DLL
My.Computer.FileSystem.WriteAllBytes(Me._loadDLLPath, My.Resources.AddNumberDll, False)
請注意上面的 AddNumberDll 是指在資源檔中的名稱

好了,DLL 檔解出來了再來是動態載入
Me._dllFuctionHandle = LoadLibrary(Me._loadDLLPath)

呼叫 API 載入 DLL 後,會傳回一個 Handle 碼,我們會利用這個 Handle 碼把 DLL 內的 Function 找出來。

Dim openMethodPointer As IntPtr  '利用 GetProcAddress 的 API 取得 DLL 的 Function Handle
Dim dllMethod As TestAdd2Number '指向委派的 Function Name


'利用 API 取得 DLL 的 Function
openMethodPointer = GetProcAddress(Me._dllFuctionHandle, "Add2Number") '這裡的 Add2Number 是 DLL 中實際可使用的 Function Name

取得了 Function Handle 後就要把它轉換成可用的 Type
dllMethod = CType(Marshal.GetDelegateForFunctionPointer(openMethodPointer, GetType(TestAdd2Number)), TestAdd2Number)

大功告成了。
最後就可以開始使用 AddNumber.dllAdd2Number這個 Function 囉~

Dim returnNumber As Integer
returnNumber = dllMethod(123,456)
回傳的結果就是兩數相加
returnNumber 的值就會等於 579

以上,看起來很煩瑣,其實一個一個操作下來並不難,
當然也可以把 Declare Delegate 都寫在同一個程式碼中,
就看程式怎麼應用了。

最後有個很重要的一點
就是使用完後請記得回收 DLL
   ''釋放 DLL
   ret = FreeLibrary(Me._dllFuctionHandle)
   ''刪除 DLL
   System.IO.File.Delete(Me._loadDLLPath)

將非 COM 元件的外部 DLL ,包入 .NET 開發的執行檔中 (PART 1)

最近開發程式時發現一個問題,
就是要整合第三方程式時,希望把第三方軟體包進去自己的程式中,
用意是不想要讓 User 直接取用第三方軟體。

問題是第三方軟體不是用 .NET 開發,
而且也不是一般的 COM 元件,
所以無法用一般方法包進去程式中。

我試了幾個方式都不行
  1. 註冊成 GAC (失敗)
  2. 註冊 DLL (失敗)
  3. 使用 ILMerge (失敗)
  4. 在程式中引用 (失敗)
  5. 以專案方式打包 (失敗)
  6. 使用 Load Assembly (失敗)
  7. 放入資源檔再解出 (可以,需搭配 API)
  8. 使用 API 載入 (可以)
會失敗的原因我猜想是因為非 COM 元件所致,
只是沒試過很不干心,不過試了之後就死心了。

其實載入 DLL 的方法有很多種,
Declare Function就是其中一個可以載入非 COM 元件的方法,
不過這個方式沒法達到我想要包進程式中的目的。

解法就是使用資源檔包入 DLL,然後在程式執行時將 DLL 解出來放到某個地方,
再以 API 去動態載入 DLL。

會使用到的 API 有以下三個
    Declare Function LoadLibrary Lib "kernel32" (ByVal LibFilePath As String) As Integer
    Declare Function GetProcAddress Lib "kernel32" (ByVal ModuleHandle As Integer, ByVal ProcName As String) As Integer
    Declare Function FreeLibrary Lib "kernel32" (ByVal ModuleHandle As Integer) As Integer

使用方式請參考
將非 COM 元件的外部 DLL ,包入 .NET 開發的執行檔中 (PART 2)