注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

BCB-DG's Blog

...

 
 
 

日志

 
 

Foxmail 7之前账号存储机制  

2013-05-10 11:46:57|  分类: Setup |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
//轉 Abbey

Foxmail 的账号存储机制

Foxmail 将每个账号的最基本信息(账号名称、存储目录)存放在安装目录下的文件Accounts.CFG 中。在每个账号的目录中则利用文件 Account.STG 存储该账号的其他信息。

Accounts.CFG 是一个复合文档,它包括一个 Ver30 存储( Storage )。在 Ver30 存储下有一个accounts.cfg 流( Stream )。你可以使用 Visual Studio 6.0 提供的 DocFile 阅读器打开它。关于存储和流的更多知识,请参阅 MSDN: [Platform SDK] Structured Storage 。

经过分析,我得到了 Foxmail 的账号存储机制。如下即为 accounts.cfg 流的 C 语言描述结构(表中均为十六进制):

偏移地址

变量名

描述

00000000

BYTE Reserved [40];

文件头。在 00000007 处存储了曾创建账号的个数。

00000040

DWORD cAccount;

现有的账号数。

第一个账号信息块( AIB , Account Information Block )的开始。

00000044

DWORD idxAccount;

该账号的顺序编号。


DWORD lenACTName;

账号名字符串的长度。


String strACTName [lenACTName];

账号名字符串。


DWORD lenACTPath;

账号所在目录的字符串长度。


String strACTPath [lenACTPath];

账号所在目录的全路径名。


BYTE Reserved [18];

0x18 个 00 (可能用以存储密码)。

下一个 AIB

注意:所有的字符串长度均不包括结尾符在内。

编程实现

现在我们就可以开始编写 Foxmail 的辅助工具 SmartFox 了。 重要约定:

1.所有账号的目录均在闪存上!

2.SmartFox 在 Foxmail 安装目录下工作!

程序的流程:获得闪存的当前盘符,打开 accounts.cfg 流,修改所有账号的目录盘符为闪存当前盘符,启动 Foxmail 。我认为没有必要在 Foxmail 退出后恢复 accounts.cfg 的内容,你认为呢?

SmartFox 是一个利用 MFC 实现的基于对话框的应用程序,静态链接 MFC 的动态链接库。

运行界面如下,单击左键启动 Foxmail ,单击右键退出。

(一)对程序中使用的 API 的介绍,更详细的内容请参见 MSDN :

1. 获得当前目录的全路径名 (Win API)

DWORD GetCurrentDirectory(
  DWORD nBufferLength,  // 保存目录名的缓冲区大小
  LPTSTR lpBuffer       // 指向缓冲区的指针
);

2. 打开一个复合文档 (Win API)

HRESULT StgOpenStorage(
  const WCHAR *pwcsName,    //复合文档的文件名
  IStorage *pstgPriority,   //先前已打开的根存储的指针
  DWORD grfMode,            //访问模式
  SNB snbExclude,           //指向一个SNB结构的指针,以确定哪些元素将被排除访问
  DWORD reserved,           //保留
  IStorage **ppstgOpen      //接受返回的IStorage接口指针
);

3. 打开一个子存储或流 (IStorage API)

HRESULT OpenStorage(
  const WCHAR *pwcsName,   //要打开的存储的名字
  IStorage *pstgPriority,  //该参数一定为NULL值
  DWORD grfMode,           //访问模式
  SNB snbExclude,          //该参数一定为NULL值
  DWORD reserved,          //保留
  IStorage **ppstg         //接受返回的IStorage接口指针
);

OpenStream 的参数与 OpenStorage 的差不多,只是返回的是一个 IStream 指针。

4. 取得流的大小,移动流的读写指针,从流读写数据 (IStream API)

HRESULT Stat(
  STATSTG *pstatstg,  //指向一个STATSTG结构的指针,STATSTG的cb域即为流的大小。
  DWORD grfStatFlag   //决定是否要在STATSTG中返回某些值的标志
);


HRESULT Seek(
  LARGE_INTEGER dlibMove,          //相对于dwOrigin的偏址
  DWORD dwOrigin,                  //起始位置
  ULARGE_INTEGER *plibNewPosition  //接受指向新位置的指针
);


HRESULT Read(
  void *pv,  //指向数据缓冲区的指针
  ULONG cb,  //要读的字节数
  ULONG *pcbRead //实际读出的字节数
);Write 的参数与 Read 的一样。 

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

