DLL入門淺析【轉】

来源:http://www.cnblogs.com/DwyaneTalk/archive/2016/01/28/5167037.html
-Advertisement-
Play Games

1、建立DLL動態庫 動態鏈接庫(DLL)是從C語言函數庫和Pascal庫單元的概念發展而來的。所有的C語言標準庫函數都存放在某一函數庫中。在鏈接應用程式的過程中,鏈接器從庫文件中拷貝程式調用的函數代碼,並把這些函數代碼添加到可執行文件中。這種方法同只把函數儲存在已編譯的OBJ文件中相比更有利於代碼


 1、建立DLL動態庫

  動態鏈接庫(DLL)是從C語言函數庫和Pascal庫單元的概念發展而來的。所有的C語言標準庫函數都存放在某一函數庫中。在鏈接應用程式的過程中,鏈接器從庫文件中拷貝程式調用的函數代碼,並把這些函數代碼添加到可執行文件中。這種方法同只把函數儲存在已編譯的OBJ文件中相比更有利於代碼的重用。但隨著Windows這樣的多任務環境的出現,函數庫的方法顯得過於累贅。如果為了完成屏幕輸出、消息處理、記憶體管理、對話框等操作,每個程式都不得不擁有自己的函數,那麼Windows程式將變得非常龐大。Windows的發展要求允許同時運行的幾個程式共用一組函數的單一拷貝。動態鏈接庫就是在這種情況下出現的。動態鏈接庫不用重覆編譯或鏈接,一旦裝入記憶體,DLL函數可以被系統中的任何正在運行的應用程式軟體所使用,而不必再將DLL函數的另一拷貝裝入記憶體。
  下麵我們一步一步來建立一個DLL。
  一、建立一個DLL工程
       新建一個工程,選擇Win32 控制台項目(Win32 Console Application),並且在應用程式設置標簽(the advanced tab)上,選擇DLL和空項目選項。
     二、聲明導出函數
     這裡有兩種方法聲明導出函數:一種是通過使用__declspec(dllexport),添加到需要導出的函數前,進行聲明;另外一種就是通過模塊定義文件(Module-Definition File即.DEF)來進行聲明。
     第一種方法,建立頭文件DLLSample.h,在頭文件中,對需要導出的函數進行聲明。

 1 #ifndef _DLL_SAMPLE_H
 2 #define _DLL_SAMPLE_H
 3 
 4 // 如果定義了C++編譯器,那麼聲明為C鏈接方式
 5 #ifdef __cplusplus
 6 extern "C" {
 7 #endif
 8 
 9 // 通過巨集來控制是導入還是導出
10 #ifdef _DLL_SAMPLE
11 #define DLL_SAMPLE_API __declspec(dllexport)
12 #else
13 #define DLL_SAMPLE_API __declspec(dllimport)
14 #endif
15 
16 // 導出/導入函數聲明
17 DLL_SAMPLE_API void TestDLL(int);
18 
19 #undef DLL_SAMPLE_API
20 
21 #ifdef __cplusplus
22 }
23 #endif
24 
25 #endif

    這個頭文件會分別被DLL和調用DLL的應用程式引入,當被DLL引入時,在DLL中定義_DLL_SAMPLE巨集,這樣就會在DLL模塊中聲明函數為導出函數;當被調用DLL的應用程式引入時,就沒有定義_DLL_SAMPLE,這樣就會聲明頭文件中的函數為從DLL中的導入函數。 

     第二種方法:模塊定義文件是一個有著.def文件擴展名的文本文件。它被用於導出一個DLL的函數,和__declspec(dllexport)很相似,但是.def文件並不是Microsoft定義的。一個.def文件中只有兩個必需的部分:LIBRARY 和 EXPORTS。

