一、基本使用
1.新加ListControl 控件,屬性中的style屬性頁下的View選擇Report。
并設置其對應的控制變量如:m_list1。

2.初始化,即設置列。 用m_list1。
m_list1.InsertColumn(0,"曲名"); //插入列
m_list1.InsertColumn(1,"類型");
m_list1.InsertColumn(2,"長度");
m_list1.InsertColumn(3,"路徑");

CRect rect4;
m_list1.GetClientRect(rect4); //獲得當前客戶區信息
m_list1.SetColumnWidth(0,rect4.Width()/4); //設置列的寬度。
m_list1.SetColumnWidth(1,rect4.Width()/5);
m_list1.SetColumnWidth(2,rect4.Width()/5);
m_list1.SetColumnWidth(3,rect4.Width()*8/20);

這部分初始化操作,最好放在對話框類的OnInitDialog()函數里,自動初始化。

3.插入數據
m_list1.InsertItem(0,"大海"); //插入第一個數據,即第0條數據。先插入,然后在修改其他的信息。
m_list1.SetItemText(0,1,"MP3"); //修改第0條數據的其他信息。
m_list1.SetItemText(0,2,"4:20");
m_list1.SetItemText(0,3,"c:\");

4.刪除所有數據
m_list1.DeleteAllItems();
二、其他技巧

1.如何設置ListView控件的完全行(Full Row)選項。
這個控件有個地方常常很惱人,那就是在報告視圖中選中一行時,它只加亮最左邊的一個欄目。
為了克服這個問題,這里告訴你一個方法:向ListView控件發送一個LVM_SETEXTENDEDLISTVIEWSTYLE消息。
::SendMessage(m_list1.m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
摘要: CFileDialog文件選擇對話框的使用:首先構造一個對象并提供相應的參數,構造函數原型如下: CFileDialog::CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRIT...
閱讀全文
開發環境VS2005
tempratureall[]為一個整數數組且已經賦值
CDC *pDC=GetDC();
CString s;
s.Format("%d", tempratureall[1]);
pDC->TextOut(20,39,s);
總是提示出錯
error C2664: “void ATL::CStringT <BaseType,StringTraits>::Format(const wchar_t *,...)”: 不能將參數 1 從“const char [3]”轉換為“const wchar_t *”
網友回復:itoa
網友回復:s.Format( _T("%d"), tempratureall[1]);
網友回復:用itoa也試過了也不行
itoa(tempratureall[1],&s,10);
網友回復:s.Format(_T("%d"), tempratureall[1]);
////////////////////////////////////////////////////////////////////////////////////////////////
我建立了以個簡單的MFC框架,然后在
void CMy1View::OnDraw(CDC* pDC)
函數中插入了如下的代碼
CString strOutput;
strOutput.Format("%s","Hello World!");
pDC -> TextOut(0,0,strOutput);
為什么編譯的時候出現了
錯誤 1 error C2664: “void ATL::CStringT<BaseType,StringTraits>::Format(const wchar_t *,...)”: 不能將參數 1 從“const char [3]”轉換為“const wchar_t *”
這個錯誤??
很不理解,書上也是這么寫的。。。我初學,真不知道怎么辦了。
如果就加
pDC -> TextOut(0,0,"Hello World!");
也出現錯誤
錯誤 1 error C2664: “BOOL CDC::TextOutW(int,int,const CString &)”: 不能將參數 3 從“const char [13]”轉換為“const CString &”
為什么呢?
////////
最佳答案
你在頭文件里包含#include <tchar.h>
然后這樣初始化
strOutput.Format(_T("%s"),_T("Hello World!"));
因為默認情況下,字符串都是unicode的形式,
用unicode在nt以上的系統是一個很好的方式
在windows平臺下,有一個很方便的宏:
#pragma once
指定當前文件在構建時只被包含(或打開)一次,這樣就可以減少構建的時間,因為加入#pragma once后,編譯器在打開或讀取第一個#include 模塊后,就不會再打開或讀取隨后出現的相同#include 模塊.
另外,還可以用條件編譯語句來實現:
#ifndef "XX_H"
#define "XX_H"
<頭文件定義正文>
#endif
顯然后者的移植性更好一點。
有下面的自定義結構體,定義在sample.h中。
--------------------------------------------
typedef struct sample{
int trueNumber;
double feature[13];
}SAMPLE;
--------------------------------------------
類A,類B都#include<sample.h>,主程序都調用了類A,類B;就會出現
error C2011: ''sample'' : ''struct'' type redefinition
解決方法:寫上宏定義:
-----------------------------------
#ifndef sample_H_H
#define sample_H_H
typedef struct sample{
int trueClass;
double feature[13];
}SAMPLE;
#endif
---------------------------------------
也可以這樣寫
----------------------------------
#if !define sample_H_H
#define sample_H_H
typedef struct sample{
int trueClass;
double feature[13];
}SAMPLE;
#endif
-------------------------------
意思是:
if(宏sample_H_H,沒有被定義過)
{
定義宏sample_H_H
........(執行)other code
}
實際上sample_H_H作為一個標記而存在
自定義一個類時,在所有的頭文件中都應用這組宏處理
注意是#ifndef不是#ifdef
-----------------------------------
#ifndef 標記名(常以類名_H_H,兩個標記名要相同)
#define 標記名
//你原來的文件內容
#endif
---------------------------------
第一類方法也是常用的方法,通過多次的數值計算來完成交換,到現在知道的有下面三種:
(1)加減法。
a = a + b;
b = a - b;
a = a - b;
該方法可以交換整型和浮點型數值的變量,但在處理浮點型的時候有可能出現精度的損失,例如對數據:
a = 3.123456
b = 1234567.000000
交換后各變量值變為:
a = 1234567.000000
b = 3.125000
很明顯,原來a的值在交換給b的過程中發生了精度損失。
(2)乘除法。
a = a * b;
b = a / b;
a = a / b;
乘除法更像是加減法向乘除運算的映射,它與加減法類似:可以處理整型和浮點型變量,但在處理浮點型變量時也存在精度損失問題。而且乘除法比加減法要多一條約束:b必不為0。
可能經驗上的某種直覺告訴我們:加減法和乘除法可能會溢出,而且乘除的溢出會特別嚴重。其實不然,采用這兩種方法都不會溢出。以加減法為例,第一步的加運算可能會造成溢出,但它所造成的溢出會在后邊的減運算中被溢出回來。
(3)異或法。
a ^= b;//a=a^b
b ^= a;//b=b^(a^b)=b^a^b=b^b^a=0^a=a
a ^= b;//a=(a^b)^a=a^b^a=a^a^b=0^b=b
異或法可以完成對整型變量的交換,對于浮點型變量它無法完成交換。
第二類方法更像是玩了一個文字游戲,此種方法采用了在代碼中嵌入匯編代碼的方法避免了臨時變量的引入,但究其本質還是會使用額外的存儲空間。此種方法可以有很多種,下邊列出幾種:
(1)使用xchg指令,這也是比較直觀、容易想到的方法,因為xchg指令的功能就是交換源操作數和目的操作數的值,這里要使用額外寄存器來暫存變量。內嵌匯編代碼如下:
_asm
{
mov eax,a
xchg b,eax
mov a,eax
}
(2)使用額外的棧。這里使用反向的出棧順序來完成交換。內嵌代碼有如下兩種形式:
_asm
{
push a
push b
pop a
pop b
}
另一種形式:
_asm push a
a = b;
_asm pop a
(3)使用mov指令。這種方法使用額外寄存器來暫存一個變量的值。
_asm mov eax,a
a = b;
_asm mov b,eax
其實第二類方法并不合格,它雖然沒有顯式的使用臨時變量,但還是會用到額外的存貯空間。不過也不能說沒有必要掌握,從實用的角度看還是很“有用”的。不是
有公司出過這樣的面試題嗎?“不使用加減法和異或法完成不使用中間變量交換兩個數值型變量的值”。此時或許只好使用這種方法了。
Excel是微軟公司辦公自動化套件中的一個軟件,他主要是用來處理電子表格。Excel以其功能強大,界面友好等受到了許多用戶的歡迎。在辦公的時候,正是由于Excel的這么多的優點,許多重要的數據,往往以Excel電子表格的形式存儲起來。這樣就給程序員帶來了一個問題,雖然Excel功能比較強大,但畢竟不是數據庫,在程序中處理數據庫中的數據比其處理Excel表格中的數據容易許多。那么如何用Visual C#讀取Excel表格中的數據?在以前用Delphi編程的時候,對于不同的用戶,他們對于打印的需求是不一樣的,如果要使得程序中的打印功能適用于每一個用戶,可以想象程序設計是十分復雜的。這時想到Excel,由于Excel表格的功能強大,又由于幾乎每一臺機器都安裝了它,如果把程序處理的結果放到Excel表格中,這樣每一個用戶就可以根據自己的需要在Excel中定制自己的打印。這樣不僅使得程序設計簡單,而且又滿足了諸多用戶的要求,更加實用了。那么用Visual C#如何調用Excel,如何又把數據存放到Excel表格中?本文就來探討一下上述問題的解決辦法。
一.程序設計及運行環境 (1).微軟視窗2000 服務器版
(2)..Net Framework SDK Beta 2
(3).Microsoft Data Access Component 2.6以上版本(MDAC2.6)
(4).Office 2000套件
二.Visual C#讀取Excel表格中的數據:
本節將通過一個程序來介紹Visual C#讀取Excel表格中的數據,并把數據以DataGrid的形式顯示出來。
(1).如何讀取數據:
其實讀取Excel表格中的數據和讀取數據庫中的數據是非常類似的,因為在某種程度上Excel表格可以看成是一張一張的數據表。其二者的主要區別在于所使用的數據引擎不一樣。在本文的程序中,通過下列代碼實現讀取Excel表格數據,具體如下:
//創建一個數據鏈接 string strCon = " Provider = Microsoft.Jet.OLEDB.4.0 ; Data Source = c:sample.xls;Extended Properties=Excel 8.0" ; OleDbConnection myConn = new OleDbConnection ( strCon ) ; string strCom = " SELECT * FROM [Sheet1$] " ; myConn.Open ( ) ; file://打開數據鏈接,得到一個數據集 OleDbDataAdapter myCommand = new OleDbDataAdapter ( strCom , myConn ) ; file://創建一個 DataSet對象 myDataSet = new DataSet ( ) ; file://得到自己的DataSet對象 myCommand.Fill ( myDataSet , "[Sheet1$]" ) ; file://關閉此數據鏈接 myConn.Close ( ) ; |
怎么樣讀取Excel表格中的數據其實和讀取數據庫中的數據沒有什么實質上的區別。
注釋:這里讀取的是C盤根目錄下的"Sample.xls"文件。
(2).用DataGrid來顯示得到的數據集:
在得到DataSet對象后,只需要通過下列二行代碼,就可以把數據集用DataGrid顯示出來了:
DataGrid1.DataMember= "[Sheet1$]" ; DataGrid1.DataSource = myDataSet ; |
(3).用Visual C#讀取Excel表格,并用DataGrid顯示出來的程序代碼(Read.cs)和程序運行的界面:
掌握了上面二點,水到渠成就可以得到以下代碼:
using System ; using System.Drawing ; using System.Collections ; using System.ComponentModel ; using System.Windows.Forms ; using System.Data ; using System.Data.OleDb ; public class Form1 : Form { private Button button1 ; private System.Data.DataSet myDataSet ; private DataGrid DataGrid1 ; private System.ComponentModel.Container components = null ;
public Form1 ( ) { file://初始化窗體中的各個組件 InitializeComponent ( ) ; file://打開數據鏈接,得到數據集 GetConnect ( ) ; } file://清除程序中使用過的資源 protected override void Dispose ( bool disposing ) { if ( disposing ) { if ( components != null ) { components.Dispose ( ) ; } } base.Dispose ( disposing ) ; }
private void GetConnect ( ) { file://創建一個數據鏈接 string strCon = " Provider = Microsoft.Jet.OLEDB.4.0 ; Data Source = c:sample.xls;Extended Properties=Excel 8.0" ; OleDbConnection myConn = new OleDbConnection ( strCon ) ; string strCom = " SELECT * FROM [Sheet1$] " ; myConn.Open ( ) ; file://打開數據鏈接,得到一個數據集 OleDbDataAdapter myCommand = new OleDbDataAdapter ( strCom , myConn ) ; file://創建一個 DataSet對象 myDataSet = new DataSet ( ) ; file://得到自己的DataSet對象 myCommand.Fill ( myDataSet , "[Sheet1$]" ) ; file://關閉此數據鏈接 myConn.Close ( ) ; } private void InitializeComponent ( ) { DataGrid1 = new DataGrid ( ) ; button1 = new Button ( ) ; SuspendLayout ( ) ; DataGrid1.Name = "DataGrid1"; DataGrid1.Size = new System.Drawing.Size ( 400 , 200 ) ;
button1.Location = new System.Drawing.Point ( 124 , 240 ) ; button1.Name = "button1" ; button1.TabIndex = 1 ; button1.Text = "讀取數據" ; button1.Size = new System.Drawing.Size (84 , 24 ) ; button1.Click += new System.EventHandler ( this.button1_Click ) ;
this.AutoScaleBaseSize = new System.Drawing.Size ( 6 , 14 ) ; this.ClientSize = new System.Drawing.Size ( 400 , 280 ) ; this.Controls.Add ( button1 ) ; this.Controls.Add ( DataGrid1 ) ; this.Name = "Form1" ; this.Text = "讀取Excle表格中的數據,并用DataGrid顯示出來!" ; this.ResumeLayout ( false ) ;
} private void button1_Click ( object sender , System.EventArgs e ) { DataGrid1.DataMember= "[Sheet1$]" ; DataGrid1.DataSource = myDataSet ;
} static void Main ( ) { Application.Run ( new Form1 ( ) ) ; } }
|
下圖是程序編譯后,運行結果:
 圖01:用Visual C#讀取"c:sample.xls"的運行界面
|
(4).總結:
以上只是讀取了Excel表格中"Sheet1"中的數據,對于其他"Sheet"中的內容,可以參照讀取"Sheet1"中的程序,只作一點修改就可以了,譬如要讀取"Sheet2"中的內容,只需要把"Read.cs"程序中的"Sheet1$"改成"Sheet2$"就可以了。
三.Visual C#調用Excel表格,并在Excel表格中存儲數據:
在Visual C#中調用Excel表格,并不像讀取Excel表格中的數據那么容易了,因為在Visual C#中調用Excel表格要使用到Excel的COM組件。如果你安裝Office套件在"C"盤,那么在"C:Program FilesMicrosoft OfficeOffice"可以找到這個COM組件"EXCEL9.OLB",在《Visual C#如何使用Active X組件》一文中,這些COM組件都是非受管代碼的,要在Visual C#中使用這些非受管代碼的COM組件,就必須把他們轉換成受管代碼的類庫。所以在用Visual C#調用Excel表格之前,必須完成從COM組件的非受管代碼到受管代碼的類庫的轉換。
(1).非受管代碼COM組件轉換成受管代碼的類庫:
首先把COM組件"EXCEL9.OLB"拷貝到C盤的根目錄下,然后輸入下列命令:
這樣在C盤的根目錄下面就產生了三個DLL文件:"Excel.dll"、"Office.dll"、"VBIDE.dll"。在產生了上面的三個文件后,這種轉換就成功完成了。在下面的程序中,就可以利用這轉換好的三個類庫編寫和Excel表格相關的各種操作了。
(2).Visual C#打開Excel表格:
在"Excel.dll"中定義了一個命名空間"Excel",在差命名空間中封裝了一個類"Application",這個類和啟動Excel表格有非常重要的關系,在Visual C#中,只需要下列三行代碼就可以完成打開Excel表格的工作,具體如下:
Excel.Application excel = new Excel.Application ( ) ; excel.Application.Workbooks.Add ( true ) ;
excel.Visible = true ; |
但此時的Excel表格是一個空的表格,沒有任何內容,下面就來介紹如何往Excel表格中輸入數據。
(3).往Excel表格中輸入數據:
在命名空間"Excel"中,還定義了一個類"Cell",這個類所代表的就是Excel表格中的一個下單元。通過給差"Cell"賦值,從而實現往Excel表格中輸入相應的數據,下列代碼功能是打開Excel表格,并且往表格輸入一些數據。
Excel.Application excel = new Excel.Application ( ) ; excel.Application.Workbooks.Add ( true ) ; excel.Cells[ 1 , 1 ] = "第一行第一列" ; excel.Cells[ 1 , 2 ] = "第一行第二列" ; excel.Cells[ 2 , 1 ] = "第二行第一列" ; excel.Cells[ 2 , 2 ] = "第二行第二列" ; excel.Cells[ 3 , 1 ] = "第三行第一列" ; excel.Cells[ 3 , 2 ] = "第三行第二列" ; excel.Visible = true ; |
(4). Visual C#調用Excel表格,并在Excel表格中存儲數據的程序代碼(Excel.cs):
了解了上面的這些知識,得到完成上述功能的程序代碼就顯得比較容易了,具體如下:
using System ; using System.Drawing ; using System.Collections ; using System.ComponentModel ; using System.Windows.Forms ; using System.Data ; using System.Data.SqlClient ; public class Form1 : Form { private Button button1 ; private System.ComponentModel.Container components = null ; public Form1 ( ) { file://初始化窗體中的各個組件 InitializeComponent ( ) ; } file://清除程序中使用的各個資源 protected override void Dispose ( bool disposing ) { if ( disposing ) { if ( components != null ) { components.Dispose ( ) ; } } base.Dispose( disposing ) ; } private void InitializeComponent ( ) { button1 = new Button ( ) ; SuspendLayout ( ) ; button1.Location = new System.Drawing.Point ( 32 , 72 ) ; button1.Name = "button1" ; button1.Size = new System.Drawing.Size ( 100 , 30 ) ; button1.TabIndex = 0 ; button1.Text = "調用Excel文件!" ; button1.Click += new System.EventHandler ( button1_Click ) ;
AutoScaleBaseSize = new System.Drawing.Size ( 5 , 13 ) ;
this.ClientSize = new System.Drawing.Size ( 292 , 273 ) ; this.Controls.Add ( button1 ) ; this.Name = "Form1" ; this.Text = "如何用Visual C#調用Excel表格!" ; this.ResumeLayout ( false ) ;
} static void Main ( ) { Application.Run ( new Form1 ( ) ) ; } private void button1_Click ( object sender , System.EventArgs e ) { Excel.Application excel = new Excel.Application ( ) ; excel.Application.Workbooks.Add ( true ) ; excel.Cells[ 1 , 1 ] = "第一行第一列" ; excel.Cells[ 1 , 2 ] = "第一行第二列" ; excel.Cells[ 2 , 1 ] = "第二行第一列" ; excel.Cells[ 2 , 2 ] = "第二行第二列" ; excel.Cells[ 3 , 1 ] = "第三行第一列" ; excel.Cells[ 3 , 2 ] = "第三行第二列" ; excel.Visible = true ; } }
|
(5).編譯源程序和程序運行界面:
在經過了下列命令編譯后:
Csc.exe /r:system.dll /r:system.windows.forms.dll /r:system.drawing.dll /r:excel.dll /r:office.dll /r:vbide.dll excel.cs |
就可以得到"Excel.exe",運行后界面如下:
 圖02:Visual C#調用Excel表格,并存儲數據的程序運行界面 |
四.Visual C#處理Office套件中的其他成員程序:
本文雖然只介紹了Visual C#在處理Excel表格中經常遇到的一些問題的解決方法,但其實對Office套件的其他成員也有很強的借鑒意義,譬如Visual C#來處理Word文檔,在調用Word文檔的時候,必須先完成COM組件從非受管代碼到受管代碼的轉換,Word的COM組件位"MSWORD9.OLB",經過轉換后也會產生三個DLL文件,但分別是"Word.dll"、"Office.dll"、"VBIDE.dll"。其實在Visual C#中調用Word,也非常容易。只需要把調用Excel表格中的代碼換成調用Word的代碼就可以了,具體如下:
Word.Application word = new Word.Application ( ) ; word.Application.Visible = true ; |
不信你試一下,看看是否達到你的要求。對于針對Word的其他的操作,總體來說和對Excel表格的操作相類似。由于針對Word只是一個文檔,程序對Word進行的操作是比較少的,所以就不一一介紹了。
五.總結:
本文介紹Visual C#來處理Excel表格的幾種最常遇到的情況,雖然針對的只是Excel表格,但對其他Office套件中的成員也具有十分的借鑒意義。
編譯:
第一步、是進行預編譯,使用-E參數可以讓GCC在預處理結束后停止編譯過程:
gcc -E hello.c -o hello.i
預處理的宏定義插入到hello.i中
第二步、是將hello.i編譯為目標代碼,這可以通過使用-c參數來完成:
gcc -c hello.i -o hello.o
也可以通過源文件直接生成
gcc -c hello.c
第三步、是將生成的目標文件鏈接成可執行文件:
gcc hello.o -o hello
也可以通過源文件直接生成
gcc -o hello hello.c
警告:
1、-pedantic 選項,那么使用了擴展語法的地方將產生相應的警告信息
2、-Wall 使用它能夠使GCC產生盡可能多的警告信息
3、-Werror,它要求GCC將所有的警告當成錯誤進行處理
庫依賴:
1、Linux下的大多數函數都默認:
頭文件放到/usr/include/目錄下
而庫文件則放到/usr/lib/目錄下
2、GCC在編譯時必須有自己的辦法來查找所需要的頭文件和庫文件。
-I選項可以向GCC的頭文件搜索路徑中添加新的目錄。
例如,如果在/home/xiaowp/include/目錄下有編譯時所需要的頭文件,為了讓GCC能夠順利地找到它們,就可以使用-I選項:
gcc -o test test.c -I /home/xiaowp/include
-L選項向GCC的庫文件搜索路徑中添加新的目錄
例如,如果在/home/xiaowp/lib/目錄下有鏈接時所需要的庫文件libfoo.so,為了讓GCC能夠順利地找到它,可以使用下面的命令
gcc -o test test.c -L /home/xiaowp/lib -lfoo
值得好好解釋一下的是-l選項,它指示GCC去連接庫文件libfoo.so。
Linux下的庫文件在命名時有一個約定,那就是應該以lib三個字母開頭,由于所有的庫文件都遵循了同樣的規范,因此在用-l選項指定鏈接的庫文件名時可以省去lib三個字母,也就是說GCC在對-lfoo進行處理時,會自動去鏈接名為libfoo.so的文件。
-static選項,強制使用靜態鏈接庫
如果在/home/xiaowp/lib/目錄下有鏈接時所需要的庫文件libfoo.so和libfoo.a
為了讓GCC在鏈接時只用到靜態鏈接庫,可以使用下面的命令:
gcc -o test test.c -L /home/xiaowp/lib -static -lfoo
選項-O可以告訴GCC同時減小代碼的長度和執行時間,其效果等價于-O1。
在這一級別上能夠進行的優化類型雖然取決于目標處理器,但一般都會包括線程跳轉(Thread Jump)和延遲退棧(Deferred Stack Pops)兩種優化。
選項-O2告訴GCC除了完成所有-O1級別的優化之外,同時還要進行一些額外的調整工作,如處理器指令調度等。
選項-O3則除了完成所有-O2級別的優化之外,還包括循環展開和其它一些與處理器特性相關的優化工作。
通常來說,數字越大優化的等級越高,同時也就意味著程序的運行速度越快。
許多Linux程序員都喜歡使用-O2選項,因為它在優化長度、編譯時間和代碼大小之間,取得了一個比較理想的平衡點。
time ./test 查看程序執行時間
優化雖然能夠給程序帶來更好的執行性能,但在如下一些場合中應該避免優化代碼:
程序開發的時候優化等級越高,消耗在編譯上的時間就越長,因此在開發的時候最好不要使用優化選項,只有到軟件發行或開發結束的時候,才考慮對最終生成的代碼進行優化。
資源受限的時候一些優化選項會增加可執行代碼的體積,如果程序在運行時能夠申請到的內存資源非常緊張(如一些實時嵌入式設備),那就不要對代碼進行優化,因為由這帶來的負面影響可能會產生非常嚴重的后果。
跟蹤調試的時候在對代碼進行優化的時候,某些代碼可能會被刪除或改寫,或者為了取得更佳的性能而進行重組,從而使跟蹤和調試變得異常困難。
加速:
使用管道代替編譯中臨時文件,
-pipe 加速編譯
gcc -pipe foo.c -o foo
GCC常用選項
-c 通知GCC取消鏈接步驟,即編譯源碼并在最后生成目標文件;
-Dmacro 定義指定的宏,使它能夠通過源碼中的#ifdef進行檢驗;
-E 不經過編譯預處理程序的輸出而輸送至標準輸出;
-g3 獲得有關調試程序的詳細信息,它不能與-o選項聯合使用;
-Idirectory 在包含文件搜索路徑的起點處添加指定目錄;
-llibrary 提示鏈接程序在創建最終可執行文件時包含指定的庫;
-O、-O2、-O3 將優化狀態打開,該選項不能與-g選項聯合使用;
-S 要求編譯程序生成來自源代碼的匯編程序輸出;
-v 啟動所有警報;
-Wall 在發生警報時取消編譯操作,即將警報看作是錯誤;
-Werror 在發生警報時取消編譯操作,即把報警當作是錯誤;
-w 禁止所有的報警
.sln:解決方案文件,為解決方案資源管理器提供顯示管理文件的圖形接口所需的信息。
.csproj:項目文件,創建應用程序所需的引用、數據連接、文件夾和文件的信息。
.aspx:Web 窗體頁由兩部分組成:視覺元素(HTML、服務器控件和靜態文本)和該頁的編程邏輯。Visual Studio 將這兩個組成部分分別存儲在一個單獨的文件中。視覺元素在.aspx 文件中創建。
.ascx:ASP.NET的用戶控件(也叫做“pagelets”),是作為一種封裝了特定功能和行為(這兩者要被用在Web應用程序的各種頁面上)的Web頁面被開發的。一個用戶控件包含了HTML、代碼和其他Web或者用戶控件的組合,并在Web服務器上以自己的文件格式保存,其擴展名是*.ascx。ASP.NET里的缺省配置并不允許Web客戶端通過URL來訪問這些文件,但是這個網站的其他頁面可以集成這些文件里所包含的功能。
.aspx.cs:Web 窗體頁的編程邏輯位于一個單獨的類文件中,該文件稱作代碼隱藏類文件(.aspx.cs)。
.cs: 類模塊代碼文件。業務邏輯處理層的代碼。
.asax:Global.asax 文件(也叫做 ASP.NET 應用程序文件)是一個可選的文件,該文件包含響應 ASP.NET 或 HTTP 模塊引發的應用程序級別事件的代碼。
.config:Web.config 文件向它們所在的目錄和所有子目錄提供配置信息。
.aspx.resx/.resx:資源文件,資源是在邏輯上由應用程序部署的任何非可執行數據。通過在資源文件中存儲數據,無需重新編譯整個應用程序即可更改數據。 51aspx.com
.XSD:XML schema的一種.從DTD,XDR發展到XSD
.pdb:PDB(程序數據庫)文件保持著調試和項目狀態信息,從而可以對程序的調試配置進行增量鏈接。
.suo:解決方案用戶選項,記錄所有將與解決方案建立關聯的選項,以便在每次打開時,它都包含您所做的自定義設置。
.asmx:.asmx文件包含 WebService 處理指令,并用作 XML Web services 的可尋址入口點 51aspx。
.vsdisco(項目發現)文件 基于 XML 的文件,它包含為 Web 服務提供發現信息的資源的鏈接 (URL-51aspx )。
.htc:一個HTML文件,包含腳本和定義組件的一系列HTC特定元素.htc提供在腳本中implement組件的機制
1.把:SkinMagicLib.h,DETOURS.lib,SkinMagicLib.lib三個文件拷入工程目錄
注意:如果你的工程是使用的共享DLL,那么你要選擇共享DLL的SkinMagicLib.h,SkinMagicLib.lib
如果你的程序是使用的靜態連接DLL的,那你選擇靜態的SkinMagicLib.h,SkinMagicLib.lib
否則連接時出問題...
2."工程"->添加到工程->文件->將DETOURS.lib,SkinMagicLib.lib添加到工程中
3.在stdafx.h中,加入#include "SkinMagicLib.h"
4.將皮膚文件拷入工程目錄中的"rec"文件夾中
5.工作空間中切換到ResourceView,右鍵選"引入",在彈出的對話框中找到皮膚文件,選中將其插入到工程文件中
在接下來的對話框中起個文件夾名:"SKINMAGIC",將皮膚文件插入到該文件夾中
6.皮膚文件插入到工程后,可在ResourceView中找到皮膚文件的ID,右鍵選屬性,改ID名字
7.在ClassView中找到CxxxAPP類.然后找到InitInstance()函數,然后在
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
之前加入以下代碼:
VERIFY( 1 == InitSkinMagicLib( AfxGetInstanceHandle(), "Demo" ,
NULL,
NULL ) );
VERIFY( 1 == LoadSkinFromResource( AfxGetInstanceHandle() , "XPGREEN" ,"SKINMAGIC") );
VERIFY( 1 == SetWindowSkin( m_pMainWnd->m_hWnd , "MainFrame" ));
VERIFY( 1 == SetDialogSkin( "Dialog" ) );
8.在CxxxAPP類中,右鍵選AddVirtualFunction,然后添加ExitInstance()函數,在其中加入:
ExitSkinMagicLib();
9.編譯,連接,執行...OK,皮膚添加完成...
從前文可知,DLL在程序編制中可作出巨大貢獻,它提供了具共性代碼的復用能力。但是,正如一門高深的武學,若被掌握在正義之俠的手上,便可助其仗義江湖;但若被掌握在邪惡之徒的手上,則必然在江湖上掀起腥風血雨。DLL正是一種這樣的武學。DLL一旦染上了魔性,就不再是正常的DLL程序,而是DLL木馬,一種惡貫滿盈的病毒,令特洛伊一夜之間國破家亡。
DLL木馬的原理 DLL木馬的實現原理是編程者在DLL中包含木馬程序代碼,隨后在目標主機中選擇特定目標進程,以某種方式強行指定該進程調用包含木馬程序的DLL,最終達到侵襲目標系統的目的。
正是DLL程序自身的特點決定了以這種形式加載木馬不僅可行,而且具有良好的隱藏性:
(1)DLL程序被映射到宿主進程的地址空間中,它能夠共享宿主進程的資源,并根據宿主進程在目標主機的級別非法訪問相應的系統資源;
(2)DLL程序沒有獨立的進程地址空間,從而可以避免在目標主機中留下"蛛絲馬跡",達到隱蔽自身的目的。
DLL木馬實現了"真隱藏",我們在任務管理器中看不到木馬"進程",它完全溶進了系統的內核。與"真隱藏"對應的是"假隱藏","假隱藏"木馬把自己注冊成為一個服務。雖然在任務管理器中也看不到這個進程,但是"假隱藏"木馬本質上還具備獨立的進程空間。"假隱藏"只適用于Windows9x的系統,對于基于WINNT的操作系統,通過服務管理器,我們可以發現系統中注冊過的服務。
DLL木馬注入其它進程的方法為遠程線程插入。
遠程線程插入技術指的是通過在另一個進程中創建遠程線程的方法進入那個進程的內存地址空間。將木馬程序以DLL的形式實現后,需要使用插入到目標進程中的遠程線程將該木馬DLL插入到目標進程的地址空間,即利用該線程通過調用Windows API LoadLibrary函數來加載木馬DLL,從而實現木馬對系統的侵害。
DLL木馬注入程序 這里涉及到一個非常重要的Windows API――CreateRemoteThread。與之相比,我們所習慣使用的CreateThread API函數只能在進程自身內部產生一個新的線程,而且被創建的新線程與主線程共享地址空間和其他資源。而CreateRemoteThread則不同,它可以在另外的進程中產生線程!CreateRemoteThread有如下特點:
(1)CreateRemoteThread較CreateThread多一個參數hProcess,該參數用于指定要創建線程的遠程進程,其函數原型為:
HANDLE CreateRemoteThread( HANDLE hProcess, //遠程進程句柄 LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); |
(2)線程函數的代碼不能位于我們用來注入DLL木馬的進程所在的地址空間中。也就是說,我們不能想當然地自己寫一個函數,并把這個函數作為遠程線程的入口函數;
(3)不能把本進程的指針作為CreateRemoteThread的參數,因為本進程的內存空間與遠程進程的不一樣。
以下程序由作者Shotgun的DLL木馬注入程序簡化而得(
單擊此處下載,在經典書籍《Windows核心編程》中我們也可以看到類似的例子),它將d盤根目錄下的troydll.dll插入到ID為4000的進程中:
#include <windows.h> #include <stdlib.h> #include <stdio.h>
void CheckError ( int, int, char *); //出錯處理函數
PDWORD pdwThreadId; HANDLE hRemoteThread, hRemoteProcess; DWORD fdwCreate, dwStackSize, dwRemoteProcessId; PWSTR pszLibFileRemote=NULL;
void main(int argc,char **argv) { int iReturnCode; char lpDllFullPathName[MAX_PATH]; WCHAR pszLibFileName[MAX_PATH]={0};
dwRemoteProcessId = 4000; strcpy(lpDllFullPathName, "d:\\troydll.dll"); //將DLL文件全路徑的ANSI碼轉換成UNICODE碼 iReturnCode = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, lpDllFullPathName, strlen(lpDllFullPathName), pszLibFileName, MAX_PATH); CheckError(iReturnCode, 0, "MultByteToWideChar"); //打開遠程進程 hRemoteProcess = OpenProcess(PROCESS_CREATE_THREAD | //允許創建線程 PROCESS_VM_OPERATION | //允許VM操作 PROCESS_VM_WRITE, //允許VM寫 FALSE, dwRemoteProcessId ); CheckError( (int) hRemoteProcess, NULL, "Remote Process not Exist or Access Denied!"); //計算DLL路徑名需要的內存空間 int cb = (1 + lstrlenW(pszLibFileName)) * sizeof(WCHAR); pszLibFileRemote = (PWSTR) VirtualAllocEx( hRemoteProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE); CheckError((int)pszLibFileRemote, NULL, "VirtualAllocEx"); //將DLL的路徑名復制到遠程進程的內存空間 iReturnCode = WriteProcessMemory(hRemoteProcess, pszLibFileRemote, (PVOID) pszLibFileName, cb, NULL); CheckError(iReturnCode, false, "WriteProcessMemory"); //計算LoadLibraryW的入口地址 PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW"); CheckError((int)pfnStartAddr, NULL, "GetProcAddress"); //啟動遠程線程,通過遠程線程調用用戶的DLL文件 hRemoteThread = CreateRemoteThread( hRemoteProcess, NULL, 0, pfnStartAddr, pszLibFileRemote, 0, NULL); CheckError((int)hRemoteThread, NULL, "Create Remote Thread"); //等待遠程線程退出 WaitForSingleObject(hRemoteThread, INFINITE); //清場處理 if (pszLibFileRemote != NULL) { VirtualFreeEx(hRemoteProcess, pszLibFileRemote, 0, MEM_RELEASE); } if (hRemoteThread != NULL) { CloseHandle(hRemoteThread ); } if (hRemoteProcess!= NULL) { CloseHandle(hRemoteProcess); } }
//錯誤處理函數CheckError() void CheckError(int iReturnCode, int iErrorCode, char *pErrorMsg) { if(iReturnCode==iErrorCode) { printf("%s Error:%d\n\n", pErrorMsg, GetLastError()); //清場處理 if (pszLibFileRemote != NULL) { VirtualFreeEx(hRemoteProcess, pszLibFileRemote, 0, MEM_RELEASE); } if (hRemoteThread != NULL) { CloseHandle(hRemoteThread ); } if (hRemoteProcess!= NULL) { CloseHandle(hRemoteProcess); } exit(0); } } |
從DLL木馬注入程序的源代碼中我們可以分析出DLL木馬注入的一般步驟為:
(1)取得宿主進程(即要注入木馬的進程)的進程ID dwRemoteProcessId;
(2)取得DLL的完全路徑,并將其轉換為寬字符模式pszLibFileName;
(3)利用Windows API OpenProcess打開宿主進程,應該開啟下列選項:
a.PROCESS_CREATE_THREAD:允許在宿主進程中創建線程;
b.PROCESS_VM_OPERATION:允許對宿主進程中進行VM操作;
c.PROCESS_VM_WRITE:允許對宿主進程進行VM寫。
(4)利用Windows API VirtualAllocEx函數在遠程線程的VM中分配DLL完整路徑寬字符所需的存儲空間,并利用Windows API WriteProcessMemory函數將完整路徑寫入該存儲空間;
(5)利用Windows API GetProcAddress取得Kernel32模塊中LoadLibraryW函數的地址,這個函數將作為隨后將啟動的遠程線程的入口函數;
(6)利用Windows API CreateRemoteThread啟動遠程線程,將LoadLibraryW的地址作為遠程線程的入口函數地址,將宿主進程里被分配空間中存儲的完整DLL路徑作為線程入口函數的參數以另其啟動指定的DLL;
(7)清理現場。
DLL木馬的防治 從DLL木馬的原理和一個簡單的DLL木馬程序中我們學到了DLL木馬的工作方式,這可以幫助我們更好地理解DLL木馬病毒的防治手段。
一般的木馬被植入后要打開一網絡端口與攻擊程序通信,所以防火墻是抵御木馬攻擊的最好方法。防火墻可以進行數據包過濾檢查,我們可以讓防火墻對通訊端口進行限制,只允許系統接受幾個特定端口的數據請求。這樣,即使木馬植入成功,攻擊者也無法進入到受侵系統,防火墻把攻擊者和木馬分隔開來了。
對于DLL木馬,一種簡單的觀察方法也許可以幫助用戶發現之。我們查看運行進程所依賴的DLL,如果其中有一些莫名其妙的DLL,則可以斷言這個進程是宿主進程,系統被植入了DLL木馬。"道高一尺,魔高一丈",現如今,DLL木馬也發展到了更高的境界,它們看起來也不再"莫名其妙"。在最新的一些木馬里面,開始采用了先進的DLL陷阱技術,編程者用特洛伊DLL替換已知的系統DLL。特洛伊DLL對所有的函數調用進行過濾,對于正常的調用,使用函數轉發器直接轉發給被替換的系統DLL;對于一些事先約定好的特殊情況,DLL會執行一些相應的操作。
本文給出的只是DLL木馬最簡單情況的介紹,讀者若有興趣深入研究,可以參考其它資料。