‘ VC开发 ’ category archive

HTC Touch Pro 的重力感应功能

839 views 七月 07, 09 by Timothy

080605_htc_touch_pro_05

 

HTC Touch Pro带的重力感应功能很实用,不过一直在寻思能否找到重力感应器的SDK。在网上搜罗了一大圈,都没结果,看来厂商并不希望公开这样的接口。果不其然,在一个国外的网站上,发现了一篇有趣的文章:

http://scottandmichelle.net/scott/comments.html?entry=784

I spent a couple of sleepless hours last night writing a little Sensor Test for my new HTC Diamond. It’s a small app that lets you move a circle around the screen by tilting the device.

Exciting, eh? Well, the fun is in getting it to work. I asked HTC if they provide a development kit (SDK) for the tilt sensor, and they said “No”, so I had to figure it out myself by digging around (and trying to remember what I knew of ARM assembly). I’m just happy I managed to figure it out, and so others can write interesting games with it, I’m giving the source code to what I figured out away. Have fun, create something with it.

相当搞笑,这位哥们本来打算要求HTC提供这样的SDK开发包,想不到被HTC无情的拒绝了,于是一怒之下,把重力感应相关的库文件给反向工程了,并公开了源码。这下可好了,咱们可以开动大脑,设计更多的和重力相关的有趣的应用了。研究了这位外国哥们的代码,重力感应器相关的API,主要包含在一个叫做”HTCSensorSDK.dll”的动态库里面,该动态库主要输出的两个函数分别是:HTCSensorOpen和HTCSensorClose ,用于打开重力感应器和关闭重力感应器。当重力感应器打开之后,可以对手机当前位置的变化进行监视,其实是通过检测注册表的某个键值实现的,具体的路径在Software\\HTC\\HTCSensor\\GSensor的EventChanged。在打开重力感应器的情况下,当这个键的值发生变化,意味着手机的位置已经发生了变动。手机位置的定义包括六种情况,以地平线位置作为参考,可分为:手机正面(屏幕)向上、手机正面(屏幕)向下、手机正面(屏幕)向左、手机正面(屏幕)向右、手机正面(屏幕)向前、手机正面(屏幕)向后。而这六种位置在注册表键EventChanged中均有不同的值表示。

为了方便应用,我将开启/关闭 重力感应器的代码抽取了出来,做成一个动态连接库,暴露的接口如下:

   1:  extern "C" __declspec(dllexport) BOOL EnableGSensor();
   2:  extern "C" __declspec(dllexport) BOOL DisableGSensor();

这样就能方便的开启和关闭重力感应器了。

下面就可以用简单的MFC Dialog程序来测试重力感应了:

定义注册表路径和被监视的键值

   1:  #define SN_GSENSOR_ROOT     HKEY_LOCAL_MACHINE
   2:  #define SN_GSENSOR_PATH     _T("Software\\HTC\\HTCSensor\\GSensor")
   3:  #define SN_GSENSOR_VALUE    _T("EventChanged")

定义注册表值掩码,以及6个不同位置的值

   1:  #define SN_GSENSOR_BITMASK  0xF
   2:   
   3:  #define orIENTATION_LANDSCAPE           0
   4:  #define orIENTATION_REVERSE_LANDSCAPE   1
   5:  #define orIENTATION_PORTRAIT            2
   6:  #define orIENTATION_UPSIDE_DOWN         3
   7:  #define orIENTATION_FACE_DOWN           4
   8:  #define orIENTATION_FACE_UP             5

定义注册表值变化的Windows Message类型

   1:  #define WM_EVENTCHANGED (WM_USER + 1)

在消息映射宏中,定义响应自定义消息WM_EVENTCHANGED的处理函数

   1:  ON_MESSAGE(WM_EVENTCHANGED,OnEventChanged)

 

这样,我们就可以在Dialog程序的初始化函数里面实现相应的代码:

   1:  BOOL CTestDlg::OnInitDialog()
   2:  {
   3:      CDialog::OnInitDialog();
   4:   
   5:      // Set the icon for this dialog.  The framework does this automatically
   6:      //  when the application's main window is not a dialog
   7:      SetIcon(m_hIcon, TRUE);            // Set big icon
   8:      SetIcon(m_hIcon, FALSE);        // Set small icon
   9:   
  10:      // TODO: Add extra initialization here
  11:      g_hSensorEvent = NULL;
  12:      
  13:      RegistryNotifyWindow(
  14:          SN_GSENSOR_ROOT, 
  15:          SN_GSENSOR_PATH, 
  16:          SN_GSENSOR_VALUE,
  17:          GetSafeHwnd(), 
  18:          WM_EVENTCHANGED, 
  19:          SN_GSENSOR, 
  20:          NULL, 
  21:          &g_hSensorEvent);
  22:   
  23:          gSensorWrapper.EnableGSensor();
  24:   
  25:      return TRUE;  // return TRUE  unless you set the focus to a control
  26:  }

