2017年11月3日 星期五

windows下架設個人用的工作管理系統(Mantis)、源碼版本控管系統(SVN)

首先我們要先去官網下載必要工具:


step 1. 安裝  XAMPP 到 C:\xampp,安裝 Win32SVN 至 C:\Subversion解壓縮 mantis 檔案至 C:\xampp\htdocs\mantis。



step 2. 從 http://localhost/phpmyadmin/ 進入"使用者帳號"刪除兩個 % 的帳號,點選 localhost 的 root 的編輯權限,接著修改密碼按下執行


step 3. 到 C:\xampp\phpMyAdmin 開啟 config.inc.php 找到這段程式碼
/* Authentication type and info */
$cfg['Servers'][$i]['auth_type'] = 'config';
$cfg['Servers'][$i]['user'] = 'root';
$cfg['Servers'][$i]['password'] = 'PASSWORD';
$cfg['Servers'][$i]['extension'] = 'mysqli';
$cfg['Servers'][$i]['AllowNoPassword'] = true;
$cfg['Lang'] = '';
 在 password 輸入你剛剛所設定的密碼。再把 127.0.0.1 與 ::1 的 root 的密碼也修改掉。

step 4. 開啟 http://localhost/mantis ,設定 mantis 資料庫設定與帳號密碼。


step 5 開啟 http://localhost/mantis 使用預設管理員帳號密碼 administrator/root 登入建立帳號密碼

step 6 增加 mantis 更改密碼功能,到 C:\xampp\htdocs\mantis 目錄下,開啟 manage_user_edit_page.php,找到 <!-- Email --> 然後加入紅字部分
</tr>
<!-- Email -->
<tr><?php
if( $t_ldap && ON == config_get( 'use_ldap_email' ) ) {
 # With LDAP
 echo '<td class="category">' . lang_get( 'email_label' ) . '</td>';
 echo '<td>' . string_display_line( user_get_email( $t_user_id ) ) . '</td>';
} else {
 # Without LDAP
 echo '<td class="category">' . lang_get( 'email_label' ) . '</td>';
 echo '<td>';
 print_email_input( 'email', $t_user['email'] );
 echo '</td>';
} ?>
</tr>
<!-- Password -->
<tr <?php echo helper_alternate_class( 1 ) ?>>
<td class="category" width="30%">
<?php echo "Password (change only)" ?>:
</td>
<td width="70%">
<input type="text" size="16" maxlength="100" name="password" value="" />
</td>
</tr>
<!-- Access Level -->
開啟 manage_user_update.php,找到 $f_user_id = gpc_get_int( 'user_id' );加入紅字部分
$f_user_id = gpc_get_int( 'user_id' );
$f_password = gpc_get_string('password');
找到 $t_result = db_query( $t_query, $t_query_params );加入紅字部分
$t_result = db_query( $t_query, $t_query_params );
if ($f_password) user_set_password($f_user_id, $f_password);
step 8. 開啟 apache 設定檔 "C:\xampp\apache\conf\httpd.conf" 在最後加入SVN參考路徑。
LoadModule dav_module modules/mod_dav.so
LoadModule dav_svn_module C:/Subversion/bin/mod_dav_svn.so
step 9. 建立SVN使用者與密碼檔案
    開啟命令提示字元,移動至 C:\xampp\apache\bin,
    下指令 htpasswd -c <檔案路徑> <名稱> ,輸入兩次密碼,將文件內容加入帳號密碼的管理檔案   ex: C:\Users\MiniServer\Desktop\svn\pwd

step 10. 建立專案
    開啟命令提示字元,移動至 C:\Subversion\bin,
    下指令 svnadmin.exe create C:/Users/MiniServer/Desktop/svn/<專案名稱>
    開啟 apache 設定檔 "C:\xampp\apache\conf\httpd.conf" 最後加入

<Location /svn/<專案名稱>>
Dav svn
SVNPath C:/Users/MiniServer/Desktop/svn/<專案名稱>
AuthType Basic
AuthName "Subversion repository"
AuthUserFile "C:\Users\MiniServer\Desktop\svn\pwd"
Require valid-user
</Location>
重啟apache,svn 網址為 http://localhost/svn/<專案名稱>

step 8. 一般是申請No-IP 動態DNS 服務,不過公司沒IT,連浮動IP都沒有了,只能用區網玩玩


2015年6月28日 星期日

