文件分割实例

///
// MainDir.h文件	// 07MainDir

#include <afxwin.h>	
#include <afxcmn.h>	// 为了使用CStatusBarCtrl类

#include "FileCutter.h"

class CMyApp : public CWinApp
{
public:
	BOOL InitInstance();
};

class CMainDialog : public CDialog
{
public:
	CMainDialog(CWnd* pParentWnd = NULL);

protected:
	// 进度条控件对象
	CProgressCtrl m_Progress;

	CFileCutter* m_pCutter;

	// 动态控制程序界面的函数
	void UIControl();
protected:
	virtual BOOL OnInitDialog();
	virtual void OnCancel();

	afx_msg void OnSourceBrowser();
	afx_msg void OnDestBrowser();
	afx_msg void OnStart();
	afx_msg void OnStop();
	afx_msg void OnSelect();
	// 处理CFileCutter类发来的消息
	afx_msg long OnCutterStart(WPARAM wParam, LPARAM);
	afx_msg long OnCutterStatus(WPARAM wParam, LPARAM lParam);
	afx_msg long OnCutterStop(WPARAM wParam, LPARAM lParam);
	DECLARE_MESSAGE_MAP()
};
///
// DirDialog.h文件

#ifndef __DIRDIALOG_H_
#define __DIRDIALOG_H_

#include <shlobj.h>

class CDirDialog
{
public:
	CDirDialog();
	// 显示对话框
	BOOL DoBrowse(HWND hWndParent, LPCTSTR pszTitle = NULL);
	// 取得用户选择的目录名称
	LPCTSTR GetPath() { return m_szPath; }

protected:
	BROWSEINFOA m_bi; 

	// 用来接受用户选择目录的缓冲区
	char m_szDisplay[MAX_PATH];
	char m_szPath[MAX_PATH];
};

CDirDialog::CDirDialog()
{
	memset(&m_bi, 0, sizeof(m_bi));

	m_bi.hwndOwner = NULL;	 
	m_bi.pidlRoot = NULL;		 	
	m_bi.pszDisplayName = m_szDisplay; 
	m_bi.lpszTitle = NULL;		 	
	m_bi.ulFlags = BIF_RETURNONLYFSDIRS;
	
	m_szPath[0] = '\0';
}

BOOL CDirDialog::DoBrowse(HWND hWndParent, LPCTSTR pszTitle)
{
	if(pszTitle == NULL)
		m_bi.lpszTitle = "选择目标文件夹";
	else
		m_bi.lpszTitle = pszTitle;

	m_bi.hwndOwner = hWndParent;
	LPITEMIDLIST pItem = ::SHBrowseForFolder(&m_bi);
	if(pItem != 0)
	{
		::SHGetPathFromIDList(pItem, m_szPath);
		return TRUE;
	}

	return FALSE;
}

#endif //__DIRDIALOG_H_


// FileCutter.h文件


#ifndef __FILECUTTER_H_
#define __FILECUTTER_H_

#include <afxwin.h>

// CFileCutter类发给主窗口的通知消息
#define WM_CUTTERSTART WM_USER + 100	// wParam == nTatolCount
#define WM_CUTTERSTOP WM_USER + 101	// wParam == nExitCode, lParam == nCompletedCount
#define WM_CUTTERSTATUS WM_USER + 102	//			lParam == nCompletedCount

class CFileCutter
{
public:
// 工作退出代码
	enum ExitCode{ 
		exitSuccess,	// 成功完成任务
		exitUserForce,  // 用户终止
		exitSourceErr,  // 源文件出错
		exitDestErr	// 目标文件出错
		};

// 构造函数
	CFileCutter(HWND hWndNotify);

// 属性
	BOOL IsRunning() const { return m_bRunning; }

// 操作
	BOOL StartSplit(LPCTSTR lpszDestDir, LPCTSTR lpszSourceFile, UINT uFileSize);
	BOOL StartMerge(LPCTSTR lpszDestFile, LPCTSTR lpszSourceDir);

	BOOL SuspendCutter();
	BOOL ResumeCutter();
	void StopCutter();
	
// 具体实现
public:
	~CFileCutter();

protected:
	// 重置参数信息和状态标志
	void Reset();
	// 进行真正的分割操作
	void DoSplit();
	// 进行真正的合并操作
	void DoMerge();
	// 工作线程
	UINT friend _CutterEntry(LPVOID lpParam);

