午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

如何抓住狡猾的狐貍?(復(fù)合文檔)

 king9413 2013-08-15

環(huán)境:Windows 98SE/2000,VC++ 6.0,F(xiàn)oxmail 4.1 簡體中文版

摘要

本文旨在介紹 Foxmail 的賬號存儲機制,并基于此編寫一個輔助工具 SmartFox ,實現(xiàn)閃存“隨身郵”功能。 

問題的出現(xiàn)與分析

最近我買了一支愛國者的經(jīng)典型“迷你王”閃存,自己安裝了 Foxmail ,并在閃存上創(chuàng)建了兩個賬號以收發(fā)郵件。但當(dāng)我在另一臺 PC 上使用 Foxmail 時,它卻提示沒有可用的賬號。這是怎么回事呢?原來, Foxmail 在創(chuàng)建賬號時,會將這個賬號所在目錄完整的路徑名記錄下來。由于閃存在不同的 PC 上獲得的盤符不一定相同,這樣在閃存的盤符改變后, Foxmail 便因無法定位該目錄而發(fā)生錯誤。

開始時,我編輯了一個批處理文件 Foxmail.BAT 用以啟動 Foxmail 。其思路是將閃存的根目錄重定向為 Z: 盤,每次將閃存的當(dāng)前盤符作為參數(shù)傳遞給這個批處理文件即可。為此我不得不將之前創(chuàng)建的賬號刪除,在命令行提示符下啟動批處理命令,然后在 Z: 盤下創(chuàng)建賬號。這樣不是太麻煩了嗎?是的!我也曾嘗試過利用中間文件自動進(jìn)行參數(shù)傳遞,但是由于 DOS 的不可重入性而不能保證每次都成功運行。因此我開始剖析 Foxmail 的賬號存儲機制。 

Foxmail 的賬號存儲機制

Foxmail 將每個賬號的最基本信息(賬號名稱、存儲目錄)存放在安裝目錄下的文件Accounts.CFG 中。在每個賬號的目錄中則利用文件 Account.STG 存儲該賬號的其他信息。

Accounts.CFG 是一個復(fù)合文檔,它包括一個 Ver30 存儲( Storage )。在 Ver30 存儲下有一個accounts.cfg 流( Stream )。你可以使用 Visual Studio 6.0 提供的 DocFile 閱讀器打開它。關(guān)于存儲和流的更多知識,請參閱 MSDN: [Platform SDK] Structured Storage 。

經(jīng)過分析,我得到了 Foxmail 的賬號存儲機制。如下即為 accounts.cfg 流的 C 語言描述結(jié)構(gòu)(表中均為十六進(jìn)制):

偏移地址

變量名

描述

00000000

BYTE Reserved [40];

文件頭。在 00000007 處存儲了曾創(chuàng)建賬號的個數(shù)。

00000040

DWORD cAccount;

現(xiàn)有的賬號數(shù)。

第一個賬號信息塊( AIB , Account Information Block )的開始。

00000044

DWORD idxAccount;

該賬號的順序編號。


DWORD lenACTName;

賬號名字符串的長度。


String strACTName [lenACTName];

賬號名字符串。


DWORD lenACTPath;

賬號所在目錄的字符串長度。


String strACTPath [lenACTPath];

賬號所在目錄的全路徑名。


BYTE Reserved [18];

0x18 個 00 (可能用以存儲密碼)。

下一個 AIB

注意:所有的字符串長度均不包括結(jié)尾符在內(nèi)。

編程實現(xiàn)

現(xiàn)在我們就可以開始編寫 Foxmail 的輔助工具 SmartFox 了。 重要約定:

1.所有賬號的目錄均在閃存上!

2.SmartFox 在 Foxmail 安裝目錄下工作!

程序的流程:獲得閃存的當(dāng)前盤符,打開 accounts.cfg 流,修改所有賬號的目錄盤符為閃存當(dāng)前盤符,啟動 Foxmail 。我認(rèn)為沒有必要在 Foxmail 退出后恢復(fù) accounts.cfg 的內(nèi)容,你認(rèn)為呢?

SmartFox 是一個利用 MFC 實現(xiàn)的基于對話框的應(yīng)用程序,靜態(tài)鏈接 MFC 的動態(tài)鏈接庫。

運行界面如下,單擊左鍵啟動 Foxmail ,單擊右鍵退出。

(一)對程序中使用的 API 的介紹,更詳細(xì)的內(nèi)容請參見 MSDN :

1. 獲得當(dāng)前目錄的全路徑名 (Win API)

