天天看點

BCC(Borland C++ Compiler)編譯 ISAPI 擴充或者用MinGW也行

今天突發神經,要寫個ISAPI擴充,找了一下編譯器,發現VS 2017的大小>VS 2015 > VS 2013 > VS 2010 > VS 6 & SP6>BCC,于是下了個BCC,很久沒用過BCC,不知道它居然更新了,從2000年的Borland C++ 5.5.1更新到了2016年的Embarcadero C++ 7.20,編譯器名稱也改了。

bcc32.exe -> bcc32c.exe

brcc32.exe -> rc.exe

是以在Code::Blocks裡面,Auto-detect不了7.2版本的,隻有再下了個5.5的。下載下傳完了,發現忘記怎麼寫ISAPI了,幸好以前的馬甲在CSDN裡留了個BCC編譯ISAPI的文章,還能baidu到,于是照本宣科,結果卻編譯不了,經過N次調試,總算成功了,于是總結一下以免備忘,萬一以後還要BCC寫ISAPI呢。

1,下載下傳Borland C++ Compiler 5.5,解壓到D:\Borland\BCC55

2、下載下傳Code::Blocks 16.01,安裝完後打開CodeBlocks,先不管預設編譯器,進入IDE界面

打開菜單Settings-Compiler...

Selected Compiler選擇Borland C++ Compiler (5.5 - 5.82)

Search Directories 頁籤裡,Compiler添加D:\Borland\BCC55\Include,Linker添加D:\Borland\BCC55\Lib和D:\Borland\BCC55\Lib\PSDK

Toolchain executables 頁籤裡,Compiler's installation directory輸入D:\Borland\BCC55

Other settings 頁籤裡,點選Advanced options,彈出一個視窗

Commands 頁籤裡,Command選擇Link object files to dynamic library,Command line macro裡将宏改一下,添加一個$def_input變量

原來的宏:$linker -q $libdirs -Tpd $link_options $link_objects,$exe_output,,$libs,,$link_resobjects

改變的宏:$linker -q $libdirs -Tpd $link_options $link_objects,$exe_output,,$libs,$def_input,$link_resobjects

OK - OK,儲存設定

3,打開菜單File - New - Project...,選擇Shared Library,下一步,選擇C,下一步,填寫工程名,比如:test,下一步,OK

4,打開main.c,編寫代碼,其實我是複制微軟官網的代碼,改了一下, 能編譯通過就行

#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <httpext.h>

#ifndef   HSE_REQ_SET_FLUSH_FLAG
#define   HSE_REQ_SET_FLUSH_FLAG                   (HSE_REQ_END_RESERVED+43)
#endif

DWORD SendOutputToClient(IN EXTENSION_CONTROL_BLOCK  *pecb, IN DWORD msecDelay);
DWORD SendOutputToClient(IN EXTENSION_CONTROL_BLOCK  *pecb, IN DWORD msecDelay, char *szBuffering) ;

BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
    FILE *fp = fopen("e:\\www\\cgi-bin\\log.txt", "rw");
    if (fp) {
        fprintf(fp, "hello");
        fclose(fp);
    }
pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);

lstrcpyn((LPSTR) pVer->lpszExtensionDesc, "ISAPI Tester", HSE_MAX_EXT_DLL_NAME_LEN);

return TRUE;
}

DWORD WINAPI HttpExtensionProc(IN EXTENSION_CONTROL_BLOCK *pECB)
{

DWORD hseStatus;
DWORD msecDelay;
char *pszBuffering;

pszBuffering = "default (on)";
msecDelay=25;

if ( (char)*(pECB->lpszQueryString) != '\0' ){
pszBuffering="off";
    pECB->ServerSupportFunction (pECB->ConnID,
HSE_REQ_SET_FLUSH_FLAG,
(LPVOID) TRUE,
NULL,
NULL
);
}
hseStatus = SendOutputToClient(pECB, msecDelay, pszBuffering);

return hseStatus;
}

BOOL WINAPI TerminateExtension(IN DWORD dwFlags)
{
return TRUE;
}

DWORD SendHeaderToClient(IN EXTENSION_CONTROL_BLOCK *pecb, IN LPCSTR pszErrorMsg)
{
HSE_SEND_HEADER_EX_INFO SendHeaderExInfo;
char szStatus[] = "200 OK";
char szHeader[1024];

wsprintf(szHeader, "Content-Type: text/plain\r\n\r\n");

SendHeaderExInfo.pszStatus = szStatus;
SendHeaderExInfo.pszHeader = szHeader;
SendHeaderExInfo.cchStatus = lstrlen(szStatus);
SendHeaderExInfo.cchHeader = lstrlen(szHeader);
SendHeaderExInfo.fKeepConn = FALSE;

if (!pecb->ServerSupportFunction(pecb->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &SendHeaderExInfo, NULL, NULL))
   return HSE_STATUS_ERROR;

return HSE_STATUS_SUCCESS;
}

DWORD SendOutput(IN EXTENSION_CONTROL_BLOCK  *pecb, IN DWORD msecDelay) {

CHAR pchOutput[1024];
DWORD hseStatus = HSE_STATUS_SUCCESS;
int i;
DWORD len;

for( i=0; i < 10 ; i++ ) {
len = wsprintfA(pchOutput, "WriteClient output %d\n", i);
if ( !pecb->WriteClient(pecb->ConnID, pchOutput, &len, HSE_IO_SYNC) ){
hseStatus = HSE_STATUS_ERROR;
break;
}
Sleep(msecDelay);
}
return hseStatus;
}

