今天突发神经,要写个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不见了,好了,就这样吧,写完手工。