1.DWORD GetCurrentDirectory(
2.  DWORD nBufferLength,  // 保存目錄名的緩沖區(qū)大小
3.  LPTSTR lpBuffer       // 指向緩沖區(qū)的指針
4.);

2. 打開一個復(fù)合文檔 (Win API)

1.HRESULT StgOpenStorage(
2.  const WCHAR *pwcsName,    //復(fù)合文檔的文件名
3.  IStorage *pstgPriority,   //先前已打開的根存儲的指針
4.  DWORD grfMode,            //訪問模式
5.  SNB snbExclude,           //指向一個SNB結(jié)構(gòu)的指針,以確定哪些元素將被排除訪問
6.  DWORD reserved,           //保留
7.  IStorage **ppstgOpen      //接受返回的IStorage接口指針
8.);

3. 打開一個子存儲或流 (IStorage API)

1.HRESULT OpenStorage(
2.  const WCHAR *pwcsName,   //要打開的存儲的名字
3.  IStorage *pstgPriority,  //該參數(shù)一定為NULL值
4.  DWORD grfMode,           //訪問模式
5.  SNB snbExclude,          //該參數(shù)一定為NULL值
6.  DWORD reserved,          //保留
7.  IStorage **ppstg         //接受返回的IStorage接口指針
8.);

OpenStream 的參數(shù)與 OpenStorage 的差不多,只是返回的是一個 IStream 指針。

4. 取得流的大小,移動流的讀寫指針,從流讀寫數(shù)據(jù) (IStream API)

01.HRESULT Stat(
02.  STATSTG *pstatstg,  //指向一個STATSTG結(jié)構(gòu)的指針,STATSTG的cb域即為流的大小。
03.  DWORD grfStatFlag   //決定是否要在STATSTG中返回某些值的標(biāo)志
04.);
05. 
06. 
07.HRESULT Seek(
08.  LARGE_INTEGER dlibMove,          //相對于dwOrigin的偏址
09.  DWORD dwOrigin,                  //起始位置
10.  ULARGE_INTEGER *plibNewPosition  //接受指向新位置的指針
11.);
12. 
13. 
14.HRESULT Read(
15.  void *pv,  //指向數(shù)據(jù)緩沖區(qū)的指針
16.  ULONG cb,  //要讀的字節(jié)數(shù)
17.  ULONG *pcbRead //實際讀出的字節(jié)數(shù)
18.);Write 的參數(shù)與 Read 的一樣。

5. 加載外部程序 (Win API)

01.HINSTANCE ShellExecute(
02.    HWND hwnd,  //父窗口句柄
03.    LPCTSTR lpVerb, //要執(zhí)行的動作:edit,explore,find,open,print,properties
04.    LPCTSTR lpFile,     //文件名
05.    LPCTSTR lpParameters,   //傳遞的命令行參數(shù)
06.    LPCTSTR lpDirectory,        //缺省工作目錄
07.    INT nShowCmd    //窗口的顯示模式
08.);
09. 
10.UINT WinExec(
11.  LPCSTR lpCmdLine,  // 命令的字符串
12.  UINT uCmdShow      //窗口的顯示模式
13.);

6. 此外,我在程序中使用了 COM 庫的缺省 IMalloc 接口管理緩沖區(qū)

1.HRESULT CoGetMalloc(      //這是一個Win API
2.  DWORD dwMemContext,  //決定該內(nèi)存塊是否被共享的標(biāo)志
3.  LPMALLOC * ppMalloc  //接受返回的內(nèi)存分配器的IMalloc接口指針
4.);

IMalloc::Alloc() ,IMalloc::Free() 的使用與 C 語言中的 alloc() 和 free() 類似,在此不再贅述。

(二)實現(xiàn)步驟:

在 StdAfx.h 中加入以下的頭文件:objidl.h ,afxole.h ,afxpriv.h ,afxtempl.h

1.在 CSmartFoxApp::InitInstance() 中加入: ::CoInitialize(NULL);

2.為 CSmartFoxDlg 添加如下數(shù)據(jù)成員:

1.IStorage * m_pRootStg; // 根存儲的接口指針
2.IStorage * m_pVer30Stg; //Ver30 存儲的接口指針
3.IStream * m_pStream; //accounts.cfg 流的接口指針
4.char * m_pBuffer; // 用以讀寫 accounts.cfg 流的緩沖區(qū)指針
5.char m_Driver; // 閃存的當(dāng)前盤符
6.CArray  m_aryPosition; // 保存流中賬號目錄所在偏移地址的數(shù)組   

3.利用 Class Wizard 為 CSmartFoxDlg 添加或修改下列函數(shù):

