开发Windows Mobile今日插件 -- 内存电量,桌面便笺,桌面记单词

本篇文章讲解的是开发 Windows Mobile 上的今日插件。关于是今日插件,在 PPC 或者 SP SDK 的帮助文档中有相关的章节介绍,在网络上也有一些帖子和资源讲解。在这里简要回顾一下。今日插件就是在windows mobile的桌面上显示的条目,例如系统提供的“日历”(Calendar),主人信息,以及许多第三方开发的今日插件等等。由于桌面是开机后的第一个屏幕,所以插件显示在屏幕上将会得到用户最多的浏览时间。这里我们主要讲解的是用户自定义插件的开发。自定义插件如下图所示:

      

一个插件就是一个位于屏幕上的窗口,每个插件负责自己的绘制和对用户输入的响应。插件可以通过给自己的父窗口(桌面窗口)发送TODAYM_DRAWWATERMARK 消息,委托父窗口为自己绘制背景,也就是通过把自己的HDC传递给shell来完成的,这样插件就看起来好像是“透明”的效果。同时,shell 也负责在相邻的插件之间绘制一条分割线。
通常,PPC 最多允许加载 12 个插件。最大插件数量是由 K_cTodayItemsMax 定义的。
对于自定义的插件,要求开发者提供一个DLL函数并注册到注册表: HKLM\Softeware\Microsoft\Today\Items;
如下图,我们使用远程注册表查看工具打开一个插件的在注册表中的位置:

      

在下面包含了所有今日插件的键。每个插件将含有下列的值: 

◆Type (DWORD);
对自定义插件来说,等于4。它是 SDK 中的插件类型枚举中的一个值( tlitCustom)。 
typedef enum _TODAYLISTITEMTYPE { 
tlitOwnerInfo = 0, 
tlitAppointments, 
tlitMail,
tlitTasks,
tlitCustom, //自定义插件 = 4
tlitNil
} TODAYLISTITEMTYPE;

◆Enabled;
插件是否启用。用户能够在设置-今日-项目中进行启用或禁用。

◆Options;
是否含有设置对话框。也就是设置-今日-项目中插件被选中时的 “选项按钮” 的 Enabled 状态。

◆DLL:
插件dll的路径。

◆Selectability;
可选项,插件是否可以被选中(用户在屏幕上按导航键时)。通常为1,表示允许被选中。当允许选中时,用户按上下方向键,被选中的插件背景会高亮。如果不能选中,就会跳过该插件。

◆Order;
可选项,插件显示循序的排序值。缺省时由系统自动设置。

下面我们再介绍插件的协议,也就是插件的DLL应当满足以下要求。

(1)要求 dll 导出序号为 240 的以下函数,以初始化和创建插件窗口;
      #define ORDINAL_INITIALIZEITEM      240
typedef HWND (*PFNCUSTOMINITIALIZEITEM)(TODAYLISTITEM *, HWND);
参数1:TODAYLISTITEM 结构的指针,包含了该插件在系统中注册的相关信息。
参数2:桌面窗口的句柄,它将成为插件窗口的父窗口。

(2)如果插件具有设置对话框,则要求dll导出序号为 241 的以下函数,作为设置对话框的窗口过程;
      #define ORDINAL_OPTIONSDIALOGPROC   241
typedef BOOL (*PFNCUSTOMOPTIONSDLGPROC)(HWND, UINT, UINT, LONG);
同时要求dll 提供资源ID为 500的一个对话框资源作为设置对话框的模板。(可以通过手工修改 resource.h 中的定义)

(3)shell 将向插件窗口发送以下信息,要求插件处理这些消息;
      WM_TODAYCUSTOM_CLEARCACHE
告知插件正在被从显示中卸载,要求插件清理自己所维护的数据缓存。

      WM_TODAYCUSTOM_QUERYREFRESHCACHE
此消息在桌面显示期间以每2秒钟一次的频率周期性对所有插件发送。询问插件是否需要进行更新。
在插件首次加载时,还要求插件告知系统插件的高度以对插件窗口进行布局。由于插件窗口被上下垂直分布,所以宽度对于系统属于已知的。如果返回TRUE,表示要求进行更新。如果不需要更新返回FALSE。

(4)同时插件还能够向父窗口发送以下消息,以辅助绘制。
      TODAYM_GETCOLOR