1 LIBRARY DLLSample
2 DESCRIPTION "my simple DLL"
3 EXPORTS
4         TestDLL @1  ;@1表示這是第一個導出函數

       第一行,''LIBRARY''是一個必需的部分。它告訴鏈接器(linker)如何命名你的DLL。下麵被標識為''DESCRIPTION''的部分並不是必需的。該語句將字元串寫入 .rdata 節,它告訴人們誰可能使用這個DLL,這個DLL做什麼或它為了什麼(存在)。再下麵的部分標識為''EXPORTS''是另一個必需的部分;這個部分使得該函數可以被其它應用程式訪問到並且它創建一個導入庫。當你生成這個項目時,不僅是一個.dll文件被創建,而且一個文件擴展名為.lib的導出庫也被創建了。除了前面的部分以外,這裡還有其它四個部分標識為:NAME, STACKSIZE, SECTIONS, 和 VERSION。另外,一個分號(;)開始一個註解,如同''//''在C++中一樣。定義了這個文件之後,頭文件中的__declspec(dllexport)就不需要聲明瞭。

  三、編寫DllMain函數和導出函數
       DllMain函數是DLL模塊的預設入口點。當Windows載入DLL模塊時調用這一函數。系統首先調用全局對象的構造函數,然後調用全局函數DLLMain。DLLMain函數不僅在將DLL鏈接載入到進程時被調用,在DLL模塊與進程分離時(以及其它時候)也被調用。

 1 #include "stdafx.h"
 2 #define _DLL_SAMPLE
 3 
 4 #ifndef _DLL_SAMPLE_H
 5 #include "DLLSample.h"
 6 #endif
 7 
 8 #include "stdio.h"
 9 
10 //APIENTRY聲明DLL函數入口點
11 BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
12 {
13  switch (ul_reason_for_call)
14  {
15   case DLL_PROCESS_ATTACH:
16   case DLL_THREAD_ATTACH:
17   case DLL_THREAD_DETACH:
18   case DLL_PROCESS_DETACH:
19    break;
20  }
21  return TRUE;
22 }
23 
24 void TestDLL(int arg)
25 {
26   printf("DLL output arg %d\n", arg);
27 }

     如果程式員沒有為DLL模塊編寫一個DLLMain函數,系統會從其它運行庫中引入一個不做任何操作的預設DLLMain函數版本。在單個線程啟動和終止時,DLLMain函數也被調用。 然後,F7編譯,就得到一個DLL了。

