2011/10/12

[vc++]LNK2019

環境:windows xp sp3+visual studio 2005
問題:error lnk2019 unresolved external symbol
error LNK2019: unresolved external symbol "extern "C" int __stdcall MyClass::Open(const char *, unsigned int, double )" (?FingerReader@@$$........@Z) referenced in function "private: boo __clrcall ApplicationDlg::OnInitMember(void)" (?Open@MyClass@Open@@$$..$......)
(詳細的錯誤訊息我沒複製下來,只知道大概是這樣。)


好了,問題來了~
它是說我的程式用了一個不認得的外部符號,偏偏我就已經告訴它要用哪個Library了,怎麼還會不認得呢?
井民全是這樣說的....
亞伯特是這樣說的....
還有CSDN上的一堆阿六仔也提供了一些說法與解法,卻沒一個能適用我遇到的狀況......囧rz

當時我只知道狀況是:
我有個Library A ,裡面的class有overload Open 這個 function,然後在dialog做initial時,不認得Open這個符號!

所以我就開個新專案,隨便寫個class,裡面有SetNumber(int total) 這個member function,Link and build過了~
再新增個SetNumber(int x, int y),Link and build又過了!(蝦餃....)

那我去GetNumber()之後,會把Return值用MessageBox秀出,發現是一堆亂碼<--原因是該專案設為使用Unicode字元集!!!

然後進 專案->屬性->組態屬性->一般->專案預設值->字元集->改為"使用多位元組字元集"

顯示的return value就正常囉~

然後我就把原來的專案設定的字元集改成一樣的設定,LNK2019就消失了XDDD

路過的大俠,懂這啥原因的,能給我解釋解釋嗎,多謝!!!

2011/08/16

[vc]Big5、Unicode、UTF-8轉換

最近在寫QRCode,我一直以為QRCode內若要顯示中文,則必須將Big5轉為Unicode後填入即可,結果證明,實際上是填入UTF-8碼才能被讀取。

那麼Unicode跟UTF-8有差嗎?
答案是:兩個本來就不一樣!

每個軟體開發者都絕對一定要會的Unicode及字元集必備知識(沒有藉口!)

一開始我打算將"中文"兩個字弄到QRCode內顯示,
其Big5為:0xA4 0xA4 0xA4 0xE5
其Unicode為:0x4E 0x2D 0x65 0x87
我把 0x4E 0x2D 0x65 0x87塞到QRCode中,想當然爾,怎麼掃都掃不出"中文"兩字,因為它其實是code point~

看過上面那篇文章後,才知道他是指 U+4E2D U+6587
再去查 Unicode Table ,對應到UTF-8的 0xE4 0xB8 0xAD(中) 0xE6 0x96 0x87(文)
再把轉出來的UTF-8 code 塞到QRCode中,才使解碼器正確的解讀出"中文"兩字!

<code>



/***********將BIG5轉換為UTF8***********/

char *szData="中文",*sendbuf_utf8=NULL;
wchar_t *sendbuf_Unicode=NULL;

//big5->unicode
int nDataLen=MultiByteToWideChar (CP_ACP, 0, szData, -1, NULL,0) ;
sendbuf_Unicode=new wchar_t[nDataLen+1];
MultiByteToWideChar(CP_ACP, 0, szData, -1, sendbuf_Unicode, nDataLen);

//unicode->UTF-8
nDataLen=WideCharToMultiByte (CP_UTF8, 0, sendbuf_Unicode, -1, NULL,0 ,NULL, NULL);
sendbuf_utf8=new char[nDataLen+1];
WideCharToMultiByte (CP_UTF8, 0, sendbuf_Unicode, -1, sendbuf_utf8,nDataLen, NULL, NULL);
//到這邊,sendbuf_utf8內的資料即是"中文"兩字的utf-8編碼了~

//delete new buffer
delete []sendbuf_utf8;sendbuf_utf8=NULL;
delete []sendbuf_Unicode;sendbuf_Unicode=NULL;



</code>

2011/07/12

[vc 2008]字串轉數字atoi()

原本在vc6下面跑的好好得,轉到vs 2008就發生這個問題.......

我先描述一下狀況:

[code]
char szBuffer[16]={0};
sprintf(szBuffer,"%s","3590525100");
int nNumber = atoi(szBuffer);
[/code]
原先我預期要得到3590525100這個數字,但是我卻得到2147483647這個數字,原因是因為atoi()回傳值型態為int,正整數部份最大只能計算到2147483647(即0x7FFFFFFF)。

解決方法:改用 sscanf()

[code]
char szBuffer[16]={0};
sprintf(szBuffer,"%s","3590525100");
unsigned int nNumber = 0;
sscanf(Buffer,"%u",&nNumber);
[/code]


然後我就順利得到3590525100這個數字了^^

ref:
JeffHung.Blog
陳鍾誠的網站

2011/05/28

[vc6]修改編輯器字型

1.透過IDE本身的Tools->Options->Format,你可以依照Category的分類,來決定Font、Size跟Colors
不過小弟大多只改Category:Source Windows而已......


2.偏偏我不愛用內建字型,但是方法1能選擇的Font又非常有限,所以這時候就要透過修改regedit來改註冊檔了~
regedit打開後點選 HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format\Source Window\FontFace 按右鍵->修改,把數值資料改為你想要用的字型名稱即可。
跟FontFace同一層有個FontSize,你也可以順便把字體放大一點~


ps.要是找不到DevStudio\6.0\Format,請先用方法1隨便選一個設定,讓VS6去寫註冊檔,再用方法2去改。


以上~

2011/05/27

[vc6]Button物件顯示Tip

1. 開啟MFC專案後,點選 project-> add   to   project-> component   and   controls-> 選擇 C:\Program Files\Microsoft Visual Studio\COMMON\MSDev98\Gallery\Visual C++ Components-> ToolTip   Support
,然後按Insert,即可將此物件插入本專案。
2. 插入後,在OnInitDialog()內會自動產生一段代碼
<code>

// CG: The following block was added by the ToolTips component.
{
// Create the ToolTip control.
m_tooltip.Create(this);
m_tooltip.Activate(TRUE);

// TODO: Use one of the following forms to add controls:
// m_tooltip.AddTool(GetDlgItem(IDC_<name>), <string-table-id>);
// m_tooltip.AddTool(GetDlgItem(IDC_<name>), "<text>");
}

</code>
假設你要的功能是當滑鼠游標移到"確定"按鈕上能顯示"我確定!"的Tip時,請將預設的
// m_tooltip.AddTool(GetDlgItem(IDC_<name>), "<text>");
修改為
m_tooltip.AddTool(GetDlgItem(IDOK),   "我確定! ");
就算大功告成了~

ps1. m_tooltip.AddTool不一定要在InitialDialog中才能做,如果你有手動創建CButton類,也可以在創建完成後,再替button物件增加tip。
ps2. m_tooltip.AddTool 可以針對同一個ItemID新增不只一次的Tip,但是畫面只會顯示最後一次所設定的tip內容。

以上~

2011/05/26

[vc6]Load Icon File

參考網址:MSDN:About Icons
Icon有分四種尺寸

  • System small
  • System large
  • Shell small
  • Shell large
System small的意思:
顯示在視窗標題列上的Icon尺寸就是System small。


System large的意思:
(圖我cut不出來,請前輩賜教!)
當你按鍵盤alt+tab組合鍵時,會跳出一個切換應用程式的視窗,視窗上面有每個應用程式自己的Icon,這個Icon的尺寸就是System large。


Shell small的意思:
在檔案總管理面看到的notepad icon尺寸Shell small。
(關於這點其實我不是很確定,因為在檢視裡面可以選擇"縮圖"、"並排"、"圖示"、"清單"、"詳細資料"五種不同的檢視方式,而這五種都會使icon尺寸產生變化,知道的前輩再請告知,謝謝^^)


Shell large的意思:
桌面上的圖示尺寸即是Shell large。


----------------------------------------我是分隔線----------------------------------------
在來說一下怎麼把icon抓出來吧~
方法一:
<code>

{//Load Icon
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
SHFILEINFO sfi;
ZeroMemory(&sfi,sizeof(SHFILEINFO));
SHGetFileInfo("C:\\windows\\system32\\notepad.exe",
 0,
 &sfi,
 sizeof(SHFILEINFO),
 SHGFI_ICON | SHGFI_SMALLICON);
HICON g_icon = sfi.hIcon;


m_btnP1.SetWindowPos(NULL,0,0,32,32,SWP_SHOWWINDOW);
m_btnP1.SetIcon(g_icon);
}