HINSTANCE ShellExecute(
    HWND hwnd, 	//父窗口句柄
    LPCTSTR lpVerb,	//要执行的动作:edit,explore,find,open,print,properties
    LPCTSTR lpFile, 	//文件名
    LPCTSTR lpParameters, 	//传递的命令行参数
    LPCTSTR lpDirectory,		//缺省工作目录
    INT nShowCmd	//窗口的显示模式
);

UINT WinExec(
  LPCSTR lpCmdLine,  // 命令的字符串
  UINT uCmdShow      //窗口的显示模式
);

6. 此外,我在程序中使用了 COM 库的缺省 IMalloc 接口管理缓冲区

HRESULT CoGetMalloc(      //这是一个Win API
  DWORD dwMemContext,  //决定该内存块是否被共享的标志
  LPMALLOC * ppMalloc  //接受返回的内存分配器的IMalloc接口指针
);

IMalloc::Alloc() ,IMalloc::Free() 的使用与 C 语言中的 alloc() 和 free() 类似,在此不再赘述。

(二)实现步骤:

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

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

2.为 CSmartFoxDlg 添加如下数据成员:

IStorage * m_pRootStg; // 根存储的接口指针 
IStorage * m_pVer30Stg; //Ver30 存储的接口指针 
IStream * m_pStream; //accounts.cfg 流的接口指针 
char * m_pBuffer; // 用以读写 accounts.cfg 流的缓冲区指针 
char m_Driver; // 闪存的当前盘符 
CArray  m_aryPosition; // 保存流中账号目录所在偏移地址的数组    

3.利用 Class Wizard 为 CSmartFoxDlg 添加或修改下列函数:

BOOL CSmartFoxDlg::OnInitDialog()
	{
	CDialog::OnInitDialog();
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	try
		{
		m_Driver = GetDriver();
		m_pStream = GetIStream();
		m_pBuffer = GetBuffer(m_pStream);
		GetAccountInfo(m_pStream, m_pBuffer);
		}
	catch (char * sMsg)
		{
		AfxMessageBox(sMsg,MB_OK,NULL);
		ClearUp();
		}
	return TRUE;  // return TRUE  unless you set the focus to a control
	}

void CSmartFoxDlg::OnClickEmail() 
{
	ShellExecute(this->m_hWnd, 
               "open", 
               "mailto: korby@sohu.com?subject=Re: 关于SmartFox的意见",  
               NULL,
               NULL, 
               SW_SHOWNORMAL);
	ClearUp();
}

void CSmartFoxDlg::OnLButtonDown(UINT nFlags, CPoint point) 
	{
	// I will modify the driver letter of accounts here, and startup FoxMail.
	try
		{
		ModifyAccountDriver(m_pStream, m_pBuffer);
		ClearUp();
		if (WinExec("FoxMail.EXE", SW_SHOWNORMAL) < 31) throw "加载FoxMail.EXE失败";
		}
	catch (char * sMsg)
		{
		AfxMessageBox(sMsg,MB_OK,NULL);
		}
	}

void CSmartFoxDlg::OnRButtonDown(UINT nFlags, CPoint point) 
{
	// If I don''''t do this, the message will be transferred to window behind.
	SetCapture();
}

void CSmartFoxDlg::OnRButtonUp(UINT nFlags, CPoint point) 
{
	::ReleaseCapture();
	ClearUp();
}

void CSmartFoxDlg::ClearUp()
	{
	if (m_pRootStg  != NULL) m_pRootStg->Release();
	if (m_pVer30Stg != NULL) m_pVer30Stg->Release();
	if (m_pStream   != NULL) m_pStream->Release();
	if (m_pBuffer   != NULL) 
		{
		IMalloc *  pMalloc;
		::CoGetMalloc(MEMCTX_TASK, &pMalloc);
		pMalloc->Free(m_pBuffer);
		pMalloc->Release();
		}
	::CoUninitialize();
	OnCancel();
	}

// All codes below find out driver letter and directories of each account.
char CSmartFoxDlg::GetDriver()
	{
	char sCurDir[256];
	int ret = ::GetCurrentDirectory(256, sCurDir);
	if (ret == NULL) throw  "取当前驱动器盘符时失败";
	else  return sCurDir[0];
	}