WD My Cloud 開箱?

新品入荷 WD My Cloud 2TB 雲端儲存系統
★專屬雲端
★共用儲存與備份
★隨地透過免費應用程式存取、分享
★充足儲存空間、高速效能、安全儲存
★將相片、影片、音樂儲存於單一位置
好貴好貴...$ 4999
http://24h.pchome.com.tw/prod/DRAG0H-A82111232?q=/S/DRAG0H
又是一個 dropbox like 的產品, 話不多說先上圖,
非常樸素又大台的盒子 ,素的讓我就隨便拍了
 正面圖,偽裝成一本書的感覺, 有一顆電源LED,接電會發亮
 屁屁圖: 看的到中間有個 3.5吋硬碟,有個鎖孔, USB 3.0, 網路孔, 除此之外就沒有了,
 上下後都有著大量的散熱孔,

 因為震動感覺有點大, 所以拆來看看(一堆卡榫, 拆了就準備斷吧...)
一拆不得了, 不是綠標的....是NAS用的...NAS用的友什麼特異功能呢..
就是...比較耐...(大概)
因為沒有風扇, 所以還是盡量讓他乖乖站好比較好散熱.

接著按照說明書上網抓設定軟件, 設定軟件裝完後會提示安裝windows agent, 裝完後 agent 上網自動檢查更新, 下載下來手動執行更新.

然後打開 WD my cloud 出現提示, 然後就 hang 住沒有回應了......(翻桌)
關掉再一次就正常了, 

打開設定軟件可以進去機器頁面設定機器, 跟 drobo transporter 不一樣, 有內建網頁服務器.
進去設定後必須設定一位管理者, 用戶可以設定多名.
在設定完一堆帳戶後終於可以登入 agent 試試看了.

滿心歡喜的打開 windows agent 後嚇了一條, 跟 dropbox 不一樣,他並不會 sync 本地與遠端的檔案, 而是像 dropbox 網頁一樣的管理檔案方式, 
回到設定頁面看看有什麼特異功能, 發現有一個 safepoints 功能, 上面功能是說做一個 snapshot必要的時候可以回復檔案.
然後有遠端關設備, 遠端重開設備, 寄信騷擾, 更新韌體, 選擇語言, 串流影音, 開關LED, 支援 Mac Time Machine 等一堆功能.

最後, 放在公司的話會被擋住一些東西, 公司內網與外網是不通的.

2015年6月3日 星期三

drobo transporter 開箱?

新品入荷  Drobo Transporter
★同步電腦及行動裝置之檔案
★雲端儲存分享相片及影片
★自動異地備援你的重要文件檔案
★世界各地存取所有文件
★行動裝置增加TB的儲存空間

講這麼多不如官網的簡潔說明:
跟 dropbox 一樣的使用, 但是有著企業級的控制
就是在自己家的 dropbox server!


