GUI(Graphical User Interface)應用,鏈接器選項:/SUBSYSTEM:WINDOWS
CUI(Console User Interface)應用,鏈接器選項:/SUBSYSTEM:CONSOLE
_tWinMain 與 _tmain 函數聲明
Int WINAPI _tWinMain(
HINSTANCE hInstanceExe,
HINSTANCE,
PTSTR pszCmdLine,
int nCmdShow);
int _tmain(
int argc,
TCHAR *argv[],
TCHAR *envp[]);
部分知識點來自《Windows 核心編程(第五版)》
(1)擴展了應用程序的特性
(2)簡化了項目管理
(3)有助于節省內存
(4)促進了資源的共享
(5)促進了本地化
(6)有助于解決平臺間的差異
(7)可以用于特殊目的
(1)創建 DLL,事實上是在創建可供一個可執行模塊調用的函數
(2)當一個模塊提供一個內存分配函數(malloc、new)的時候,它必須同時提供另一個內存釋放函數(free、delete)
(3)在使用 C 和 C++ 混編的時候,要使用 extern "C" 修飾符
(4)一個 DLL 可以導出函數、變量(避免導出)、C++ 類(導出導入需要同編譯器,否則避免導出)
(5)DLL 模塊:cpp 文件中的 __declspec(dllexport) 寫在 include 頭文件之前
(6)調用 DLL 的可執行模塊:cpp 文件的 __declspec(dllimport) 之前不應該定義 MYLIBAPI
1、包含可執行文件的目錄
2、Windows 的系統目錄,可以通過 GetSystemDirectory 得到
3、16 位的系統目錄,即 Windows 目錄中的 System 子目錄
4、Windows 目錄,可以通過 GetWindowsDirectory 得到
5、進程的當前目錄
6、PATH 環境變量中所列出的目錄
DllMain 函數
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
// 第一次將一個DLL映射到進程地址空間時調用
// The DLL is being mapped into the process' address space.
break;
case DLL_THREAD_ATTACH:
// 當進程創建一個線程的時候,用于告訴DLL執行與線程相關的初始化(非主線程執行)
// A thread is bing created.
break;
case DLL_THREAD_DETACH:
// 系統調用 ExitThread 線程退出前,即將終止的線程通過告訴DLL執行與線程相關的清理
// A thread is exiting cleanly.
break;
case DLL_PROCESS_DETACH:
// 將一個DLL從進程的地址空間時調用
// The DLL is being unmapped from the process' address space.
break;
}
return (TRUE); // Used only for DLL_PROCESS_ATTACH
}
LoadLibrary、LoadLibraryExA、LoadPackagedLibrary、FreeLibrary、FreeLibraryAndExitThread 函數聲明
// 載入庫
HMODULE WINAPI LoadLibrary(
_In_ LPCTSTR lpFileName
);
HMODULE LoadLibraryExA(
LPCSTR lpLibFileName,
HANDLE hFile,
DWORD dwFlags
);
// 若要在通用 Windows 平臺(UWP)應用中加載 Win32 DLL,需要調用 LoadPackagedLibrary,而不是 LoadLibrary 或 LoadLibraryEx
HMODULE LoadPackagedLibrary(
LPCWSTR lpwLibFileName,
DWORD Reserved
);
// 卸載庫
BOOL WINAPI FreeLibrary(
_In_ HMODULE hModule
);
// 卸載庫和退出線程
VOID WINAPI FreeLibraryAndExitThread(
_In_ HMODULE hModule,
_In_ DWORD dwExitCode
);
GetProcAddress 函數聲明
FARPROC GetProcAddress(
HMODULE hInstDll,
PCSTR pszSymbolName // 只能接受 ANSI 字符串,不能是 Unicode
);
DumpBin.exe 查看 DLL 信息
在 VS 的開發人員命令提示符 使用 DumpBin.exe 可查看 DLL 庫的導出段(導出的變量、函數、類名的符號)、相對虛擬地址(RVA,relative virtual address)。如:
LoadLibrary 與 FreeLibrary 流程圖
LoadLibrary 與 FreeLibrary 流程圖
DLL 庫的編寫(導出一個 DLL 模塊) DLL 頭文件
// MyLib.h
#ifdef MYLIBAPI
// MYLIBAPI 應該在全部 DLL 源文件的 include "Mylib.h" 之前被定義
// 全部函數/變量正在被導出
#else
// 這個頭文件被一個exe源代碼模塊包含,意味著全部函數/變量被導入
#define MYLIBAPI extern "C" __declspec(dllimport)
#endif
// 這里定義任何的數據結構和符號
// 定義導出的變量(避免導出變量)
MYLIBAPI int g_nResult;
// 定義導出函數原型
MYLIBAPI int Add(int nLeft, int nRight);
DLL 源文件
// MyLibFile1.cpp
// 包含標準Windows和C運行時頭文件
#include <windows.h>
// DLL源碼文件導出的函數和變量
#define MYLIBAPI extern "C" __declspec(dllexport)
// 包含導出的數據結構、符號、函數、變量
#include "MyLib.h"
// 將此DLL源代碼文件的代碼放在此處
int g_nResult;
int Add(int nLeft, int nRight)
{
g_nResult = nLeft + nRight;
return g_nResult;
}
DLL 庫的使用(運行時動態鏈接 DLL)
// A simple program that uses LoadLibrary and
// GetProcAddress to access myPuts from Myputs.dll.
#include <windows.h>
#include <stdio.h>
typedef int (__cdecl *MYPROC)(LPWSTR);
int main( void )
{
HINSTANCE hinstLib;
MYPROC ProcAdd;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
// Get a handle to the DLL module.
hinstLib = LoadLibrary(TEXT("MyPuts.dll"));
// If the handle is valid, try to get the function address.
if (hinstLib != NULL)
{
ProcAdd = (MYPROC) GetProcAddress(hinstLib, "myPuts");
// If the function address is valid, call the function.
if (NULL != ProcAdd)
{
fRunTimeLinkSuccess = TRUE;
(ProcAdd) (L"Message sent to the DLL function\n");
}
// Free the DLL module.
fFreeResult = FreeLibrary(hinstLib);
}
// If unable to call the DLL function, use an alternative.
if (! fRunTimeLinkSuccess)
printf("Message printed from executable\n");
return 0;
}
典型程序運行步驟
(1)操作系統創建進程,把控制權交給程序的入口(往往是運行庫中的某個入口函數)
(2)入口函數對運行庫和程序運行環境進行初始化(包括堆、I/O、線程、全局變量構造等等)。
(3)入口函數初始化后,調用 main 函數,正式開始執行程序主體部分。
(4)main 函數執行完畢后,返回到入口函數進行清理工作(包括全局變量析構、堆銷毀、關閉I/O等),然后進行系統調用結束進程。
一個程序的 I/O 指代程序與外界的交互,包括文件、管程、網絡、命令行、信號等。更廣義地講,I/O 指代操作系統理解為 “文件” 的事物。
_start -> __libc_start_main -> exit -> _exit
其中 main(argc, argv, __environ) 函數在 __libc_start_main 里執行。
int mainCRTStartup(void)
執行如下操作:
(1)初始化和 OS 版本有關的全局變量。
(2)初始化堆。
(3)初始化 I/O。
(4)獲取命令行參數和環境變量。
(5)初始化 C 庫的一些數據。
(6)調用 main 并記錄返回值。
(7)檢查錯誤并將 main 的返回值返回。
大致包含如下功能:
啟動與退出:包括入口函數及入口函數所依賴的其他函數等。
標準函數:有 C 語言標準規定的C語言標準庫所擁有的函數實現。
I/O:I/O 功能的封裝和實現。
堆:堆的封裝和實現。
語言實現:語言中一些特殊功能的實現。
調試:實現調試功能的代碼。
包含:
標準輸入輸出(stdio.h)
文件操作(stdio.h)
字符操作(ctype.h)
字符串操作(string.h)
數學函數(math.h)
資源管理(stdlib.h)
格式轉換(stdlib.h)
時間/日期(time.h)
斷言(assert.h)
各種類型上的常數(limits.h & float.h)
變長參數(stdarg.h)
非局部跳轉(setjmp.h)
今天的分享就到這里了,大家要好好學C++喲~
寫在最后:對于準備學習C/C++編程的小伙伴,如果你想更好的提升你的編程核心能力(內功)不妨從現在開始!
編程學習書籍分享:
編程學習視頻分享:
整理分享(多年學習的源碼、項目實戰視頻、項目筆記,基礎入門教程)
歡迎轉行和學習編程的伙伴,利用更多的資料學習成長比自己琢磨更快哦!
對于C/C++感興趣可以關注小編在后臺私信我:【編程交流】一起來學習哦!可以領取一些C/C++的項目學習視頻資料哦!已經設置好了關鍵詞自動回復,自動領取就好了!
為什么要使用MingW呢?其實主要還是使用GCC,在Windows下主要有Mingw和Cywin,這里使用Mingw,因為GCC在生成動態庫,依賴項比較少,不像在VS生成依賴特定VC庫版本.在某些沒有VC庫對應的版本時是無法執行的.
這篇文章是17年寫,是因為當時項目要調用C/C++生成動態庫(dll),因為當時一個同事是使用VS2015開發的,導致在現場的老機器無法直接使用.
這里是生成c語言的動態庫,主要是為了讓c#調用.先來一段測試代碼.
//vs編譯的話,要將函數導出
_declspec(dllexport) int _stdcall add(int a, int b)
{
return a + b;
}
VS2008生成dll
看一下VS2008生成dll,依賴的dll文件(VS生成dll,依賴具體某個版本vcruntime)
vs2008生成dll文件依賴項,分別依賴msvcr90d.dll和kernel32.dll
不得不說,原先沒用過GCC生成dll,便在百度上進行搜索,發現全是肯定.代碼還是和用VC有區別,用GCC不需要對函數進行導出.
代碼如下:
int add(int a, int b)
{
return a + b;
}
//注意編譯,只需要指定為*.dll 加上-shared
gcc -o callback.dll -shared main.c
GCC生成dll,依賴kernel32.dll和msvrt.dll
[DllImport("callback.dll", SetLastError = true)]
static extern int add(int a, int b);
private void button1_Click(object sender, EventArgs e)
{
try
{
string txt1 = textBox1.Text;
string txt2 = textBox2.Text;
int sum = add(Convert.ToInt32(txt1), Convert.ToInt32(txt2)); //調用c語言生成動態庫中的方法
textBox3.Text = sum.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace);
}
}
效果:
C#調用C語言生成的動態庫
個人能力有限,如果您發現有什么不對,請私信我
如果您覺得對您有用的話,可以點個贊或者加個關注,歡迎大家一起進行技術交流