驱 动 版 Hello World

各个版本的Hello World  记得自己初学Java时,第一个Hello World程序,从我找到一本Java教材,到懂得如何设置环境变量,到成功打印出Hello World信息,我用了三天的时间(不要笑话我,现在我还是这么笨)。虽然我都毕业已经一年多,可那件事情想来仍历历在目。今天有点闲空,就随手写了各个版本——虽说是“各个版本”,其实也就仅有的几个版本——的Hello World程序,借此来怀念一下大学时光。批处理版 阅读详情
 
我们学习程序设计,都是从“Hello World”开始的,驱动程序也不例外,今天我就写一个驱动版的“Hello World”来热热身,目的希望大家能对驱动程序的基本框架有所了解。

驱动程序分为2类,一个是Kernel模式驱动,另一个是Windows模式驱动,2种模式本质是相同,但细节不同,本文介绍的是内核模式驱动和驱动程序的安装、使用。

驱动程序同普通的EXE,DLL一样,都属于PE文件,而且都有一个入口函数。但EXE中,入口函数是main()/WinMain()和Unicode的wmain()/wWinmain(),DLL的入口函数则可有可无,它是DllMain()。驱动程序也有入口函数,而且是必须的,它是DriverEntry(),再次提示,它是必须的,因为I/O管理器会首先调用驱动程序的DriverEntry(),它的作用就像DllMain()--完成一些初始化工作。DriverEntry()一共有2个参数:1PDRIVER_OBJECT DriverObject,指向驱动程序对象的指针,我们操作驱动程序,全靠它,它是由I/O管理器传递进来的;2)PUNICODE_STRING RegistryPath,驱动程序的服务主键,这个参数的使用并不多,但要注意,在DriverEntry()返回后,它可能会消失,所以如果需要使用,记住先要保存下来。DriverEntry()的返回一个NTSTATUS值,它是一个ULONG值,具体的定义,请参见DDK中的NTSTATUS.H头文件,里边有详细的定义。

既然要写驱动版的“Hello World”,就需要确定如何来与驱动程序通信,常用的共享内存,共享事件,IOCTL宏,或者直接用ReadFile()或WriteFile()进行读写,在本文里我就采用一种简单的、但又很常用的IOCTL宏,它依赖的IRP派遣例程是IRP_MJ_DEVICE_CONTROL,Win32程序使用DeviceIoControl()与驱动进行通信,根据不同的IOCTL宏,输出不同的调试信息。为了简便,我并没有使用ReadFile()将信息读出来,而是直接用DbgPrint()输出,所以需要使用DbgView查看,其他调试工具也可以。PS:偷懒!

驱动程序与I/O管理器通信,使用的是IRP,即I/O请求包。IRP分为2部分:1)IRP首部;2)IRP堆栈。IRP首部信息如下:

IRP首部:
IO_STATUS_BLOCK IoStatus         包含I/O请求的状态
PVOID AssociatedIrp.SystemBuffer 如果执行缓冲区I/O,这个指针指向系统缓冲区
PMDL MdlAddress        如果直接I/O,这个指针指向用户缓冲区的存储器描述符表
PVOID UserBuffer        I/O缓冲区的用户空间地址

IRP堆栈:
UCHAR MajorFunction               指示IRP_MJ_XXX派遣例程
UCHAR MinorFunction               同上,一般文件系统和SCSI驱动程序使用它
 
union Parameters               MajorFunction的联合类型
{
struct Read                       IRP_MJ_READ的参数
ULONG Length
ULONG Key
LARGE_INTEGER ByteOffset
 
struct Write                      IRP_MJ_WRITE的参数
ULONG Length
ULONG Key
LARGE_INTEGER ByteOffset
 
struct DeviceIoControl            IRP_MJ_DEVICE_CONTROL参数
ULONG OutputBufferLength
ULONG InputBufferLength
ULONG IoControlCode
PVOID Type3InputBuffer
} 
PDEVICE_OBJECT DeviceObject       请求的目标设备对象的指针
PFILE_OBJECT FileObject                请求的目标文件对象的指针,如果有的话