</code>
SHGetFileInfo的第五個參數:看你要哪一種icon,範例是用system small,也有SHGFI_LARGEICON。
如果SHFILEINFO::hIcon return為NULL,可能是沒有這個icon,或是第五個參數下錯。


方法二:
<code>

{
HICON phIcon;
phIcon = ::ExtractIcon((HINSTANCE)GetCurrentProcess(), "C:\\windows\\system32\\notepad.exe",(UINT)0);
if(!phIcon)return;
m_btnP1.SetIcon(phIcon);
m_btnP1.SetWindowPos(this,0,0,36,36,SWP_NOZORDER);
}

</code>
ExtractIcon的最後一個參數:可以決定要抓這個檔案的第幾個icon,0 抓第一張,以此類推。


方法三:
<code>

{
HICON phIconLarge,phIconSmall;
int total = ::ExtractIconEx("C:\\windows\\system32\\notepad.exe",0,&phIconLarge,&phIconSmall,1);


m_btnP1.SetIcon(phIconLarge);
m_btnP1.SetWindowPos(this,0,0,36,36,SWP_NOZORDER);
m_btnP2.SetIcon(phIconSmall);
m_btnP2.SetWindowPos(this,40,0,20,20,SWP_NOZORDER);
}

</code>
ExtractIconEx第二個參數帶-1,回傳值表示這個檔案有幾張icon,帶0,回傳的數字表示是第幾張icon,但我不確定是large的還是small的index。


reference:
MSDN:SHGetFileInfo
MSDN:ExtractIcon
MSDN:ExtractIconEx
以上~

[vc6]如何看DLL內的function name and point

之前有廠商給了DLL,可是怎麼Load都無法使用,所以需要透過工具去看DLL內是否有定義好export。

正常的DLL看起來像這樣:

















異常的看起來像這樣:

















差異在於funcion那邊會看到奇怪的名字,所以就無法使用。

看DLL的工具可以參考這篇網誌:[微軟]使用 Dependency Walker 工具,檢查程式與 DLL 的相依性

ps.使用dependency walker開啟dll時,dll的檔案路徑不可包含中文!

[vc6]視窗透明化

最近在製作產品,剛好需要這個功能,所以把參考網址跟code貼出來,當作筆記。


<Code>
//設定半透明效果
//1.加入WS_EX_LAYERED擴展屬性
SetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE,GetWindowLong(this->GetSafeHwnd(),GWL_EXSTYLE)^0x80000);
//2.載入User32.dll
HINSTANCE hInst = LoadLibrary("User32.DLL");
if(hInst)
{  //3.先定義一個函數,等一下要拿來接User32.dll內的"SetLayeredWindowAttributes" function point
    typedef BOOL (WINAPI *MYFUNC)(HWND,COLORREF,BYTE,DWORD);
    //4.宣告一個型態為MYFUNC的變數fun
    MYFUNC fun = NULL;
    //5.取得SetLayeredWindowAttributes函數指針
    fun=(MYFUNC)GetProcAddress(hInst, "SetLayeredWindowAttributes");
    //6.如果有找到"SetLayeredWindowAttributes"這個function的位址,就開使用吧~
    if(fun)fun(this->GetSafeHwnd(),0,160,2);//param 3:0->全透明,255->不透明
    //7.用完記得釋放Library
    FreeLibrary(hInst);
}
</Code>
SetLayeredWindowAttributes 的用法
第一個參數:要透明化的那個視窗的 window  handle
第二個參數:可以用RGB(255,255,255),則視窗上白色的地方會變透明
第三個參數:0(全透明,會完全看不到)~255(不透明)
第四個參數:LWA_ALPHA(0x00000002)跟參數3搭配使用
LWA_COLORKEY(0x00000001)跟參數2搭配使用-->針對特定顏色的透明化

詳細請參考 msdn說明

以上~

2011/03/17

[Eclipse]效能改善

reference:暗夜-解決eclipse效能不好...

開啟eclipse\eclipse.ini後

修改這兩行的數字變成256跟512
-Xms256m
-Xmx512m
新增這三行在檔案最後面
-XX:PermSize=265M
-XX:MaxPermSize=265M
-XX:+UseParalle1GC

編輯完成後存檔離開~
先開啟工作管理員準備監看eclipse.exe會吃多少資源,然後開啟你的eclipse,看有沒有改善吧!