先上照片, 正面圖: 重心在下方的設計像是不倒翁一般的站立著,節省平面的放置空間且不易傾倒, 但是也看不出來是什麼東西( 存在感0 ), 我覺得しまかぜ比較好看..(毆
側面圖 : 方方正正沒有存在感
上方圖: 有著 LOG
下方圖: 有散熱孔但是產品本身沒有風扇, 所以不會吹出或吸入空氣
接口圖: 根據規格具有 1 Gigabit 網路孔, 並含有一個 USB 2.0 的接口, 但是這個USB 只能用來擴充一個無限轉接器( Wifi ), 如果想接家裡的無線網路還必須另外買, 不是很方便的設計, 買了又是一個不好看的凸出物且不方便, 感覺不是很好.

硬碟與插槽:無螺絲設計, 先將硬碟卡入磁碟支架再插入卡槽, 雖然有防呆但是插錯邊還是插得下去!! 硬碟支援SATA II/III 但不支援SSD ??????
先不論網路速度瓶頸, 在SSD越來越便宜且越來越大的今天實在是無法接受阿. 而且SSD不怕震動.
磁碟支架: 上面有LED燈號表示的狀態
閃綠色: 開機中
藍色:平常狀態
藍綠呼吸燈: 格式化硬碟中
閃藍色: 硬碟在動~~~轉轉轉
黃色: 阿阿阿~~空間要塞滿了~~~會不會死掉  >///////<
紅色: 硬碟裡塞得滿滿的, 再也裝不下了 >///////<
紅黃呼吸燈:  網路不通
不過嫌LED太亮的還可以關閉燈號, 讓他更沒存在感
補上說明書的


 合體圖

接著開始試用, 首先根據說明書註冊帳號


一進去官網發現有兩種帳號, 不過滑鼠過去有顯示機器類型, 所以就註冊個人用帳號

進入後會要求信箱寄送確認信才能再動作.,接著可以輸入硬體的序號與mac address
你可以在機器底部找到這兩項資訊, 接著開始下載程式到電腦上開始使用
支援的平台蠻多的, 除了主流的 Mac, windows, iOS, android 外還有 amazon的電子書

首先安裝 windows app 來玩玩,不過安裝完要求要重開機 (....無言)
 重開機完出現登入畫面, 接著填入剛剛註冊的 e-mail & password.
接著跟 dropbox 一樣選擇要同步的資料夾
 裝完你會在右下角見到tray,點擊tray會見到功能表,tray會根據硬體有不同的顏色, 像是下圖的紅色是無法連線到硬體...(喂! 誰偷拔走了! 那不可以吃阿!)

接著看看他有什麼功能, 有設定, 檢查新版software, 教學, 暫停...ect.
大致上 dropbox 有的都有, 然後在 Preferences -> Special Folder 馬上發現一個 bug,
當我選擇了我的文件夾之後會跳出備份警告...但是沒有按鈕可以按確定...(三小...關不掉)
另外有wifi裝在硬體上的話也可以在 agent 設定


 簡短心得, 在桌面agent部分與 dropbox 相似度極高, 跟官方所宣傳一樣有相同的用戶體驗(或更糟?).

其他特殊功能的部分持續發現中.

P.S 發生了點插曲....有人手賤拆了機子改裝成裝 3.5吋硬碟....orz


2014年12月9日 星期二

簡單的 Makefile 說明

 GCC 下 makefile 的用法為在 command line 下執行

make -f <file> <rule>

如果不設定指定檔案, 默認執行 Makefile 這個檔案, 與 all 這個 rule.
* 在 windows MinGW 下記得將  MinGW 路徑加入環境變數, 不然會不認識 make
* 在 linux MinGW 使用 cross compiler 則用 mingw32-make -f <file> <rule>

Visual studio 下 必須先開啟 Visual studio Tools 底下的命令列模式( EX: Visual studio 2005 命令提示字元), 然後執行

NMAKE /f <file> <rule>

makefile 其他常見的 rule
make clean     清除編譯過程中所產生的文件
make distclean 清除編譯過程中所產生的文件,還有 configure 所產生的 Makefile
make install   安裝至系統
make dist      將原始碼壓縮成 *.tar.gz
make distcheck 將原始碼壓縮成 *.tar.gz 檔,並測試。 

簡易 Makefile 語法解釋如下
---------------------------------------------------------------------------------------------------------------------

# 以 # 開頭的即為註解。
 !include <ntwin32.mak>

 # 變數宣告不分大小寫
Target=Gilgamesh.exe
# 慣例上, 給外部使用的變數名稱使用大寫
OBJECTS = Gilgamesh.obj Gilgamesh.rbj
 # 利用 $(cflags)${cflags} 來存取已定義的變數
cflags = $(cflags) /openmp
# 慣例上,內部使用的變數名稱使用小寫
extdflags = -DVisualStudio -DNDEBUG -DWINDOWS
# Makefile會在展開後,再決定變數的值。
# := 表示變數的值決定於它在 Makefile 中的位置, 而不是展開後
dflags :=  $(extdflags) $(dflags)
# 取消該變數
LIB_DIR=
# ?= 若變數未定義,則替它指定新的值。否則,採用原有的值。
FOO ?= wtf
# 與C語法相同, 自身再加上數值
cflags += -O2
# rule 功用為指示如何進行編譯
all: $(Target)
# <target>:<dependencies>
# target要建立的檔案
# dependencies:相依項目。決定是否要重新編譯。
Gilgamesh.obj: Gilgamesh.c
# 建立檔案的指令, 前面必須是 Tab, 不能為空白, 所有 Tab 開頭會被視為 shell script / batch 指令
    $(cc) $(cflags) $(cvars) $(dflags) $(hflags) Gilgamesh.c

Gilgamesh.res: Gilgamesh.rc
    $(rc) -DWIN32 -DNDEBUG $(hflags) -r Gilgamesh.rc

Gilgamesh.rbj: Gilgamesh.res
    cvtres $(cvflags) -machine:ix86 -o Gilgamesh.rbj Gilgamesh.res

$(Target): $(OBJECTS)
    $(link) /libpath:$(LIB_DIR) /out:$(Target) $(OBJECTS)

clean:
# @:不要顯示執行的指令。
# \換行用
    @for %%x in (obj exp pdb ilk res rbj) do \
    if exist *.%%x del *.%%x
# -:指令出錯也不中斷執行。
    -for %%x in (exe dll lib) do if exist Gilgamesh.%%x del Gilgamesh.%%x

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

因為我很懶, 通常我會將命令先寫成 *.bat 或 *.sh

Windows MinGW32 範例 gcc.bat
 ---------------------------------------------------------------------------------------------------------------------
@ECHO off

SET GCCBIN=c:\MinGW\bin
SET GCCMAKE=C:\MinGW\msys\1.0\bin
SET path=%GCCBIN%;%GCCMAKE%;%path%

IF EXIST %GCCBIN%\gcc.exe (
GOTO HELL
)

ECHO can not find MinGW
PAUSE

GOTO :EOF

:HELL
make -f WindowsGCC.mak clean

make -f WindowsGCC.mak ALL
---------------------------------------------------------------------------------------------------------------------

Windows visual studio 範例 vc2005.bat
---------------------------------------------------------------------------------------------------------------------
@echo off

IF EXIST "%VS80COMNTOOLS%vsvars32.bat" (
call "%VS80COMNTOOLS%vsvars32.bat"
GOTO HELL
)


ECHO can not find visual studio
PAUSE

GOTO :EOF

:HELL
NMAKE /f "WindowsVC.mak" clean

NMAKE /f "WindowsVC.mak" ALL
---------------------------------------------------------------------------------------------------------------------

Linux gcc 範例 gcc.sh
---------------------------------------------------------------------------------------------------------------------
make -f ./Makefile clean
make -f ./Makefile all
--------------------------------------------------------------------------------------------------------------------- 

openSSL 1.0.1e 編譯

0. 現成編好的
http://slproweb.com/products/Win32OpenSSL.html

1. 必要條件
openssl source code (使用版本為 openssl-1.0.1e)
activeperl
NASM (非必要, 有會比較快, 快在哪? ...阿栽.)
Microsoft Visual Studio (這邊用的為 vs2005)

2. 編譯環境
安裝 activeperl, NASM, 將 NASM 安裝路徑加入系統path裡

開啟 程式集->Microsoft Visual Studio 2005->Visual Studio Tools->Visual Studio 2005 Command Prompt
(編譯X64 就開啟 Visual Studio 2005 x64 Win64 Command Prompt, IA64依此類推)
* 2005 64bit compiler 預設是沒安裝的, 要用記得安裝一下

3. 移動到目錄
>cd D:\openssl-1.0.1e

4. 指定編譯類型
X86
>perl Configure VC-WIN32 no-asm
or
>perl Configure VC-WIN32

X64
>perl Configure VC-WIN64A no-asm

IA64
>perl Configure VC-WIN64I no-asm

5. 製造編譯用 *.mak 檔
X86
>ms\do_ms.bat
or
>ms\do_nasm.bat

X64
>ms\do_win64a.bat

IA64
>ms\do_win64i.bat

6. 編譯
先修改 ms\ntdll.mak , 將 CFLAG= /MD ... 改為 CFLAG= /MT ...
nmake -f ms\ntdll.mak   動態連結
or
nmake -f ms\nt.mak      靜態連結

7. 產出
D:\openssl-1.0.1e\outdll32
or
D:\openssl-1.0.1e\out32

ssleay32.lib    實現SSL協議
libeay32.lib    實現各種演算法

8. 懶人用 batch

REM VS2005 X86
@echo off

IF NOT EXIST "%VS80COMNTOOLS%vsvars32.bat" (
ECHO can not find visual studio 2005
PAUSE
GOTO :EOF
)

call "%VS80COMNTOOLS%vsvars32.bat"

SET OPENSSLDIR=.\openssl-1.0.1e
SET OUTPUTDIR=.\OpenSSL_VC2005_X86

CD %OPENSSLDIR%
perl Configure VC-WIN32
start /wait cmd.exe /c .\ms\do_nasm.bat
nmake -f .\ms\nt.mak
nmake -f .\ms\nt.mak clean
CD ..

IF EXIST %OUTPUTDIR% (
DEL %OUTPUTDIR%\libeay32.lib
DEL %OUTPUTDIR%\ssleay32.lib
DEL %OUTPUTDIR%\openssl.exe
) else (
MKDIR %OUTPUTDIR%
)

COPY %OPENSSLDIR%\out32\libeay32.lib %OUTPUTDIR%\libeay32.lib
COPY %OPENSSLDIR%\out32\ssleay32.lib %OUTPUTDIR%\ssleay32.lib
COPY %OPENSSLDIR%\out32\openssl.exe  %OUTPUTDIR%\openssl.exe

DEL %OPENSSLDIR%\out32\libeay32.lib
DEL %OPENSSLDIR%\out32\ssleay32.lib
DEL %OPENSSLDIR%\out32\openssl.exe


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

REM VS2005 X64
@echo off

SET CompilerBat="C:\Program Files\Microsoft Visual Studio 8\VC\bin\amd64\vcvarsamd64.bat"
IF EXIST %CompilerBat% (
CALL %CompilerBat%
GOTO BuildProject
}

SET CompilerBat="C:\Program Files (x86)\Microsoft Visual Studio 8\VC\bin\amd64\vcvarsamd64.bat"
IF EXIST %CompilerBat% (
CALL %CompilerBat%
GOTO BuildProject
)

ECHO can not find visual studio 2005
PAUSE
GOTO :EOF

:BuildProject

SET OPENSSLDIR=.\openssl-1.0.1e
SET OUTPUTDIR=.\OpenSSL_VC2005_X64

CD %OPENSSLDIR%
perl Configure VC-WIN64A no-asm
start /wait cmd.exe /c .\ms\do_win64a.bat
nmake -f .\ms\nt.mak
nmake -f .\ms\nt.mak clean
CD ..

IF EXIST %OUTPUTDIR% (
DEL %OUTPUTDIR%\libeay32.lib
DEL %OUTPUTDIR%\ssleay32.lib
DEL %OUTPUTDIR%\openssl.exe
) else (
MKDIR %OUTPUTDIR%
)

COPY %OPENSSLDIR%\out32\libeay32.lib %OUTPUTDIR%\libeay32.lib
COPY %OPENSSLDIR%\out32\ssleay32.lib %OUTPUTDIR%\ssleay32.lib
COPY %OPENSSLDIR%\out32\openssl.exe  %OUTPUTDIR%\openssl.exe

DEL %OPENSSLDIR%\out32\libeay32.lib
DEL %OPENSSLDIR%\out32\ssleay32.lib
DEL %OPENSSLDIR%\out32\openssl.exe 


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

2014年12月8日 星期一

python 上一個簡單的 trace tools

寫程式時很怕測試人員沒頭沒尾的回報, 還是丟了一個用克林貢語寫的 bug 描述.

所以我都會額外自己發展一個小工具讓程序輸出 log, 讓測試人員回報用.

ptlog.py

#-------------------------------------------------------------------------------
# Name:        ptlog
# Purpose:
#
# Author:      nanoha
#
# Created:     10/12/2013
# Copyright:   (c) nanoha 2013
# Licence:     no licence
#-------------------------------------------------------------------------------

import os, inspect, sys
from time import strftime

__author__  = "nanoha <nanoha@nanoha.org>"
__status__  = "production"
__version__ = "0.0.6.0"
__date__    = "2014-05-07"

__IsPrint = True

if (os.name == 'nt'):
    __output = u'C:\\temp\\pt.txt'
elif (os.name == 'posix'):
    __output = u'/temp/pt.txt'

def SetOutput(szLog):
    global __output
    __output = szLog

def IsPrint(bPrint):
    global __IsPrint
    __IsPrint = bPrint

def __WriteFile(szFileName, szFuncName, szMsgline, szFlag, szMsg):
    hFile = None
    try:
        now = strftime('%Y-%m-%d %H:%M:%S')
        szFormatMsg = '[%s][%s][%s][%s][%s]%s\n' % ( now, szFileName, szFuncName, szMsgline, szFlag, szMsg)
        hFile = open(__output, 'a+')
        hFile.write(szFormatMsg)
    except Exception as error:
        try:
            now = strftime('%Y-%m-%d %H:%M:%S')
            szFormatMsg = u'[%s][%s][%s][%s][%s]%s\n' % (unicode(now), unicode(szFileName), unicode(szFuncName), unicode(szMsgline), unicode(szFlag), unicode(error))
            hFile = open(__output, 'a+')
            hFile.write(szFormatMsg.encode('utf-8'))
        except Exception as error:
            print error
    finally:
        if hFile:
            hFile.close()


def PTError(szMsg):
    try:
        Callerframel = inspect.stack()[1][0]
        if (__IsPrint):
            print '[%s][%s][%s][%s]%s' % (Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'PTERROR', szMsg)
        if (os.path.exists(__output)):
            __WriteFile(Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'PTERROR', szMsg)
    except Exception as error:
        pass

def convertToStr(szMsg):
    ret = ''
    try:
        if isinstance(szMsg, unicode):
            ret = szMsg.encode('utf-8')
        elif isinstance(szMsg, str):
            ret = szMsg
    except Exception as error:
        PTError(str(error))
    finally:
        return ret

def debug(szMsg):
    Callerframel = inspect.stack()[1][0]
    if (__IsPrint):
        try:
            print '[%s][%s][%s][%s]%s' % (Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'DEBUG', szMsg)
        except Exception as error:
            PTError(str(error))

    if (os.path.exists(__output)):
        szMsg = convertToStr(szMsg)
        if szMsg:
            __WriteFile(Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'DEBUG', szMsg)

def info(szMsg):
    Callerframel = inspect.stack()[1][0]
    if (__IsPrint):
        try:
            print '[%s][%s][%s][%s]%s' % (Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'INFO', szMsg)
        except Exception as error:
            PTError(str(error))

    if (os.path.exists(__output)):
        szMsg = convertToStr(szMsg)
        if szMsg:
            __WriteFile(Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'INFO', szMsg)

def warning(szMsg):
    Callerframel = inspect.stack()[1][0]
    if (__IsPrint):
        try:
            print '[%s][%s][%s][%s]%s' % (Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'WARNING', szMsg)
        except Exception as error:
            PTError(str(error))

    if (os.path.exists(__output)):
        szMsg = convertToStr(szMsg)
        if szMsg:
            __WriteFile(Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'WARNING', szMsg)

def error(szMsg):
    Callerframel = inspect.stack()[1][0]
    if (__IsPrint):
        try:
            print '[%s][%s][%s][%s]%s' % (Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'ERROR', szMsg)
        except Exception as error:
            PTError(str(error))

    if (os.path.exists(__output)):
        szMsg = convertToStr(szMsg)
        if szMsg:
            __WriteFile(Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'ERROR', szMsg)

def critical(szMsg):
    Callerframel = inspect.stack()[1][0]
    if (__IsPrint):
        try:
            print '[%s][%s][%s][%s]%s' % (Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'CRITICAL', szMsg)
        except Exception as error:
            PTError(str(error))

    if (os.path.exists(__output)):
        szMsg = convertToStr(szMsg)
        if szMsg:
            __WriteFile(Callerframel.f_code.co_filename, Callerframel.f_code.co_name, Callerframel.f_lineno, 'CRITICAL', szMsg)
#-------------------------------------------------------------------------------

測試, 如果 C:\\temp\\pt.txt 不存在則不輸出,
要輸出別的名稱可用 ptlog.SetOutput('.\\a.txt')
#-------------------------------------------------------------------------------
import ptlog

def main():
    ptlog.debug('test 1')
    ptlog.info('test 2')
    ptlog.warning('test 3')
    ptlog.error('test 4')
    ptlog.critical('test 5')

if __name__ == '__main__':
    main()

#-------------------------------------------------------------------------------

獲取 windows 上安裝的所有 browser

上頭丟了一個奇怪的需求, 說我們軟件開啟的網頁要能指定 browser.

當下只有 囧 可以形容.


要知道什麼軟件是瀏覽器, 目前大概只能針對已知的 browser 個別處理.

首先要做的是就是先獲取 browser 執行檔的路徑.

所有 browser 安裝後一定會產生 Shortcut, 從 Shortcut 取得就ok了

1. 所以先取得所有 *.lnk 在 start menu下的.

wchar_t szPath[MAX_PATH];
CFileInfoList files;

memset(szPath, 0x00, sizeof(szPath));
if (SHGetSpecialFolderPathW(NULL, szPath, CSIDL_PROGRAMS, false))
{
    files.GetFolderInfo(szPath);
}

memset(szPath, 0x00, sizeof(szPath));
if (SHGetSpecialFolderPathW(NULL, szPath, CSIDL_COMMON_PROGRAMS, false))
{
   files.GetFolderInfo(szPath);

}

2. 接著寫取得快捷路徑的方法:
BOOL GetShortcutTarget(const wchar_t *file, CString &szTarget)
{
    HRESULT hr;
    IShellLinkW *pLink;  //IShellLink对象指针
    IPersistFile *ppf;  //IPersisFil对象指针
    wchar_t szPah[MAX_PATH];
    WIN32_FIND_DATAW FileData;

    szTarget.Empty();

    hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (void**)&pLink);
    if (FAILED(hr))
    {
        SAVELOG_DANGER("CoCreateInstance failed");
        return FALSE;
    }

    //从IShellLink对象中获取IPersistFile接口
    hr = pLink->QueryInterface(IID_IPersistFile, (void**)&ppf); 
    if (FAILED(hr)) 
    {
        SAVELOG_DANGER("QueryInterface failed");
        pLink->Release(); 
        return FALSE; 
    }

    // open Shortcut
    hr = ppf->Load(file, STGM_READ);
    if (SUCCEEDED(hr))
    {
        memset(szPah, 0x00, sizeof(szPah));
        hr = pLink->GetPath(szPah, MAX_PATH, &FileData, SLGP_SHORTPATH);
        if (SUCCEEDED(hr))
        {
            szTarget = szPah;
        }
    }

    ppf->Release(); 
    pLink->Release();
    return SUCCEEDED(hr);
}