001.BOOL CSmartFoxDlg::OnInitDialog()
002.    {
003.    CDialog::OnInitDialog();
004.    SetIcon(m_hIcon, TRUE);         // Set big icon
005.    SetIcon(m_hIcon, FALSE);        // Set small icon
006.    try
007.        {
008.        m_Driver = GetDriver();
009.        m_pStream = GetIStream();
010.        m_pBuffer = GetBuffer(m_pStream);
011.        GetAccountInfo(m_pStream, m_pBuffer);
012.        }
013.    catch (char * sMsg)
014.        {
015.        AfxMessageBox(sMsg,MB_OK,NULL);
016.        ClearUp();
017.        }
018.    return TRUE;  // return TRUE  unless you set the focus to a control
019.    }
020. 
021.void CSmartFoxDlg::OnClickEmail()
022.{
023.    ShellExecute(this->m_hWnd,
024.               "open",
025.               "mailto: korby@sohu.com?subject=Re: 關(guān)于SmartFox的意見",  
026.               NULL,
027.               NULL,
028.               SW_SHOWNORMAL);
029.    ClearUp();
030.}
031. 
032.void CSmartFoxDlg::OnLButtonDown(UINT nFlags, CPoint point)
033.    {
034.    // I will modify the driver letter of accounts here, and startup FoxMail.
035.    try
036.        {
037.        ModifyAccountDriver(m_pStream, m_pBuffer);
038.        ClearUp();
039.        if (WinExec("FoxMail.EXE", SW_SHOWNORMAL) < 31) throw "加載FoxMail.EXE失敗";
040.        }
041.    catch (char * sMsg)
042.        {
043.        AfxMessageBox(sMsg,MB_OK,NULL);
044.        }
045.    }
046. 
047.void CSmartFoxDlg::OnRButtonDown(UINT nFlags, CPoint point)
048.{
049.    // If I don''''t do this, the message will be transferred to window behind.
050.    SetCapture();
051.}
052. 
053.void CSmartFoxDlg::OnRButtonUp(UINT nFlags, CPoint point)
054.{
055.    ::ReleaseCapture();
056.    ClearUp();
057.}
058. 
059.void CSmartFoxDlg::ClearUp()
060.    {
061.    if (m_pRootStg  != NULL) m_pRootStg->Release();
062.    if (m_pVer30Stg != NULL) m_pVer30Stg->Release();
063.    if (m_pStream   != NULL) m_pStream->Release();
064.    if (m_pBuffer   != NULL)
065.        {
066.        IMalloc *  pMalloc;
067.        ::CoGetMalloc(MEMCTX_TASK, &pMalloc);
068.        pMalloc->Free(m_pBuffer);
069.        pMalloc->Release();
070.        }
071.    ::CoUninitialize();
072.    OnCancel();
073.    }
074. 
075.// All codes below find out driver letter and directories of each account.
076.char CSmartFoxDlg::GetDriver()
077.    {
078.    char sCurDir[256];
079.    int ret = ::GetCurrentDirectory(256, sCurDir);
080.    if (ret == NULL) throw  "取當(dāng)前驅(qū)動器盤符時失敗";
081.    else  return sCurDir[0];
082.    }
083. 
084.IStream * CSmartFoxDlg::GetIStream()
085.    {
086.    USES_CONVERSION;
087.    // Get interface Storage pointer of Accounts.CFG
088.    IStream *  pStream;
089.    HRESULT    hr;
090.    hr = ::StgOpenStorage(T2COLE("Accounts.CFG"),
091.                 NULL,
092.                 STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
093.                 NULL,
094.                 0,
095.                 &m_pRootStg);
096.    if (hr != S_OK) throw "打開Accounts.CFG文件時失敗";
097.     
098.    hr = m_pRootStg->OpenStorage(T2COLE("Ver30"),
099.                    NULL,
100.                    STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
101.                    NULL,
102.                    0,
103.                    &m_pVer30Stg);
104.    if (hr != S_OK) throw "打開Ver30存儲時失敗";
105.     
106.    hr = m_pVer30Stg->OpenStream(T2COLE("accounts.cfg"),
107.                    NULL,
108.                    STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
109.                    NULL,
110.                    &pStream);
111.    if (hr != S_OK) throw "打開accounts.cfg流時失敗";
112.     
113.    return pStream;
114.    }
115. 
116.char * CSmartFoxDlg::GetBuffer(IStream * pStream)
117.    {
118.    STATSTG    StatStg;
119.    IMalloc *  pMalloc;
120.    char *     pBuffer;
121.    HRESULT    hr;
122.     
123.    hr = pStream->Stat(&StatStg, NULL);
124.    if (hr != S_OK) throw "讀取accounts.cfg流的大小時失敗";
125.     
126.    hr = ::CoGetMalloc(MEMCTX_TASK, &pMalloc);
127.    if (hr != S_OK) throw "獲取COM庫的IMalloc接口指針時失敗";
128.     
129.    pBuffer = (char *)pMalloc->Alloc(ULONG(StatStg.cbSize.QuadPart));
130.    if (pBuffer == NULL) throw "申請緩沖區(qū)時失敗";
131.             
132.    pMalloc->Release();
133.    return pBuffer;
134.    }
135. 
136.void CSmartFoxDlg::GetAccountInfo(IStream * pStream, char * pBuffer)
137.    {
138.    // I will find out names and directories of each account.
139.    STATSTG    StatStg;
140.    ULONG      cbReaded;
141.    HRESULT    hr;
142.     
143.    char *     p = pBuffer;
144.    DWORD      cAccount;
145.    DWORD      len;
146.    CString    name;    // Gets the name of account.
147.    CString    path;    // Gets the path of account.
148.     
149.    CString    strEdit; //Displays text in edit control.
150.    CEdit *    pEdit = (CEdit *) GetDlgItem(IDC_EDIT);
151.    strEdit.Format("閃存當(dāng)前為%c:盤, 現(xiàn)存賬號及其目錄: \r\n", m_Driver);
152.     
153.    hr = pStream->Stat(&StatStg, NULL);
154.    if (hr != S_OK) throw "讀取accounts.cfg流的大小時失敗";
155.     
156.    hr = pStream->Read(pBuffer, ULONG(StatStg.cbSize.QuadPart), &cbReaded);
157.    if (hr != S_OK) throw "讀取accounts.cfg流的內(nèi)容時失敗";
158.     
159.    p += 0x40;
160.    cAccount = (*p);    //Count of accounts.
161.    p += 0x4;
162. 
163.    for (DWORD i = 1; i <= cAccount; i++)
164.        {
165.        // Value of (*p) is index of account.
166.        if (DWORD(*p) != i) throw "accounts.cfg流損壞";
167.        p += 0x4;   //Skips the index number.
168.        len = DWORD(*p);    //Gets length of name.
169.        p += 0x4;   //Skips the length number. The string does not include NULL.
170.         
171.        name.Empty();
172.        path.Empty();
173.         
174.        for (DWORD n = 0; n < len; n++, p++) name += char (*p); // Gets account name.
175.         
176.        len = DWORD(*p); // Gets length of directory.
177.        p += 4; // Skips the length number.
178.                 
179.        m_aryPosition.Add(p-pBuffer);  //Stores offset into array.
180.         
181.        for (n = 0; n< len; n++, p++) path += char (*p); //Gets account path.
182. 
183.        strEdit += name+"\t"+path+"\r\n";
184.         
185.        p += 0x18; //Skips 0x18 Nulls.
186.        }
187.    pEdit->SetWindowText(strEdit);
188.    }
189. 
190.void CSmartFoxDlg::ModifyAccountDriver(IStream *pStream, char * pBuffer)
191.    {
192.    STATSTG    StatStg;
193.    ULONG      cbWrited;
194.    HRESULT    hr;
195. 
196.    LARGE_INTEGER  MovOffset;
197.    ULARGE_INTEGER NewPosition;
198.     
199.    char *     p;
200. 
201.    for (int i = 0; i< m_aryPosition.GetSize(); i++)
202.        {
203.        p = pBuffer + m_aryPosition.GetAt(i);
204.        (*p) = m_Driver;
205.        }
206. 
207.    hr = pStream->Stat(&StatStg, NULL);
208.    if (hr != S_OK) throw "讀取accounts.cfg流的大小時失敗";
209. 
210.    MovOffset.QuadPart = 0;
211.    hr = pStream->Seek(MovOffset, 0, &NewPosition);
212.    if (hr != S_OK) throw "移動accounts.cfg流的讀寫指針時失敗";
213.     
214.    pStream->Write(pBuffer, ULONG(StatStg.cbSize.QuadPart), &cbWrited);
215.    if (hr != S_OK) throw "向accounts.cfg流寫入數(shù)據(jù)時失敗";
216.    }

編譯鏈接生成 SmartFox.EXE 后,將其拷貝到 Foxmail 在閃存上的安裝目錄。利用 SmartFox.EXE 運行 Foxmail 就可以實現(xiàn)“隨身郵”的功能了!

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多