Visual C++編程疑難問題解(一)

[前言:]編程中遇到的問題很多,再優秀的程序員也不會沒有問題,但很多解決方法被淹沒在論壇浩瀚的“水”中了,爲了便於同道查詢同時保存這些精華,我將在實際編程中常見的問題以及論壇中優秀的回貼收集起來以專題的形式發表。本文中的所有問題及回答均來自論壇。

  問題一:如何實現指定盤符的光驅彈出彈入

  鑰匙在這裏:

void ctrl_cdrom_door(
LPCTSTR drivename ,//驅動器的名字如f:等.
bool fOpen file://彈出時用true,彈入時用false
)
{
 TCHAR devstr[128],ctrlstr[128];
 wsprintf(devstr,_T("open %s type cdaudio alias mycd wait"),drivename);
 wsprintf(ctrlstr,_T("set mycd door %s wait"),fOpen?_T("open"):_T("closed"));
 mciSendString(devstr,NULL,0,NULL);
 mciSendString(ctrlstr,NULL,0,NULL);
 mciSendString(_T("close mycd wait"),NULL,0,NULL);
}
file://測試的例子代碼.
void CMainFrame::OnTestOpen()
{
 // TODO: Add your command handler code here
 ctrl_cdrom_door("F:",true);
}
void CMainFrame::OnTestClose()
{
 // TODO: Add your command handler code here
 ctrl_cdrom_door("F:",false);
}

  問題二:如何實現繁簡體互換?

  鑰匙在這裏:


// j2f.cpp : 簡體(gb)==>繁體==>big5的過程
// 反向轉換是類似的.
// 注意直接從簡體-->big5不能做到一一對應.會有很多?出現,
// 故此需要先轉成繁體.再轉成big5.
// 我感覺這種方法應當和winnt或office裏提供的繁簡或字符集互轉是一致的.
#include "stdafx.h"
#include
#include
#include
#include
#include
using namespace std;

void j2f(const string &s)
{
 int n=s.length ();
 int r=LCMapString(
   MAKELCID(MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED),SORT_CHINESE_PRC),
   LCMAP_TRADITIONAL_CHINESE,
   s.c_str (),s.length (),NULL,0);
 if (!r) cout <<"error :"<  char *ft=new char[r+1];
 r=LCMapString(
   MAKELCID(MAKELANGID(LANG_CHINESE,SUBLANG_CHINESE_SIMPLIFIED),SORT_CHINESE_PRC),
   LCMAP_TRADITIONAL_CHINESE,
   s.c_str (),s.length (),ft,r+1);//這個api搞掂簡體轉繁體,下面會打印繁體出來
 if (r) {
  ft[r]=0;
  cout<   wchar_t *pws=new wchar_t[r+1];
  int r1=MultiByteToWideChar(936,0,ft,r,pws,r+1);
 BOOL f=FALSE;
 r1=WideCharToMultiByte(950,0,pws,r1,ft,r+1,"?",&f);//代碼頁切換搞掂gb->big5
 ft[r1]=0;
 cout<   for (int i=0;i    cout<<"";
   printf("0x%02x ",(BYTE)ft[i]);
  }
 cout<<")"<  delete [] pws;
 }

 delete []ft;
}

//從標準輸入簡體國標-->big5繁體標準輸出,輸入兩個空行退出
int main(int argc, char* argv[])
{

 for(;;){
  char line[1024];
  cin.getline (line,sizeof(line));
 string s(line);
 if (!cin ||s.length ()==0) break;
 j2f(s);
}
_getch();
return 0;
}

 

  問題三:多線程中如何得到視圖指針?

  鑰匙在這裏:有兩種方法可以實現你的要求:

  1)第一種方法:

  要是多線程不是在App.cpp裏出現,那麼要在多線程的.cpp中加上extern CYourApp theApp;

