在Windows Mobile上自动启动应用程序(译文)
By S.F.
本文链接 https://www.kyfws.com/news/automatically-starting-your-application-on-windows/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 9 分钟阅读 - 4251 个词 阅读量 0在Windows Mobile上自动启动应用程序(译文)
原文地址:https://www.codeproject.com/Articles/27917/Automatically-Starting-Your-Application-on-Windows
原文作者:Joel Ivory Johnson
译文由本站翻译
前言
一篇文章讨论了可用于通过计划或响应系统事件在Windows Mobile上自动启动应用程序的各种方法.
介绍
弄清楚如何使应用程序自动启动对我来说一直是一个谜.信息分散,需要进行一些研究以收集所有信息.在研究过程中,我将所有笔记都放在了各种文档中,并决定将它们整理成一个文档并与所有人共享.此处提供的信息已在Windows Mobile 5和Windows Mobile 6设备上进行了测试,但也可以应用于Windows CE的多个版本(Windows Mobile的操作系统).
通过制作自己的可执行文件来实现类似的功能,该可执行文件可以通过SystemState
类监视系统更改并做出相应的响应,但这将导致程序占用更大的空间.
关于代码
自动启动程序的第一种方法集中在我称为配置任务的位置.制作快捷方式,注册表项或将文件放置在特定位置.对于这些方法,我不提供代码示例.在本文的后面,我将讨论通过代码自动启动应用程序的方法.对于这些方法,我在文章中包括了示例代码,并在文章中附加了完整的源代码. 代码示例依赖于一些通过P/Invoke调用的Win32函数.这两个代码示例都引用一个名为Win32的项目,其中包含对这些函数的引用.此外,Win32还包含将信息正确传递给WinAPI函数所需的结构和枚举.要运行代码示例,您将需要Windows Mobile设备(或仿真器)和Visual Studio 2008. 请记住,在Windows Mobile设备上,应防止内存中不存在程序的多个实例(如果启动了第二个实例,它将通知第一个实例,然后立即终止).
“自动启动"是什么意思?
当我使用"自动启动"一词时,我指的是基于事件的任何程序的启动(用户单击程序图标除外).可以使用以下四种方法之一自动启动程序:
从存储卡自动运行
用户插入存储卡可能会导致应用程序以两种方式之一启动.存储卡上可能有插入时启动的程序,或者Windows Mobile设备可以响应于插入的存储卡而启动其主存储器中已经存在的程序.后者可以通过注册一个应用程序以从系统更改开始(稍后讨论)来实现. 将存储卡插入Windows Mobile/Windows CE设备时,操作系统会在某个文件夹中自动查找名为Autorun.exe的程序.如果找到该程序,则它将立即运行. OS所在的文件夹将取决于设备所使用的处理器类型.对于绝大多数Windows Mobile设备,该文件夹将为”/2577".下表是其他Windows CE设备可能的文件夹名称的表: 如果您在存储卡上已经有一个要自动运行的应用程序,但又不想重命名该可执行文件或将其放置在此文件夹中,那么您始终可以创建第二个可执行文件,其目的是启动第一个可执行文件.
启动快捷方式
可以对要自动启动的应用程序创建快捷方式,并将其放置在\ Windows \ StartUp中.如果您需要启动一个不依赖于其他可执行文件的可执行文件,请使用此方法. Windows Mobile设备上的快捷方式格式很简单.它将始终采用00#" <\ program Files \ path>“的形式,其中00替换为在#号后出现的字符数; “#“是定界符,然后是可执行文件的完整路径.以下是Windows Media Player快捷方式的示例:
23#"\windows\wmplayer.exe"
不过,您不必手动创建快捷方式.有一个本地API调用” SHCreateShortcut”,它将为您创建一个快捷方式.第一个参数是要创建的快捷方式的完整路径,第二个参数是快捷方式所指向的文件的完整路径.
在启动时启动应用程序
对于Windows Mobile设备,自动启动条目的位置是HKEY_LOCAL_MACHINE \ Init.与台式机上的启动项(只需要启动可执行文件的路径)不同,Windows Mobile设备的条目结构更复杂.有两个与需要自动启动的应用程序相关的键,一个是” LaunchXX"键,另一个是可选的" DependXX"键. XX将替换为数字.该编号也称为序列号. " LaunchXX"的值是一个字符串值(" REG_SZ"),其中包含要启动的可执行文件的路径. DependXX键用于指定当前应用程序对哪些应用程序具有依赖关系(以及必须以什么顺序启动应用程序). DependXX键包含一个单词(2字节)值的列表,其中包含所需应用程序的序列号值. 以下屏幕截图是Windows Mobile 5手机上的注册表的. Launch21引用名为" coldinit"的应用程序.如果我们看Depend21,我们会看到" coldinit.exe"对由0x14(十进制20)标识的应用程序具有依赖性.因此,必须在Launch20" Device.exe"中标识的应用程序之后启动" coldinit.exe".
使用此方法启动的应用程序必须通过调用SignalStarted(DWORD)函数来通知应用程序成功启动.此函数是本地调用.对于C程序,此函数的头文件在Winbase.h和库Coredll.lib中定义.使用托管代码的开发人员将需要P/Invoke此方法.该函数的唯一参数是可执行文件的序列号.序列号作为唯一的命令参数传递给应用程序.请注意,序列号是应用程序能够通过命令行参数接收的唯一参数.必须传递给应用程序的任何其他信息应通过配置文件或注册表项传递.
在指定时间启动程序
Windows CE/Windows Mobile OS包含用于在指定时间自动启动程序的功能.可通过从CoreDLL库调用CeRunApAtTime
来使用该功能.正如Jim Wilson在MSDN上的许多"如何做"视频中提到的那样,该函数期望开始时间在WinAPI的SystemTime结构中指定,而不是在DateTime结构中指定(CeRunAppAtTime是非托管函数)使用平台调用功能调用).将时间从" DateTime"转换为" SystemTime"并不困难;有WinAPI函数可以为您完成此任务.为了使调用此函数更容易,我在Win32Helper类中放置了以下代码.我还提供了一个重载函数,允许通过TimeSpan对象将时间作为当前时间的偏移量进行传递.
public static void RunAppAtTime(string applicationEvent, DateTime startTime)
{
long fileTimeUTC = startTime.ToFileTime();
long fileTimeLocal = 0 ;
SystemTime systemStartTime = new SystemTime();
CoreDLL.FileTimeToLocalFileTime(ref fileTimeUTC, ref fileTimeLocal);
CoreDLL.FileTimeToSystemTime(ref fileTimeLocal, systemStartTime);
CoreDLL.CeRunAppAtTime(applicationEvent, systemStartTime);
}
public static void RunAppAtTime(
string applicationEvent,
TimeSpan timeDisplacement
)
{
DateTime targetTime = DateTime.Now + timeDisplacement;
RunAppAtTime(applicationEvent, targetTime);
}
applicationEvent是启动应用程序的完整路径,而startTime是执行该应用程序的时间.如果您要指定相对于当前时间的开始时间,则该方法还有一个重载版本,可以接受" TimeSpan"对象而不是" DateTime". 如果应用程序试图安排自己在以后的时间重新启动,则它将需要能够传递其完整路径.我使用反射来找到那条路.
Module[] m = this.GetType().Assembly.GetModules();
target = m[0].FullyQualifiedName;
由于系统更改而运行程序
有许多系统更改可用于触发程序的执行. WinAPI函数" CeRunAppAtEvent"用于将程序与事件关联.一旦关联,该程序将在每次事件发生时启动!因此,当您不再希望程序自动启动时,还必须记住将其与事件解除关联. 我在Win32类中创建了一个名为" WhichEvent"的枚举,该枚举包含可用于触发程序执行的事件的ID号. 当由于系统状态更改而启动程序时,会将单个参数传递给该程序,以指示触发程序执行的状态更改. (我不在这里讨论如何执行此操作的详细信息).有关可能的参数的完整列表,请参见AutoStartArgumentString.cs中的示例代码. 我创建了一个简单命名为" Core"的类,用于声明来自" Core" DLL.dll库的平台调用的方法,并在其中声明了" CeRunApAtEvent"函数.以下计划Windows计算器在设备退出挂起状态时启动:
CoreDLL.CeRunAppAtEvent(@"\Windows\Calc.exe",
(int)WhichEvent.NOTIFICATION_EVENT_DEVICE_CHANGE);
在该调用之后,Windows计算器将在每次唤醒设备时启动.为了防止计算器启动,必须再次调用:
CoreDLL.CeRunAppAtEvent(@"\Windows\Calc.exe",
(int)WhichEvent.NOTIFICATION_EVENT_NONE);
本文包括一个示例应用程序,可用于导致应用程序启动各种事件.最初,该程序只会注册一个程序以在唤醒时启动.但是,我已经扩展了程序,以便它也可以响应其他事件而启动程序.请注意,通过更改对CERunAppAtEvent的调用中的值,您可以使用该程序使可执行文件由于其他事件(例如,插入存储卡或ActiveSync完成其同步)而启动.
自动启动程序时,会通过命令行将指示启动事件的字符串传递给它.我在源代码中包含了一个名为ShowCommandLine的程序,它只不过显示了接收到的命令行参数.在下面的屏幕快照中,您可以看到在连接到网络连接后启动程序时收到的命令行参数.
防止多个实例
通常,.NET框架将确保您的程序的多个实例未在运行.对于由于系统事件而启动的程序,这并不总是有效.可以快速连续触发几个系统事件,也可以触发两次相同的事件(由于某种奇怪的原因,唤醒事件通常触发两次).我第一次尝试调度ShowCommandLine程序以在另一个事件唤醒时启动,但最终却运行了该程序的多个实例.
为了解决这个问题,我在加载表单之前创建了一个事件对象(使用P/Invoke).如果快速连续启动程序的多个实例,则事件的创建将失败,并且我们可以使用此失败来知道这不是程序的第一个实例,然后立即将其卸载.
static void Main()
{
IntPtr eventHandle = IntPtr.Zero;
const string ApplicationEventName = "ShowCommandLineEvent";
//We will use this as our handle to ensure only one instance of the
//program is started
try
{
//Try to create the event. If the creation fails then it is
//because another instance of this application is already
//running. If another instance exists then this instance
//should immediatly terminate.
eventHandle = CoreDLL.CreateEvent(IntPtr.Zero, true,
false, ApplicationEventName);
int lastError = Marshal.GetLastWin32Error();
//MessageBox.Show(String.Format("event handle {0}",eventHandle));
if (0 == lastError)
{
Application.Run(new Form1());
}
}
finally
{
//When the application is no longer
//running it should release the event
if (eventHandle != IntPtr.Zero)
CoreDLL.CloseHandle(eventHandle);
}
}
当以这种方式创建一个程序的多个实例时,您可能希望向该程序的第一个实例发送通知,以便它可以响应该事件.
下一步是什么?
本文在很大程度上是我为准备计划设计的软件解决方案所做的研究的结果.在我的研究的下一部分中,我将开发一种解决方案,以使手机响应未通过CeRunAppAtEvent
函数直接暴露的其他事件.
历史
- SHCreateShortcut
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
WinMobile5 VS2008 C# WinMobile .NETCF Visual-Studio Mobile Dev Intermediate WinMobile6 新闻 翻译