询问系统当前使用的前景色,背景色,高亮前景色等信息。以便绘制时,和系统使用的主题风格保持一致。

      TODAYM_DRAWWATERMARK
要求shell 为插件绘制背景。也就是把背景位图复制到插件窗口的背景。

好了,关于插件开发规则我们就简要介绍到此。在开发插件时,开发者的主要任务是编写插件窗口\设置对话框的窗口过程,完成属于自己的功能。这里要求具有的是windows开发的一些基础。我们不细作介绍了。下面是我在这几天制作的插件。

(1)根据 SDK 中 范例改编而成的 memWatcher 插件 和 “桌面便笺”。效果如下:

      

在左图是SDK中的 memWatcher 范例在模拟器中的显示效果,右图是经过我适当改写后,在实际HTC S1中运行的效果。SDK范例显示了程序和存储的百分比,并且创建了两个进度条窗口显示。经过我的改写,我把进度条去掉了,从而可以把信息压缩到一行以内,这样可以节省屏幕空间,并且增加了电池电量的显示。
获取这些信息的相关API函数是:
      GlobalMemoryStatus,GetStoreInformation,GetSystemPowerStatusEx;

下面我们看下桌面便笺插件,这个插件发布在 pdafans 论坛后,很快就有网友向我反馈了备忘录的内容在重启后消失,这是因为我做的这个插件也仅仅是个范例来使用,测试了我的想法是可行的,所以并没有考虑那么多。便笺的内容被放到了内存里,声明周期和DLL一样,这也一旦DLL被卸载,存储在内存中的内容也就失去了。这也提醒了我们一点,我们的插件应该将数据持久化。所以我又修改了这个插件,把备忘信息和图标索引存储到了注册表中,也就是插件注册的键下面新增了两个值。这样我们就可以保证每次插件启动时都会从注册表中读取出上次的用户记录的内容。
当用鼠标点击桌面便笺时,就会弹出一个对话框用于设置新的备忘内容,如下图所示:

      

这个对话框中具有一点难度和技巧性的是上面的图标选择反馈,全部是通过鼠标点击事件来完成的。我们在对话框的 WM_ONPAINT 消息处理中,在对话框上绘制了所有可选图标,每个图标实际上是16*16像素大小,所以我指定的网格是20*20像素,在每个网格中绘制一个图标,并对被选中图标绘制了一个蓝色矩形框表示选中状态。当鼠标点击到其他图标时,我们就要更新这个蓝色矩形。同时我们也要根据鼠标位置在网格中正确的定位要当前位置选中的鼠标索引。这里的处理并不算非常难,但是需要少许的耐心。
显示和隐藏输入面板,在 .NET CF中,有一个inputPanel控件,我们 可以方便的设置它的Visible属性去控制。而在EVC中,我们是通过下面的API函数去显示或者隐藏SIP的。
      SipShowIM(SIPF_ON) 和 SipShowIM(SIPF_OFF);
或者我们也可以使用:SHSipPreference(hDlg, SIP_UP) ,去要求 Shell 浮出输入面板。

(2)桌面记单词插件。
桌面记单词插件的灵感是来自桌面上的类似工具,即有一个顶层窗口,以一个固定的频率切换词条显示,以帮助用户背单词。我这里就是模拟这种软件的效果做的一个今日插件。当然它不仅仅可以背单词,也可以显示其他字典内容,例如唐诗宋词,名言名句等等。用户可以自定义字典文件,本质上就是一个文本文件,并通过修改配置文件把字典添加进来。

为了降低读文件的频率,我在插件内维护了一个词条缓存(缓存10个词条),每次一次性尝试从文件中加载10个词条文件(每个词条也就是文本文件中的一行)到内存中。当词条正在滚动期间,文件保持打开状态。当暂停滚动时,将会关闭文件。插件利用每2秒钟接收到的消息去滚动词条。效果如下图所示:

      

开发这个插件时,我忽然发现 Pocket PC 的操作系统是不支持读写 ini 文件的相关API函数的。我去网络上找了下相关代码,但是没有看到特别满意的。因此我自己用C语言写了几个和API函数功能相同的读 ini 文件的函数。函数命名也是完全相同的,为了在 PC上进行测试,我在每个函数名前面加了 Ce ,以和系统的API函数区分开。我这里仅仅为了插件功能写了有限的几个函数,这里以 CeGetPrivateProfileString 为例给出代码。
在PC上,这个函数负责读取 ini 文件某个 section 中某个 key 的值。为了同时在 unicode 和 多字节字符串环境中适用,我又把相关的文件和字符串操作函数进行了宏定义,并且以这种方式命名: 
      t_多字节版本函数名