操作IRP。对于不同的IRP函数,操作也是不同的:有的只操作IRP首部;有的只操作IRP堆栈;还有操作IRP整体,下面是一些常用的函数:

IRP整体:

    名称                            描述                                   调用者
IoStartPacket             发送IRP到Start I/O例程              Dispatch
IoCompleteRequest     表示所有的处理完成                  DpcForIsr
IoStartNextPacket      发送下一个IRP到Start I/O例程     DpcForIsr
IoCallDriver                发送IRP请求                              Dispatch
IoAllocateIrp             请求另外的IRP                            Dispatch
IoFreeIrp                 释放驱动程序分配的IRP                I/O Completion

IRP堆栈:
    名称                                             描述                         调用者
IoGetCurrentIrpStackLocation   得到调用者堆栈的指针             Dispatch
IoMarkIrpPending              为进一步的处理标记调用者I/O堆栈  Dispatch
IoGetNextIrpStackLocation 得到下一个驱动程序的I/O堆栈的指针 Dispatch
IoSetNextIrpStackLocation      将I/O堆栈指针压入堆栈            Dispatc

在驱动程序,IRP派遣例程起着很重要的作用,每个IRP派遣例程,几乎都有对应的Win32函数,下面是几个常用的:

IRP派遣例程:

   名称                                      描述                           调用者
IRP_MJ_CREATE                   请求一个句柄                    CreateFile
IRP_MJ_CLEANUP            在关闭句柄时取消悬挂的IRP      CloseHandle
IRP_MJ_CLOSE                     关闭句柄                         CloseHandle
IRP_MJ_READ                      从设备得到数据                 ReadFile
IRP_MJ_WRITE                    传送数据到设备                 WriteFile
IRP_MJ_DEVICE_CONTROL    控制操作(利用IOCTL宏)   DeviceIoControl
IRP_MJ_INTERNAL_DEVICE_CONTROL  控制操作(只能被内核调用)       N/A
IRP_MJ_QUERY_INFORMATION    得到文件的长度             GetFileSize
IRP_MJ_SET_INFORMATION         设置文件的长度             SetFileSize
IRP_MJ_FLUSH_BUFFERS             写输出缓冲区或者丢弃输入缓冲区  FlushFileBuffers FlushConsoleInputBuffer PurgeComm
IRP_MJ_SHUTDOWN             系统关闭                     InitiateSystemShutdown

====================================================================================================

下面开始写我们的驱动版的“Hello World”,程序很简单,先介绍一下流程:

1,调用IoCreateDevice()创建一个设备,并返回一个设备对象。
2,调用IoCreateSynbolicLink()创建一个符号连接,使Win32程序可以使用驱动程序
3,设置IRP_MJ_DEVICE_CONTROL派遣例程HelloWorldDispatch()和卸载例程HelloWorldUnLoad()。

如果Win32程序使用DeviceIoControl(),则执行HelloWorldDispatch()函数
4,调用IoGetCurrentIrpStackLocation()得到当前调用者的IRP指针
5,取得IO控制代码,完成后使用IoCompleteRequest()完成IRP操作

如果使用ControlService()停止驱动程序,则执行HelloWorldUnLoad()函数
4,调用IoDeleteSymbolicLink()删除符号连接
5,调用IoDeleteDevice()删除已建立的设备

驱动入口DriverEntry()

//创建设备
IoCreateDevice(DriverObject,        //驱动程序对象
               0,                   //扩展设备的大小,由于不需要,所以置0
               &DeviceNameString,   //设备名称
               FILE_DEVICE_UNKNOWN, //设备类型
               0,                   //指示设备允许的操作
               FALSE,               //如果为TRUE,表示只能有一个线程使用该设备,为FALSE,则没有限制
               &lpDeviceObject);    //返回的设备对象