//獲得文檔模板:
POSITION curTemplatePos = theApp.GetFirstDocTemplatePosition();
CDocTemplate *m_doc=theApp.GetNextDocTemplate(curTemplatePos);
file://獲得文檔:
curTemplatePos=m_doc->GetFirstDocPosition();
CYourDoc *m_pdoc=(CA8Doc*)m_doc->GetNextDoc(curTemplatePos);

file://獲得視圖:
curTemplatePos=m_pdoc->GetFirstViewPosition();
CYourView *m_pview=(CYourView*)m_pdoc->GetNextView(curTemplatePos);

file://調用視圖函數:
m_pview->Put();

  2)第二種方法:


//獲得窗體指針:
CMainFrame *pFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd;

file://獲得與該窗體符合的視圖:
CYourView *m_pView = (CYourView *) pFrame->GetActiveView();

file://調用視圖函數:
m_pView->Put();

問題四:如何使程序在啓動時不創建一個新文檔?

  鑰匙在這裏: 在程序的InitInstance中的ProcessShellCommand函數之前加入:

    cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing

  問題五:如何在MDI程序中得到所有的視圖?

  鑰匙在這裏:

   必須用一些文檔中沒有記載的函數:

CDocument::GetFirstViewPosition(); // DOCCORE.CPP
CDocument::GetNextView(); // DOCCORE.CPP
CMultiDocTemplate::GetFirstDocPosition(); // DOCMULTI.CPP
CMultiDocTemplate::GetNextDoc(); // DOCMULTI.CPP

   同時還需要與CWinApp的成員m_templateList打交道。

  問題六: ADO中如何得到某個數據庫中的所有表的數目?

  鑰匙在這裏:


HRESULT hr = S_OK;
_ConnectionPtr pConnection = NULL;
_CatalogPtr pCatalog = NULL;
_bstr_t strCnn("Provider=sqloledb;Data Source=MyServer;"
"Initial Catalog=pubs;User Id=sa;Password=;");
try
{
 file://Define a command object for a stored procedure.

 pConnection.CreateInstance(__uuidof(Connection));
 hr = pCatalog.CreateInstance(__uuidof (Catalog));
 hr = pConnection->Open(strCnn,"","",adConnectUnspecified);
 pCatalog->PutActiveConnection(_variant_t((IDispatch *) pConnection));
 long nTBCount = pCatalog->Tables->Count;//這就是你想要的表的數目
 pConnection->Close();
 pConnection = NULL;
}
catch(_com_error &e)
{
 ....
}


  問題七:從應用角度講阻塞與非阻塞SOCKET有什麼區別?

  鑰匙在這裏:

    從系統性能上看,用非阻塞的socket效率和性能更高,但是編程更復雜,特別是當你使用事件或者消息的時候,但是,你可以通過4個工作線程管理 100多個socket連接,效率非常高,不需要每個工作線程只管理一個socket連接。 用阻塞的方式比較簡單,但當較多客戶端時消耗系統資源太多。

   所謂用4個線程管理100多socket,不過是這樣一種構思:建立一個線程池,當有socket的事件需要處理時,從線程池中取一個線程來執行,執行完,將線程歸還到線程池中。 這樣的做法在如下的條件下會工作的更好:

   (1)、每個socket連接的時間較長,不斷的與服務器交互。

   (2)、每個連接的socket並不是每時每刻都在收發數據 具體的實現方式:(我是在windows環境下實現的,linux上現在正在研究) 可以使用OVERLAPED IO,或者完成端口(CompeleteIO)

  問題八: 怎樣設置棧的大小?

  鑰匙在這裏:

   方法一:STACKSIZE 定義.def文件

   語法:STACKSIZE reserve[,commit]

      reserve:棧的大小;commit:可選項,與操作系統有關,在NT上只一次分配物理內存的大小

   方法二:設定/STACK


   打開工程,依次操作菜單如下:Project->Setting->Link,在Category 中選中Output,然後在Reserve中設定堆棧的最大值和commit。

   注意:reserve默認值爲1MB,最小值爲4Byte;commit是保留在虛擬內存的頁文件裏面,它設置的較