3. 針對特定名稱快捷取路徑
Internet Explorer.lnk
Mozilla Firefox.lnk
Waterfox.lnk
Navigator.lnk
Pale Moon.lnk
SeaMonkey.lnk
Google Chrome.lnk
Safari.lnk
Opera.lnk

4. 針對 Safari 處理
   Safari 的快捷長的不大一樣, 所以取不到路徑, 改成取 Working Directory 並加檔案名稱

BOOL GetShortcutWorkDirectory(const wchar_t *file, CString &szDir)
{
    HRESULT hr;
    IShellLinkW *pLink;  //IShellLink对象指针
    IPersistFile *ppf;  //IPersisFil对象指针
    wchar_t szPah[MAX_PATH];
    WIN32_FIND_DATAW FileData;

    szDir.Empty();

    hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (void**)&pLink);
    if (FAILED(hr))
    {
        SAVELOG_DANGER("CoCreateInstance failed");
        return FALSE;
    }

    //从IShellLink对象中获取IPersistFile接口
    hr = pLink->QueryInterface(IID_IPersistFile, (void**)&ppf);
    if (FAILED(hr))
    {
        SAVELOG_DANGER("QueryInterface failed");
        pLink->Release();
        return FALSE;
    }

    // open Shortcut
    hr = ppf->Load(file, STGM_READ);
    if (SUCCEEDED(hr))
    {
        memset(szPah, 0x00, sizeof(szPah));
        hr = pLink->GetWorkingDirectory(szPah, MAX_PATH);
        if (SUCCEEDED(hr))
        {
            szDir = szPah;
        }
    }

    ppf->Release();
    pLink->Release();
    return SUCCEEDED(hr);
}