	// 参数信息
	CString m_strSource;
	CString m_strDest;
	UINT m_uFileSize;
	BOOL m_bSplit;
	// 状态标志
	BOOL m_bContinue;	//  是否继续工作
	BOOL m_bRunning;	//  是否处于工作状态

	// 同步以上两组数据
	CRITICAL_SECTION m_cs;	// Data gard

private:
	// 对象的生命周期全局有效的数据
	HWND m_hWndNotify;	// 接受消息通知事件的窗口句柄
	HANDLE m_hWorkEvent;	// 通知开始工作的事件对象句柄
	CWinThread* m_pThread;	// 工作线程
	BOOL m_bSuspend;	// 暂停标志
	BOOL m_bExitThread;	// 退出标志
};

#endif // __FILECUTTER_H_


// MainDir.cpp文件

#include <afxdlgs.h> // 为了使用CFileDialog类
#include "DirDialog.h"
#include "resource.h"
#include "Cutter.h"

CMyApp theApp;

BOOL CMyApp::InitInstance()
{
	CMainDialog dlg;
	m_pMainWnd = &dlg;
	dlg.DoModal();
	return FALSE;
}

CMainDialog::CMainDialog(CWnd* pParentWnd):CDialog(IDD_FILECUTTER_DIALOG, pParentWnd)
{
}

BEGIN_MESSAGE_MAP(CMainDialog, CDialog)
ON_BN_CLICKED(IDC_SOURCEBROWSER, OnSourceBrowser)	// 选择源文件的“选择”按钮
ON_BN_CLICKED(IDC_DESTBROWSER, OnDestBrowser)		// 选择目标文件的“选择”按钮
ON_BN_CLICKED(IDC_START, OnStart)			// 开始“分割”按钮
ON_BN_CLICKED(IDC_STOP, OnStop)				// “终止”分割按钮
ON_BN_CLICKED(IDC_SELECTSPLIT, OnSelect)		// 分割单选框按钮
ON_BN_CLICKED(IDC_SELECTMERGE, OnSelect)		// 合并单选框按钮
// 下面是3各CFileCutter类发来的消息
ON_MESSAGE(WM_CUTTERSTART, OnCutterStart)		
ON_MESSAGE(WM_CUTTERSTATUS, OnCutterStatus)
ON_MESSAGE(WM_CUTTERSTOP, OnCutterStop)
END_MESSAGE_MAP()

BOOL CMainDialog::OnInitDialog()
{
	CDialog::OnInitDialog();
	SetIcon(theApp.LoadIcon(IDI_MAIN), FALSE);

	// 创建CFileCutter对象
	m_pCutter = new CFileCutter(m_hWnd);

	// 默认选中分割单项框
	((CButton*)GetDlgItem(IDC_SELECTSPLIT))->SetCheck(1);
	// 初始化单位选择组合框。可以在这里继续添加其它项
	((CComboBox*)GetDlgItem(IDC_UNIT))->AddString("1");
	((CComboBox*)GetDlgItem(IDC_UNIT))->AddString("30");
	((CComboBox*)GetDlgItem(IDC_UNIT))->AddString("60");
	((CComboBox*)GetDlgItem(IDC_UNIT))->SetCurSel(0);

	// 子类化进度条控件。也就是让m_Progress对象取得进度条控件的控制权
	m_Progress.SubclassWindow(*GetDlgItem(IDC_PROGRESS));

	UIControl();
	return TRUE;
}

