来自 http://rvelthuis.de/programs/console.html 的 Console 库,很好用。
运行截图
之前的一篇文章展示了如何在Console中使用Timer,他使用的是CallBack方式进行回调的。这里演示直接处理 WM_TIMER 的方法,代码如下
#include “stdafx.h”
#include
#include
#include
int count =0;
VOID ShowTimer()
{
count++;
printf(“WM_TIMER in work thread count=%d\n”,count);
}
DWORD CALLBACK Thread(PVOID pvoid)
{
MSG msg;
UINT timerid=SetTimer(NULL,111,3000,NULL);
BOOL bRet;
PeekMessage(&msg,NULL,WM_USER,WM_USER,PM_NOREMOVE);
while((bRet = GetMessage(&msg,NULL,0,0))!= 0)
{
if(bRet==-1)
{
// handle the error and possibly exit
}
else
{
if (msg.message == WM_TIMER) { ShowTimer(); }
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
KillTimer(NULL,timerid);
printf(“thread end here\n”);
return 0;
}
int main()
{
DWORD dwThreadId;
printf(“Use timer in workthread of console application\n”);
HANDLE hThread = CreateThread(NULL,0,Thread,0,0,&dwThreadId);
_getch();
return 0;
}
运行结果和之前的相同。
代码和EXE在这里下载
另外,还有如下的一个例子,来自 http://read.pudn.com/downloads121/sourcecode/windows/console/512780/1ms/1ms.cpp__.htm
//************************************************************************************
//本程序使用了两种实现定时的方法,都是基于API的,而不是MFC
//
// 1. 是使用线程定时休眠的方法,启动线程ThreadProc,在线程中输出,然后Sleep 1000ms
//
// 2. 使用定时器方法,设置一个1000ms的定时器(SetTimer),然后捕捉该消息,然后调用回调函数
// TimerProc,在该函数中输出DEF,注意,SetTimer可以直接写回调函数地址,不必捕捉消息。
// 3. 本例子已经很详细了。
// 4. 祝你好运。
//
//************************************************************************************
#include “stdafx.h”
#include
DWORD WINAPI ThreadProc( LPVOID lpParameter )//方法1
{
static DWORD tick=0;
DWORD tmp = GetTickCount();
while(1)
{
tick = GetTickCount()-tmp;//获得系统从启动开始到现在的ms数
tmp = GetTickCount();
printf(“THREAD_PROC: abc %d, tickcount: %d\n”,tick,tmp);//打印abc和上次打印到这次打印之间的毫秒间隔。
Sleep(1000);//每隔1000ms打印abc
}
}
VOID TimerProc()//方法2
{
static DWORD tick=0;
static DWORD tmp = GetTickCount();
tick = GetTickCount()-tmp;
tmp = GetTickCount();
printf(” TimerProc________________def %d\n”,tick);
}
int main()
{
DWORD dwthread;
::CreateThread(NULL,0,ThreadProc,(LPVOID)0,1024,&dwthread);//生成一个线程,在该线程里每1000ms输出一个”abc”,然后SLEEP
SetTimer(0,0,1000,0);//设置一个定时器,定时器的回调函数为0,仅产生定时器消息,在main函数里捕捉该消息
MSG msg;
bool flag;
while(1)//该循环捕捉定时器消息,并且防止main函数退出
{
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
while( (flag = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (flag == -1)
{
// handle the error and possibly exit
}
else if(msg.message==WM_TIMER)
{
TimerProc();//如果是定时器消息,调用timerproc
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
}
本文是参考 http://bbs.csdn.net/topics/390293365?page=1#post-393016986 完成的。
修改了其中的Demo,使得程序能够在 VS2008 Express 下编译成功。
修改后的代码如下,删除了读取识别出来盘符上指定文件的代码
//编程识别u盘的插入,通过处理WM_DEVICECHANGE的消息,来达到识别的效果
//vc下编译通过,能够识别
#include “stdafx.h”
#include
#include
#include
#include
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
TCHAR FirstDriveFromMask (ULONG unitmask);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPreInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//windows窗口类的成员
TCHAR szClassName[] = TEXT(“MainWClass”);
WNDCLASSEX wndclass;
wndclass.cbSize = sizeof(wndclass);
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = ::LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor = ::LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szClassName;
wndclass.hIconSm = NULL;
::RegisterClassEx(&wndclass);
HWND hwnd = ::CreateWindowEx(
0,
szClassName,
TEXT(“mc”),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
UINT Cret = GetLastError();
//printf(“%d\n”,GetLastError());
if (hwnd == NULL)
{
::MessageBox(NULL,TEXT(“Create Window error!”),TEXT(“error”),MB_OK);
return -1;
}
::ShowWindow(hwnd,nCmdShow);
::UpdateWindow(hwnd);
MSG msg;
while (::GetMessage(&msg,NULL,0,0))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return msg.wParam;
}
//回调函数处理消息
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:/*初始化*/
MessageBox(hwnd, TEXT(“go!”), TEXT(“405 Studio”), MB_OK);
break;
case WM_DEVICECHANGE:
if(wParam == DBT_DEVICEARRIVAL) //设备激活
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
TCHAR szMsg[80],Disk;
Disk = FirstDriveFromMask(lpdbv ->dbcv_unitmask);
wsprintf (szMsg,TEXT(“Drive %c: Media has arrived./n”),Disk);
MessageBox (hwnd, szMsg, TEXT(“WM_DEVICECHANGE”), MB_OK);
//Sleep(1000);
::PostQuitMessage(0);
}
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
return 0;
}
//提取u盘的盘符
TCHAR FirstDriveFromMask (ULONG unitmask)
{
char i;
for (i = 0; i < 26; ++i)
{
if (unitmask & 0x1)
break;
unitmask = unitmask >> 1;
}
return (i + ‘A’);
}
首先运行本程序,然后插入U盘,可以看出识别出来插入U盘的盘符,另外弹出来的是Windows自动播放的对话框。运行结果如下:
源程序和编译后的EXE可以从下面下载
本文内容来自 http://biancheng.dnbcw.info/60/60901.html 是在一个控制台窗口中创建一个线程,然后在线程中处理Timer的消息
整理后的代码如下,VC2008 Express编译通过
#include “stdafx.h”
#include
#include
#include
int count =0;
VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)
{
count++;
printf(“WM_TIMER in work thread count=%d\n”,count);
}
DWORD CALLBACK Thread(PVOID pvoid)
{
MSG msg;
UINT timerid=SetTimer(NULL,111,3000,TimerProc);
BOOL bRet;
PeekMessage(&msg,NULL,WM_USER,WM_USER,PM_NOREMOVE);
while((bRet = GetMessage(&msg,NULL,0,0))!= 0)
{
if(bRet==-1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
KillTimer(NULL,timerid);
printf(“thread end here\n”);
return 0;
}
int main()
{
DWORD dwThreadId;
printf(“Use timer in workthread of console application\n”);
HANDLE hThread = CreateThread(NULL,0,Thread,0,0,&dwThreadId);
_getch();
return 0;
}
运行结果
下载 ConTimer
最近在看编译原理方面的东西,状态机是很重要的部分。之前遇到过一个问题,用这个问题让我深刻的理解了状态机。当年写了这篇文章,但是看起来这次更新忘记上传了。现在补上这篇文章。具体的问题是这样的:
“我不是计算机专业毕业的,也没有研究过离散数学(听说状态机属于离散数学)。前几天刚好有机会听别人讲这方面的问题,于是针对这个题目研究了一下。记录与此。
一.问题
请画出车库门控制器的 FSM(finite state machine)。车库门的功能如下:
1. 开启车库门
2. 关闭车库门
3. 车库门进行中(正在开启或关闭中)停止
4. 当车库门是处于第 3 点的状态时,往(停止前)反方向运行
PS:以上这些功能只须使用一个控制键”
解决方法和分析的过程在PDF 中能找到。
下面这段程序能够实现将你选中的邮件以MSG格式保存到一个目录中,并且生成一个包含邮件内容的TXT文件。举例来说:你在Outlook中选中200封邮件,然后指定 c:\m\ 作为存储目录。运行之后你就会发现,m目录中出现 0 1 2 3….这样的目录,其中是以MSG格式保存的每封邮件,并且每个目录下还有其中邮件TXT格式的内容。程序是以20MB为限,当一个目录中的邮件大于20MB时自动创建另外的目录。之所以选择20MB是因为通常的Mail系统所支持的附件的上限是这个数值。因此,你可以将生成的TXT作为正文,压缩之后的目录作为附件发送到你的邮件服务器上。比较推荐的是 QQ Mail,容量足够大,速度也不慢。当然如果你不放心,或者说觉得它的速度慢,还可以选择一个对你来说速度快的免费邮件服务器,然后在QQ Mail中设定自动收取这个邮箱的邮件。因为 QQ Mail的容量是一直增长的,所以不必担心邮箱爆掉。另外,QQ Mail提供了一个全文搜索的功能,使用户可以很方便的搜索到文件的内容—-这也是为什么要按照TXT保存邮件正文一次。
Function ReplaceCharsForFileName(sName As String) As String
sName = Replace(sName, “/”, ” “, 1, -1)
sName = Replace(sName, “\”, ” “, 1, -1)
sName = Replace(sName, “:”, ” “, 1, -1)
sName = Replace(sName, “?”, ” “, 1, -1)
sName = Replace(sName, “<“, ” “, 1, -1) sName = Replace(sName, “>”, ” “, 1, -1)
sName = Replace(sName, “|”, ” “, 1, -1)
sName = Replace(sName, “*”, ” “, 1, -1)
‘chr(34)='”‘
sName = Replace(sName, Chr(34), ” “, 1, -1)
ReplaceCharsForFileName = sName
End Function
Sub SaveChooseFile()
Dim objItem As Object
Dim strPath As String
Dim strFilePath As String
Dim iSize As Long
Dim iNum As Integer
Dim objFSO As Object
‘exit if you do not choose any files
If ActiveExplorer.Selection.Count = 0 Then Exit Sub
‘the path string should be ended by “\”
strPath = InputBox(“please enter a path :”, ” Enter path”)
Set fso = CreateObject(“Scripting.FileSystemObject”)
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
‘File size counter
iSize = 0
‘Directory counter
iNum = 0
For Each objItem In ActiveExplorer.Selection
‘Create file and dir
If iSize = 0 Then
strFilePath = strPath & Str(iNum)
If Not (objFSO.FolderExists(strFilePath)) Then
objFSO.CreateFolder (strFilePath)
End If
Set ts = fso.CreateTextFile(strFilePath & “\” & Str(iNum) & “Body.txt”, ForAppending, True)
End If
iSize = iSize + objItem.Size
‘write all the chosen file to a file
ts.Write (“==============================================================” & vbCrLf)
ts.Write (objItem.ReceivedTime & vbCrLf)
ts.Write (objItem.SenderName & vbCrLf)
ts.Write (objItem.Subject & vbCrLf)
‘Meeting requirement doesn’t have ‘to’ field
If (objItem.Class <> olMeetingRequest) _
And (objItem.Class <> olMeetingCancellation) _
And (objItem.Class <> olMeetingResponseNegative) _
And (objItem.Class <> olMeetingResponsePositive) _
Then ts.Write (objItem.To & vbCrLf)
ts.Write (objItem.Body & vbCrLf)
ts.Write (“==============================================================”)
‘save the mail as MSG format
objItem.SaveAs strFilePath & “\” & ReplaceCharsForFileName(objItem.Subject) & “.MSG”, OlSaveAsType.olMSGUnicode
‘save the contents to another text if the size is larger than 20MB
If iSize >= 20971520 Then
iSize = 0
iNum = iNum + 1
ts.Close
End If
Next
MsgBox “Email text extraction completed!”, vbOKOnly + vbInformation, “DONE!”
ts.Close
Set objItem = Nothing
End Sub
额外的问题:有时候在保存邮件的过程中会出现如下错误:
当出现错误的时候,可以切换到Debug下面,通过查看邮件的标题得知出现问题的邮件是那封,删除这个邮件即可。这并不是上面程序本身导致的,对于导致问题的邮件,你使用Outlook 直接保存为MSG文件也会出现同样的问题,目前我只在一些会议邀请的邮件上遇到过这样的问题,如果你没有这类邮件应该不会遇到这个问题。
继续前一篇提到的问题。仍然是根据天杀的建议更专业一点的方法是通过判断当前进程的父进程来得知是如何运行的。比如,双击运行父进程就是Explore.exe,命令行运行,父进程就是 cmd.exe 。
代码如下:
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils,
windows,
Tlhelp32,
PsApi;
//检查自己的进程的父进程
procedure CheckParentProc;
var //检查自己的进程的父进程
Pn: TProcesseNtry32;
sHandle:THandle;
H,hMod:Hwnd;
Found:Boolean;
Buffer:array[0..1023]of Char;
cbNeeded:DWORD;
begin
//得到所有进程的列表快照
sHandle:= CreateToolhelp32Snapshot(TH32CS_SNAPALL,0);
Found:= Process32First(sHandle,Pn);//查找进程
while Found do //遍历所有进程
begin
//比较枚举出来的ID是否是当前的ID
if Pn.th32ProcessID = GetCurrentProcessId then
begin
//父进程的句柄
H:= OpenProcess(PROCESS_ALL_ACCESS,True,Pn.th32ParentProcessID);
if EnumProcessModules( H,@hMod,sizeof(hMod),cbNeeded) then
begin
ZeroMemory(@Buffer,MAX_PATH+1);
GetModuleFileNameEx(H, hMod,Buffer,MAX_PATH+1); //枚举进程文件所在路径
//只是简单把它打印出来而已
writeln(strpas(Buffer));
end;
CloseHandle(H);
end;
Found:= Process32Next(sHandle,Pn);//查找下一个
end;
CloseHandle(sHandle);
end;
begin
CheckParentProc;
readln;
end.
下图是这个程序分别在cmd窗口,直接双击和Delphi IDE环境中的运行结果。
完整代码下载
=============================================================
2022年01月24日补充 VC 下的实现方法
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <Psapi.h>
int getProcessName(DWORD pid, LPWSTR fname, DWORD sz)
{
HANDLE h = NULL;
int e = 0;
h = OpenProcess
(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE,
pid
);
if (h)
{
if (GetModuleFileNameEx(h, NULL, fname, sz) == 0)
e = GetLastError();
CloseHandle(h);
}
else
{
e = GetLastError();
}
return (e);
}
DWORD getParentPID(DWORD pid)
{
HANDLE h = NULL;
PROCESSENTRY32 pe = { 0 };
DWORD ppid = 0;
pe.dwSize = sizeof(PROCESSENTRY32);
h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32First(h, &pe))
{
do
{
if (pe.th32ProcessID == pid)
{
ppid = pe.th32ParentProcessID;
break;
}
} while (Process32Next(h, &pe));
}
CloseHandle(h);
return (ppid);
}
boolean IsRuninCMD() {
DWORD pid, ppid;
int e;
wchar_t fname[256];
pid = GetCurrentProcessId();
ppid = getParentPID(pid);
e = getProcessName(ppid, fname, MAX_PATH);
wprintf(L"PPID=%d\nErr=%d\nEXE={%s}\n", ppid, e, fname);
if ((wcsstr(fname, L"cmd.exe") != NULL) ||
(wcsstr(fname, L"powershell.exe") != NULL)) {
return true;
} else return false;
}
void main(int argc, char* argv[])
{
if (IsRuninCMD()) {
printf("in cmd\n");
}
else {
printf("NOT in cmd\n");
getchar();
}
}
前几天在写console程序的时候忽然想起来“如何判断当前程序是直接运行还是从CMD窗口中运行”的问题,网上搜索了一下,并无明确结果,为此我请教了一下天杀,他给我出了2个办法。第一个办法是通过GetStartupInfo得到当前窗口的 Title,从下图可以看到直接运行的时候是只有文件名,如果从cmd下面运行会有完整的路径和文件名
例程:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils,
windows;
var
Info:STARTUPINFO;
begin
GetStartupInfo(Info);
writeln(Info.lpTitle);
readln;
end.
上面 STARTUPINFO结构体的 dwFlags 位也能反映这个差别:
STARTF_TITLEISLINKNAME 0x00000800
The lpTitle member contains the path of the shortcut file (.lnk) that the user invoked to start this process. This is typically set by the shell when a .lnk file pointing to the launched application is invoked. Most applications will not need to set this value.
This flag cannot be used with STARTF_TITLEISAPPID.
完整程序下载 cmd1
检查当前可执行文件是否为64位的工具
很多时候我们经常发现拿到手的某个EXE或者EFI文件无法执行,出现的错误信息也很少,无法得知是因为文件损坏还是这个程序只能在64位Windows下运行。
因此编写了一个简单的工具用来检查EXE或者EFI程序是否是64位的。
用法很简单:直接拖拽到cc64.exe 上或者在命令行下用 cc64 文件名来运行。
原理上是分析PE结构,Machine Types 字段,有如下定义
Constant Value Description
IMAGE_FILE_MACHINE_UNKNOWN 0x0 The contents of this field are assumed to be applicable to any machine type
IMAGE_FILE_MACHINE_AM33 0x1d3 Matsushita AM33
IMAGE_FILE_MACHINE_AMD64 0x8664 x64 《—————– 64 bit
IMAGE_FILE_MACHINE_ARM 0x1c0 ARM little endian
IMAGE_FILE_MACHINE_ARMV7 0x1c4 ARMv7 (or higher) Thumb mode only
IMAGE_FILE_MACHINE_EBC 0xebc EFI byte code
IMAGE_FILE_MACHINE_I386 0x14c Intel 386 or later processors and compatible processors《———– 32 bit
IMAGE_FILE_MACHINE_IA64 0x200 Intel Itanium processor family 《———– 64 bit
IMAGE_FILE_MACHINE_M32R 0x9041 Mitsubishi M32R little endian
IMAGE_FILE_MACHINE_MIPS16 0x266 MIPS16
IMAGE_FILE_MACHINE_MIPSFPU 0x366 MIPS with FPU
IMAGE_FILE_MACHINE_MIPSFPU16 0x466 MIPS16 with FPU
IMAGE_FILE_MACHINE_POWERPC 0x1f0 Power PC little endian
IMAGE_FILE_MACHINE_POWERPCFP 0x1f1 Power PC with floating point support
IMAGE_FILE_MACHINE_R4000 0x166 MIPS little endian
IMAGE_FILE_MACHINE_SH3 0x1a2 Hitachi SH3
IMAGE_FILE_MACHINE_SH3DSP 0x1a3 Hitachi SH3 DSP
IMAGE_FILE_MACHINE_SH4 0x1a6 Hitachi SH4
IMAGE_FILE_MACHINE_SH5 0x1a8 Hitachi SH5
IMAGE_FILE_MACHINE_THUMB 0x1c2 ARM or Thumb (“interworking”)
IMAGE_FILE_MACHINE_WCEMIPSV2 0x169 MIPS little-endian WCE v2
64Bit .EXE and .EFI file checking utilty
This is autility which can be use to check if a EXE or EFI is a64bit image.
Usage: Drag and drop the file to this utilty in Windows file manager or run cc64 filename in the console window.
It will check the PE header when running. In the Machine type of PE header, you can find the the specified CPU that the PE image can be run.
www.lab-z.com
zoologist
2013-03-28
使用下面的程序,可以实现将选中的邮件以MSG的格式批量保存到指定的目录下。每个文件会以收到的时间命名。
Function DateToString(d As Date) As String
Dim s As String
s = Format([d], “yyyymmddhhnnss”)
DateToString = s
End Function
Sub SaveSelMails()
Dim objItem As Object
Dim strPath As String
Dim strFilename As String
‘Exit if you don’t choose any files
If ActiveExplorer.Selection.Count = 0 Then Exit Sub
strPath = InputBox(“Please enter the full path:”, “Enter path”)
‘write all the chosen emails to the path
For Each objItem In ActiveExplorer.Selection
strFilename = DateToString(objItem.ReceivedTime)
objItem.SaveAs strPath & strFilename & “.msg”, olMSGUnicode
Next
MsgBox “Email text extraction completed!”, vbOKOnly + vbInformation, “DONE!”
Set objItem = Nothing
End Sub
需要注意的是,输入时要输入以 ‘\’ 结尾的字符串,比如: ‘c:\tmp\’
此外,还有更简单的方式:在Outlook中使用Ctrl+左键选中邮件之后,直接拖拽到目录中。