RegistryNotifyWindow函数,通过监视注册表的指定路径下,指定键值的变化,向我们的窗口发送WM_EVENTCHANGED消息,而我们在消息映射宏中所定义的OnEventChanged函数,就用来响应和处理这样的自定义消息。

最后,在我们的OnEventChanged函数中,就可以获得当前手机的位置了:

   1:  afx_msg LRESULT CHangupPhoneDlg::OnEventChanged(WPARAM wParam, LPARAM lParam)
   2:  {
   3:          nLastEvent = (wParam & SN_GSENSOR_BITMASK);
   4:          
   5:          switch (nLastEvent)
   6:          {
   7:          case orIENTATION_LANDSCAPE:
   8:              szMessage = _T("Last Event: 正面向右");
   9:              break;
  10:          case orIENTATION_REVERSE_LANDSCAPE:
  11:              szMessage = _T("Last Event: 正面向左");
  12:              break;
  13:          case orIENTATION_PORTRAIT:
  14:              szMessage = _T("Last Event: 正面向后");
  15:              break;
  16:          case orIENTATION_UPSIDE_DOWN:
  17:              szMessage = _T("Last Event: 正面向前");
  18:              break;
  19:          case orIENTATION_FACE_DOWN:
  20:              szMessage = _T("Last Event: 正面向下");
  21:              break;
  22:          case orIENTATION_FACE_UP:
  23:              szMessage = _T("Last Event: 正面向上");
  24:              break;
  25:          default:
  26:              szMessage = _T("Last Event: Unknown");
  27:              break;
  28:          }
  29:   
  30:          this->GetDlgItem(IDC_DISPLAY)->SetWindowTextW(szMessage);
  31:   
  32:   
  33:      return 0L;
  34:  }

启动测试程序,翻转手机到不同的位置,就能在Dialog框中,实时看到当前手机位置状态,Cool!

测试程序比较简单,就不放上来了,把封装的重力感应器的动态库放上来,供有兴趣的同学下载。

可以进行测试的手机型号:多普达(Dopod) Touch Diamond、多普达(Dopod) Touch Pro 或者 HTC Touch Diamond 和 HTC Touch Pro.

我的开发测试环境: Windows Vista 64bit, Visual Studio 2008 SP1, Windows Mobile 6.0 Professional SDK.

点击下载此文件

汉语编程 偶也来恶搞一把

129 views 十一月 03, 07 by Timothy

这几天的热门话题,要属汉语编程了。一直以来,汉语编程,就一直成为争议的话题。其实偶更倾向于英文代码,中文的代码,看起来确实比较别扭。其实VS2005支持双字节,也支持中文。所以偶们也来搞恶一把,Let’s do it!!

[code]
#include "常用头文件"

整形变量 主函数入口(整形变量 参数个数, 字符指针 字符参数数组)
{
整形变量 某某某 = 6 呀
某某某 = 某某某 减去 1呀
如果(某某某 等于 5)
{
输出("某某某本来为6,减去1 果然等于");
输出("%d\n",某某某);
}
函数返回 0 呀
}
[/code]

运行效果:

其实没什么秘密,答案就在头文件里面
代码大家下载了看吧。呵呵。

点击下载此文件

WinLogon事件通知包编程

213 views 五月 18, 07 by Timothy

今天看到CSDN中有网友问道如何获取用户按Ctrl+Alt+Del锁定桌面的事件。回帖后大致整理了一下,希望对大家有帮助。
首先我们要了解一下WinLogon,他是负责提供给用户交互式界面的一个程序。

WinLogon初始化时会创建3个桌面:
(1)、winlogon桌面:主要显示window 安全等界面,如你按下CTRL+ALT+DEL,登陆的界面等
(2)、应用程序桌面:我们平时见到的那个有我的电脑的界面(这个大家都见过了吧,呵呵)
(3)、屏幕保护桌面:屏幕保护显示界面。