//创建符号连接
IoCreateSymbolicLink(&DeviceLinkString,   //存放符号连接的UNICODE_STRING
                     &DeviceNameString);  //设备名称

//派遣例程和卸载例程
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=HelloWorldDispatch;吧
DriverObject->DriverUnload=HelloWorldUnLoad;

IRP派遣例程HelloWorldDispatch()

IrpStack=IoGetCurrentIrpStackLocation(pIrp);   //得到当前调用者的IRP堆栈

//获取IO控制代码,并执行指定操作,这里只是DbgPrint()
IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode;
switch (IoControlCodes)  {
......

IoCompleteRequest(pIrp,IO_NO_INCREMENT);   //完成IRP操作

卸载例程HelloWorldUnLoad()

//删除符号连接和设备
IoDeleteSymbolicLink(&DeviceLinkString);
IoDeleteDevice(DriverObject->DeviceObject);

====================================================================================================完整代码:
#ifndef __HELLOWORLD_C__
#define __HELLOWORLD_C__

#define DEBUGMSG

#include "HelloWorld.h"

//驱动入口
NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS ntStatus=STATUS_SUCCESS;
    PDEVICE_OBJECT lpDeviceObject=NULL;   //指向设备对象的指针
    UNICODE_STRING DeviceNameString;      //设备名称
    UNICODE_STRING DeviceLinkString;      //符号连接

    //调试信息
    #ifdef DEBUGMSG
           DbgPrint("Starting DriverEntry()/n");
    #endif

    RtlInitUnicodeString(&DeviceNameString,NT_DEVICE_NAME);  //初始化Unicode字符串

    //创建设备
    ntStatus=IoCreateDevice(DriverObject,0,&DeviceNameString,FILE_DEVICE_UNKNOWN,
                            0,FALSE,&lpDeviceObject);

    //使用NT_SUCCESS宏检测函数调用是否成功
    if (!NT_SUCCESS(ntStatus))
    {
        #ifdef DEBUGMSG
               DbgPrint("Error IoCreateDevice()/n");
        #endif
        goto Error;
    }

    RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);

    //创建符号连接
    ntStatus=IoCreateSymbolicLink(&DeviceLinkString,&DeviceNameString);

    if (!NT_SUCCESS(ntStatus))
    {
        #ifdef DEBUGMSG
               DbgPrint("Error IoCreateSymbolicLink()/n");
        #endif
        goto Error;
    }

    //设置IRP派遣例程和卸载例程
    DriverObject->MajorFunction[IRP_MJ_CREATE]=//HelloWorldDispatch;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]=//HelloWorldDispatch;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=HelloWorldDispatch;
    DriverObject->DriverUnload=HelloWorldUnLoad;

    return ntStatus;

    Error:
          #ifdef DEBUGMSG
                 DbgPrint("Error DriverEntry()/n");
          #endif

          return ntStatus;
}

NTSTATUS HelloWorldDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp)
{
    NTSTATUS ntStatus=STATUS_SUCCESS;
    ULONG IoControlCodes=0;             //I/O控制代码
    PIO_STACK_LOCATION IrpStack=NULL;   //IRP堆栈

    //设置IRP状态
    pIrp->IoStatus.Status=STATUS_SUCCESS;
    pIrp->IoStatus.Information=0;

    #ifdef DEBUGMSG
           DbgPrint("Starting HelloWorldDispatch()/n");
    #endif

    IrpStack=IoGetCurrentIrpStackLocation(pIrp);    //得到当前调用者的IRP

    switch (IrpStack->MajorFunction)
    {
            case IRP_MJ_CREATE:
                 #ifdef DEBUGMSG
                        DbgPrint("IRP_MJ_CREATE/n");
                 #endif
                 break;

            case IRP_MJ_CLOSE:
                 #ifdef DEBUGMSG
                        DbgPrint("IRP_MJ_CLOSE/n");
                 #endif
                 break;

            case IRP_MJ_DEVICE_CONTROL:

                 #ifdef DEBUGMSG
                        DbgPrint("IRP_MJ_DEVICE_CONTROL/n");
                 #endif

                 //取得I/O控制代码
                 IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode;

                 switch (IoControlCodes)
                 {
                         //启动
                         case START_HELLPWORLD:
                              DbgPrint("Starting /"Hello World/"/n");
                              break;

                         //停止
                         case STOP_HELLPWORLD:
                              DbgPrint("Stoping /"Hello World/"/n");
                              break;

                         default:
                              pIrp->IoStatus.Status=STATUS_INVALID_PARAMETER;
                              break;
                 }

                 break;

            default:break;
    }

    ntStatus=pIrp->IoStatus.Status;

    IoCompleteRequest(pIrp,IO_NO_INCREMENT);

    return ntStatus;
}