这是因为对多字节版本的函数我们通常更加熟悉它的命名。例如对于 t_strcpy 等等。下面是这个函数的代码:

ContractedBlock.gifExpandedBlockStart.gifCode_CeGetPrivateProfileString
#ifdef  UNICODE   // r_winnt

    
#define t_fopen         _wfopen
    
#define t_fgets            fgetws
    
#define t_sprintf        swprintf    //格式化文本
    
#define t_strcpy        wcscpy
    
#define t_strncpy        wcsncpy        //拷贝指定个数的字符
    
#define t_strcat        wcscat        //append a string
    
#define t_strtol        wcstol
    
#define t_strlen        wcslen
    
#define t_strcmp        wcscmp
    
#define t_stricmp        _wcsicmp    //忽略大小写的字符串比较
    
#define t_strncmp        wcsncmp        //比较n个字符
    
#define t_strchr        wcschr        //find a character in a string
    
#define t_strrchr        wcsrchr        //从结尾向前查找字符

#else  //ASCII CODE

    
#define t_fopen         fopen
    
#define t_fgets            fgets        //读取一行文本
    
#define t_sprintf        sprintf        //格式化文本
    
#define t_strcpy        strcpy
    
#define t_strncpy        strncpy        //拷贝指定个数的字符
    
#define t_strcat        strcat        //append a string
    
#define t_strtol        strtol        //把字符串转换成long(int32)
    
#define t_strlen        strlen
    
#define t_strcmp        strcmp        //比较字符串
    
#define t_stricmp        _stricmp    //忽略大小写的字符串比较
    
#define t_strncmp        strncmp        //比较n个字符
    
#define t_strchr        strchr        //查找字符
    
#define t_strrchr        strrchr        //从结尾向前查找字符

#endif

#define LINESIZE    260    //行缓冲区大小