到这里,仿佛我们找到了方法,就是编写Hook程序,截获CTRL+ALT+DEL,按下的消息,这样不就可以判断用户是否要锁定桌面了吗?其实不然,在WinLogon初始化时,就向系统注册截获CTRL+ALT+DEL消息,所以其他程序就无法得到CTRL+ALT+DEL的消息。
WinLogon会和GINA DLL进行交互,缺省是MSGINA.DLL(在System32目录下)。微软同时也为我们提供的接口,自己可以编GINA DLL来代替MSGINA.DLL。

到这里,我们还能想到的方法,就是采用建立远程线程的方式,注入到WinLogon.exe中,这样也可以截获到Ctrl+Alt+Del的按下的事件。不过写远程线程比较麻烦,而且调试起来也不方便。

其实WinLogin还为我们提供了另外一种方式,就是WinLogin通知包的方式。

Winlogon通知包(Winlogon Notification Package)”就是处理winlogon在切换状态时发出的事件的DLL。你可以通过“Winlogon Notification Package”来监视winlogon事件的响应。你可以注册这些DLL,那么winlogon.exe会在启动时加载它们,并且会在系统状态切换时来调用注册DLL的事件处理函数。

其实说简单一点,就是我们可以自己编写处理winlogon的事件的逻辑,将其作为函数导出,把它编译为Win32 Dll,然后添加信息到注册表。接下来的工作,我们就不用操心了,Winlogon会根据不同的事件,自己来调用我们DLL提供的函数进行处理。

为了注册你的“Winlogon Notification Package”,必须在“HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify”下创建你的“notification package”子键。在“Notify”项下,可以根据需要创建如下键值:

Asynchronous[REG_DWORD]:表明是否异步处理winlogon事件,如设为 1,winlogon将启动一个新线程来处理。
DllName[REG_EXPAND_SZ]:指定要加载的DLL名。
Impersonate[REG_DWORD]:表明是否以登陆用户的权限来处理事件。
Lock[REG_SZ]:锁定桌面事件。
Logoff[REG_SZ]:注销事件。
Logon[REG_SZ]:登陆事件。
Shutdown[REG_SZ]:关机事件。
StartScreenSaver[REG_SZ]:启动屏保事件。
StartShell[REG_SZ]:启动shell(一般指explorer.exe)事件。
Startup[REG_SZ]:系统开机事件。
StopScreenSaver[REG_SZ]:停止屏保事件。
Unlock[REG_SZ]:解除桌面锁定事件。

其中每个事件对应DLL中的一个导出函数,即每当有事件发生时,winlogon.exe便调用相应的函数。
譬如:DllName的值为“test.dll”,Lock的值为“testLock”,那么系统锁定时,winlogon.exe将调用test.dll中导出的“testLock”函数。

关于DLL的实现非常地简单:只要导出处理事件是要调用的函数就行,其他和别的DLL无异。以下是代码的简单实现:

// WinLogonNotify.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "WinLogonNotify.h"

#ifdef _MANAGED
#pragma managed(push, off)
#endif

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
      )
{
 switch (ul_reason_for_call)
 {
 case DLL_PROCESS_ATTACH:
 case DLL_THREAD_ATTACH:
 case DLL_THREAD_DETACH:
 case DLL_PROCESS_DETACH:
  break;
 }
    return TRUE;
}

#ifdef _MANAGED
#pragma managed(pop)
#endif

// This is an example of an exported function.
WINLOGONNOTIFY_API void _stdcall testLock(DWORD param)
{
 MessageBox(NULL,L"Lock!",L"Tips",MB_OK);
 return;
}

以上代码在Visual Studio 2005 开发平台上编译通过。

大家可以大包下载代码参考。

点击下载此文件

这个帖子的源地址如下:

标题:如何获取锁定计算机事件
http://community.csdn.net/Expert/topic/5536/5536030.xml?temp=6.365603E-02

VC雕虫小技:关于弹出菜单

138 views 十二月 04, 06 by Timothy

在用VC做一些软件功能的时候,为了方便使用,通常会涉及到托盘编程。也就是在程序最小化的时候,会在右下角的托盘区域添加一个图标,这个图标通常会支持弹出菜单功能。问题就在这里出现了,你也许会发现我们使用TrackPopupMenu呼出的弹出菜单,如果用户在取消选择的时候,在桌面任意地方单击,这个菜单也不会消失,很是郁闷,通常让初学者感到万念俱灰~~。
托盘程序中调用弹出菜单示例代码如下:

if(message==WM_SHELL_NOTIFY && lParam==WM_RBUTTONDOWN)
{
CPoint pt;
GetCursorPos(&pt);
CMenu *pMenu,menu;
menu.LoadMenu(IDR_SYSMENU);
pMenu=menu.GetSubMenu(0);
pMenu->TrackPopupMenu(TPM_RIGHTBUTTON,pt.x,pt.y,this,0);
}

要解决这个问题,其实比较简单,在响应托盘消息以及鼠标右击消息的时候,需要我们先调用一个函数,在你弹出菜单之前,设置你的窗口为最前窗口,问题就会迎刃而解。
修改后的代码如下:
if(message==WM_SHELL_NOTIFY && lParam==WM_RBUTTONDOWN)
{
this->SetForegroundWindow(); //这里将主窗口设置为最前端窗口
CPoint pt;
GetCursorPos(&pt);
CMenu *pMenu,menu;
menu.LoadMenu(IDR_SYSMENU);
pMenu=menu.GetSubMenu(0);
pMenu->TrackPopupMenu(TPM_RIGHTBUTTON,pt.x,pt.y,this,0);
}

编译后,再看看效果,问题就这样解决了。

Wake On Lan 远程唤醒

227 views 十一月 20, 06 by Timothy

远程唤醒,是现在很多网卡都支持的功能。而远程唤醒的实现,主要是向目标主机发送特殊格式的数据包,是AMD公司制作的Magic Packedt这套软件以生成网络唤醒所需要的特殊数据包,俗称魔术包(Magic Packet)。Magic Packet格式虽然只是AMD公司开发推广的技术,并非世界公认的标准,但是仍然受到很多网卡制造商的支持,因此 许多具有网络唤醒功能的网卡都能与之兼容。
要实现远程唤醒,还需要硬件的设置:
主板和网卡必须都支持远程唤醒功能。一般目前的主板都支持这个功能,支持的主板上通常都有一个专门的3芯插座,以便在关机时为网卡供电。但
并非所有的网卡都支持该功能(特别是一些价格较便宜的低档网卡),要判断网卡是否支持远程唤醒功能的方法很简单,支持远程唤醒的网卡上都有一个3针的WOL接口和一条3芯的远程唤醒电缆,通过判断网卡是否带有WOL接口即可(有些较新的网卡可能没有WOL接口也能支持远程唤醒。这是因为现在流行的主板支持PCI2.2标准,而PCI 2.2标准不需要通过专门的WOL接口为网卡供电,允许主板直接通过PCI插槽向网卡提供Standby电源)。

1.硬件连接
网卡安装完毕后将远程唤醒电缆的一端插入到网卡的WOL接口上,另外一端与主板的3针WOL远程唤醒接口相连(该接口旁通常标有WOL_CON的字样,当然如果主板和网卡都支持PCI2.2标准则无须做这一步)。

2.CMOS设置
打开CMOS远程唤醒功能很简单,只要将CMOS设置中的“Power Management Setup”的“Wake Up On LAN”项设置为“Enable”即可。

软件的实现方面,其实就是通过socket向目标的机器发送魔术包了,魔术包的格式,包含有连续6个字节的“FF”和连续重复16次的MAC地址。程序中我们可以采用UDP方式广播发送,不需要端口号,只要知道对方机器的MAC地址即可 :)

程序运行界面如下:

程序打包下载:

点击下载此文件

C++/CLI的泛型

111 views 十一月 13, 06 by Timothy

泛型,其实在C++里面已经不是一个新概念了。他允许C++程序员参数化类型,把类型作为参数抽象出来,更好的实现代码的复用,并且使你的程序更加简洁,不用去为同一个函数重载多个不同参数类型的版本。在.NET 2.0中也加入了对泛型的支持。而随着STL也被集成到.NET中来,成为STL.NET,我们在采用C++/CLI开发的时候,就有了更多的选择。利用.NET框架所提供的数据集合System::Collection,或者是使用泛型System::Collection::Generic,还可以使用STL.NET。
Stanley B. Lippman在文章STL.NET Primer中提到,STL.NET被经过重新设计,集成到.NET中来,结合了泛型和模板的机制,看看他对STL.NET的评价:In my judgment, when combined with the
revised C++/CLI dynamic programming support, this coming version of Visual C++ is
simply the most satisfying language in which to program. I think STL.NET is a very exciting
library addition, and I hope that after you finish this series of articles you will agree with
me.
泛型与模板的应用,在C++/CLI中,会变得更有乐趣。
下面,是在C++/CLI中使用泛型、模板的一些例子。这里没有采用STL.NET,关于STL.NET的介绍,可以参考Lippman的文章:http://msdn2.microsoft.com/en-us/library/ms379600(VS.80).aspx