VOID HelloWorldUnLoad (IN PDRIVER_OBJECT DriverObject)
{
     UNICODE_STRING DeviceLinkString;
     PDEVICE_OBJECT DeviceObjectTemp1=NULL;
     PDEVICE_OBJECT DeviceObjectTemp2=NULL;

     #ifdef DEBUGMSG
            DbgPrint("Starting HelloWorldUnLoad()/n");
     #endif

     RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);
     IoDeleteSymbolicLink(&DeviceLinkString);  //删除符号连接

     if (DriverObject)
     {
         DeviceObjectTemp1=DriverObject->DeviceObject;

         //删除设备
         while (DeviceObjectTemp1)
         {
                DeviceObjectTemp2=DeviceObjectTemp1;
                DeviceObjectTemp1=DeviceObjectTemp1->NextDevice;
                IoDeleteDevice(DeviceObjectTemp2);
         }
     }
}

#endif

====================================================================================================
驱动程序的编译需要使用DDK中的build实用程序,它是一个命令行程序,使用不是很方便。VC知识库有一篇在VC++ 6.0中编译驱动的文章,有兴趣可以去看看。

1,makefile
编译驱动程序,首先应该准备一个makefile,这个文件很简单,只有一句代码:
#
# DO NOT EDIT THIS FILE!!! Edit ./sources. if you want to add a new source
# file to this component. This file merely indirects to the real make file
# that is shared by all the driver components of the Windows NT DDK
#

!INCLUDE $(NTMAKEENV)/makefile.def

正如描述的那样,不要修改这个文件---它是通用的!

2,sources
准备的第二个文件就是sources,它描述了一些编译的细节。针对本文的程序,sources文件的内容是这样的:
TARGETNAME=HelloWorld   //驱动名称
TARGETPATH=.         //编译后SYS的路径
TARGETTYPE=DRIVER     //类型为驱动程序

SOURCES= HelloWorld.c   //只有一个源文件

有了这2个文件后,就可以使用build进行编译了。进入「开始」菜单/程序/Development Kits/Windows 2000 DDK,
分别有3个CMD程序:1)Checked 64 Bit Build Environment,“Debug”的64位版本;2)Checked Build Environment
“Debug”的32位版本;3)Free Build Environment,“Release”的32位版本。不用说,肯定是使用Free Build Environment。

New or updated MSVC detected. Updating DDK environment....

Setting environment for using Microsoft Visual C++ tools.
Starting dirs creation...Completed.

C:/NTDDK>cd/

C:/>cd HelloWorld

C:/HelloWorld>build
BUILD: Object root set to: ==> objfre
BUILD: /i switch ignored
BUILD: Compile and Link for i386
BUILD: Loading c:/NTDDK/build.dat...
BUILD: Computing Include file dependencies:
BUILD: Examining c:/helloworld directory for files to compile.
  c:/helloworld - 1 source files (127 lines)
