1.關(guān)于文章的獲取
許多讀者發(fā)來e-mail詢問本系列文章的相關(guān)事宜,如:
(1) 是否已出版?
(2) 哪里可以下載打包版?
(3) 哪里可以下載筆者的其它文章?
還有一些讀者對日前筆者在天極網(wǎng)發(fā)表的《C語言嵌入式系統(tǒng)編程修煉之道》非常喜愛,給予了熱情洋溢的贊揚(yáng),詢問筆者能否繼續(xù)創(chuàng)作嵌入式編程方面的文章。
對于這些問題,統(tǒng)一作答如下:
(1)本系列文章暫時尚未出版;
(2)您可以在天極網(wǎng)軟件頻道下載筆者的多數(shù)拙作。另外,我也將不定期將這些文章上傳到我的博客( http://blog.donews.com/21cnbao/)。所有文章中的例程源代碼均經(jīng)過親手調(diào)試,驗(yàn)證無誤;
(3)就嵌入式系統(tǒng)開發(fā),筆者將繼續(xù)進(jìn)行此方面的創(chuàng)作,新近將推出《基于嵌入式實(shí)時OS VxWorks的多任務(wù)程序設(shè)計(jì)》及《領(lǐng)悟:從Windows多線程到VxWorks的多任務(wù)》。
非常感謝讀者朋友對這些文章的喜愛,在下將竭盡所能地為您提供更多的好文章。
2.關(guān)于DLL的疑問
你好,看了你寫的"VC++ DLL編程深入淺出",特別有收獲。 只是有個地方我老搞不明白,就是用DLL導(dǎo)出全局變量時,指定了.lib的路徑(#pragma comment(lib,"dllTest.lib")),那么.dll的文件的路徑呢,我嘗試著把.dll文件移到別的地方程序就無法正常運(yùn)行了,請問.dll在這里怎么指定。
希望您能在百忙中抽空給我解答一下,不勝感激!
一位編程愛好者
回答:
Windows按下列順序搜索DLL:
(1)當(dāng)前進(jìn)程的可執(zhí)行模塊所在的目錄;
(2)當(dāng)前目錄;
(3)Windows 系統(tǒng)目錄,通過GetSystemDirectory 函數(shù)可獲得此目錄的路徑;
(4)Windows 目錄,通過GetWindowsDirectory 函數(shù)可獲得此目錄的路徑;
(5)PATH 環(huán)境變量中列出的目錄。
因此,隱式鏈接時,DLL文件的路徑不需要指定也不能指定,系統(tǒng)指定按照1~5的步驟尋找DLL,但是對應(yīng)的.lib文件卻需要指定路徑;如果使用Windows API函數(shù)LoadLibrary動態(tài)加載DLL,則可以指定DLL的路徑。
你好,我是一位C++初學(xué)者,我在PCONLINE看了教學(xué)之后,受益不淺。我想問一下能否在DLL里使用多線程?MSDN上用#using <mscorlib.dll>這個指令之后實(shí)現(xiàn)了多線程,不過好象不支持DLL..
請問有什么辦法支持制作多線程DLL??能否給一個源碼來?
回答:
在DLL中可以處理多線程,WIN32對于多線程的支持是操作系統(tǒng)本身提供的一種能力,并不在于用戶編寫的是哪一類程序。即便是一個控制臺程序,我們都可以使用多線程:
#include <stdio.h>
#include <windows.h>
void ThreadFun(void)
{
while(1)
{
printf( "this is new thread\n" );
Sleep( 1000 );
}
}
int main()
{
DWORD threadID;
CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFun, NULL, 0, &threadID );
while(1)
{
printf( "this is main thread\n" );
Sleep( 1000 );
}
}
觀察程序運(yùn)行的結(jié)果為在控制臺窗口上交替輸出this is main thread、this is new thread。
我們來看下面的一個多線程DLL的例子。
DLL程序提供一個接口函數(shù)SendInit,在此接口中啟動發(fā)送線程SendThreadFunc,在這個線程的對應(yīng)工作函數(shù)中我們使用原始套接字socket發(fā)送報(bào)文。參考微軟出版的經(jīng)典書籍《Windows核心編程》,我們發(fā)現(xiàn),不宜在DLL被加載的時候(即進(jìn)程綁定時)啟動一個新的線程。
這個線程等待一個CEvent事件(用于線程間通信),應(yīng)用程序調(diào)用DLL中的接口函數(shù)SendMsg( InterDataPkt sendData )可以釋放此事件。下面是相關(guān)的源代碼:
(1)發(fā)送報(bào)文線程入口函數(shù)
///////////////////////////////////////////////////////////////////////////
//函數(shù)名:SendThreadFunc
//函數(shù)功能:發(fā)送報(bào)文工作線程入口函數(shù),使用UDP協(xié)議
////////////////////////////////////////////////////////////////////////////
DWORD WINAPI SendThreadFunc( LPVOID lpvThreadParm )
//提示:對于線程函數(shù)應(yīng)使用WINAPI聲明,WINAPI被宏定義為__stdcall
{
/* 創(chuàng)建socket */
sendSock = socket ( AF_INET, SOCK_DGRAM, 0 );
if ( sendSock == INVALID_SOCKET )
{
AfxMessageBox ( "Socket創(chuàng)建失敗" );
closesocket ( recvSock );
}
/* 獲得目標(biāo)節(jié)點(diǎn)端口與地址 */
struct sockaddr_in desAddr;
desAddr.sin_family=AF_INET;
desAddr.sin_port=htons( DES_RECV_PORT ); //目標(biāo)節(jié)點(diǎn)接收端口
desAddr.sin_addr.s_addr = inet_addr( DES_IP );
/* 發(fā)送數(shù)據(jù) */
while(1)
{
WaitForSingleObject( hSendEvent, 0xffffffffL );//無限等待事件發(fā)生
ResetEvent( hSendEvent );
sendto( sendSock, (char *)sendSockData.data, sendSockData.len, 0, (struct sockaddr*)&desAddr, sizeof(desAddr) );
}
return -1;
}
(2)MFC規(guī)則DLL的InitInstance函數(shù)
/////////////////////////////////////////////////////////////////////////////
// CMultiThreadDllApp initialization
BOOL CMultiThreadDllApp::InitInstance()
{
if ( !AfxSocketInit() ) //初始化socket
{
AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
return FALSE;
}
return TRUE;
}
(3)啟動發(fā)送線程
////////////////////////////////////////////////////////////////////////////////
//函數(shù)名:SendInit
//函數(shù)功能:DLL提供給應(yīng)用程序調(diào)用接口,用于啟動發(fā)送線程
/////////////////////////////////////////////////////////////////////////////
void SendInit(void)
{
hSendThread = CreateThread( NULL, 1000, SendThreadFunc, this, 1, &uSendThreadID );
}
(4)SendMsg函數(shù)
////////////////////////////////////////////////////////////////////////////////
//函數(shù)名:SendMsg
//函數(shù)功能:DLL提供給應(yīng)用程序調(diào)用接口,用于發(fā)送報(bào)文
/////////////////////////////////////////////////////////////////////////////
extern "C" void WINAPI SendMsg( InterDataPkt sendData )
{
sendSockData = sendData;
SetEvent( hSendEvent ); //釋放發(fā)送事件
}
以上程序僅僅是一個簡單的例子,其實(shí)在許多工程應(yīng)用中,我們經(jīng)??吹竭@樣的處理方式。這個DLL對用戶而言僅僅使一個簡單的接口函數(shù)SendMsg,對調(diào)用它的應(yīng)用程序屏蔽了多線程的技術(shù)細(xì)節(jié)。與之類似,MFC提供的CSocket類在底層自己采用了多線程機(jī)制,所以使我們免去了對多線程的使用。
您好,看了您的DLL文章,發(fā)現(xiàn)導(dǎo)出函數(shù)可以直接用_declspec(dllexport)聲明或在.def文件中定義,變量的導(dǎo)出也一樣。我想知道類是否也可以在.def文件中導(dǎo)出?您的文章中只講了在類前添加_declspec(dllexport)導(dǎo)出類的方法。請您指教!
回答:
一般我們不采用.def文件導(dǎo)出類,但是這并不意味著類不能用.def文件導(dǎo)出類。
使用Depends查看連載2的"導(dǎo)出類"例程生成的DLL,我們發(fā)現(xiàn)其導(dǎo)出了如圖21的眾多"怪"symbol,這些symbol都是經(jīng)過編譯器處理的。因此,為了以.def文件導(dǎo)出類,我們必須把這些"怪"symbol全部導(dǎo)出,實(shí)在是不劃算啊!所以對于類,我們最好直接以_declspec(dllexport)導(dǎo)出。
圖1 導(dǎo)出類時導(dǎo)出的symbol
您好,看了您的DLL文章,知道怎么創(chuàng)建DLL了,但是面對一個具體的工程,我還是不知道究竟應(yīng)該把什么做成DLL?您能給一些這方面的經(jīng)驗(yàn)嗎?
回答:
DLL一般用于軟件模塊中較固定、較通用的可以被復(fù)用的模塊,這里有一個非常好的例子,就是豪杰超級解霸。梁肇新大師把處理視頻和音頻的算法模塊專門做成了兩個DLL,供超級解霸的用戶界面GUI程序調(diào)用,實(shí)在是DLL設(shè)計(jì)的模范教程。所謂"萬變不離其宗",超級解霸的界面再cool,用到的還是那幾個DLL!具體請參考《編程高手箴言》一書。
您好,您的DLL文章講的都是Windows的,請問Linux操作系統(tǒng)上可以制作DLL嗎?如果能,和Windows有什么不一樣?謝謝!
回答:
在Linux操作系統(tǒng)中,也可以采用動態(tài)鏈接技術(shù)進(jìn)行軟件設(shè)計(jì),但與Windows下DLL的創(chuàng)建和調(diào)用方式有些不同。
Linux操作系統(tǒng)中的共享對象技術(shù)(Shared Object)與Windows里的DLL相對應(yīng),但名稱不一樣,其共享對象文件以.so作為后綴。與Linux共享對象技術(shù)相關(guān)的一些函數(shù)如下:
(1)打開共享對象,函數(shù)原型:
//打開名為filename共享對象,并返回操作句柄;
void *dlopen (const char *filename, int flag);
(2)取函數(shù)地址,函數(shù)原型:
//獲得接口函數(shù)地址
void *dlsym(void *handle, char *symbol);
(3)關(guān)閉共享對象,函數(shù)原型:
//關(guān)閉指定句柄的共享對象
int dlclose (void *handle);
(4)動態(tài)庫錯誤函數(shù),函數(shù)原型:
//共享對象操作函數(shù)執(zhí)行失敗時,返回出錯信息
const char *dlerror(void);
從這里我們分明看到Windows API――LoadLibrary、FreeLibrary和GetProcAddress的影子!又一個"萬變不離其宗"!
本系列文章的連載暫時告一段落,您可以繼續(xù)給筆者發(fā)送email(mailto:21cnbao@21cn.com)討論DLL的編程問題。對于文中的錯誤和紕漏,也熱誠歡迎您指正。