大會使棧開闢較大的值,可能增加內存的開銷和啓動時間

問題九:如何獲取本機上正在使用的UDP端口?

   鑰匙在這裏:

    可以通過端口掃描技術實現。

    端口掃描技術(port scanning)
  
  端口掃描就是通過連接到目標系統的TCP或UDP端口,來確定什麼服務正在運行。一般來說端口掃描有三個用途:

   * 識別目標系統上正在運行的TCP和UDP服務。
   * 識別目標系統的操作系統類型(Windows 9x, Windows NT,或UNIX,等)。
   * 識別某個應用程序或某個特定服務的版本號。
  
    端口掃描技術:

   1. TCP connect scan:這種方法最簡單,直接連到目標端口並完成一個完整的三次握手過程(SYN, SYN/ACK, 和ACK)。缺點是容易被目標系統檢測到。

    2. TCP SYN scan:這種技術也叫“半開式掃描”(half-open scanning),因爲它沒有完成一個完整的TCP連接。這種方法向目標端口發送一個SYN分組(packet),如果目標端口返回SYN/ACK,那 麼可以肯定該端口處於檢聽狀態;否則,返回的是RST/ACK。這種方法比第一種更具隱蔽性,可能不會在目標系統中留下掃描痕跡。

   3. TCP FIN scan:這種方法向目標端口發送一個FIN分組。按RFC793的規定(http://www.ietf.org/rfc/rfc0793.txt), 對於所有關閉的端口,目標系統應該返回RST標誌。這種方法通常用在基於UNIX的TCP/IP堆棧。

   4. TCP Xmas Tree scan:這種方法向目標端口發送一個含有FIN, URG,和PUSH標誌的分組。根據RFC793,對於所有關閉的端口,目標系統應該返回RST標誌。

   5. TCP Null scan:這種方法向目標端口發送一個不包含任何標誌的分組。根據RFC793,對於所有關閉的端口,目標系統應該返回RST標誌。

    6. UDP scan:這種方法向目標端口發送一個UDP分組。如果目標端口以“ICMP port unreachable”消息響應,那麼說明該端口是關閉的;反之,如果沒有收到“ICMP port unreachable”響應消息,則可以肯定該端口是打開的。由於UDP協議是面向無連接的協議,這種掃描技術的精確性高度依賴於網絡性能和系統資源。 另外,如果目標系統採用了大量分組過濾技術,那麼UDP掃描過程會變得非常慢。如果你想對Internet進行UDP掃描,那麼你不能指望得到可靠的結 果。
  
  另外,有某種系統的IP協議是這樣實現的,對於所有掃描的端口,不管他們處於關閉或者監聽狀態,都返回RST標誌(我們知道,這不符合RFC793的規定)。因此,掃描這種系統時,用不同的掃描技術可能得到不同的掃描結果。
  
   端口掃描工具:
  
   * Strobe

   Strobe是一個很古老的端口掃描工具,最快且最可靠的TCP掃描工具之一。缺點在於沒有UDP掃描功能。
   URL: ftp.win.or.jp/pub/network/misc/strobe-1.05.tar.gz
  
   * Udp_scan

    Udp_scan最初來自SATAN(Security Administrator Tool for Analyzing Networks, 安全管理員的網絡分析工具,SATAN的新版本改稱SAINT,由http://wwdsilx.wwdsi.com發佈)。Udp_scan是最可靠的 UDP掃描工具之一,但隱蔽性不好,容易被目標系統檢測到。

   URL: ftp://ftp.technotronic.com/unix/network-sanners/udpscan.c
  
   * Netcat

   Netcat是最有用的網絡工具之一,功能很多,有網絡安全工具包中的瑞士軍刀之稱。Netcat提供基本的TCP、UDP掃描功能。

   URL: http://www.l0pht.com/netcat
  
   * PortPro and Portscan

  是Windows NT上最快的端口掃描工具之一。Portscan可以指定一個掃描範圍,PortPro只能遞增掃描,他們都不能一次掃描多個ip地址。

   URL: PortPro: http://www.securityfocus.com
  
   * Network Mapper(nmap)

    Nmap是一個高級端口掃描工具,提供了多種掃描方法。Nmap有一些有趣的功能,如用分解(fragment)TCP頭(就是把一個TCP頭分解到多個 分組中發送)的方法繞過一些具有簡單分組過濾功能的防火牆。Nmap的另一個有趣功能是可以發送欺騙地址掃描。具體的實現方法是:以僞造的IP地址向目標 系統發送大量SYN分組,並在其中混以真實地址的SYN分組,這會導致目標系統花大量時間去響應那些僞造分組,從而造成拒絕服務(denial of service),這就是所謂的SYN flood攻擊。

   URL: http://www.insecure.org/nmap

  問題十:如何利用DirectoryEntry組件來查看網絡  

  鑰匙在這裏:

   DirectoryEntry組件提供了Path屬性,根據文檔,此屬性指定了目錄服務中用來訪問對象的對象名,其格式如下:

   protocol://servername:port number/distinguished name

   此語句的第一部分定義了訪問將使用的協議,如

   LDAP: (Lightweight Directory Access Protocol)

   IIS: (提供IIS元數據來讀及配置Internet Infomation Server)

   WinNT: (提供在非常有限的性能下對Windows NT域的訪問)

   NDS: (提供對Novell Directory Service的訪問)

   等等(詳細信息清參考MSDN)。

    據此,我們構造了一個DirectoryEntry實例,將它的Path設爲"WinNT:",以通過對它的所有子項的枚舉來發現網絡上的所有域(以 及工作組)。這樣,再對所發現的域(以及工作組)的子項進行枚舉,就可以發現網絡上的所有計算機。下面的一個控制檯小程序演示了這一點。

using System;
using System.DirectoryServices;

class TempClass
{
 static void Main()
 {
  EnumComputers();
 }

 static void EnumComputers()
 {
  using(DirectoryEntry root = new DirectoryEntry("WinNT:"))
  {
   foreach(DirectoryEntry domain in root.Children)
   {
    Console.WriteLine("Domain | WorkGroup:/t"+domain.Name);
    foreach(DirectoryEntry computer in domain.Children)
    {
     Console.WriteLine("Computer:/t"+computer.Name);
    }
   }
  }
 }
}

  上面代碼中兩個嵌套的foreach循環看起來並不是太好,並且控制檯的顯示效果也並不那麼美觀。下面,我將對代碼進行一些改動,並將它移植到WinForm上。

  新建一個Windows Application[C#],在Form上添加一個TreeView,命名爲treeView1。

  添加以下幾個函數:

//用指定的文本構造一個節點,將其添加爲參數parant的子節點,並返回剛構造的節點
private TreeNode AddNode(TreeNode parant,string text)
{
 TreeNode node = new TreeNode(text);
 parant.Nodes.Add(node);
 return node;
}

//遞歸地找到參數entry的所有子節點,並在treeView1中顯示;這裏的entry與entryNode需相對應
private void EnumChildren(DirectoryEntry entry,TreeNode entryNode)
{
 if(entry.Children!=null) file://如果無子節點則結束
 {
  foreach(DirectoryEntry i in entry.Children)
  {
   file://將各子節點加入TreeView,並進行遞歸
   EnumChildren(i,AddNode(entryNode,i.Name));
  }
 }
}

//用給定的字符串構造根節點,並列出其所有子節點
private void Enumerate(string path)
{
 try
 {
  using(DirectoryEntry root = new DirectoryEntry(path))
  {
   TreeNode node = new TreeNode(root.Name);
   treeView1.Nodes.Add(node);
   EnumChildren(root,node);
  }
 }
 catch {}
}

   這樣,通過傳遞 "WinNT:" 給函數Enumerate(string),就可以在TreeView中看到網絡上的所有計算機,以及每臺計算機上的用戶、組、服務等資源等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章