BUILD: Saving c:/NTDDK/build.dat...
BUILD: Compiling c:/helloworld directory
Compiling - helloworld.c for i386
BUILD: Linking c:/helloworld directory
Linking Executable - i386/helloworld.sys for i386
BUILD: Done

  1 file compiled
  1 executable built

C:/HelloWorld>

现在C:/HelloWorld/i386目录下,就有了HelloWorld.sys。

====================================================================================================
驱动程序的安装如同安装服务一样,唯一不同的是,创建服务时,类型是内核驱动,其他跟操作服务没什么区别。

安装驱动程序流程:
1,调用OpenSCManager()打开服务控制管理器
2,调用CreateService()创建一个服务,服务类型为内核驱动
3,调用OpenService()取得服务句柄
启动服务
4,调用StartService()启动服务
停止服务
4,调用ControlService()停止服务
删除服务
4,调用DeleteService()删除服务
5,调用CloseServiceHandle()关闭服务句柄

操作驱动程序流程:
1,调用CreateFile()取得设备句柄
2,调用DeviceIoControl()传递I/O控制代码
3,调用CloseHandle()关闭设备句柄


这里有一个完整的驱动安装程序,所以我就不写了,只给出操作驱动程序的代码

完整代码:

#define DEBUGMSG
http://www.xfocus.net/tools/200411/882.html

#include <windows.h>
#include <winioctl.h>
#include <stdio.h>

#define DEVICE_HELLO_INDEX 0x860

#define START_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define STOP_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+1,METHOD_BUFFERED,FILE_ANY_ACCESS)

#define erron GetLastError()

#define MY_DEVICE_NAME "////.//HelloWorld"

#define MY_DEVICE_START "-start"
#define MY_DEVICE_STOP "-stop"

BOOL DriverControl (TCHAR *Maik);

void Usage (TCHAR *Paramerter);

int main (int argc,TCHAR *argv[])
{
    if (argc!=2)
    {
        Usage(argv[0]);
        return 0;
    }

    if (strcmpi(argv[1],MY_DEVICE_START)==0 || strcmpi(argv[1],MY_DEVICE_STOP)==0)
        DriverControl(argv[1]);
    else
    {
        Usage(argv[0]);
        return 0;
    }

    return 0;
}

