• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 298  文章 - 377  trackbacks - 0
            <2025年6月>
            25262728293031
            1234567
            891011121314
            15161718192021
            22232425262728
            293012345

            常用鏈接

            留言簿(34)

            隨筆分類

            隨筆檔案

            文章檔案

            相冊

            收藏夾

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            SPI
            隨著WOSA模型的出現,在Ws2_32.dll和協議堆棧之間多了一層開放的接口,就是SPI。Winsock2 SPI和Winsock2 API在性質上是一樣的,只是他們的服務對象不同,API提供的接口工作在應用層的上層,為應用程序提供接口,在Winsock之上,而SPI提供的接口工作在應用層的最底層,為核心的網絡服務提供接口,在Winsock之下。如果按照OSI分層標準來劃分,SPI應該是工作在會話層,API工作在應用層。如果有人非要象我一開始這樣,非要分出個一二三四的話,這樣的理解或許能讓你得到一點滿足,當然這種理解不是完全正確的,但是至少在層次的先后上,這樣的理解應該是正確的。我們的工作逐漸進入了OSI的下層,要學習,就要多費一些口舌了。

            SPI的上沿是Ws2_32.dll,而它的下沿是協議堆棧和名字空間,當然分層服務可以多層疊放,擴展上下沿。這里講述向協議堆棧的服務。SPI包含兩種服務,一種是基礎服務,一種是分層服務。

            SPI實例實際上就是一個動態庫,開放了一個初始化函數:WSPStartup 或者 NSPStartup。其他函數的指針通過一個函數分配表在此初始化函數中指定給Ws2_32.dll。Ws2_32.dll在需要的時候將相應的服務/協議實現模塊裝載入內存,在不需要的時候卸載。

            一般來說,當一個應用程序調用 Winsock2 API 函數的時候,Winsock2 都會調用相應的 Winsock2 SPI 函數。如 WSPAccept, WSPConnect, etc. 下述特例不經過SPI:
            htonl,htons,ntohs,ntohl,inet_addr,inet_ntoa,gethostname,getXbyY,WSAAsnyGetXByY,wsacANCELaSYNCrEQUEST,WSAEnumProtocols,WSAIsBlocking,WSASetBlocking,WSAUnhookBlocking,WSAGetLastError,WSASetLastError,WSACreateEvent,WSACloseEvent,WSASetEvent,WSAResetEvent,WSAWaitForMultipleEvents.SPI的函數原型定義在ws2spi.h中,前綴都有WSP,NSP,WPU,WSC,有興趣可以瀏覽一下。相應的對應函數有30個。都會在WSPStartup的實現中得到這些函數指針。

            要注意的是:WSPStartup并不是在WSAStartup調用時被調用的,一旦要WSPStartup的時候也就是說明需要用到服務提供者了,那么Ws2_32什么時候加載服務提供者呢?應用程序創建了套接字的時候,Ws2_32就根據套接字的地址家族,類型,協議信息等加載相應的提供者,這時候,WSPStartup就會被調用,服務提供者就開始調度它的傳輸函數等等內容了。

            現在有個問題,既然我們知道Windows Sockets動態庫和協議堆棧之間沒有直接聯系了,那么,如果我的應用程序想要調用我的SPI實例的擴展函數的時候那該怎么辦呢?Ws2_32.dll不可能再擴展專門的函數調用,讓他按照規范再去調用協議堆棧提供者的擴展函數。其實,Ws2_32.dll提供了一個函數來解決這個額外的擴展問題,那就是WSAIoctl函數,通過命令碼 SIO_GET_EXTENSION_FUNCTION_POINTER,輸入緩沖區是擴展函數的標識符,輸出參數就是該函數的指針了。那么,應用程序就可以直接跳過Ws2_32.dll而直接調用擴展函數。

            在動手編寫SPI實例之前,我們需要了解一下基礎服務和分層服務的區別以及系統如何標志這兩種服務,然后講述如何在系統中安裝一個自己的SPI實例,來擴展你的網絡傳輸功能。以及如何卸載自己的SPI實例,來恢復系統原來默認的設置。最后再來講述如何編寫SPI的實例。

            基礎服務執行核心的網絡傳輸協議功能,而分層服務在基礎服務的基礎上執行自定義的通訊控制,所有的真正的數據交換是通過基礎服務提供者來實現的,分層服務提供者無需再去實現網絡協議的功能。簡單的網絡封包的截取和管理可以在此進行。

            需要提及套接字句柄。當調用下層SPI的時候,如果調用WSPSocket,WSPAccept,WSPJoinLeaf時,服務提供者必須返回一個套接字句柄,Winsock2允許在此句柄上直接調用 ReadFile/WriteFile來讀寫數據。這個句柄有兩種類型:IFS(可安裝文件系統句柄)和 Non IFS(不可安裝的文件系統句柄)。微軟的基礎服務提供者都是IFS句柄,但是分層服務提供者可以是IFS,也可以是Non IFS。但是如果分層提供者是IFS提供者,就必須將下一級IFS提供者的句柄上傳到上一級提供者或者WS2_32.dll,此時對套接字的ReadFile/WriteFile調用會跳過分層服務提供者的WSPSend/WSPRecv函數而直接通過基礎服務提供者的功能進行數據讀寫,而且分層服務提供者將不能處理一個完成端口的重疊I/0操作,這些操作也將繞過分層提供者,而直接通過基礎提供者完成數據的讀寫。所以,最好將分層服務提供者定義為Non IFS句柄的服務提供者,這樣就可以對網絡數據進行完整的監控。具體做法是在協議信息結構中將dwServiceFlags1標志的XP1_IFS_HANDLES標志去掉。這樣,你的分層提供這就是一個Non IFS分層服務提供者,Non IFS服務提供者使用WSPCreateSocketHandle上調函數建立套接字句柄,Winsock2會把ReadFile/WriteFile的調用重定向到WSPSend和WSPReceive上去,但這無疑會對系統性能產生負面影響。

            Windows系統為服務提供者維護了一個目錄,這些信息保存在注冊表中,要更改/安裝服務提供者,就必須對此目錄信息進行維護。系統提供了一些函數簡化對此信息的訪問,他們都是以WSC開頭。

            對于基礎服務提供者的安裝相當簡單,只要準備一個WSAPROTOCOL_INFOW結構,用來代表基礎服務提供者信息,正確填充合適的值,然后調用WSCInstallProvider函數就可以完成基礎服務提供者的安裝。但是這種方法只對新增加的基礎服務提供者來講是很方便的,但是我們往往要利用系統的基礎服務提供者來實現基本的協議,比如說TCP協議的實現,所以在這種情況下,方便的方法是不通過WSCInstallProvider函數,而是我們自己修改目錄條目信息,對于基礎提供者,只要把基礎提供者的動態庫路徑信息改成我們自己虛擬的基礎服務提供者路徑就可以了。這樣安裝的后續工作必須是把所有調用傳給原來的基礎服務提供者,所以在我們虛擬的服務提供者程序中,必須能夠檢索到原來的服務提供者路徑信息。記住,安裝之前千萬要備份原來的目錄信息。否則,一旦發生錯誤會引起網絡無法訪問。

            基礎服務提供者安裝代碼實例如下所示:

            void CBSPinstallDlg::OnBtnInstall() 
            {
            // Update m_strFileName which is our mimic Base Provider filename
            UpdateData(TRUE);
            if(0==BackupBSP()) InstallBSP();
            }

            int CBSPinstallDlg::EnumRegisterKey(HKEY hKey,TCHAR* pszKeyName,TCHAR** ppBuf,DWORD* pdwBufSize)
            {
            HKEY hSubKey=NULL;
            if(RegOpenKeyEx(hKey,pszKeyName,0,KEY_ALL_ACCESS,&hSubKey)!=ERROR_SUCCESS) 
            return -1;
            TCHAR szKey[20]={'\0'};
            if(hKey==HKEY_CURRENT_CONFIG) strcpy(szKey,"HKEY_LOCAL_MACHINE"; else
            if(hKey==HKEY_CURRENT_USER) strcpy(szKey,"HKEY_CURRENT_USER"; else
            if(hKey==HKEY_LOCAL_MACHINE) strcpy(szKey,"HKEY_LOCAL_MACHINE"; else
            if(hKey==HKEY_USERS) strcpy(szKey,"HKEY_USERS"; else
            if(hKey==HKEY_PERFORMANCE_DATA) strcpy(szKey,"HKEY_PERFORMANCE_DATA"; else
            if(hKey==HKEY_DYN_DATA) strcpy(szKey,"HKEY_DYN_DATA"; else 
            return -1;

            int len=strlen(szKey)+strlen(pszKeyName)+5;
            TCHAR* pszItem=new TCHAR[len+1];
            memset(pszItem,0,len+1);
            sprintf(pszItem,"[%s\\%s]\r\n",szKey,pszKeyName);

            TCHAR* pBuf=*ppBuf;
            DWORD dwBufSize=*pdwBufSize;
            TCHAR* pTmp=new TCHAR[dwBufSize+len];
            memset(pTmp,0,dwBufSize+len);
            memmove(pTmp,pBuf,dwBufSize);
            memmove(pTmp+dwBufSize,pszItem,len);
            delete[] pszItem; pszItem=NULL;
            delete[] pBuf; pBuf=NULL;
            dwBufSize+=len;
            *ppBuf=pTmp;
            *pdwBufSize=dwBufSize;

            // "Num_Catalog_Entries"=dword:00000013
            // "Next_Catalog_Entry_ID"=dword:0000043e
            // "Serial_Access_Num"=dword:00000014
            DWORD cbClass=0;
            DWORD cSubKeys=0;
            DWORD cbMaxSubKeyLen=0;
            DWORD cbMaxClassLen=0;
            DWORD cValues=0;
            DWORD cbMaxValueNameLen=0;
            DWORD cbMaxValueLen=0;
            int nRet=RegQueryInfoKey(hSubKey,
            NULL,
            &cbClass,
            NULL,
            &cSubKeys,
            &cbMaxSubKeyLen,
            &cbMaxClassLen,
            &cValues,
            &cbMaxValueNameLen,
            &cbMaxValueLen,
            NULL,
            NULL);
            if(nRet!=ERROR_SUCCESS) 
            return -1;
            for(DWORD dwIndex=0;dwIndex<cValues;dwIndex++)
            {
            DWORD dwItemNameSize=cbMaxValueNameLen+1;
            TCHAR* pszItemName=new TCHAR[dwItemNameSize];
            memset(pszItemName,0,dwItemNameSize);
            DWORD dwDataSize=cbMaxValueLen+1;
            BYTE* pbyData=new BYTE[dwDataSize];
            memset(pbyData,0,dwDataSize);
            DWORD dwType=0;
            nRet=RegEnumValue(hSubKey,dwIndex,pszItemName,&dwItemNameSize,NULL,&dwType,pbyData,&dwDataSize);
            if(nRet!=ERROR_SUCCESS)
            {
            delete[] pszItemName; pszItemName=NULL;
            delete[] pbyData; pbyData=NULL;
            return -1;
            }
            TCHAR* pBuf=*ppBuf;
            DWORD dwBufSize=*pdwBufSize;
            if(dwDataSize>0)
            {
            TCHAR* szTmp=new TCHAR[dwDataSize*4+266];
            memset(szTmp,0,dwDataSize*4+266);
            szTmp[0]='"';
            int nPos=1;
            memmove(szTmp+nPos,pszItemName,strlen(pszItemName));
            nPos+=strlen(pszItemName);
            szTmp[nPos]='"';nPos++;
            szTmp[nPos]='=';nPos++;
            switch(dwType)
            {
            case REG_SZ:
            szTmp[nPos]='"';nPos++;
            memmove(szTmp,pbyData,dwDataSize);
            nPos+=dwDataSize;
            szTmp[nPos]='"';nPos++;
            szTmp[nPos]='\r';nPos++;
            szTmp[nPos]='\n';nPos++;
            break;
            case REG_DWORD:
            {
            char sz[20]="dword:\0";
            sprintf(sz,"%s%08x",sz,*(DWORD*)pbyData);
            memmove(szTmp+nPos,sz,strlen(sz));
            nPos+=strlen(sz);
            }
            break;
            case REG_BINARY:
            {
            char sz[20]="hex:\0";
            memmove(szTmp+nPos,sz,strlen(sz));
            nPos+=strlen(sz);
            int j=0;
            for(UINT i=0;i<dwDataSize;i++)
            {
            if(( (j==0 && nPos%78==0) || \
             ( j>0 && (nPos-81-(j-1)*80)%77==0) \
                && i<dwDataSize-1)
            {
            j++;
            szTmp[nPos]='\\';nPos++;
            szTmp[nPos]='\r';nPos++;
            szTmp[nPos]='\n';nPos++;
            if(j>0)
            {
            szTmp[nPos]=' ';nPos++;
            szTmp[nPos]=' ';nPos++;
            }
            }
            if(i<dwDataSize-1)
            {
            TCHAR szAppend[20]={'\0'};
            sprintf(szAppend,"%02x,",pbyData[i]);
            strcat(szTmp,szAppend);
            nPos+=3;
            }
            else
            {
            TCHAR szAppend[20]={'\0'};
            sprintf(szAppend,"%02x",pbyData[i]);
            strcat(szTmp,szAppend);
            nPos+=2;
            }
            }
            }
            default:
            break;
            }
            delete[] pszItemName; pszItemName=NULL;
            delete[] pbyData; pbyData=NULL;
            szTmp[nPos]='\r';nPos++;
            szTmp[nPos]='\n';nPos++;
            TCHAR* pTmp=new TCHAR[dwBufSize+nPos];
            memset(pTmp,0,dwBufSize+nPos);
            memmove(pTmp,pBuf,dwBufSize);
            memmove(pTmp+dwBufSize,szTmp,nPos);
            delete[] szTmp;szTmp=NULL;
            delete[] pBuf;pBuf=NULL;
            dwBufSize+=nPos;
            *ppBuf=pTmp;
            *pdwBufSize=dwBufSize;
            }
            if(pszItemName) delete[] pszItemName; pszItemName=NULL;
            if(pbyData) delete[] pbyData; pbyData=NULL;
            }

            {
            TCHAR* pBuf=*ppBuf;
            DWORD dwBufSize=*pdwBufSize;
            TCHAR* pTmp=new TCHAR[dwBufSize+2];
            memset(pTmp,0,dwBufSize+2);
            memmove(pTmp,pBuf,dwBufSize);
            pTmp[dwBufSize]='\r';
            pTmp[dwBufSize+1]='\n';
            *ppBuf=pTmp;
            *pdwBufSize=dwBufSize+2;
            delete[] pBuf; pBuf=NULL;
            }

            DWORD dwSubKeyLen=cbMaxSubKeyLen+1;
            // [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters\Protocol_Catalog9\Catalog_Entries]
            for(dwIndex=0;dwIndex<cSubKeys;dwIndex++)
            {
            TCHAR* szKeyName=new TCHAR[dwSubKeyLen];
            memset(szKeyName,0,dwSubKeyLen);
            int nRet=RegEnumKey(hSubKey,dwIndex,szKeyName,dwSubKeyLen);
            if(nRet!=ERROR_SUCCESS)
            {
            delete[] szKeyName; szKeyName=NULL;
            return -1;
            }
            TCHAR szKeyNameAnother[266]={'\0'};
            wsprintf(szKeyNameAnother,"%s\\%s",pszKeyName,szKeyName);
            delete[] szKeyName; szKeyName=NULL;
            nRet=EnumRegisterKey(HKEY_LOCAL_MACHINE,szKeyNameAnother,ppBuf,pdwBufSize);
            if(nRet==-1) return -1;
            }

            RegCloseKey(hSubKey);
            return 0;
            }

            int CBSPinstallDlg::BackupBSP()
            {
            TCHAR szSysDir[266]={'\0'};
            GetWindowsDirectory(szSysDir,266);
            TCHAR szBackupFile[266]={'\0'};
            sprintf(szBackupFile,"%s\\MinBSP.reg",szSysDir);
            CFileStatus status;
            if(CFile::GetStatus(szBackupFile,status))
            {
            if(IDNO==::MessageBox(NULL,"你已經安裝了基礎服務提供者,是不是還要繼續安裝?","確認",MB_YESNO))
            {
            PostQuitMessage(0);
            return -1;
            }
            }

            if(m_strFileName.GetLength()==0)
            {
            AfxMessageBox("請指定基礎服務提供者。";
            m_edtFileName.SetFocus();
            m_edtFileName.SetSel(0,-1);
            return -1;
            }

            // 開始安裝
            // 首先備份原來的服務提供者目錄條目信息
            CFile f;
            if(f.Open(szBackupFile,CFile::modeCreate|CFile::modeReadWrite))
            {
            TCHAR szTmp[266]="Windows Registry Editor Version 5.00\r\n\r\n\0";
            DWORD dwBufSize=strlen(szTmp);
            TCHAR* lpBuf=new TCHAR[dwBufSize];
            memset(lpBuf,0,dwBufSize);
            memmove(lpBuf,szTmp,dwBufSize);

            TCHAR szKeyName[1024]="SYSTEM\\CurrentControlSet\\Services\\WinSock2\\Parameters\\Protocol_Catalog9\0";
            int nRet=EnumRegisterKey(HKEY_LOCAL_MACHINE,szKeyName,&lpBuf,&dwBufSize);
            if(nRet==-1) {f.Close();delete[] lpBuf;lpBuf=NULL;return -1;}

            nRet=dwBufSize*2+2;
            WCHAR* pwszResult=new WCHAR[nRet];
            memset(pwszResult,0,nRet);
            nRet=MultiByteToWideChar(CP_ACP,0,lpBuf,dwBufSize,pwszResult,nRet);
            BYTE by=0xFF;
            f.Write(&by,1);
            by=0xFE;
            f.Write(&by,1);
            f.Write(pwszResult,nRet*2);
            f.Close();
            delete[] pwszResult; pwszResult=NULL;
            delete[] lpBuf;lpBuf=NULL;
            }
            return 0;
            }

            void CBSPinstallDlg::InstallBSP()
            {
            // 備份完成
            // 接下來,更改系統原來的基礎服務提供者目錄條目信息為我的基礎提供者信息
            // 枚舉協議
            LPWSAPROTOCOL_INFOW lpProtocolInfo=NULL;
            DWORD dwProtocolInfoSize=0;
            int nErrorCode=0;
            int nRet=WSCEnumProtocols(NULL,lpProtocolInfo,&dwProtocolInfoSize,&nErrorCode);
            if(nRet==SOCKET_ERROR && nErrorCode!=WSAENOBUFS)
            {
            TRACE1("WSCEnumProtocols() returns error: %d\n",nErrorCode);
            return;
            }
            lpProtocolInfo=(LPWSAPROTOCOL_INFOW) new BYTE[dwProtocolInfoSize];
            memset(lpProtocolInfo,0,dwProtocolInfoSize);
            __try
            {
            // 取得協議信息
            int nTotalProtocols=WSCEnumProtocols(NULL,lpProtocolInfo,&dwProtocolInfoSize,&nErrorCode);
            if(nTotalProtocols==SOCKET_ERROR) return;
            // 查找TCP/IP協議信息
            TCHAR szTCPIP[20]="[TCP/IP]\0";
            TCHAR szTCP95[20]=".TCP\0";
            TCHAR szProtocol[MAX_PATH]={'\0'};
            TCHAR szKeyName[]="SYSTEM\\CurrentControlSet\\Services\\WinSock2\\Parameters\\Protocol_Catalog9\\Catalog_Entries\0";
            HKEY hSubKey=NULL;
            if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,szKeyName,0,KEY_ALL_ACCESS,&hSubKey)!=ERROR_SUCCESS) return;
            for(int i=0;i<nTotalProtocols;i++)
            {
            memset(szProtocol,0,MAX_PATH);
            WCHAR* pwsz=(lpProtocolInfo+i)->szProtocol;
            BSTR bstr=SysAllocString(pwsz);
            WideCharToMultiByte(CP_ACP,0,bstr,-1,szProtocol,255,NULL,NULL);
            SysFreeString(bstr);
            // if(strstr(szProtocol,szTCPIP)!=NULL || strstr(szProtocol,szTCP95)!=NULL) break;
            if((lpProtocolInfo+i)->ProtocolChain.ChainLen==1)
            {
            // 更改基礎服務提供者信息
            TCHAR szSubKeyName[MAX_PATH]={'\0'};
            int nRet=RegEnumKey(hSubKey,i,szSubKeyName,MAX_PATH);
            if(nRet!=ERROR_SUCCESS) return;
            HKEY hSubKeySub=NULL;
            DWORD dwSubKeyLen=strlen(szKeyName);
            TCHAR* pszKeyName=new TCHAR[dwSubKeyLen+strlen(szSubKeyName)+2];
            memset(pszKeyName,0,dwSubKeyLen+strlen(szSubKeyName)+2);
            wsprintf(pszKeyName,"%s\\%s",szKeyName,szSubKeyName);
            if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,pszKeyName,0,KEY_ALL_ACCESS,&hSubKeySub)!=ERROR_SUCCESS)
            {
            if(pszKeyName) delete[] pszKeyName; pszKeyName=NULL;
            return;
            }
            DWORD cbClass=0;
            DWORD cSubKeys=0;
            DWORD cbMaxSubKeyLen=0;
            DWORD cbMaxClassLen=0;
            DWORD cValues=0;
            DWORD cbMaxValueNameLen=0;
            DWORD cbMaxValueLen=0;
            nRet=RegQueryInfoKey(hSubKeySub,
            NULL,
            &cbClass,
            NULL,
            &cSubKeys,
            &cbMaxSubKeyLen,
            &cbMaxClassLen,
            &cValues,
            &cbMaxValueNameLen,
            &cbMaxValueLen,
            NULL,
            NULL);
            if(nRet!=ERROR_SUCCESS) 
            {
            cbMaxValueNameLen=MAX_PATH;
            cbMaxValueLen=MAX_PATH+sizeof(WSAPROTOCOL_INFOW)+2;
            }
            DWORD dwItemNameSize=cbMaxValueNameLen+1;
            TCHAR* pszItemName=new TCHAR[dwItemNameSize];
            memset(pszItemName,0,dwItemNameSize);
            DWORD dwDataSize=cbMaxValueLen+1;
            BYTE* pbyData=new BYTE[dwDataSize];
            memset(pbyData,0,dwDataSize);
            DWORD dwType=0;
            nRet=RegEnumValue(hSubKeySub,0,pszItemName,&dwItemNameSize,NULL,&dwType,pbyData,&dwDataSize);
            if(nRet!=ERROR_SUCCESS)
            {
            if(pszKeyName) delete[] pszKeyName; pszKeyName=NULL;
            if(pszItemName) delete[] pszItemName; pszItemName=NULL;
            if(pbyData) delete[] pbyData; pbyData=NULL;
            return;
            }
            memset(pbyData,0,MAX_PATH);
            TCHAR szDllPath[MAX_PATH+2];
            wsprintf(szDllPath,"%%SystemRoot%%\\System32\\%s",m_strFileName);
            int nLen=strlen(szDllPath);
            memmove(pbyData,szDllPath,nLen);
            nRet=RegSetValueEx(hSubKeySub,pszItemName,NULL,dwType,pbyData,dwDataSize);
            if(nRet!=ERROR_SUCCESS)
            {
            if(pszKeyName) delete[] pszKeyName; pszKeyName=NULL;
            if(pszItemName) delete[] pszItemName; pszItemName=NULL;
            if(pbyData) delete[] pbyData; pbyData=NULL;
            return;
            }
            if(pszKeyName) delete[] pszKeyName; pszKeyName=NULL;
            if(pszItemName) delete[] pszItemName; pszItemName=NULL;
            if(pbyData) delete[] pbyData; pbyData=NULL;
            }
            }

            if(i<nTotalProtocols) TRACE1("TCP/IP 協議信息在目錄條目中的第 %d 項。\n",i+1);
            else TRACE("無法根據協議描述判斷TCP/IP協議信息。\n";
            }
            __finally
            {
            if(lpProtocolInfo) delete[] lpProtocolInfo;
            lpProtocolInfo=NULL;
            }
            }

            而對于分層服務提供者而言,安裝要復雜一點。他需要建立兩個WSAPROTOCOL_INFOW結構,一個代表分層提供者(ChainLen==0),另一個代表協議鏈(ChainLen>1),利用該協議鏈把分層服務提供者和基礎服務提供者連接起來。
            分層服務提供者安裝的具體的步驟,概括起來大致分為五步:
            第一,初始化兩個協議信息結構,初始化可以借助于WSCEnumProtocols函數取回的信息來完成;
            第二,用WSCInstallProvider調用來安裝你的分層服務提供者;
            第三,在第二步成功的基礎上,列舉所有的目錄條目,獲得新安裝的分層提供者的目錄編號。并利用這個目錄信息來設置一個協議鏈的目錄信息,通過這個協議鏈,將你的分層提供者和基礎服務提供者(或者是它下一層的分層協議提供者)連接起來。
            第四,調用WSCInstallProvider安裝這個協議鏈。
            第五,分層服務提供者安裝成功后,Windows會在服務提供者目錄的最后加入這個新的條目。但是我們為了讓我們的分層服務提供者成為系統默認的TCP服務提供者的話,就必須對目錄進行重新排列,讓新安裝的協議鏈目錄條目放在最上面。這些排序過程通過調用WSCWriteProviderOrder完成,該函數將所有的服務提供者重新排序。需要包含 <sporder.h>頭文件和sporder.lib庫。

            以下是單純針對TCP協議(其他協議處理方法雷同)的分層服務提供者示例:

            int CLSPinstallDlg::EnumProtocols(LPWSAPROTOCOL_INFOW* lppInfo,DWORD& dwProtocolInfoSize,int& nTotalProtocols)
            {
            LPWSAPROTOCOL_INFOW lpProtocolInfo=*lppInfo;
            if(lpProtocolInfo!=NULL) delete[] lpProtocolInfo; lpProtocolInfo=NULL;
            int nErrorCode=0;
            int nRet=WSCEnumProtocols(NULL,lpProtocolInfo,&dwProtocolInfoSize,&nErrorCode);
            if(nRet==SOCKET_ERROR&&nErrorCode!=WSAENOBUFS) return nErrorCode;
            lpProtocolInfo = (LPWSAPROTOCOL_INFOW)new BYTE[dwProtocolInfoSize];
            ASSERT(lpProtocolInfo!=NULL);
            nTotalProtocols = WSCEnumProtocols(NULL,lpProtocolInfo,&dwProtocolInfoSize,&nErrorCode);
            if(nTotalProtocols== SOCKET_ERROR) return -1;
            *lppInfo=lpProtocolInfo;

            return 0;
            }

            void CLSPinstallDlg::InstallLSP()
            {
            LPWSAPROTOCOL_INFOW lpProtocolInfo=NULL;
            DWORD dwProtocolInfoSize=0;
            int nTotalProtocols=0;
            _try
            {
            // 第一步,初始化兩個協議信息結構,初始化可以借助于WSCEnumProtocols函數取回的信息來完成;
            int nRet=EnumProtocols(&lpProtocolInfo,dwProtocolInfoSize,nTotalProtocols);
            if(nRet!=0)
            {
            TRACE1("WSCEnumProtocols() returns error: %d\n",nRet);
            return;
            }
            DWORD dwLayeredCatalogId,dwTcpCatalogId;
            WSAPROTOCOL_INFOW stuLayeredInfo,stuTcpChainInfo;
            for(int i=0;i<nTotalProtocols;i++)
            {
            if(lpProtocolInfo[i].iAddressFamily == AF_INET && lpProtocolInfo[i].iProtocol == IPPROTO_TCP)
            {
            dwTcpCatalogId = lpProtocolInfo[i].dwCatalogEntryId;
            memmove(&stuTcpChainInfo, &lpProtocolInfo[i], sizeof(WSAPROTOCOL_INFOW));
            memmove(&stuLayeredInfo, &lpProtocolInfo[i], sizeof(WSAPROTOCOL_INFOW));
            stuTcpChainInfo.dwServiceFlags1 = lpProtocolInfo[i].dwServiceFlags1 & ~XP1_IFS_HANDLES; 
            break;
            }
            }

            // 第二步,用WSCInstallProvider調用來安裝你的分層服務提供者;
            wcscpy(stuLayeredInfo.szProtocol, L"Mini Layered Provider";
            stuLayeredInfo.ProtocolChain.ChainLen = LAYERED_PROTOCOL;
            WCHAR wszDllPath[MAX_PATH*2+2]={'\0'};
            swprintf(wszDllPath,L"%%SystemRoot%%\\System32\\%s",m_strFileName);
            int nErrorCode=0;
            nRet=WSCInstallProvider(&guidMiniProvider,wszDllPath,&stuLayeredInfo,1,&nErrorCode);
            if(nRet==SOCKET_ERROR)
            {
            printf("WSCInstallProvider failed %d\n", nErrorCode);
            return;
            }

            // 第三步,在第二步成功的基礎上,列舉所有的目錄條目,獲得新安裝的分層提供者的目錄編號。
            // 并利用這個目錄信息來設置一個協議鏈的目錄信息,通過這個協議鏈,將分層提供者和基礎服務提供者
            // (或者是它下一層的分層協議提供者)連接起來。
            nRet=EnumProtocols(&lpProtocolInfo,dwProtocolInfoSize,nTotalProtocols);
            if(nRet!=0)
            {
            TRACE1("WSCEnumProtocols() returns error: %d\n",nRet);
            return;
            }
            for (i=0;i<nTotalProtocols;i++)
            {
            if(memcmp(&lpProtocolInfo[i].ProviderId, &guidMiniProvider,sizeof (GUID))==0)
            {
            dwLayeredCatalogId = lpProtocolInfo[i].dwCatalogEntryId;
            break;
            }
            }
            WCHAR wszChainName[MAX_PATH*2+2]={'\0'};
            swprintf(wszChainName, L"Mini Layered TCP [%s]", stuTcpChainInfo.szProtocol);
            wcscpy(stuTcpChainInfo.szProtocol,wszChainName);
            if (stuTcpChainInfo.ProtocolChain.ChainLen==BASE_PROTOCOL)
            {
            stuTcpChainInfo.ProtocolChain.ChainEntries[1] = dwTcpCatalogId;
            }
            else
            {
            for (i=stuTcpChainInfo.ProtocolChain.ChainLen;i>0;i--)
            {
            stuTcpChainInfo.ProtocolChain.ChainEntries[i+1] = stuTcpChainInfo.ProtocolChain.ChainEntries[i];
            }
            }
            stuTcpChainInfo.ProtocolChain.ChainLen++;
            stuTcpChainInfo.ProtocolChain.ChainEntries[0] = dwLayeredCatalogId;

            // 第四步,調用WSCInstallProvider安裝這個協議鏈
            nRet=WSCInstallProvider(&guidMiniProviderChain,wszDllPath,&stuTcpChainInfo,1,&nErrorCode);
            if(nRet==SOCKET_ERROR)
            {
            printf("WSCInstallProvider for protocol chain failed %d\n", nErrorCode);
            return;
            }

            // 第五步,安裝成功后將所有的服務提供者重新排序。
            LPDWORD lpCatalogEntries=(LPDWORD)new BYTE[nTotalProtocols*sizeof(DWORD)];
            ASSERT(lpCatalogEntries!=NULL);
            memset(lpCatalogEntries,0,nTotalProtocols*sizeof(DWORD));
            DWORD dwIndex=0;
            for (i=0;i<nTotalProtocols;i++)
            {
            if(memcmp(&lpProtocolInfo[i].ProviderId, &guidMiniProvider, sizeof (GUID))==0 || \
               memcmp (&lpProtocolInfo[i].ProviderId, &guidMiniProviderChain, sizeof (GUID))==0)
            lpCatalogEntries[dwIndex++] = lpProtocolInfo[i].dwCatalogEntryId;
            }
            for (i=0;i<nTotalProtocols;i++)
            {
            if(memcmp (&lpProtocolInfo[i].ProviderId, &guidMiniProvider, sizeof (GUID))!=0 && \
               memcmp (&lpProtocolInfo[i].ProviderId, &guidMiniProviderChain, sizeof (GUID))!=0)
            lpCatalogEntries[dwIndex++]=lpProtocolInfo[i].dwCatalogEntryId;
            }
            nRet=WSCWriteProviderOrder(lpCatalogEntries,nTotalProtocols);
            delete[] lpCatalogEntries; lpCatalogEntries=NULL; 
            if(nRet!=ERROR_SUCCESS)
            {
            printf("WSCWriteProviderOrder failed %d\n", nErrorCode);
            return;
            }
            }
            __finally
            {
            if(lpProtocolInfo) delete[] lpProtocolInfo; lpProtocolInfo=NULL;
            }
            }

            好了,到目前,我們安裝了基礎服務提供者和分層服務提供者,到了該卸載他們的時候了,對于基礎服務提供者,簡單地恢復原來的目錄信息就可以了,對于分層服務提供者,調用WSCDeinstallProvider卸載函數可以簡單的卸載,但是對于分層服務提供者,一定要維護好協議鏈,把所有和你的分層服務提供者的協議鏈相關的信息都清除掉,恢復安裝之前的協議鏈狀態,免得發生協議鏈斷開的現象發生,如果斷開了,就會影響網絡的功能。Windows SPI在這方面存在缺陷,如果很多人裝了很多個分層服務提供者,那怎么辦呢?會很麻煩的。

            接下來,我們最后來看看怎么樣實現一個服務提供者的最基本的實例,我們可以試圖在這里對網絡封包進行截取。

            以下是基礎服務提供者實例,對于分層服務提供者,麻煩!我懶得寫了:

            int WSPAPI WSPStartup(
                WORD wVersion,
                LPWSPDATA lpWSPData,
                LPWSAPROTOCOL_INFOW lpProtocolInfo,
                WSPUPCALLTABLE UpCallTable,
                LPWSPPROC_TABLE lpProcTable)
            {

            int nTotalProtocols=0;
                int nProviderPathLen=MAX_PATH;
                TCHAR szLibraryPath[MAX_PATH]={'\0'};
                TCHAR szProviderPath[MAX_PATH]={'\0'};;
                LPWSAPROTOCOL_INFOW lpOrigProtocolInfo=NULL;
            theApp.m_hLibOrigBase=NULL;
                LPWSPSTARTUP WSPStartupFunc = NULL;

                EnterCriticalSection(&theApp.m_CriticalSection);
                if (!theApp.m_nEntityCount)
                {
            CFile f;
            TCHAR szFileName[MAX_PATH]={'\0'};
            GetWindowsDirectory(szFileName,MAX_PATH);
            sprintf(szFileName,"%s\\%s",szFileName,OLD_BSP_CATALOG);
            if(!f.Open(szFileName,CFile::modeRead)) return WSAEPROVIDERFAILEDINIT;
            DWORD dwLen=f.GetLength();
            DWORD dwSize=MAX_PATH+sizeof(WSAPROTOCOL_INFOW);
            nTotalProtocols=dwLen/dwSize;
            lpOrigProtocolInfo=new WSAPROTOCOL_INFOW[nTotalProtocols];
            for(int i=0;i<nTotalProtocols;i++)
            {
            f.Seek(dwSize*i+MAX_PATH,0);
            f.Read(&lpOrigProtocolInfo[i],sizeof(WSAPROTOCOL_INFOW));
            if(lpProtocolInfo->ProviderId==lpOrigProtocolInfo->ProviderId)
            {
            f.Seek(dwSize*i,0);
            f.Read(szProviderPath,MAX_PATH);
            break;
            }
            }
            f.Close();
            delete[] lpOrigProtocolInfo; lpOrigProtocolInfo=NULL;
            if(!ExpandEnvironmentStrings(szProviderPath,szLibraryPath,MAX_PATH)) return WSAEPROVIDERFAILEDINIT;

            theApp.m_hLibOrigBase=LoadLibrary(szLibraryPath);
            if(theApp.m_hLibOrigBase==NULL) return WSAEPROVIDERFAILEDINIT;

            WSPStartupFunc=(LPWSPSTARTUP)GetProcAddress(theApp.m_hLibOrigBase,"WSPStartup";
            if(WSPStartupFunc==NULL) return WSAEPROVIDERFAILEDINIT;

            int nRet=(*WSPStartupFunc)(wVersion,lpWSPData,lpProtocolInfo,UpCallTable,lpProcTable);
            if(nRet!=ERROR_SUCCESS) return nRet;

            memmove(&theApp.m_NextProcTable, lpProcTable, sizeof(WSPPROC_TABLE));

            lpProcTable->lpWSPAccept = WSPAccept;
            lpProcTable->lpWSPAddressToString = WSPAddressToString;
            lpProcTable->lpWSPAsyncSelect = WSPAsyncSelect;
            lpProcTable->lpWSPBind = WSPBind;
            lpProcTable->lpWSPCancelBlockingCall = WSPCancelBlockingCall;
            lpProcTable->lpWSPCleanup = WSPCleanup;
            lpProcTable->lpWSPCloseSocket = WSPCloseSocket;
            lpProcTable->lpWSPConnect = WSPConnect;
            lpProcTable->lpWSPDuplicateSocket = WSPDuplicateSocket;
            lpProcTable->lpWSPEnumNetworkEvents = WSPEnumNetworkEvents;
            lpProcTable->lpWSPEventSelect = WSPEventSelect;
            lpProcTable->lpWSPGetOverlappedResult = WSPGetOverlappedResult;
            lpProcTable->lpWSPGetPeerName = WSPGetPeerName;
            lpProcTable->lpWSPGetSockOpt = WSPGetSockOpt;
            lpProcTable->lpWSPGetSockName = WSPGetSockName;
            lpProcTable->lpWSPGetQOSByName = WSPGetQOSByName;
            lpProcTable->lpWSPIoctl = WSPIoctl;
            lpProcTable->lpWSPJoinLeaf = WSPJoinLeaf;
            lpProcTable->lpWSPListen = WSPListen;
            lpProcTable->lpWSPRecv = WSPRecv;
            lpProcTable->lpWSPRecvDisconnect = WSPRecvDisconnect;
            lpProcTable->lpWSPRecvFrom = WSPRecvFrom;
            lpProcTable->lpWSPSelect = WSPSelect;
            lpProcTable->lpWSPSend = WSPSend;
            lpProcTable->lpWSPSendDisconnect = WSPSendDisconnect;
            lpProcTable->lpWSPSendTo = WSPSendTo;
            lpProcTable->lpWSPSetSockOpt = WSPSetSockOpt;
            lpProcTable->lpWSPShutdown = WSPShutdown;
            lpProcTable->lpWSPSocket = WSPSocket;
            lpProcTable->lpWSPStringToAddress = WSPStringToAddress;

            theApp.m_lpWSPData = lpWSPData;
            theApp.m_lpProcTable = lpProcTable;
                }
            else
                {
                    lpWSPData = theApp.m_lpWSPData;
                    lpProcTable = theApp.m_lpProcTable;
                }
                theApp.m_nEntityCount++;
                LeaveCriticalSection(&theApp.m_CriticalSection);

               return 0;
            }

            int WSPAPI WSPCleanup (LPINT lpErrno)
            {
            int nRet=theApp.m_NextProcTable.lpWSPCleanup(lpErrno);

            EnterCriticalSection(&theApp.m_CriticalSection);
            theApp.m_nEntityCount--;
                if (theApp.m_nEntityCount == 0)
                {
                    FreeLibrary(theApp.m_hLibOrigBase);
                    theApp.m_hLibOrigBase = NULL;
                }
                LeaveCriticalSection(&theApp.m_CriticalSection);

            return nRet;
            }

            對于不同的I/0操作模式,需要進行不同的處理
            對于WSARecv,WSARecvFrom,WSASend,WSASendTo的重疊調用,保存重疊信息,重點是完成例程
            // 客戶程序如果用完成例程,那么將不能直接監視到收發的數據,為了解決這個問題
            // 我們可以在服務提供者內提供我們自己的完成例程截獲數據,在我們截獲了數據之后,
            // 再把控制權交還給客戶機的完成例程。
            //
            // 首先,保存重疊操作參數信息的結構
            typedef struct tagOVERLAPPED_RECORDER
            {
            DWORD                  dwType; // WSASend,WSASendTo,WSARecv,WSARecvFrom
                                               // 用來標志監視的數據操作方式
                SOCKET                 s,                                                 
                LPWSABUF               lpBuffers,                                       
                DWORD                  dwBufferCount,                                      
                LPDWORD                lpNumberOfBytesSent,                              
                DWORD                  dwFlags,                                            
              const SOCKADDR_IN FAR  *lpFromOrTo,                         
                int                    iFromOrTolen,                        
                LPWSAOVERLAPPED        lpOverlapped,                             
            LPWSAOVERLAPPED_COMPLETION_ROUTINE   lpCompletionRoutine,
                LPWSATHREADID          lpThreadId,                            
              LPINT                  lpErrno  
            }
            OVERLAPPED_RECORDER, *POVERLAPPED_RECORDER, *LPOVERLAPPED_RECORDER;

            // 其次替換重疊操作的完成例程為自定義的例程
            // 例程完成后,控制權交給客戶機的完成例程,我們可以刪除上面保存的重疊參數信息結構了
            // 完成例程原型如下:
            void CALLBACK CompletionRoutine (
             IN    DWORD             dwError, 
             IN    DWORD             cbTransferred, 
             IN    LPWSAOVERLAPPED   lpOverlapped, 
             IN    DWORD             dwFlags 
            );

            posted on 2007-08-17 13:19 聶文龍 閱讀(1857) 評論(2)  編輯 收藏 引用 所屬分類: net work

            FeedBack:
            # re: SPI 2015-11-25 10:35 李超
            最近在弄這方面的東西,大神能否知道一下怎么處理不同的I/O操作?  回復  更多評論
              
            # re: SPI 2015-11-25 10:36 李超
            可能比較久遠了。。。  回復  更多評論
              
            色偷偷久久一区二区三区| 人妻系列无码专区久久五月天| 久久久久亚洲精品中文字幕 | 亚洲欧美一级久久精品| 亚洲国产成人久久笫一页| 亚洲精品美女久久久久99| 久久精品亚洲中文字幕无码麻豆| 99久久精品国产高清一区二区| 久久精品国产清自在天天线| 四虎国产精品成人免费久久| 久久国产欧美日韩精品| 欧美日韩中文字幕久久久不卡| 久久久久久久精品成人热色戒| 精品一区二区久久| 亚洲精品乱码久久久久久按摩 | 人妻无码精品久久亚瑟影视| 久久午夜羞羞影院免费观看| 亚洲人成网站999久久久综合| 国产精品99久久精品| 伊人久久大香线蕉综合网站| 91久久精品国产成人久久| 无码精品久久久久久人妻中字| 久久精品国产99久久久香蕉| 精品久久无码中文字幕| 综合久久久久久中文字幕亚洲国产国产综合一区首 | 青青国产成人久久91网| 亚洲va中文字幕无码久久| 午夜肉伦伦影院久久精品免费看国产一区二区三区| 久久精品国产久精国产一老狼| 久久久久黑人强伦姧人妻| 日韩欧美亚洲综合久久影院d3| 精品久久久久久亚洲精品| 精品国产乱码久久久久软件| 欧美精品九九99久久在观看| 狠狠88综合久久久久综合网| 99久久国产宗和精品1上映| 韩国三级中文字幕hd久久精品 | 精品国产乱码久久久久久呢| 久久婷婷五月综合成人D啪| 久久综合一区二区无码| 欧美日韩中文字幕久久久不卡|