//这是我们自己实现的,用来模拟堆栈结构的模板类
template
ref class MyStack
{
private:
ArrayList ^ mList;
public:
MyStack()
{
mList= gcnew ArrayList();
}

void push(T const & type)
{
mList->Add(type);
}

int pop(T &value)
{
if(mList->Count > 0)
{
value=*reinterpret_cast( mList[mList->Count - 1]);
mList->RemoveAt(mList->Count – 1);
return true;
}
else
{
return false;
}
}
};

int main(array ^args)
{
//实例化这个模板类
MyStack ^ mystack = gcnew MyStack ();
mystack->push(12);
mystack->push(13);

int a,b;
mystack->pop(a);
mystack->pop(b);
Console::WriteLine("a is:{0} and b is:{1}",a,b);

//.NET 2.0提供的泛型支持,为我们封装好了一个Stack
System::Collections::Generic::Stack ^ stack =
gcnew System::Collections::Generic::Stack ();
stack->Push("World");
stack->Push("Hello");

Console::WriteLine("{0} {1}",stack->Pop(),stack->Pop());

return 0;
}

运行结果如图:

VC雕虫小技:使当前所有窗口最小化

296 views 七月 18, 06 by Timothy

要使用当前的所有窗口最小化,一般的做法,就是通过EnumWindows枚举所有窗口,然后再调用ShowWindow函数来最小化所有的窗口。不过还有一个做法,就是Win键+M键组合。不过,我们可以在程序中来模拟这样的按键,也能达到最小化所有的窗口。使用到的模拟键盘按键的函数keybd_event:

keybd_event(VK_LWIN, 0, 0, 0);
keybd_event(77, 0, 0, 0);
keybd_event(VK_LWIN, 0, 0×02, 0);
keybd_event(77, 0, 0×02, 0);

调试windows服务的一点经验

129 views 六月 16, 06 by Timothy

最近的项目涉及到windows服务的问题,那么写在windows服务中的代码怎样调试呢?windows服务的调试方法和我们一般的程序方法有些不一样,但是也是在IDE里面调试。MS的IDE也为我们考虑到了这些方面。跟我来看看怎样来调试一个windws服务吧?[cool]

大致分为一下几个步骤:

1. 建立windows服务程序,不管你是用win32 API,还是ATL,还是.NET,都可以实现的。
2.将代码写好,编译,保证没有错误。
3.现在到了调试的过程了,首先,在IDE中,找到你要调试的地方,加上断点,然后安装好服务。
服务的安装,其实就是设计到注册表的操作。在.NET平台下,我一般习惯用installutil这个程序来安装服务。安装好之后,我们打开控制面板的“Administrator Tools”,找到”Services”选项,双击打开。在服务列表中,我们能找到我们安装的服务。然后运行这个服务。
4.现在到了关键的地方,在服务开始的时候,就会执行你的代码。为了调试的方便,我是在服务的代码开始处让,程序先Sleep20秒,这样的好处看后面你就知道了。
5.切换回我们的IDE,在Debug菜单,里面有个Process(进程)选项。点击打开,在对话框的进程列表中,选择我们的服务相对应的进程,然后点击旁边的Attach(附加)按钮。这样,IDE就开始加载我们要调试的进程了。加载完毕之后,程序会停顿一下。(为什么?因为我们在服务的开始代码处,Sleep了20秒,喝口水等一下吧)。之后,黄色的光标,跳动到你的断点处,现在就可以像以往一样,来调试这个windows服务了。
之所以在程序开始的地方Sleep20秒,是为我们在IDE附加这个Service进程的时候,留下一段缓冲的时间,不然当你的服务开始运行的时候,你的断点处的代码早就被执行过了,你断点自然不起作用了。明白了吧?呵呵。

还有一些调试windows服务小的窍门,就是写日志文件。这也是调试windows service的一个比较好的方法,虽然比较笨,但是通过阅读你自己留下的日志,也能起到不小的作用的。所以在编写程序的时候,要在关键的地方,记录日志,也是为以后程序的维护提供方便。
好了,一点小小的经验,写下来,希望对大家有帮助 。[cool]

Page: 1 of 13 1 2 3 4 5 ... 13