5. 最後成果

    // define by windows
    unit = new CBrowserList::CBrowserInfo();
    unit->m_Name = PTwcsdup(g_Language.GetString("default", "Default"));
    unit->m_Path = PTwcsdup(L"");
    this->m_list.Add(unit);

    memset(szPath, 0x00, sizeof(szPath));
    if (SHGetSpecialFolderPathW(NULL, szPath, CSIDL_PROGRAMS, false))
    {
        files.GetFolderInfo(szPath);
        for (i=0; i<files.Count(); i++)
        {
            _GetFileName(files[i]->fullName, FileName);
            if (0 == FileName.CompareNoCase(L"Internet Explorer.lnk"))
            {
                if (GetShortcutTarget(files[i]->fullName, Target))
                {
                    unit = new CBrowserList::CBrowserInfo();
                    unit->m_Name = PTwcsdup(L"Internet Explorer");
                    unit->m_Path = PTwcsdup(Target);
                    this->m_list.Add(unit);               
                }

            }
        }
    }

    memset(szPath, 0x00, sizeof(szPath));
    if (SHGetSpecialFolderPathW(NULL, szPath, CSIDL_COMMON_PROGRAMS, false))
    {
        files.GetFolderInfo(szPath);
        for (i=0; i<files.Count(); i++)
        {
            _GetFileName(files[i]->fullName, FileName);
            if (0 == FileName.CompareNoCase(L"Mozilla Firefox.lnk"))
            {
                if (GetShortcutTarget(files[i]->fullName, Target))
                {
                    unit = new CBrowserList::CBrowserInfo();
                    unit->m_Name = PTwcsdup(L"Mozilla Firefox");
                    unit->m_Path = PTwcsdup(Target);
                    this->m_list.Add(unit);                   
                }

            }
            else if (0 == FileName.CompareNoCase(L"Waterfox.lnk"))
            {
                if (GetShortcutTarget(files[i]->fullName, Target))
                {
                    unit = new CBrowserList::CBrowserInfo();
                    unit->m_Name = PTwcsdup(L"Waterfox");
                    unit->m_Path = PTwcsdup(Target);
                    this->m_list.Add(unit);                   
                }      
     
            }
            else if (0 == FileName.CompareNoCase(L"Navigator.lnk"))
            {
                if (GetShortcutTarget(files[i]->fullName, Target))
                {
                    unit = new CBrowserList::CBrowserInfo();
                    unit->m_Name = PTwcsdup(L"Navigator");
                    unit->m_Path = PTwcsdup(Target);
                    this->m_list.Add(unit);                   
                }           

            }
            else if (0 == FileName.CompareNoCase(L"Pale Moon.lnk"))
            {
                if (GetShortcutTarget(files[i]->fullName, Target))
                {
                    unit = new CBrowserList::CBrowserInfo();
                    unit->m_Name = PTwcsdup(L"Pale Moon");
                    unit->m_Path = PTwcsdup(Target);
                    this->m_list.Add(unit);                   
                }   
        
            }
            else if (0 == FileName.CompareNoCase(L"SeaMonkey.lnk"))
            {
                if (GetShortcutTarget(files[i]->fullName, Target))
                {
                    unit = new CBrowserList::CBrowserInfo();
                    unit->m_Name = PTwcsdup(L"SeaMonkey");
                    unit->m_Path = PTwcsdup(Target);
                    this->m_list.Add(unit);                   
                } 
          
            }
            else if (0 == FileName.CompareNoCase(L"Google Chrome.lnk"))
            {
                if (GetShortcutTarget(files[i]->fullName, Target))
                {
                    unit = new CBrowserList::CBrowserInfo();
                    unit->m_Name = PTwcsdup(L"Google Chrome");
                    unit->m_Path = PTwcsdup(Target);
                    this->m_list.Add(unit);                  
                }

            }
            else if (0 == FileName.CompareNoCase(L"Safari.lnk"))
            {
                if (GetShortcutWorkDirectory(files[i]->fullName, Target))
                {
                    unit = new CBrowserList::CBrowserInfo();
                    unit->m_Name = PTwcsdup(L"Safari");
                    unit->m_Path = PTwcsdup(Target);
                    unit->m_Path = PTwcscat(unit->m_Path, L"Safari.exe");
                    this->m_list.Add(unit);                   
                }

            }
            else if (0 == FileName.CompareNoCase(L"Opera.lnk"))
            {
                if (GetShortcutTarget(files[i]->fullName, Target))
                {
                    unit = new CBrowserList::CBrowserInfo();
                    unit->m_Name = PTwcsdup(L"Opera");
                    unit->m_Path = PTwcsdup(Target);
                    this->m_list.Add(unit);                
                }

            }
        }
    }


6. 呼叫
void openURL(const wchar_t *url)
{
    if (g_SettingsInfo.Browser.IsEmpty())
    {
        ShellExecuteW(NULL, L"open", url, NULL, NULL, SW_SHOWNORMAL);
        return;
    }
    // check Browser was exist
    if (FileExists(g_SettingsInfo.Browser.GetString()))
    {
        ShellExecuteW(NULL, NULL, g_SettingsInfo.Browser.GetString(), url, NULL, SW_SHOWNORMAL);
        return;
    }
    //
    ShellExecuteW(NULL, _T("open"), url, NULL, NULL, SW_SHOWNORMAL);
}