void CMainDialog::UIControl()
{
	BOOL bIsWorking = m_pCutter->IsRunning();
	
	// 设置选项分组框中3个控件的状态
	GetDlgItem(IDC_SELECTSPLIT)->EnableWindow(!bIsWorking);
	GetDlgItem(IDC_SELECTMERGE)->EnableWindow(!bIsWorking);
	GetDlgItem(IDC_UNIT)->EnableWindow(!bIsWorking);
	// 设置分割、终止两个按钮的状态
	GetDlgItem(IDC_START)->EnableWindow(!bIsWorking);
	GetDlgItem(IDC_STOP)->EnableWindow(bIsWorking);

	if(bIsWorking)
	{
		return;
	}
	
	// 根据用户的选择设置不同的文本
	BOOL bSplit = ((CButton*)GetDlgItem(IDC_SELECTSPLIT))->GetCheck();
	if(bSplit)	// 请求分割	
	{
		GetDlgItem(IDC_START)->SetWindowText("分割");
		GetDlgItem(IDC_SOURCETITLE)->SetWindowText("请选择要分割的文件:");
		GetDlgItem(IDC_DESTTITLE)->SetWindowText("请选择分割后保存到的文件夹:");
	
		GetDlgItem(IDC_UNIT)->EnableWindow(TRUE);
	}
	else		// 请求合并
	{
		GetDlgItem(IDC_START)->SetWindowText("合并");
		GetDlgItem(IDC_SOURCETITLE)->SetWindowText("请选择待合并文件的文件夹:");
		GetDlgItem(IDC_DESTTITLE)->SetWindowText("请选择合并后保存到的文件夹:");
		
		GetDlgItem(IDC_UNIT)->EnableWindow(FALSE);
	}

	// 初始化状态信息
	GetDlgItem(IDC_STATUSTEXT)->SetWindowText("    状态显示:");
	m_Progress.SetPos(0);

}

void CMainDialog::OnCancel()
{
	// 是否真的退出?
	BOOL bExit = TRUE;
	if(m_pCutter->IsRunning())
	{
		if(MessageBox("工作还未完成,确实要退出吗?", NULL, MB_YESNO) == IDNO)
		{
			bExit = FALSE;
		}
	}

	if(bExit)
	{
		delete m_pCutter;
		CDialog::OnCancel();
	}
}

void CMainDialog::OnSelect()
{
	UIControl();
}

void CMainDialog::OnSourceBrowser()
{
	BOOL bSplit = ((CButton*)GetDlgItem(IDC_SELECTSPLIT))->GetCheck();
	if(bSplit)	// 请求分割
	{
		CFileDialog sourceFile(TRUE);
		// 显示选择文件对话框
		if(sourceFile.DoModal() == IDOK)
		{
			GetDlgItem(IDC_EDITSOURCE)->SetWindowText(sourceFile.GetPathName());
			
			// 设置默认目录
			// 例如,如果用户选择文件“D:\cd\精选歌曲.iso”,那么“D:\cd\精选歌曲”将会被设置为默认目录
			CString strDef = sourceFile.GetPathName();
			strDef = strDef.Left(strDef.ReverseFind('.'));
			GetDlgItem(IDC_EDITDEST)->SetWindowText(strDef);
		}
	}
	else		// 请求合并
	{
		CDirDialog sourceFolder;
		// 显示选择目录对话框
		if(sourceFolder.DoBrowse(*this) == IDOK)
		{
			GetDlgItem(IDC_EDITSOURCE)->SetWindowText(sourceFolder.GetPath());

			// 设置默认目录
			// 例如,如果用户选择目录“D:\cd”,那么“D:\cd\cd”将会被设置为默认目录
			CString strDef = sourceFolder.GetPath();
			strDef.TrimRight('\\');
			strDef = strDef + '\\' + strDef.Mid(strDef.ReverseFind('\\') + 1);
			// 防止用户选择根目录
			strDef.TrimRight(':');
			GetDlgItem(IDC_EDITDEST)->SetWindowText(strDef);
		}
	}
}

void CMainDialog::OnDestBrowser()
{
	CDirDialog destFolder;
	// 显示选择目录对话框
	if(destFolder.DoBrowse(*this) == IDOK)
	{
		GetDlgItem(IDC_EDITDEST)->SetWindowText(destFolder.GetPath());
	}
}

void CMainDialog::OnStart()
{
	CString strSource, strDest;

	// 检查输入
	GetDlgItem(IDC_EDITSOURCE)->GetWindowText(strSource);
	GetDlgItem(IDC_EDITDEST)->GetWindowText(strDest);
	if(strSource.IsEmpty() || strDest.IsEmpty())
	{
		MessageBox("文件或路径名称不能为空");
		return;
	}

	BOOL bSplit = ((CButton*)GetDlgItem(IDC_SELECTSPLIT))->GetCheck();
	if(bSplit)	// 请求分割
	{
		CString str;
		GetDlgItem(IDC_UNIT)->GetWindowText(str);
		m_pCutter->StartSplit(strDest, strSource, atoi(str)*1024*1024);
	}
	else		// 请求合并
	{
		m_pCutter->StartMerge(strDest, strSource);
	}
}