2、使用DLL動態庫

  上文簡單的介紹瞭如何建立一個簡單DLL,下麵再我簡單的介紹一下如何使用一個DLL。當一個DLL被生成後,它創建了一個.dll文件和一個.lib文件;這兩個都是你需要的。要使用DLL,就需要載入這個DLL。

  隱式鏈接

    這裡有兩個方法來載入一個DLL;一個方法是捷徑另一個則相比要複雜些。捷徑是只鏈接到你.lib 文件並將.dll文件置入你的新項目的路徑中去。因此,創建一個新的空的Win32控制台項目並添加一個源文件。將你做的DLL放入你的新項目相同的目錄下。

 1 #include "stdafx.h"
 2 #include "DLLSample.h"
 3 
 4 #pragma comment(lib, "DLLSample.lib") //你也可以在項目屬性中設置庫的鏈接
 5 
 6 int main()
 7 {
 8         TestDLL(123);
 9         return(1);
10 }

    這就是載入一個DLL的簡單方法。

  顯式鏈接

    難點的載入DLL的方法稍微有點複雜。你將需要函數指針和一些Windows函數。但是,通過這種載入DLLs的方法,你不需要DLL的.lib或頭文件,而只需要DLL。

 1 #include <iostream>
 2 #include <windows.h>
 3 typedef void (*DLLFunc)(int);
 4 int main()
 5 {
 6         DLLFunc dllFunc;
 7         HINSTANCE hInstLibrary = LoadLibrary("DLLSample.dll");
 8 
 9         if (hInstLibrary == NULL)
10         {
11          FreeLibrary(hInstLibrary);
12         }
13         dllFunc = (DLLFunc)GetProcAddress(hInstLibrary, "TestDLL");
14         if (dllFunc == NULL)
15         {
16          FreeLibrary(hInstLibrary);
17         }
18         dllFunc(123);
19         std::cin.get();
20         FreeLibrary(hInstLibrary);
21         return(1);
22 }

     首先你會註意到:這裡包括進了文件“windows.h”同時移走了“DLLSample.h”。原因很簡單:因為windows.h包含了一些Windows函數,當然你現在將只需要其中幾個而已。它也包含了一些將會用到的Windows特定變數。你可以去掉DLL的頭文件(DLLSample.h)因為-如我前面所說-當你使用這個方法載入DLL時你並不需要它。

   下麵你會看到:下麵的一句代碼:

    typedef void (*DLLFunc)(int);
      
   這是一個函數指針類型的定義。指向一個函數是一個int型的參數,返回值為void類型。一個HINSTANCE是一個Windows數據類型:是一個實例的句柄;在此情況下,這個實例將是這個DLL。你可以通過使用函數LoadLibrary()獲得DLL的實例,它獲得一個名稱作為參數。在調用LoadLibrary函數後,你必需查看一下函數返回是否成功。你可以通過檢查HINSTANCE是否等於NULL(在Windows.h中定義為0或Windows.h包含的一個頭文件)來查看其是否成功。如果其等於NULL,該句柄將是無效的,並且你必需釋放這個庫。換句話說,你必需釋放DLL獲得的記憶體。如果函數返回成功,你的HINSTANCE就包含了指向DLL的句柄。

    一旦你獲得了指向DLL的句柄,你現在可以從DLL中重新獲得函數。為了這樣作,你必須使用函數GetProcAddress(),它將DLL的句柄(你可以使用HINSTANCE)和函數的名稱作為參數。你可以讓函數指針獲得由GetProcAddress()返回的值,同時你必需將GetProcAddress()轉換為那個函數定義的函數指針。舉個例子,對於Add()函數,你必需將GetProcAddress()轉換為AddFunc;這就是它知道參數及返回值的原因。現在,最好先確定函數指針是否等於NULL以及它們擁有DLL的函數。這隻是一個簡單的if語句;如果其中一個等於NULL,你必需如前所述釋放庫。

    一旦函數指針擁有DLL的函數,你現在就可以使用它們了,但是這裡有一個需要註意的地方:你不能使用函數的實際名稱;你必需使用函數指針來調用它們。在那以後,所有你需要做的是釋放庫如此而已。

  模塊句柄

    進程中的每個DLL模塊被全局唯一的32位元組的HINSTANCE句柄標識。進程自己還有一個HINSTANCE句柄。所有這些模塊句柄都只有在特定的進程內部有效,它們代表了DLL或EXE模塊在進程虛擬空間中的起始地址。在Win32中,HINSTANCE和HMODULE的值是相同的,這個兩種類型可以替換使用。進程模塊句柄幾乎總是等於0x400000,而DLL模塊的載入地址的預設句柄是0x10000000。如果程式同時使用了幾個DLL模塊,每一個都會有不同的HINSTANCE值。這是因為在創建DLL文件時指定了不同的基地址,或者是因為載入程式對DLL代碼進行了重定位。
    模塊句柄對於載入資源特別重要。Win32 的FindResource函數中帶有一個HINSTANCE參數。EXE和DLL都有其自己的資源。如果應用程式需要來自於DLL的資源,就將此參數指定為DLL的模塊句柄。如果需要EXE文件中包含的資源,就指定EXE的模塊句柄。
    但是在使用這些句柄之前存在一個問題,你怎樣得到它們呢?如果需要得到EXE模塊句柄,調用帶有Null參數的Win32函數GetModuleHandle;如果需要DLL模塊句柄,就調用以DLL文件名為參數的Win32函數GetModuleHandle。

  應用程式怎樣找到DLL文件

    如果應用程式使用LoadLibrary顯式鏈接,那麼在這個函數的參數中可以指定DLL文件的完整路徑。如果不指定路徑,或是進行隱式鏈接,Windows將遵循下麵的搜索順序來定位DLL:
      1. 包含EXE文件的目錄,
      2. 進程的當前工作目錄,
      3. Windows系統目錄,
      4. Windows目錄,
      5. 列在Path環境變數中的一系列目錄。
    這裡有一個很容易發生錯誤的陷阱。如果你使用VC++進行項目開發,並且為DLL模塊專門創建了一個項目,然後將生成的DLL文件拷貝到系統目錄下,從應用程式中調用DLL模塊。到目前為止,一切正常。接下來對DLL模塊做了一些修改後重新生成了新的DLL文件,但你忘記將新的DLL文件拷貝到系統目錄下。下一次當你運行應用程式時,它仍載入了老版本的DLL文件,這可要當心!

  調試DLL程式

    Microsoft 的VC++是開發和測試DLL的有效工具,只需從DLL項目中運行調試程式即可。當你第一次這樣操作時,調試程式會向你詢問EXE文件的路徑。此後每次在調試程式中運行DLL時,調試程式會自動載入該EXE文件。然後該EXE文件用上面的搜索序列發現DLL文件,這意味著你必須設置Path環境變數讓其包含DLL文件的磁碟路徑,或者也可以將DLL文件拷貝到搜索序列中的目錄路徑下。
或者當你調試EXE程式時,在Project Setting中,將Debug選項卡中的Category設置為Additional DLLs。就可以同時調試EXE和它調用的DLL(當然,你需要有DLL的源代碼)了。