BOOL DriverControl (TCHAR *Maik)
{
     HANDLE hDevice=NULL;  //设备句柄
     DWORD RetBytes=0;

     //获得设备句柄
     hDevice=CreateFile(MY_DEVICE_NAME,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

     if (hDevice==INVALID_HANDLE_VALUE)
     {
         #ifdef DEBUGMSG
                printf("CreateFile() GetLastError reports %d/n",erron);
         #endif
         return FALSE;
     }

     //启动
     if (strcmpi(Maik,MY_DEVICE_START)==0)
     {
         //传递启动的I/O控制代码
         if (!(DeviceIoControl(hDevice,START_HELLPWORLD,NULL,0,NULL,0,&RetBytes,NULL)))
         {
             #ifdef DEBUGMSG
                    printf("DeviceIoControl() GetLastError reports %d/n",erron);
             #endif
             CloseHandle(hDevice);
             return FALSE;
         }
     }

     //停止
     if (strcmpi(Maik,MY_DEVICE_STOP)==0)
     {
         //传递停止的I/O控制代码
         if (!(DeviceIoControl(hDevice,STOP_HELLPWORLD,NULL,0,NULL,0,&RetBytes,NULL)))
         {
             #ifdef DEBUGMSG
                    printf("DeviceIoControl() GetLastError reports %d/n",erron);
             #endif
             CloseHandle(hDevice);
             return FALSE;
         }
     }

     if (hDevice)
         CloseHandle(hDevice);  //关闭句柄

     return TRUE;
}

void Usage (TCHAR *Paramerter)
{
     fprintf(stderr,"============================================================================/n"
             "      驱动版Hello World/n"
             "作者:dahubaobao[EST]/n"
             "主页:www.eviloctal.com or www.ringz.org/n"
             "邮件:dahubaobao@eviloctal.com/n"
             "OICQ:382690/n/n"
             "%s -start/t启动/n"
             "%s -stop /t停止/n/n"
             "本程序只是用做代码交流,如有错误,还请多多包含!/n"
             "============================================================================/n"
             ,Paramerter,Paramerter);
}

头文件HelloWorld.h代码:

#ifndef __HELLOWORLD_H__
#define __HELLOWORLD_H__

#include <ntddk.h>

#define DEVICE_HELLO_INDEX 0x860

//2个IOCTL宏
#define START_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define STOP_HELLPWORLD CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+1,METHOD_BUFFERED,FILE_ANY_ACCESS)

#define NT_DEVICE_NAME L"//Device//HelloWorld"        //设备名称
#define DOS_DEVICE_NAME L"//DosDevices//HelloWorld"   //符号连接

NTSTATUS HelloWorldDispatch (IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp);

VOID HelloWorldUnLoad (IN PDRIVER_OBJECT DriverObject);

#endif

 
VsCode及hello world 一早起来,觉得Eclipse有点太“经典”了,于是找了一个免费的VsCode。 PS:学个python,结果IDE搞了N天,什么时候是个头啊.... VsCode安装 下载地址:https://code.visualstudio.com/ 我是用的是Windows x64 Stable版本 配置中文 俺的英文水平比较差,原来用惯了工具还要,换个新工具,自然还是zh_cn比较容易上手... 阅读详情

相关推荐

Flutter学习之Hello World

Flutter之入门学习第一天

m0_60264901的博客 1380

Windows驱动开发入门 -- HelloWorld

一、驱动开发环境搭建 在Download the Windows Driver Kit (WDK)页面中下载最新版本的Visual Studio和WDK进行安装。如果要下载老版本可以到Other WDK downloads下载。 本文演示环境为:Visual Studio 2017版本,WDK for Windows 10 17740。其他版本亦可。 二、Hello Word工程 在Visual ...

while(1) { smile(); } 1万+

AI 新玩法:创意版Hello World实现指南

在科技飞速发展的今天,AI 模型已经成为了众多技术领域中的焦点。从图像识别到自然语言处理,AI 模型展现出了令人惊叹的能力。而 “Hello World”,作为编程世界的经典入门案例,就像一把开启编程大门的钥匙,每个程序员的学习之旅,往往都从这简单的一行代码开始。当我们将 AI 模型与 Hello World 相结合,又会碰撞出怎样的火花呢?本文将带你探索如何利用 AI 模型实现创意版的 Hello World,为你揭开 AI 与传统编程融合的神秘面纱,感受技术创新带来的无限可能 。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 1013

驱动版Hello World

我们学习程序设计,都是从“Hello World”开始的,驱动程序也不例外,今天我就写一个驱动版的“Hello World”来热热身,目的希望大家能对驱动程序的基本框架有所了解。驱动程序分为2类,一个是Kernel模式驱动,另一个是Windows模式驱动,2种模式本质是相同,但细节不同,本文介绍的是内核模式驱动和驱动程序的安装、使用。驱动程序同普通的EXE,DLL一样,都属于PE文件,而且

zhangjie0072的专栏 4088

解锁 Hello World 的 N 种炫酷玩法

Hello World作为编程入门的经典起点,承载着开发者对代码世界的初体验。本文突破传统文本输出形式,从多技术维度探索创意实现方案:通过C语言结合EasyX图形库打造动态艺术字动画,用汇编语言还原复古编程魅力;基于Arduino与STM32硬件平台,让LED以闪烁节奏 “拼写” 问候语;接入OpenAI API与LangChain框架,实现 AI 对话式Hello World交互。文中附完整源码与实现逻辑,既展现编程语言的表现力、硬件交互的趣味性,也揭示AI模型的创新应用场景,为开发者提供多元编程视角。

邓邓子的博客 1138

windows驱动开发helloworld

windows驱动内核开发,入门程序helloworld,手把手学习驱动程序开发

驱动程序简单入门

我们学习程序设计,都是从“Hello World”开始的,驱动程序也不例外,今天我就写一个驱动版的“Hello World”来热热身,目的希望大家能对驱动程序的基本框架有所了解。 驱动程序分为2类,一个是Kernel模式驱动,另一个是Windows模式驱动,2种模式本质是相同,但细节不同,本文介绍的是内核模式驱动和驱动程序的安装、使用。 驱动程序同普通的EXE,DLL一样,都属于PE文件,而且都有

不论你在什么时候开始,重要的是开始之后就不要停止。 1578

STM32F103新手必看:0.96寸OLED屏幕从接线到动态显示全攻略(附IIC避坑指南)

本文为STM32F103新手提供了0.96寸OLED屏幕从硬件接线到软件驱动的完整实战指南。重点解析了IIC通信的时序、设备地址(0x78/0x7A)的常见陷阱,并详细演示了如何实现字符显示与平滑滚动显示等动态效果,帮助开发者快速上手并避开常见调试难题。

stone的博客 556

Linux内核入门(二)Hello World 驱动程序

本文主要探讨如何在 linux下编写一个Hello World入门级别的驱动程序以及演示驱动程序的加载、卸载以及调试的方法与步骤

Mr_Jaychong的博客 2186

单细胞空间转录组分析流程学习python版(三)

空转python版学习

zfyyzhys的博客 2087

Codex辅助小说创作:零成本AI写作指南与实战技巧

自然语言处理技术通过深度学习模型理解语义上下文,其核心原理是基于大规模文本训练的语言模型预测能力。在创意写作领域,这项技术的价值在于突破传统写作工具的功能局限,实现从内容生成到风格模仿的智能化辅助。特别是提示词工程的应用,让创作者能够精准控制AI输出的内容和风格。在实际应用场景中,AI写作工具显著降低了小说创作门槛,适用于网络小说、奇幻科幻等多种文学类型。本文以OpenAI Codex为例,详细解析如何利用AI技术完成从故事大纲设计、章节生成到平台发布的全流程创作,其中提示词优化和内容质量把控是提升生成效果

weixin_32678995的博客 568

HelloWorld版的驱动程序

extern “C”{  #include “wdm.h”  } extern "C" NTSTATUS DriverEntry(INPDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { NTSTATUS status =             STATUS_DEVICE_CONFIGURATION_ERR

autumn20080101的专栏 606

国家大地2000坐标系投影坐标转高德地图经纬度坐标-【高斯投影坐标】

国家大地2000坐标系投影坐标转换成高德经纬度坐标显示画图。

devil_zhuzhu的博客 1万+

驱动开发之六:驱 动 版 Hello World

我们学习程序设计,都是从“Hello World”开始的,驱动程序也不例外,今天我就写一个驱动版的“Hello World”来热热身,目的希望大家能对驱动程序的基本框架有所了解。驱动程序分为2类,一个是Kernel模式驱动,另一个是Windows模式驱动,2种模式本质是相同,但细节不同,本文介绍的是内核模式驱动和驱动程序的安装、使用。驱动程序同普通的EXE,DLL一样,都属于PE文件,而且都有一个

rageliu的专栏 2821

RM3100应用笔记驱动程序SPI接口

PNI磁传感器套件RM3100磁传感器套件由2个Sen-XY-f(pn13104)地磁传感器,1个Sen-Z-f(pn13101)地磁传感器和MagI2C(pn13156)控制芯片组成,能够实现三维空间的磁场大小测量。

AIoT技术栈的博客 4923

1分钟了解LangChain是什么?

是一个基于大型语言模型(LLM)开发应用程序的框架, 它旨在简化语言模型应用的开发流程,特别是在构建对话系统和其他基于语言的AI解决方案时.目标是将复杂的语言模型技术转化为可通过简单API调用实现的功能,从而使开发者能够更加专注于创造性的解决方案设计。高效地解决了开发大语言模型应用的痛点问题。Langchain 最主要的特色是可以将 LLM 应用研发过程中的交互 Prompt、LLM 模型调用、语言模型与环境互动的自适应等方式融为一体。

gorgor在码农 2631

【Hello World 】

几乎每一个程序员都是从Hello World!开始自己的程序人生,作为一个初学编程的小朋友,也需要先编程来输出Hello World!。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 8693

PCL——KITTI数据集.bin文件转.pcd并可视化【2025最新版】

kitti数据集中的bin格式点云主要存储的字段为:`x`、`y`、`z`、`intensity`,对应于PCL中的PointXYZI。因此,只需将bin中的属性信息读取出来放入`PointXYZI`即可。本文给出简单易用的实现代码。博客长期更新,本文最近更新时间为:2025年1月11日。

点云侠的博客 3958

Docker: hello world

第一个Docker镜像:hello world

不负热爱 3577

2025年GESP12月认证C++一级真题解析

摘要 本文包含13道C++编程选择题,每题2分,涉及语法、运算符、循环、条件判断等知识点。题目考察了变量命名规则、运算符优先级、赋值语句、循环控制、格式化输出等内容。部分题目解析与标答存在差异,例如第4题逗号表达式输出应为5但标答为4,第6题赋值运算结果应为4 4但标答为3 4等。这些题目适合用于检验基础编程能力,但需注意部分标答可能存在争议,建议结合具体编译环境验证。最后一题考察"漂亮数"判断,要求填写数字某位能被3整除的条件表达式。

lan_in的博客 755

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

几乎所有编程语言的hello, world程序(3)

LSL// Hello World in Linden Scripting Language (LSL)default { state_entry() { llSay(0, "Hello World"); } }Lua# Hello World in Luaprint "Hello world"m4# Hello World for the m4 macro

nomasp 6385

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

matlab hello world,hello world

hello world(程序代码)编辑锁定讨论上传视频Hello World 中文意思是『你好,世界』。因为 The C Programming Language 中使用它做为第一个演示程序,非常著名,所以后来的程序员在学习编程或进行设备调试时延续了这一习惯。中文名你好,世界外文名Hello World创造者Brian Kernighan(布赖恩·克尼根)类型程序代码出处The C...

weixin_32635501的博客 2576

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Docker 入门之hello world

据说程序员写的第一个程序都是输出hello world,前2天在contos7中安装了docker,今天就输出个hello world 来入门吧。 docker允许在容器内部使用docker run 运行应用程序,我们可以使用docker run 来输出hello world。 一、检查下docker是否正确安装。 docker info //如果安装正确就会有很多docker的信息,如下图: docker-info 如果安装正确执行docker info 后就回输出如图很多信息,我.

wzs535131的博客 3133

Hello World

<br />来源于:维基百科<br />“Hello, World ”程序指的是只在计算机 屏幕上输出“Hello, World!”(意为“世界,你好!”)这行字符串的计算机程序 。一般来说,这是每一种计算机编程语言 中最基本、最简单的程序,亦通常是初学者所编写的第一个程序。它还可以用来确定该语言的编译器 、程序开发环境 ,以及运行环境 是否已经安装妥当。<br />将输出字符串“Hello World”作为第一个示范程序,现在已经成为编程语言学习的传统。该程序因Brian Kernighan

独风孤月http://www.bobolee.me 1258

java输出hello world_java输出Hello World

一、输出“Hello World!”1、新建一个java项目,点击File->New->Java Project,创建java项目的界面之后,输入项目名称wly,点击finish。2、创建好java项目之后,鼠标右键项目,选择New->Class,创建一个类,mypackage为包名,Name类名Hello,首字母大写,点击finish。3、编写程序输出“Hello World!...

weixin_32429589的博客 1万+
上一篇: 挂接file system
下一篇: KFSENSOR(IDS)介绍
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值