void CMainDialog::OnStop()
{
	m_pCutter->SuspendCutter();
	if(MessageBox("确实要终止吗?", NULL, MB_YESNO) == IDYES)
	{
		m_pCutter->StopCutter();
	}
	else
	{
		m_pCutter->ResumeCutter();
	}
}
 

// 下面的代码处理CFileCutter类发来的消息


long CMainDialog::OnCutterStart(WPARAM wParam, LPARAM)		// WM_CUTTERSTART 开始工作
{
	// 设置进度条范围
	int nTotalFiles = wParam;	// 总文件数量
	m_Progress.SetRange(0, nTotalFiles);
	
	UIControl();
	return 0;
}

long CMainDialog::OnCutterStatus(WPARAM wParam, LPARAM lParam)	// WM_CUTTERSTATUS 工作进度
{
	// 设置进度条进度
	int nCompleted = (int)lParam;
	m_Progress.SetPos(nCompleted);
	// 显示状态
	CString s;
	s.Format(" 完成%d个文件", nCompleted);
	GetDlgItem(IDC_STATUSTEXT)->SetWindowText(s);
	return 0;
}

long CMainDialog::OnCutterStop(WPARAM wParam, LPARAM lParam)	// WM_CUTTERSTOP 停止工作
{
	int nErrorCode = wParam;
	switch(nErrorCode)
	{
	case CFileCutter::exitSuccess:
		MessageBox("操作成功完成", "成功");
		break;
	case CFileCutter::exitSourceErr:
		MessageBox("源文件出错", "失败");
		break;
	case CFileCutter::exitDestErr:
		MessageBox("目标文件出错", "失败");
		break;
	case CFileCutter::exitUserForce:
		MessageBox("用户终止", "失败");
		break;
	}
	UIControl();
	return 0;
}


/
// FileCutter.cpp文件

#include "FileCutter.h"


// 内部工作线程
UINT _CutterEntry(LPVOID lpParam)
{
	// 得到CFileCutter对象的指针
	CFileCutter* pCutter = (CFileCutter*)lpParam;

	// 循环处理用户的工作请求
	while(::WaitForSingleObject(pCutter->m_hWorkEvent, INFINITE) == WAIT_OBJECT_0 && 
									!pCutter->m_bExitThread)
	{
		// 设置状态标志,说明正在工作
		::EnterCriticalSection(&pCutter->m_cs);
		pCutter->m_bRunning = TRUE;
		::LeaveCriticalSection(&pCutter->m_cs);

		// 开始真正的工作
		if(pCutter->m_bSplit)
			pCutter->DoSplit();
		else
			pCutter->DoMerge();

		// 准备接受新的工作任务
		pCutter->Reset();
	}

	return 0;
}

//-----------------------接口成员------------------------//

CFileCutter::CFileCutter(HWND hWndNotify) 
{
	// 初始化全局有效变量

	m_hWndNotify = hWndNotify;
	m_bExitThread = FALSE;
	m_bSuspend = FALSE;
	// 创建等待事件对象
	m_hWorkEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
	// 创建工作线程
	m_pThread = AfxBeginThread(_CutterEntry, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL);
	m_pThread->m_bAutoDelete = FALSE;
	m_pThread->ResumeThread();

	// 初始化工作期间有效变量

	// 创建关键代码段
	::InitializeCriticalSection(&m_cs);	
	Reset(); 
}

void CFileCutter::Reset()
{
	::EnterCriticalSection(&m_cs);
	
	// 重置参数信息
	m_strSource.Empty();
	m_strDest.Empty();
	m_uFileSize = 0;
	m_bSplit = TRUE;

	// 重置状态标志
	m_bContinue = TRUE;
	m_bRunning = FALSE;
	
	::LeaveCriticalSection(&m_cs);
}