DWORD SendOutputToClient(IN EXTENSION_CONTROL_BLOCK  *pecb, IN DWORD msecDelay, char *szBuffering) {
CHAR    pchBuffer[1024];
DWORD   hseStatus = HSE_STATUS_SUCCESS;

wsprintfA(pchBuffer, "WriteClient buffering %s", szBuffering);

hseStatus = SendHeaderToClient(pecb, pchBuffer);

if (hseStatus == HSE_STATUS_SUCCESS) {

hseStatus = SendOutput(pecb, msecDelay );

if (hseStatus != HSE_STATUS_SUCCESS) {

wsprintfA(pchBuffer, "Send Failed: Error (%d)<\br>", GetLastError());
SendHeaderToClient(pecb, pchBuffer);
}
}

pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_DONE_WITH_SESSION, NULL, NULL, NULL);

return (hseStatus);
}
           

5,工程根目錄下建立test.def檔案

LIBRARY "test"
EXPORTS
    GetExtensionVersion
    HttpExtensionProc
    TerminateExtension
           

6,菜單點選Project - Properties...

Build targets 頁籤裡,右邊大概中間位置的output filename将bin\debug\libtest.dll改為bin\debug\test.dll,然後Release版本的也這樣改

左邊中下位置,點選Build options,彈出視窗

新視窗的左邊清單框裡選中工程名,這樣debug和release版本都能用同樣的編譯選項了。

Compiler settings 頁籤裡,Compiler flags子頁籤中,Target組裡面選中.DLL executable (-tWD)和32-bit multi-threaded (-tWM)

Linker settings 頁籤裡,Link libraries裡,添加user32,添加kernel32,添加import32,添加cw32mt,cw32mt是靜态連結庫,cw32mti是動态連結庫,需要BCC的dll檔案才行,是以我選擇了cw32mt,将包含的函數編譯進工程裡,但這樣遠遠還不夠,還需要obj支援

于是other linker options裡面,添加

-Gi c0d32.obj

這個是動态連結庫支援檔案,沒有它cw32mt裡有兩個函數就無法連接配接,但在工程裡如果取消标準庫裡的一些函數,比如strcpy、strlen,cw32mt就可以不用,但是dll檔案會在IIS裡奔潰,同時将IIS程式池也搞奔潰掉,我擺渡了很久才找到這個選項

最後,Custom variables頁籤裡,添加key = def_input,value = test.def,這樣開始在Link object files to dynamic library的宏裡面設定的$def_input就有了執行個體

OK - OK,儲存設定

7,Ctrl + F9,工程進行Build,在bin\debug或者bin\release目錄下就生成了test.dll,将test.dll放入IIS裡設定的可執行目錄下,就能運作看到結果了。

——————追加:第二天繼續MinGW編譯ISAPI的測試——————

因為機器裡已經安裝了Cygwin和MSYS2,是以首先用Cygwin裡的gcc編譯項目,gcc版本6.40,編譯項目成功,但是用VS的dumpbin -exports dll檔案卻找不到函數位址

于是使用MSYS2,并在MSYS2模拟器中安裝了gcc 7.2,編譯 項目成功,同樣dumpbin找不到函數位址

dumpbin是VS的dll工具,就是說它能找到位址,那麼IIS同樣也能找到位址,它找不到IIS也找不到,是以在IIS中,ISAPI擴充會傳回500錯誤

這個問題把我折騰慘了,最後鬼使神差下載下傳了單獨版本的MinGW,安裝完GCC後,編譯項目成功,用dumpbin也能找到函數位址

于是對比了一下,發現GCC線程模式不一樣,Cygwin和MSYS2裡的gcc都是posix線程,而MinGW裡的卻是win32線程,是以能用,總算吸取了教訓,漲了點姿勢。

指令行編譯

gcc -c test.c
dllwrap --def test.def -o test.dll test.o
dumpbin -exports test.dll
           

如果是用CodeBlocks,那麼還是要更改一下配置

Settings-Compiler,選擇GNU GCC Compiler,Other settings-Advanced options

Commands 頁籤裡,Command選擇Link object files to dynamic library,Command line macro裡将宏改一下,在$exe_output後面添加一個$def_input變量

原宏:$linker -shared -Wl,--output-def=$def_output -Wl,--out-implib=$static_output -Wl,--dll $libdirs $link_objects $link_resobjects -o $exe_output $link_options $libs

現宏:$linker -shared -Wl,--output-def=$def_output -Wl,--out-implib=$static_output -Wl,--dll $libdirs $link_objects $link_resobjects -o $exe_output $def_input $link_options $libs

因為IDE預設是g++最終生成DLL,是以不知為何會帶上一個動态連結庫,使用dumpbin檢視,是一個名為libgcc_s_dw2-1.dll的東東,而且附帶兩個函數:

25  __deregister_frame_info

6A  __register_frame_info

于是Git-bash進入${MINGW_LIB},運作

grep -iaR "__deregister_frame_info" .

最終找到是${MINGW_LIB}/gcc/mingw32/6.3.0/libgcc_eh.a這個檔案所持有的函數

于是點選Project - Build options,選中工程名,Linker settings的Other linker options裡添加-static-libgcc

重新編譯工程,dumpbin發現,libgcc_s_dw2-1.dll不見了,好了,就這樣吧,寫完手工。

繼續閱讀