3、從DLL動態庫中導出變數

  前面介紹了怎麼從DLL中導出函數,下麵我們來看一下如何從DLL中導出變數來。聲明為導出變數時,同樣有兩種方法:

      第一種是用__declspec進行導出聲明

 1 #ifndef _DLL_SAMPLE_H
 2 #define _DLL_SAMPLE_H
 3 
 4 // 如果定義了C++編譯器,那麼聲明為C鏈接方式
 5 #ifdef __cplusplus
 6 extern "C" {
 7 #endif
 8 
 9 // 通過巨集來控制是導入還是導出
10 #ifdef _DLL_SAMPLE
11 #define DLL_SAMPLE_API __declspec(dllexport)
12 #else
13 #define DLL_SAMPLE_API __declspec(dllimport)
14 #endif
15 
16 // 導出/導入變數聲明
17 DLL_SAMPLE_API extern int DLLData;
18 
19 #undef DLL_SAMPLE_API
20 
21 #ifdef __cplusplus
22 }
23 #endif
24 
25 #endif

    第二種是用模塊定義文件(.def)進行導出聲明

1 LIBRARY DLLSample
2 DESCRIPTION "my simple DLL"
3 EXPORTS
4         DLLData DATA  ;DATA表示這是數據(變數)

  下麵是DLL的實現文件

 1 #include "stdafx.h"
 2 #define _DLL_SAMPLE
 3 
 4 #ifndef _DLL_SAMPLE_H
 5 #include "DLLSample.h"
 6 #endif
 7 
 8 #include "stdio.h"
 9 
10 int DLLData;
11 
12 //APIENTRY聲明DLL函數入口點
13 BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
14 {
15  switch (ul_reason_for_call)
16  {
17   case DLL_PROCESS_ATTACH:
18       DLLData = 123;  // 在入口函數中對變數進行初始化
19       break
20   case DLL_THREAD_ATTACH:
21   case DLL_THREAD_DETACH:
22   case DLL_PROCESS_DETACH:
23    break;
24  }
25  return TRUE;
26 }

  同樣,應用程式調用DLL中的變數也有兩種方法。
    第一種是隱式鏈接:

 1 #include <stdio.h>
 2 #include "DLLSample.h"
 3 
 4 #pragma comment(lib,"DLLSample.lib")
 5 
 6 
 7 int main(int argc, char *argv[])
 8 {
 9  printf("%d ", DLLSample);
10  return 0;
11 }

  第二種是顯式鏈接:

 1 #include <iostream>
 2 #include <windows.h>
 3 
 4 int main()
 5 {
 6         int my_int;
 7         HINSTANCE hInstLibrary = LoadLibrary("DLLSample.dll");
 8 
 9         if (hInstLibrary == NULL)
10         {
11          FreeLibrary(hInstLibrary);
12         }
13         my_int = *(int*)GetProcAddress(hInstLibrary, "DLLData");
14         if (dllFunc == NULL)
15         {
16          FreeLibrary(hInstLibrary);
17         }
18         std::cout<<my_int;
19         std::cin.get();
20         FreeLibrary(hInstLibrary);
21         return(1);
22 }

  通過GetProcAddress取出的函數或者變數都是地址,因此,需要解引用並且轉類型。

 

4、從DLL動態庫中導出類

  前面介紹了怎麼從DLL中導出函數和變數,實際上導出類的方法也是大同小異,廢話就不多說了,下麵給個簡單例子示範一下,也就不多做解釋了。
  DLL頭文件:

 1 #ifndef _DLL_SAMPLE_H
 2 #define _DLL_SAMPLE_H
 3 
 4 // 通過巨集來控制是導入還是導出
 5 #ifdef _DLL_SAMPLE
 6 #define DLL_SAMPLE_API __declspec(dllexport)
 7 #else
 8 #define DLL_SAMPLE_API __declspec(dllimport)
 9 #endif
10 
11 // 導出/導入變數聲明
12 DLL_SAMPLE_API class DLLClass
13 {
14   public:
15     void Show();
16 };
17 
18 #undef DLL_SAMPLE_API
19 
20 #endif

  DLL實現文件:

 1 #include "stdafx.h"
 2 #define _DLL_SAMPLE
 3 
 4 #ifndef _DLL_SAMPLE_H
 5 #include "DLLSample.h"
 6 #endif
 7 
 8 #include "stdio.h"
 9 