CFileCutter::~CFileCutter()
{
	// 设置结束标志
	m_bExitThread = TRUE;

	// 设置强制退出标志
	::EnterCriticalSection(&m_cs);
	m_bContinue = FALSE;
	::LeaveCriticalSection(&m_cs);

	// 防止线程在m_hWorkEvent事件上等待
	::SetEvent(m_hWorkEvent);

	// 确保工作线程结束
	::WaitForSingleObject(m_pThread->m_hThread, INFINITE);
	
	// 释放所有资源
	::CloseHandle(m_hWorkEvent);
	::DeleteCriticalSection(&m_cs); 
	delete m_pThread;
}

BOOL CFileCutter::StartSplit(LPCTSTR lpszDestDir, LPCTSTR lpszSourceFile, UINT uFileSize)
{
	if(m_bRunning)
		return FALSE;

	// 保存参数
	::EnterCriticalSection(&m_cs);
	m_strSource = lpszSourceFile; 
	m_strDest = lpszDestDir;
	m_uFileSize = uFileSize;
	m_bSplit = TRUE;
	::LeaveCriticalSection(&m_cs);

	// 通知线程开始工作
	::SetEvent(m_hWorkEvent);
	return TRUE;
}

BOOL CFileCutter::StartMerge(LPCTSTR lpszDestFile, LPCTSTR lpszSourceDir)
{
	if(m_bRunning)
		return FALSE;

	// 保存参数
	::EnterCriticalSection(&m_cs);
	m_strSource = lpszSourceDir; 
	m_strDest = lpszDestFile;
	m_bSplit = FALSE;
	::LeaveCriticalSection(&m_cs);

	// 通知线程开始工作
	::SetEvent(m_hWorkEvent);
	return TRUE;
}

BOOL CFileCutter::SuspendCutter()
{
	if(!m_bRunning)
		return FALSE;

	// 暂停工作线程
	if(!m_bSuspend)
	{
		m_pThread->SuspendThread();
		m_bSuspend = TRUE;
	}
	return TRUE;
}

BOOL CFileCutter::ResumeCutter()
{
	if(!m_bRunning)
		return FALSE;

	// 唤醒工作线程
	if(m_bSuspend)
	{
		m_pThread->ResumeThread();
		m_bSuspend = FALSE;
	}
	return TRUE;
}

void CFileCutter::StopCutter()
{
	// 设置强制退出标志
	::EnterCriticalSection(&m_cs);
	m_bContinue = FALSE;
	::LeaveCriticalSection(&m_cs);

	// 防止线程处于暂停状态
	ResumeCutter();
}

//-------------------------实现代码-------------------------//


void CFileCutter::DoSplit()
{
	int nCompleted = 0;
	CString strSourceFile = m_strSource;
	CString strDestDir = m_strDest;
	CFile sourceFile, destFile;
	
	// 打开源文件
	BOOL bOK = sourceFile.Open(strSourceFile,
		CFile::modeRead|CFile::shareDenyWrite|CFile::typeBinary);
	if(!bOK)
	{
		// 通知用户,源文件出错
		::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitSourceErr, nCompleted);
		return;
	}

	// 确保目标目录存在(逐层创建它们)
	int nPos = -1;
	while((nPos = strDestDir.Find('\\', nPos+1)) != -1)
	{
		::CreateDirectory(strDestDir.Left(nPos), NULL);
	} 
	::CreateDirectory(strDestDir, NULL);
	if(strDestDir.Right(1) != '\\')
		strDestDir += '\\';

	// 通知用户,开始分割文件
	int nTotalFiles = sourceFile.GetLength()/m_uFileSize + 1;
	::PostMessage(m_hWndNotify, WM_CUTTERSTART, nTotalFiles, TRUE);
	
	// 开始去读源文件,将数据写入目标文件
	const int c_page = 4*1024;
	char buff[c_page];
	DWORD dwRead;

	CString sDestName;
	int nPreCount = 1;
	UINT uWriteBytes;
	do
	{
		// 创建一个目标文件
		sDestName.Format("%d__", nPreCount);
		sDestName += sourceFile.GetFileName();
		if(!destFile.Open(strDestDir + sDestName, CFile::modeWrite|CFile::modeCreate))
		{
			::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitDestErr, nCompleted);
			sourceFile.Close();
			return;
		}

		// 向目标文件写数据,直到大小符合用户的要求,或者源文件读完
		uWriteBytes = 0;
		do
		{
			// 首先判断是否要求终止执行
			if(!m_bContinue)
			{
				destFile.Close();
				sourceFile.Close();
				if(!m_bExitThread)
					::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitUserForce, nCompleted);
				return;
			}

			// 进行真正的读写操作
			dwRead = sourceFile.Read(buff, c_page);
			destFile.Write(buff, dwRead);
			uWriteBytes += dwRead;
		}while(dwRead > 0 && uWriteBytes < m_uFileSize);

		// 关闭这个目标文件
		destFile.Close();

		// 通知用户,当前的状态信息
		nCompleted = nPreCount++;
		::PostMessage(m_hWndNotify, WM_CUTTERSTATUS, 0, nCompleted);
	}while(dwRead > 0);

	// 关闭源文件
	sourceFile.Close();

	// 通知用户,工作完成
	::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitSuccess, nCompleted);
}


