發表文章

目前顯示的是有「程式設計」標籤的文章

Driver 的數位簽章

圖片
Microsoft 為了安全性的考量,在 x64 OS 下的 Driver 一定要有合法的數位簽章,否則下場就是這樣,甚麼都不能做。

[心得] 如何造成程式 Crash

圖片
最近在改程式的時候,發生以下異常存取的狀況 原因是以下的指標指到不合法的記憶體,但查很久就是查不到在那裡被改的 if ( m_pStaticCoreVoltage ->IsWindowVisible()) { m_pStaticCoreVoltage->SetWindowText(m_strMonCoreVoltage); }

[心得] 在 struct 中的 vector 存取異常

很久沒有寫心得了,這個問題花了我整整兩天的時間才解決,所以值得分享一下。首先我們看以下的定義 typedef struct _Profile { INT nFormat; BOOL bFanMode; ULONG ulFanSpeed; #ifdef BOOST BOOL bBoost; #endif INT nOCMode; vector<INT> OffsetVector; } Profile, *PProfile; 應該沒甚麼問題,除了條件式的組譯:如果有定義 BOOST 的話,就多一個 bBoost 的欄位 在我的 Solution 中有 ProjectA 和 ProjectB,其中 ProjectA 會產生 Profile 的物件,並以 Call by address 的方式傳給 Project B 中做後續的資料處理與回傳。 而問題出在 ProjectB 中只要對 OffsetVector 做寫入就會產生記憶體存取異常。我找過幾個方向: 因為 vector 會動態配置記憶體來存取資料,所以初期一直朝這個方向找答案,是否在傳遞的過程中,vector 所指到的記憶體已經改變了?所以才造成異常。但相同的動作在 ProjectA 做卻完全沒問題 struct alignment 的問題,是否兩個 Project 的配置不一樣,造成相同的欄位卻存取到不同的位址?因此我用 sizeof() 取得兩個這數所佔空間的大小,還真的不一樣,而且 OffsetVector 是指向 struct 中不同的 offset。但我查 Project alignment 的設定,一樣都是預設值 這就怪了,最後發現原來問題是我自己造成的。原來我在 ProjectA 有定義 BOOST,因此有 bBoost 這個欄位,而 ProjectB 是事後才加入的,並沒有定義 BOOST,因此兩邊在存取 OffsetVector 時,就差了一個 BOOL 的欄位空間,故 ProjectB 在存取的位址實際上是錯的,當然就產生記憶體存取異常了,還害我誤會了 vector 所以結論是: 以後 不要在 struct 中設定條件式組譯 ,避免又發生同樣的問題。 而在程式碼中的條件式組譯,請定義在 Project Property | C/C++ ...

dynamic_cast 與 operator override 的應用

假設我有一個 Interface 如下 struct IProperty abstract { ~IProperty() { } virtual LPCTSTR toString() = 0; virtual BOOL operator==( const IProperty& other) const = 0; virtual BOOL operator!=( const IProperty& other) const = 0; };

DPI-Aware Application

圖片
一般 MFC Dialog base 的程式,如果使用者調整 Font size 則 Dialog 是會跟著 Font size 等比例放大的。

【已解決】CMyButton 反應太慢

CMy Button  是一個 Owner-Drawn 的 CButton,最近在使用時發現:如果連續按的話,有時會沒有反應。 原本懷疑:  利用 OnSetCursor  攔截 Mouse Over 的狀態,如果 Mouse 停留在 Button 的區域內,會一直產生重繪的動作 ( 取消後問題仍在,故排除 ) 按下 Button 後執行的程式佔用太多的時間,導致第二次 Button click 來不及處理 ( 但使用標準的 CButton 則沒有問題,故排除 )

如何防止 Windows 重新啟動或關機

一般 MFC Dialog base 的程式,我們會在 OnInitDialog() 做一些初始化的動作;然後在 PostNcDestroy() 做一些清除資源,或將資料寫回 HD 的動作。 但在 Windows 重新啟動或關機的情況下,是不會執行到 PostNcDestroy() 的,這時就會造成一些程式動作未完成。 Windows 有兩個 Message 跟重新啟動或關機有關: WM_QUERYENDSESSION  :詢問程式是否可關機,回傳 TRUE 表示可以,FALSE 則不行。此外 lParam 會指出一些程式關閉的情況,不過在 MFC OnQueryEndSession() 都省略掉了。 WM_ENDSESSION :程式執行完 WM_QUERYENDSESSION 後呼叫 (不論其傳回結果為 TRUE 或 FASE),其傳回結果會反應在 wParam;而 lParam 的內容同 WM_QUERYENDSESSION,不過在 MFC OnEndSession() 一樣被省略了。 所以這兩個地方都可以拿來做跟 PostNcDestroy() 一樣的工作,差別在 WM_QUERYENDSESSION 可以傳回 FALSE 來阻止 Windows 重新啟動或關機而己。

[已解決] side-by-side error

圖片
好久沒有寫心得了,最近寫程式拿到另一個平台執行時,發生以下問題 原因是我的程式需要 MFC DLL,Debug version 使用 Share library,而新的平台並未安裝對應的 DLL,導至程式執行時找不到,所以就產生問題了 另外 Release version 使用 Static library,內建在程式中,所以就沒問題 而開發平台有安裝對應的 DLL,所以總是看不到問題,下次如果要部署 Share library 版本,記得在安裝程式中安裝對應的 DLL 版本。

【已解決】CResourceException 異常處理

圖片
最近開發一個自訂控制項,當視窗經過多次放大、縮小後就會產生 First-chance exception at 0x000007fefcfe940d in XXX_x64.exe: Microsoft C++ exception: CResourceException * __ptr64 at memory location 0x0027b360.. First-chance exception at 0x000007fefcfe940d in XXX_x64.exe: Microsoft C++ exception: CInvalidArgException * __ptr64 at memory location 0x00276b90.. Warning: Uncaught exception in WindowProc (returning 0). 經過搜尋的結果,應該是使用資源後未釋放,造成資源耗盡。問題是沒有捕捉到產生異常的地方,要修改也無從下手。於是先用 try catch 在有可能的地方試試看

scanf 兩三事

圖片
記得以前寫程式的時候,scanf 是很常用到的輸入功能。但寫 Windows 程式後就很少碰到它了,直到之前發生了一些靈異現像,才重新勾起對它的回憶。首先看看以下的程式碼吧 int _tmain(int argc, _TCHAR* argv[]) { short int i=0, j=0; _tprintf(_T("Please input number : ")); _tscanf_s(_T(" %d "),&i); _tprintf(_T("i = %d, j = %d\n"), i, j); return 0; }

[已解決] Static Library Project 的 MFC 不支援 template

最近為了用 Unit Test 而將一些程式碼放在 Static Library Project 中,但在使用 CArray template 時卻出現 Compiler error C2976: 'CArray' : too few template arguments 經比對一般的 MFC Project 才發現 stdafx.h 未加入 #include <afxtempl.h> 加入後就一切正常了 Test project 也要加入,而且設定使用 Shared DLL MFC 才能正常測試

operator LPCTSTR() 實作

在 C# 裡有一個 ToString() 的方法可以把物件的內容轉為字串,但在 C++ 中卻找不到對應的方法。後來看到 CString 覆寫 operator LPCTSTR() ,才驚覺原來還有這種作法啊! class MyClass { private: TCHAR m_lpszName[MAX_PATH]; public: MyClass(LPCTSTR lpszName); ~MyClass(void); operator LPCTSTR() const; };

C Bit Field 使用注意事項

圖片
C Bit Field 在存取暫存器的欄位是非常好用的,例如

【已解決】刪除指向介面指標 (Interface pointer) 的物件不會執行解構函式 (Destructor)

一般物件在生命週期結束時,會呼叫解構函式,以便執行釋放資源的動作。但對於實作介面的物件,如果是以介面指標的方式來呼叫,竟然不會呼叫解構函式,Oh My God。讓我們一步一步來看看吧! #include "stdafx.h" #include <iostream> using namespace std; struct IAnimal { virtual void Eat() = 0; }; class Dog : public IAnimal { public: Dog() { cout << "Dog construct." << endl; } ~Dog() { cout << "Dog destruct." << endl; } void Eat() { cout << "Dog eat ..." << endl; } }; int _tmain(int argc, _TCHAR* argv[]) { Dog* pDog = new Dog(); pDog->Eat(); delete pDog; return 0; }

【已解決】C++ Event 的實作方式

Event 在 Windows 的程式設計中,已經習以為常,尤其是 Windows 的 Event。但要在 C++ 中實作,還真不曉得要如何下手,原本在 C# 中如此基本的東西,轉到 C++ 竟是如此困難。 還好 Microsoft 還是留了一手 Event Handling ,但看了半天還不是很懂,於是從 Sample code 中實驗終於大致瞭解了,所以記錄以備後用 (雖然 Microsoft 說這個功能要取消了,不過取消前還是將就著用吧!)

【已解決】循環引用的疑問

最近在移植程式的過程中,碰到一個循環引用的問題,困擾了我不少時間,所以有寫下來的必要,以免下次再犯。直接來看例子吧! 這是最上層的介面 IElement,其中的 Method 可以處理實作 IVisitor 介面的物件 #pragma once #include "IVisitor.h" // <== Cause cross reference struct IElement { virtual VOID Accept(IVisitor* pVisitor) = 0; virtual VOID Traverse(IVisitor* pVisitor) = 0; }; typedef IElement* PIElement; 接下來是 IComputer 介面,繼承 IElement 介面

在 C++ 使用 namespace

寫程式常常會碰到名稱 (類別、函式、變數 ... 等等) 衝突的問題 (因為英文懂得太少了),這個時候命名空間就可以發揮功用了,只要在自己的命名空間內,即使名稱跟別人一樣也不會衝突了。 同時在 Visual Studio 的 Class View 中,也會依據命名空間來分類,方便 Source Code 的管理。唯一的缺憾就是不能像  Net 一樣使用 "." 當作命名空間的名稱,所以只好用 "_" 來取代了!

好玩的 manifest

圖片
先前在  Tooltip Controls  曾經提到 Manifest,當時還不甚瞭解,而今天又碰到類似的問題,所以決定作個實驗來澄清一下! 首先找個專案來設定,把 Embed Manifest 設為 No ,也就是把 Manifest file 放在外面,不要內嵌到程式中

The Desktop Item Position Saver (DIPS) Utility 程式解析

圖片
前言: DIPS Utility ( Windows via C/C++, Fifth Edition Page 633) 是 Jeffrey Richter 示範 DLL Injection 的一個範例,內容讀起來對我來說有點複雜,所以做一下心得報告以加深印象。

【已解決】Global Hook 抓不到其它的程式

圖片
Hook 對我來說是一個全新的東西,它的概念有點像以前在寫 DOS TSR (常駐程式) 的作法,必須攔截原本的中斷,然後改成呼叫自己的中斷服務常式,但自己的服務常式中又必須呼叫原本的中斷,以達到插入自己程式碼到系統中目的。 我實作的是 WH_CBT 的 Hook,當一個視窗有動作時 (包含 activating, creating, destroying, minimizing, maximizing, moving, or sizing) 都會呼叫這個 Hook,根據 MSDN 的說明,把 CBTProc 放在 DLL 中可以達到 Global Hook 的目的。 但奇怪的是,在偵錯的過程中,我把中斷點設在 CBTProc,只有點到我自己的程式時才有反應,點到其它的程式完全沒反應,不是 Global Hook 嗎?為甚麼點別的程式會攔截不到呢?