IStream * CSmartFoxDlg::GetIStream()
	{
	USES_CONVERSION;
	// Get interface Storage pointer of Accounts.CFG
	IStream *  pStream;
	HRESULT    hr;
	hr = ::StgOpenStorage(T2COLE("Accounts.CFG"), 
                 NULL, 
                 STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 
                 NULL, 
                 0, 
                 &m_pRootStg);
	if (hr != S_OK) throw "打开Accounts.CFG文件时失败";
	
	hr = m_pRootStg->OpenStorage(T2COLE("Ver30"), 
	                NULL, 
	                STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 
	                NULL, 
	                0, 
	                &m_pVer30Stg);
	if (hr != S_OK) throw "打开Ver30存储时失败";
	
	hr = m_pVer30Stg->OpenStream(T2COLE("accounts.cfg"), 
	                NULL, 
	                STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 
	                NULL, 
	                &pStream);
	if (hr != S_OK) throw "打开accounts.cfg流时失败";
	
	return pStream;
	}

char * CSmartFoxDlg::GetBuffer(IStream * pStream)
	{
	STATSTG    StatStg;
	IMalloc *  pMalloc;
	char *     pBuffer;
	HRESULT    hr;
	
	hr = pStream->Stat(&StatStg, NULL);
	if (hr != S_OK) throw "读取accounts.cfg流的大小时失败";
	
	hr = ::CoGetMalloc(MEMCTX_TASK, &pMalloc);
	if (hr != S_OK) throw "获取COM库的IMalloc接口指针时失败";
	
	pBuffer = (char *)pMalloc->Alloc(ULONG(StatStg.cbSize.QuadPart));
	if (pBuffer == NULL) throw "申请缓冲区时失败";
			
	pMalloc->Release();
	return pBuffer;
	}

void CSmartFoxDlg::GetAccountInfo(IStream * pStream, char * pBuffer)
	{
	// I will find out names and directories of each account.
	STATSTG    StatStg;
	ULONG      cbReaded;
	HRESULT    hr;
	
	char *     p = pBuffer;
	DWORD	   cAccount;
	DWORD      len;
	CString    name;	// Gets the name of account.
	CString    path;	// Gets the path of account.
	
	CString    strEdit;	//Displays text in edit control.
	CEdit *    pEdit = (CEdit *) GetDlgItem(IDC_EDIT);
	strEdit.Format("闪存当前为%c:盘, 现存账号及其目录: \r\n", m_Driver);
	
	hr = pStream->Stat(&StatStg, NULL);
	if (hr != S_OK) throw "读取accounts.cfg流的大小时失败";
	
	hr = pStream->Read(pBuffer, ULONG(StatStg.cbSize.QuadPart), &cbReaded);
	if (hr != S_OK) throw "读取accounts.cfg流的内容时失败";
	
	p += 0x40;
	cAccount = (*p);	//Count of accounts.
	p += 0x4;

	for (DWORD i = 1; i <= cAccount; i++)
		{
		// Value of (*p) is index of account.
		if (DWORD(*p) != i) throw "accounts.cfg流损坏"; 
		p += 0x4;	//Skips the index number.
		len = DWORD(*p);	//Gets length of name.
		p += 0x4;	//Skips the length number. The string does not include NULL.
		
		name.Empty();
		path.Empty();
		
		for (DWORD n = 0; n < len; n++, p++) name += char (*p); // Gets account name.
		
		len = DWORD(*p); // Gets length of directory.
		p += 4; // Skips the length number.
				
		m_aryPosition.Add(p-pBuffer);  //Stores offset into array.
		
		for (n = 0; n< len; n++, p++) path += char (*p); //Gets account path.

		strEdit += name+"\t"+path+"\r\n";
		
		p += 0x18; //Skips 0x18 Nulls.
		}
	pEdit->SetWindowText(strEdit);
	}

void CSmartFoxDlg::ModifyAccountDriver(IStream *pStream, char * pBuffer)
	{
	STATSTG    StatStg;
	ULONG      cbWrited;
	HRESULT    hr;

	LARGE_INTEGER  MovOffset;
	ULARGE_INTEGER NewPosition;
	
	char *     p;

	for (int i = 0; i< m_aryPosition.GetSize(); i++)
		{
		p = pBuffer + m_aryPosition.GetAt(i);
		(*p) = m_Driver;
		}

	hr = pStream->Stat(&StatStg, NULL);
	if (hr != S_OK) throw "读取accounts.cfg流的大小时失败";

	MovOffset.QuadPart = 0;
	hr = pStream->Seek(MovOffset, 0, &NewPosition);
	if (hr != S_OK) throw "移动accounts.cfg流的读写指针时失败";
	
	pStream->Write(pBuffer, ULONG(StatStg.cbSize.QuadPart), &cbWrited);
	if (hr != S_OK) throw "向accounts.cfg流写入数据时失败";
	}
  评论这张
 
阅读(1384)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017