void CFileCutter::DoMerge()
{
	int nCompleted = 0;
	CString strSourceDir = m_strSource;
	CString strDestFile = m_strDest;
	if(strSourceDir.Right(1) != '\\')
		strSourceDir += '\\';
	if(strDestFile.Right(1) != '\\')
		strDestFile += '\\';
	
	// 取得源目录中待合并的文件的文件名称和数量
	CString strFileName;
	int nTotalFiles = 0;
	CFileFind find;
	BOOL bRet;
	if(find.FindFile(strSourceDir + "*.*"))
	{
		do
		{
			bRet = find.FindNextFile();
			if(find.IsDirectory() && find.IsDots())
				continue;
			if(find.GetFileName().Find("__", 0) != -1)
			{
				nTotalFiles++;
				strFileName = find.GetFileName();
			}
		}while(bRet);
	}
	find.Close();

	if(nTotalFiles == 0)
	{
		// 通知用户,源文件出错
		::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitSourceErr, nCompleted);
		return;
	}

	// 取得文件名称
	strFileName = strFileName.Mid(strFileName.Find("__") + 2);

	// 确保目标目录存在(逐层创建它们)
	int nPos = 0;
	while((nPos = strDestFile.Find('\\', nPos+1)) != -1)
	{
		::CreateDirectory(strDestFile.Left(nPos + 1), NULL);
	}
	::CreateDirectory(strDestFile, NULL);
	
	// 创建目标文件
	CFile sourceFile, destFile;
	strDestFile += strFileName;
	if(!destFile.Open(strDestFile, CFile::modeRead|CFile::modeWrite|CFile::modeCreate))
	{
		::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitDestErr, nCompleted);
		return;
	}

	// 通知用户,开始分割文件
	::PostMessage(m_hWndNotify, WM_CUTTERSTART, nTotalFiles, nCompleted);	

	// 开始去读源文件,将数据写入目标文件
	const int c_page = 4*1024;
	char buff[c_page];
	int nPreCount = 1;
	CString sSourceName;
	DWORD dwRead;
	do
	{
		// 打开一个源文件
		sSourceName.Format("%d__", nPreCount);
		sSourceName += strFileName;
		if(!sourceFile.Open(strSourceDir + sSourceName, CFile::modeRead|CFile::shareDenyWrite))
		{
			break;
		}

		// 将这个源文件中的数据全部写入目标文件
		do
		{
			if(!m_bContinue)
			{
				sourceFile.Close();
				destFile.Close();
				if(!m_bExitThread)
					::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitUserForce, nCompleted);
				return;
			}
			dwRead = sourceFile.Read(buff, c_page);
			destFile.Write(buff, dwRead);
		}
		while(dwRead > 0);
		sourceFile.Close();

		// 通知用户,当前的状态信息
		nCompleted = nPreCount++;
		::PostMessage(m_hWndNotify, WM_CUTTERSTATUS, 0, nCompleted);
	}
	while(TRUE);

	// 通知用户,工作完成
	::PostMessage(m_hWndNotify, WM_CUTTERSTOP, exitSuccess, nCompleted);
}

http://www.pythonschool.com/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值