讓我們寫一個 Win32 文字編輯器吧 - 1. 簡介

plle發表於2022-04-03

讓我們寫一個 Win32 文字編輯器吧 - 1. 簡介

在之前的一篇文章 《曾經我認為C語言就是個弟弟》 中,我們通過使用 Windows 系統自帶的 EDIT 控制元件,建立了一個簡單的文字編輯器。而且在文章的最後,還承諾要不使用 EDIT 控制元件,自己製作一個十六進位制編輯器。

後來想到,既然十六進位制編輯器都做了,作為一個程式設計師,不如寫一個文字編輯器吧,既可以編輯二進位制,又可以編輯文字,豈不美哉。

由於實現一個編輯器的複雜性相對比較大,一篇內容肯定完不成。所以,這裡決定將整個過程作為一個系列來編輯。

本篇是系列的第一篇。在本篇文章中,將會對我們期望獲得的結果進行簡單的描述,並進行基礎程式碼框架進行構建。

本文主要包含兩個部分,如下:

  1. 專案簡介

在專案簡介部分,將會對我們要實現的目標編輯器的樣子,以及使用方法進行介紹。同時,也會對本系列接下來的文章要討論的主題進行大致說明。

  1. 基礎程式碼框架搭建

在程式碼框架搭建部分,將會對專案的建立,設定進行說明。並編輯我們的基礎程式碼。

1. 專案簡介

a. 目標

Windows11 中,已經將記事本的編輯控制元件由 EDIT 替換為 RichEditD2DPT,詳情參考Windows 11 Notepad。但是由於關於 RichEditD2DPT 控制元件的描述太少,不知道其具體的用方法。
所以,這裡我們以 EDIT 控制元件的介面為準,並實現附加功能。

在此,我們的目標是:通過 C 語言,呼叫 Win32 介面,生成一個文字編輯器。目標編輯器除了實現和 Windows 提供的預設文字編輯器 EDIT所有訊息處理,還提供如下功能:

  1. 可以設定字型顏色

    對於 EDIT 控制元件,雖然可以設定其文字字型,但是沒有設定顏色的方法。

  2. 可以編輯比較大的檔案

    對於平時的檔案編輯器來說,編輯小檔案基本上都差不多,但是當遇到比較大的檔案時(比如1G),很可能無能為力,甚至卡死。
    所以,既然我們要做一個新的編輯器,自然要考慮大檔案的編輯問題。

  3. 採取Direct Write方式實現,而不是和普通的編輯器一樣,通過 GDI

    做此選擇的原因,除了因為 Direct Write 支援顏色之外,還有一些其它優點,詳情可以點選Direct Write進行參考。

  4. 支援 EDIT控制元件 的所有訊息。

    為了使得舊 Win32 程式碼更好的使用本編輯器,所有 EDIT 控制元件支援的操作,本專案都應該支援。

  5. 處理\r\n

    此選擇和Windows 11中的選擇具有相同的理由,為了更好的處理換行。

  6. 支援撤銷操作/恢復上一步操作

    在編輯文字時,難免會想恢復到不久之前的版本,撤銷操作允許你做到這個。而當你後悔撤銷的時候,也應該能夠恢復到最新版本,恢復上一步允許你做到重新執行你之前的操作。

  7. 支援 Unicode 編碼

  8. 可以設定註解

    在編輯時,尤其是要編輯二進位制檔案時,我們有時候可能要對某個位元組,或某段文字進行註解。我們的編輯器允許提供一個註解結構,以在顯示文件時,可以進行註解顯示。

  9. 可以進行二進位制檔案編輯。

    二進位制編輯雖然不常用,但是,不能在需要用的時候找不到。所以,這裡提供了二進位制編輯功能。此功能和註解相結合,就可以進行輔助二進位制檔案的分析。

b. 目標編輯器的樣子