//从appname(section)中读取string类型key
DWORD CeGetPrivateProfileString(
    LPCTSTR lpAppName,                
//section name: [lpAppName]
    LPCTSTR lpKeyName,                //lpKeyName=lpReturnedString
    LPCTSTR lpDefault,                //未找到时的默认值
    LPTSTR lpReturnedString,    //[out] 查找到的结果
    DWORD nSize,                            //[in]lpReturnedString的字符数,注意单位不是字节!
    LPCTSTR lpFileName
    )
{
    DWORD ret 
= 0;
    FILE 
*stream;
    
bool bFindVal = false;
    
bool bFindSection = false;
    TCHAR line[ LINESIZE ];
    size_t sectionLength, keyLength, lineLength;
    
    stream 
= t_fopen(lpFileName, _T("r"));
    
if(stream == NULL)
    {
        
//设置默认值
        t_strcpy(lpReturnedString, lpDefault);
        ret 
= t_strlen(lpReturnedString); 
        
return ret;
    }
    
    sectionLength 
= t_strlen(lpAppName);
    
    
while(t_fgets(line, LINESIZE, stream) != NULL)
    {
        
//忽略注释行和空行
        if(line[0== 0 || line[0== ';'continue;
        lineLength 
= t_strlen(line);
        
//注意:把LF(0xa)字符替换成0,这在UNICODE环境下可能出现结尾是LF)
        if(line[ lineLength - 1 ] == 0x0a)
        {
            line[ lineLength 
- 1 ] = 0;
            lineLength
--;
            
//注意此时可能会成为空字符串
            if(lineLength == 0continue;
        }
        
        
//尝试寻找到 section
        if(!bFindSection)
        {
            
if(line[0!= '['continue//本行是否是 [section]
            
//这里是我们想要的Section吗?
            
//检查这一行的宽度是否正好是section长度加2, [lpAppName]
            if(line[sectionLength + 1!= ']'continue;
            
if(t_strncmp(line+1, lpAppName, sectionLength) != 0continue;
            
//Now Section will appear on next line 
            
//读取section前求出 Key 的长度
            keyLength = t_strlen(lpKeyName);
            bFindSection 
= true;            
            
continue;
        }
        
        
//查找Key, Section End?
        if(line[0]=='['break//遇到了下一个
            
        
if(lineLength < keyLength+1 || line[keyLength] != '='continue//"KeyName="
        if(t_strncmp(line, lpKeyName, keyLength)!=0continue;
        
//Now We Get the Key! 
        t_strcpy(lpReturnedString, line + keyLength + 1);
        
//Now It's done.
        bFindVal = true;
        
break;
    }
    
    fclose(stream);
    
if(!bFindVal)
    {
        
//设置默认值
        t_strcpy(lpReturnedString, lpDefault); 
    }
    ret 
= t_strlen(lpReturnedString); 
    
return ret;
}

下面是我提供了一个演示程序,由于我们知道了插件的协议,所以我们也可以显示出其他插件的选项对话框,为了更具可读性,代码经过了精简。

ContractedBlock.gifExpandedBlockStart.gifCode_显示其他插件的选项对话框
TCHAR path[256];
GetDlgItemText(hDlg, IDC_DLLPATH, path, 
256);
// load dll
g_PluginModule = LoadLibrary(path);
// get dlgproc address 窗口过程函数的导出序号是241
g_PluginProc = (DLGPROC)GetProcAddress(g_PluginModule, (LPCTSTR)241);
// create options dlg
g_PluginDlg = CreateDialog(g_PluginModule, (LPCTSTR)MAKEINTRESOURCE(500), NULL, g_PluginProc);
ShowWindow(g_PluginDlg, SW_SHOW);
SetWindowPos(g_PluginDlg, NULL, 
308000, SWP_NOSIZE);
return TRUE;

运行效果如图所示:



最后我们开发好插件以后,可以利用SDK提供的打包工具,把插件制作成 cab 包,这样复制到设备上即可自动安装。打包是使用SDK提供的工具完成,但是我们首先需要自己为我们的软件编写一个 inf 文件,描述软件的发装过程。inf文件详细描述了需要拷贝的文件清单,源目录,目标目录,要添加的注册表信息等内容。这里可以参考 SDK中的范例,细节就不再描述了。这里我使用 mymemo 的 inf 文件做一个例子说明:为了更具可读性,文件内容经过了精简。

ContractedBlock.gifExpandedBlockStart.gifmymemo.inf
[Version]
Signature   
= "$Windows NT$"
Provider    
= "Microsoft"
CESignature 
= "$Windows CE$"

[CEStrings]
AppName     
= "MyMemo"
InstallDir  
= %CE2%             ; "\Windows" 

[CEDevice]
UnsupportedPlatforms    
= "HPC","Jupiter","Palm PC2"
VersionMin         
= 3.0
VersionMax        
= 6.0

[PPC2003_Device]
ProcessorType           
= 2577      ; ARM CPU

[SourceDisksNames.PPC2003_Device]
1 = ,"ARM Files",,ARMV4Rel

[SourceDisksFiles]
"mymemo.dll"                    = 1     ; the Today plugin dll

[DestinationDirs]
Files.Windows       
= 0,%CE2%           ; "\Windows" directory

[Files.Windows]
"mymemo.dll","mymemo.dll",,0x00000001

[Reg.Version1]
HKLM,Software
\Microsoft\Today\Items\%AppName%,Enabled,0x00010001,0
HKLM,Software
\Microsoft\Today\Items\%AppName%,Type,0x00010001,4
HKLM,Software
\Microsoft\Today\Items\%AppName%,Options,0x00010001,0
HKLM,Software\Microsoft\Today\Items\%AppName%,Selectability,0x00010001,1
HKLM,Software\Microsoft\Today\Items\%AppName%,IconIndex,0x00010001,0
HKLM,Software\Microsoft\Today\Items\%AppName%,DLL,0x00000002,"%InstallDir%\mymemo.dll"
HKLM,Software\Microsoft\Today\Items\%AppName%,Memo,0x00000002,"Type here to input memo"

打包工具是一个命令行程序,我们执行以下命令:
cabwiz mymemo.inf  /err errinfo.txt  /cpu PPC2003_Device

      其中,/err选项指定错误输出文件,当打包失败时,这是诊断问题的重要信息。
/cpu选项指定是inf文件中定义过的CPU类型,如果在inf文件中定义了多种CPU类型,可以同时为多种CPU打包,所以一个inf文件可以多用。

最后我们给出相关的下载连接:
(1)程序存储电量百分比显示和桌面便笺插件的CAB包下载链接:
      http://files.cnblogs.com/hoodlum1980/PPCCAB_MyMemo_MemWatcher.rar
(2)桌面记单词插件的CAB包下载链接:
      http://files.cnblogs.com/hoodlum1980/Recite_CAB_ARMV4.rar
(3)然后在给出一个我以前写的C语言的俄罗斯方块(最早发表在编程论坛),移植到PPC上的版本:
      http://files.cnblogs.com/hoodlum1980/Tetris.rar
运行效果截图:

      

(4)最后我们给出本文提及所有源代码的合集下载连接,全部使用EVC4.0使用C++开发。每个插件包含了用于打包的 inf 文件。
      http://files.cnblogs.com/hoodlum1980/TodayPlugins.rar

转载于:https://www.cnblogs.com/hoodlum1980/archive/2009/08/01/1536246.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/379600.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法---递归

递归结题三部曲 何为递归&#xff1f;程序反复调用自身即是递归。 我自己在刚开始解决递归问题的时候&#xff0c;总是会去纠结这一层函数做了什么&#xff0c;它调用自身后的下一层函数又做了什么…然后就会觉得实现一个递归解法十分复杂&#xff0c;根本就无从下手。 相信…

给定条件找最小值c语言程序_根据给定条件最小化n的最小步骤

给定条件找最小值c语言程序Problem statement: 问题陈述&#xff1a; Given a number n, count minimum steps to minimize it to 1 performing the following operations: 给定数字n &#xff0c;执行以下操作&#xff0c;计算最少的步骤以将其最小化为1&#xff1a; Operat…

那个年代的苏联歌曲

小时候&#xff0c;不时听父亲提起电影《这里的黎明静悄悄》&#xff0c;怎么也想不到如此美丽的名字为什么要和战争联系起来。后来在大学看了这部电影之后&#xff0c;开始认为这名字是合适的&#xff0c;因为电影讲的是女性——战场中的女性&#xff0c;各自都怀揣着爱情去保…

linux系统编程---进程总结

进程控制总结1 进程创建的三种方式forkvfrokclone2 进程终止进程正常退出returnexit_exit进程异常退出进程收到某个信号&#xff0c;而该信号使进程终止abort3 进程等待进程等待的方法waitwaitpid4 进程替换替换原理替换函数制作一个简单的shell1 进程创建的三种方式 参考文章…

银行账务转账系统(事务处理)

流程如下&#xff1a; 创建项目工程如下&#xff1a; transfer包下的代码如下&#xff1a; package beyond.transfer.dao;import java.sql.Connection; import java.sql.SQLException;import org.apache.commons.dbutils.QueryRunner;import beyond.utils.DataSourceUtils;pu…

【msdn wpf forum翻译】TextBox中文本 中对齐 的方法

原文链接&#xff1a;http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/49864e35-1dbf-4292-a361-93f1a8400558问题&#xff1a;TextBox中文本中对齐&#xff0c;使用 TextBox.HorizontalContentAlignment"Center"行不通&#xff08;TextBox.VerticalConte…

c语言 函数的参数传递示例_C语言中带有示例的remove()函数

c语言 函数的参数传递示例C语言中的remove()函数 (remove() function in C) The remove() function is defined in the <stdio.h> header file. remove()函数在<stdio.h>头文件中定义。 Prototype: 原型&#xff1a; int remove(const char* filename);Parameter…

使用ThreadLocal绑定连接资源(事务)

dao层代码如下&#xff1a; package beyond.transfer.dao;import java.sql.Connection; import java.sql.SQLException;import org.apache.commons.dbutils.QueryRunner;import beyond.utils.DataSourceUtils; import beyond.utils.MyDataSourceUtils;public class TransferDa…

算法---栈和队列

栈和队列1 栈栈的顺序存储栈的链式存储2 队列队列的顺序存储队列的链式存储3 栈和队列的应用用栈实现队列用队列实现栈最小栈1 栈 参考文章&#xff1a; https://zhuanlan.zhihu.com/p/346164833 https://zhuanlan.zhihu.com/p/120965372#:~:text%E6%A0%88%E6%98%AF%E4%B8%80%…

在WebBrowser中通过模拟键盘鼠标操控网页中的文件上传控件

引言 这两天沉迷了Google SketchUp&#xff0c;刚刚玩够&#xff0c;一时兴起&#xff0c;研究了一下WebBrowser。 我在《WebBrowser控件使用技巧分享》一文中曾谈到过“我现在可以通过WebBrowser实现对各种Html元素的操控&#xff0c;唯独无法控制Html的上传控件”&#xff0c…

编写最简单的字符设备驱动

编写最简单的字符设备驱动1 编写驱动代码2 编写makefile3 编译和加载驱动4 编写应用程序测试驱动参考文章&#xff1a; linux驱动开发第1讲&#xff1a;带你编写一个最简单的字符设备驱动 linux驱动开发第2讲&#xff1a;应用层的write如何调用到驱动中的write 1 编写驱动代码…

Linux设备驱动开发---字符设备驱动程序

字符设备驱动程序1 主设备和次设备的概念设备号的注册和释放静态方法动态方法区别2 设备文件操作struct file_operations与struct file、struct inode关系3 分配和注册字符设备class_createcdev_adddevice_create4 字符设备驱动程序字符设备通过字符&#xff08;一个接一个的字…

Java中的异常栈轨迹和异常链

Java中允许对异常进行再次抛出&#xff0c;以提交给上一层进行处理&#xff0c;最为明显的例子为Java的常规异常。 常规异常&#xff1a;有Java所定义的异常&#xff0c;不需要异常声明&#xff0c;在未被try-catch的情况下&#xff0c;会被默认上报到main()方法。 Example: pu…

同步---信号量

信号量1 信号量2 驱动程序和测试程序3 内核的具体实现总结1 信号量 Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已经被占用的信号量时&#xff0c;信号量会将其放到一个等待队列&#xff0c;然后让其睡眠&#xff0c;这时处理器去执行其他代码。当持有信号量的进…

算法---KMP算法

字符串1 KMP算法状态机概述构建状态转移1 KMP算法 原文链接&#xff1a;https://zhuanlan.zhihu.com/p/83334559 先约定&#xff0c;本文用pat表示模式串&#xff0c;长度为M&#xff0c;txt表示文本串&#xff0c;长度为N&#xff0c;KMP算法是在txt中查找子串pat&#xff0…

文件上传 带进度条(多种风格)

文件上传 带进度条 多种风格 非常漂亮&#xff01; 友好的提示 以及上传验证&#xff01; 部分代码&#xff1a; <form id"form1" runat"server"><asp:ScriptManager ID"scriptManager" runat"server" EnablePageMethods&quo…

同步---自旋锁

1 自旋锁的基本概念 自旋锁最多只能被一个可执行线程持有&#xff0c;如果一个执行线程试图获得一个已经被使用的自旋锁&#xff0c;那么该线程就会一直进行自旋&#xff0c;等待锁重新可用。在任何时刻&#xff0c;自旋锁都可以防止多余一个的执行线程同时进入临界区。 Linu…

实习日志----4.播放时段参数设置

由于客户在下发广告时&#xff0c;一则广告可在多个时段播放&#xff0c;这就需要设置多个播放时段的参数。 但在这种情况下&#xff0c;我并不知道用户每次需要下发几个时段&#xff0c;所以前台不能设定死。 因此我要实现这么一个功能&#xff0c;让用户根据自己的需要来动态…

linux系统编程---线程总结

线程总结1 线程的实现线程创建线程退出线程等待线程清理2 线程的属性线程的分离线程的栈地址线程栈大小线程的调度策略线程优先级3 线程的同步互斥锁读写锁条件变量信号量线程是系统独立调度和分配的基本单位。同一进程中的多个线程将共享该进程中的全部系统资源&#xff0c;例…

如何给Linux操作系统(CentOS 7为例)云服务器配置环境等一系列东西

1.首先&#xff0c;你得去购买一个云服务器&#xff08;这里以阿里云学生服务器为例&#xff0c;学生必须实名认证&#xff09; 打开阿里云&#xff0c;搜索学生服务器点击进入即可 公网ip为连接云服务器的主机 自定义密码为连接云服务器是需要输入的密码 购买即可 点击云服…