2011/03/16

[Android]Java JNI包SGClient(C dll)

前置作業
(1)安裝Android開發環境(請參考 Android BMI)
(2)安裝JNI開發環境(請參考 智慧生活科技專業社群)

進入正題

1.將SGClient dll的source code改寫為純c語言後,產出libsgclnt.so
a.安裝好android-ndk後,裡面會有一些sample,我們把samples\hello-jni複製一份放在samples底下,並將目錄名稱改為sgclnt
b.將原本由C++開發的SGClient改寫為純C後,將程式碼放在C:\cygwin\android-ndk-r5b\samples\sgclnt\jni目錄底下
c.sgclnt\jni底下有個hello-jni.c,rename為SGClntJNI.c,編輯SGClntJNI.c
先include sgclient.h後,把sgclient.h的每個function轉為jni格式的function,
例如:
c:
int SG_Create(int Count);

jni:
jint Java_com_mds_sgclnt_SGClient_SGCreat(JNIEnv* env, jobject thiz, jint Count)
{
    return SG_Create(Count);
}
型態轉換請參考:The Java Native Interface Programmer's Guide and Specification-CH12 JNI Type
命名規則->
SGCreat:Java應用程式呼叫JNI介面提供的function name
SGClient:第二步驟時會建立Java的Class,Class name為SGClient,透過這個Class來呼叫JNI介面提供的Function
com_mds_sgclnt:SGClient這個class會被包在com.mds.sgclnt這個package裡面
Java:固定
jint:int型態在jni中的轉型

d.在相同目錄下,建立Android.mk這個MAKEFILE,makefile內容如下
#-----Android.mk-----
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:=sgclnt
#註:JAVA在System.loadLibrary("sgclnt")時會用到的dll名稱
LOCAL_SRC_FILES:=SGClntJNI.c
LOCAL_SRC_FILES+=SGClnt.c
LOCAL_SRC_FILES+=file1.c
LOCAL_SRC_FILES+=file2.c
LOCAL_SRC_FILES+=file3.c
#註:把剛剛放在JNI目錄內的.c檔都用+=的方式包進來
include $(BUILD_SHARED_LIBRARY)
#-----Android.mk-----
編輯完後存檔。

e.開啟cygwin,並切換到samples\sgclnt目錄,執行ndk-build,便會在sample\sgclnt底下產生libs與obj兩個目錄,
其中libs\armeabi\libsgclnt.so即為dll檔

2.建立Java端SGClient package
f.建立C:\cygwin\android-ndk-r5b\samples\sgclnt\src\com\mds\sgclnt\SGClient.java
這邊的com\mds\sgclnt與SGClient.java會呼應到SGClntJNI.c中的function命名規則,剛剛我們是用hello-jni copy過來的,所以只要把不同的目錄or檔案rename即可。

g.用Java寫一個package,package名稱與剛剛開啟的路徑有關,這邊的package名稱就叫做com.mds.sgclnt,
並將SGClient元件(C)預定要產出的介面也宣告在SGClient class(JAVA)中,並在宣告前加上public static native。
SGClient.java內容:

package com.mds.sgclnt;

import android.app.Activity;
import android.widget.TextView;
import android.os.Bundle;

public class SGClient extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        //TextView  tv = new TextView(this);
        //tv.setText( stringFromJNI() );
        //setContentView(tv);
      
        int nRet = 0;
nRet = SGCreate();//調用C Function,並觀測返回值是否正確!
    }

    static {
        System.loadLibrary("sgclnt");
    }
    public static native int SGCreate();
    public static native int SGDestory();
    .
    .
    .
}

3.測試
h.修改專案資訊
開啟samples\sgclnt\AndroidManifest.xml
manifest tag內有package參數,修改package="com.mds.sgclnt"
因為有用到網路,目前是在Android Virtual Device內運行,所以要增加<uses-permission android:name="android.permission.INTERNET"/>這行放在uses-sdk這個tag結束之後,
activity tag要修改android::name=".SGClient"
存檔後結束編輯

i.開啟專案並建立Builder
這邊跟智慧生活科技專業社群裡面介紹的步驟都一樣,唯一差別是Main標籤中的Arguments要輸入:--login -c "cd $NDK_Sample/sgclnt && ndk-build"

祝好運~