作為一個現代的編輯器,我們希望它有一般編輯器都應該有的能力,下面是一個編輯器的例子:
![](https://img2022.cnblogs.com/blog/456172/202204/456172-20220403204453442-1758134648.png)

不難看到,作為一個編輯器,應該支援行號,高亮,多字型,滾動條等內容。這在我們的編輯器中,都將一一實現,並詳細描述實現過程。

c. 專案結構

對於本專案來說,一共包含兩個子專案,如下:

  • vicapp:
    用於對編輯器控制元件進行呼叫的樣例程式
  • vitality-controls:
    編輯器控制元件的實現專案,將作為一個 DLL 檔案提供給呼叫者。

d. 參考連結

程式碼地址:https://github.com/vitalitylee/vitality-controls

2. 基礎程式碼框架搭建

接下來,我們詳細說明整個專案的構建過程。

a. 開啟 Visual Studio ,並點選建立新專案如下:

b. 在建立新專案對話方塊中,選擇空專案,並點選下一步,如下:

c. 在配置新專案對話方塊中,設定專案內容,如下:

d. 右鍵專案vitality-conrols,並點選屬性,彈出屬性對話方塊,如下:

e. 在常規選項卡中,設定配置型別為 動態庫,如下:


f. 一次點選配置屬性->連結器->系統,並設定子系統視窗,如下:


g. 滑鼠右鍵原始檔資料夾,選擇新增->新建項,彈出新增新項對話方塊,如下:

注意,這裡新增 .c檔案,而不是 .cpp


h. 輸入 DLL 的入口程式碼,如下:

#include <Windows.h>

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

i. 新增預處理宣告

再次開啟專案 vitality-controls 的屬性頁面,不難發現,比新增原始檔之前,左側多了一個C/C++節點,左側依次選擇配置屬性->C/C++->前處理器選項,在右側的前處理器定義中,新增 VITALITY_CONTROLS_EXPORTS宣告,最終的值應為_DEBUG;VITALITY_CONTROLS_EXPORTS;_CONSOLE;%(PreprocessorDefinitions),如下圖所示:

j. 新增介面宣告檔案

為了在兩個專案中公用一套程式碼,新建的.h檔案,放置在瞭解決方案根目錄下的shared-include目錄下,如下:

k. 修改介面程式碼

向新建的vitality-controls.h檔案中,輸入如下程式碼:

#pragma once

#ifdef VITALITY_CONTROLS_EXPORTS
#define VIC_API __declspec(dllexport)
#else
#define VIC_API __declspec(dllimport)
#endif // VITALITY_CONTROLS_EXPORTS

#include <stdio.h>

VIC_API void vic_prints(const char* str);

並向main.c中新增新建的vitality-controls.h檔案引用,並新增vic_prints函式實現,修改後程式碼如下:

#include <Windows.h>

#include "../../shared-include/vitality-controls.h"

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
  
VIC_API void vic_prints(const char* str) {
    puts(str);
}

l. 生成介面

滑鼠右鍵解決方案,並點選重新生成解決方案快捷選單,如下:

最終,你將得到一個編譯好的vitality-controls.dll檔案,如下:

m. 檢視匯出函式是否正常匯出

點選作業系統的開始選單,並點選Developer Command Prompt for VS XXXX,其中XXXX隨著你使用的Visual Studio 版本不同而不同,本文中使用的版本為Visual Studio 2022

所以,顯示如下:

點選選單後,會出現命令列視窗,在視窗中輸入命令cd/d [path]切換到目標檔案所在目錄,其中[path]為你生成的目標 DLL 的所在目錄,如:

切換到目標目錄,就可以使用dumpbin檢視匯出函式。在命令列中輸入dumprin /exports vitality-controls.dll,看到如下內容,說明你生成成功了:

n. 新建測試專案

右鍵解決方案, 點選選單新增->新建專案,根據之前的步驟,新增一個新建專案 vicapp,新增主檔案vicapp-main.c,並輸入如下程式碼:

#include "../../shared-include/vitality-controls.h"

int main(int argc, char** argv) {
	vic_prints("hello vic.");
	return 0;
}

如下所示:

o. 設定啟動專案

右鍵 vicapp 專案,並點選設為啟動專案選單,如下:

設定完成後,點選啟動按鈕,將預設啟動啟動專案

p. 新增專案引用

為了可以使得 vicapp 程式能夠引用到 vitality-controls.dll 目標檔案,需要設定兩個專案之間的引用關係。

右鍵點選 vicapp 專案,點選快捷選單新增->引用,彈出新增引用對話方塊,如下:


p. 執行程式

點選Visual Studio本地 Windows 偵錯程式按鈕,程式將啟動,並輸出 hello vic.,如下:

至此,我們專案的基礎結構已經搭建完成。

下篇文章,我們將首先實現控制元件的初始化,以及控制元件展示功能,並討論一下我們之後的專案計劃,敬請期待。

讓我們寫一個Win32文字編輯器吧 系列文章,其程式碼對應專案vitality-controls,主要對一個文字編輯器的實現過程進行說明。

如果要獲取到實時更新,歡迎微信掃描下方二維碼,關注微信公眾號程式設計之路漫漫,碼途求知己,天涯覓一心。

相關文章