10 //APIENTRY聲明DLL函數入口點
11 BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
12 {
13  switch (ul_reason_for_call)
14  {
15   case DLL_PROCESS_ATTACH:
16   case DLL_THREAD_ATTACH:
17   case DLL_THREAD_DETACH:
18   case DLL_PROCESS_DETACH:
19    break;
20  }
21  return TRUE;
22 }
23 
24 void DLLClass::Show()
25 {
26   printf("DLLClass show!");
27 }

  應用程式調用DLL

 1 #include "DLLSample.h"
 2 
 3 #pragma comment(lib,"DLLSample.lib")
 4 
 5 
 6 int main(int argc, char *argv[])
 7 {
 8  DLLClass dc;
 9   dc.Show();
10  return 0;
11 }

  大家可能發現了,上面我沒有使用模塊定義文件(.def)聲明導出類也沒有用顯式鏈接導入DLL。用Depends查看前面編譯出來的DLL文件,會發現裡面導出了很奇怪的symbol,這是因為C++編譯器在編譯時會對symbol進行修飾。這是我從別人那兒轉來的截圖。

    
  網上找了下,發現了C++編譯時函數名的修飾約定規則

  __stdcall調用約定:

   1、以"?"標識函數名的開始,後跟函數名;
   2、函數名後面以"@@YG"標識參數表的開始,後跟參數表;
   3、參數表以代號表示:

    X——void,
    D——char,
    E——unsigned char,
    F——short,
    H——int,
    I——unsigned int,
    J——long,
    K——unsigned long,
    M——float,
    N——double,
    _N——bool,
    ....

    PA——表示指針,後面的代號表明指針類型,如果相同類型的指針連續出現,以"0"代替,一個"0"代表一次重覆;
   4、參數表的第一項為該函數的返回值類型,其後依次為參數的數據類型,指針標識在其所指數據類型前; 
     5、參數表後以"@Z"標識整個名字的結束,如果該函數無參數,則以"Z"標識結束。
   其格式為"?functionname@@YG*****@Z"或?functionname@@YG*XZ

      int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”
       void Test2()                          -----“?Test2@@YGXXZ”

  __cdecl調用約定:
    規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的"@@YG"變為"@@YA"。

  __fastcall調用約定:
    規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的"@@YG"變為"@@YI"。
  VC++對函數的省缺聲明是"__cedcl",將只能被C/C++調用。
  雖然因為C++編譯器對symbol進行修飾的原因不能直接用def文件聲明導出類和顯式鏈接,但是可以用另外一種取巧的方式。

  在頭文件中類的聲明中添加一個友元函數:
    friend DLLClass* CreatDLLClass();
  然後聲明CreatDLLClass()為導出函數,通過調用該函數返回一個DLLClass類的對象,同樣達到了導出類的目的。
  這樣,就可以用顯式鏈接來調用CreatDLLClass(),從而得到類對象了。

 

5、使用DLL動態庫在進程間共用數據

  在Win16環境中,DLL的全局數據對每個載入它的進程來說都是相同的,因為所有的進程用的都收同一塊地址空間;而在Win32環境中,情況卻發生了變化,每個進程都有了它自己的地址空間,DLL函數中的代碼所創建的任何對象(包括變數)都歸調用它的進程所有。當進程在載入DLL時,操作系統自動把DLL地址映射到該進程的私有空間,也就是進程的虛擬地址空間,而且也複製該DLL的全局數據的一份拷貝到該進程空間。(在物理記憶體中,多進程載入DLL時,DLL的代碼段實際上是只載入了一次,只是將物理地址映射到了各個調用它的進程的虛擬地址空間中,而全局數據會在每個進程都分別載入)。也就是說每個進程所擁有的相同的DLL的全局數據,它們的名稱相同,但其值卻並不一定是相同的,而且是互不幹涉的。
因此,在Win32環境下要想在多個進程中共用數據,就必須進行必要的設置。在訪問同一個Dll的各進程之間共用存儲器是通過存儲器映射文件技術實現的。也可以把這些需要共用的數據分離出來,放置在一個獨立的數據段里,並把該段的屬性設置為共用。必須給這些變數賦初值,否則編譯器會把沒有賦初始值的變數放在一個叫未被初始化的數據段中。

  在DLL的實現文件中添加下列代碼:

1 #pragma data_seg("DLLSharedSection")      // 聲明共用數據段,並命名該數據段
2    int SharedData = 123;       // 必須在定義的同時進行初始化!!!!
3 #pragma data_seg()

  在#pragma data_seg("DLLSharedSection")和#pragma data_seg()之間的所有變數將被訪問該Dll的所有進程看到和共用。僅定義一個數據段還不能達到共用數據的目的,還要告訴編譯器該段的屬性,有三種方法可以實現該目的(其效果是相同的),一種方法是在.DEF文件中加入如下語句:

1 SETCTIONS
2     DLLSharedSection READ WRITE SHARED

  另一種方法是在項目設置的鏈接選項(Project Setting --〉Link)中加入如下語句:

1 /SECTION:DLLSharedSection,rws

  還有一種就是使用指令:

1 #pragma comment(linker,"/section:.DLLSharedSection,rws")

  那麼這個數據節中的數據可以在所有DLL的實例之間共用了。所有對這些數據的操作都針對同一個實例的,而不是在每個進程的地址空間中都有一份。

    當進程隱式或顯式調用一個動態庫里的函數時,系統都要把這個動態庫映射到這個進程的虛擬地址空間里。這使得DLL成為進程的一部分,以這個進程的身份執行,使用這個進程的堆棧。

  下麵來談一下在具體使用共用數據段時需要註意的一些問題:

·         所有在共用數據段中的變數,只有在數據段中經過了初始化之後,才會是進程間共用的。如果沒有初始化,那麼進程間訪問該變數則是未定義的。
·         所有的共用變數都要放置在共用數據段中。如何定義很大的數組,那麼也會導致很大的DLL。
·         不要在共用數據段中存放進程相關的信息。Win32中大多數的數據結構和值(比如HANDLE)只在特定的進程上下文中才是有效地。
·         每個進程都有它自己的地址空間。因此不要在共用數據段中共用指針,指針指向的地址在不同的地址空間中是不一樣的。
·         DLL在每個進程中是被映射在不同的虛擬地址空間中的,因此函數指針也是不安全的。

  當然還有其它的方法來進行進程間的數據共用,比如文件記憶體映射等,這就涉及到通用的進程間通信了,這裡就不多講了。

轉自:http://www.cppblog.com/suiaiguo/archive/2009/07/20/90619.html  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • * Request 和 Response 原理: * request對象和response對象由伺服器創建,我們只需要在service方法中使用這兩個對象即可 * 繼承體繫結構: ServletRequest <--繼承-- HttpServletRequest <--實現--- RequestFa
  • 00前言 我相信能夠輕鬆地構建高質量增長的web應用程式是至關重要的一個自由和開放的社會。這可以防止玩家最大的壟斷信息的流通。 因此我從2007年開始web2py項目,主要是作為一種教學工具與簡化web開發的目標,更快,更安全。隨著時間的流逝,它已經成功地贏得成千上萬的情感知識淵博的用戶和數百名開發
  • 1 public class Stack { 2 private int maxSize=2;//棧容量,初始為2,(用於表達式求值,操作數棧) 3 private int top=-1;//棧頂指針 4 private int[] data=new int[maxSize];//數據 5 //判空
  • foreach迴圈時動態往數組裡添加數據,有一次做項目中,foreach的時候需要動態往數組裡添加數據(我們這裡隨便舉個例子) 結果: 哎?奇了怪了,這說明foreach迴圈時可以動態的往數組裡添加數據,為什麼$arr的數據確實被添加上了,但是沒有被foreach迴圈出來呢?網上查找得知,forea
  • zookeeper結合PropertyPlaceholderConfigurer實現的統一配置組件,巧妙的應用了PropertyPlaceholderConfigurer搜索多種數據源的優勢,且對原有代碼沒有任務的侵入性。
  • 語法 awk [ -F re] [parameter...] ['pattern {action}' ] [-f progfile][in_file...] 獲得普通外部變數 [xingxing.dxx@30_28_6_20 ~]$ test='test code' [xingxing.dxx@30
  • 漢字元在IntelliJ的控制台輸出亂碼。編譯器在編譯的時候,把漢字元編譯成非UTF-8而引起亂碼。我是在做Jsoup解析的時候出現的錯誤,其實歸根結底確實編譯器的原因。 解決方法: 1.修改.idea/encoding.xml。將對應工程的編碼方式(如GBK)改為UTF-8; 2.如果是Maven
  • 1, Swift 修改導航欄顏色 self.navigationController?.navigationBar.barTintColor 2, Swift button 屬性設置時直接進行初始化 var leftButton : UIButton = UIButton(type: UIButto
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...