《Windows核心编程》---执行可执行文件

操作系统实现-进入内核 博客网址:www.shicoder.top 微信:18223081347 欢迎加群聊天 :452380935 这一次我们正式进入内核,编写相关的内核代码,也就是kernel代码 数据类型定义 因为我们在内核中会使用一些数据,因此先提前定义一些数据类型 #define EOF -1 #define NULL ((void *)0) // 空指针 #define EOS '\0' // 字符串结尾 #define bool _Bool #define true 1 #define false 0 . 阅读详情

方法一:Shell调用:

Win32中可以通过WinExec和ShellExecute函数来执行另一个可执行程序。

WinExec函数原型如下:

UINT WINAPI WinExec(

  __in  LPCSTR lpCmdLine, //包含可执行文件名和命令行参数

  __in  UINT uCmdShow //指定窗口的显示方式,如果有窗口的话

);

如果文件被成功执行,那么函数返回一个大于31的值。使用WinExec函数执行文件和在Windows“开始”菜单的“运行”中键入命令在效果上是一样的。注意,该函数是为了兼容16位Windows而保留的!!

 

函数ShellExecute即可以用来执行一个可执行文件,也可以指定一个数据文件名让Windows自动查找关联到这个数据文件的可执行文件,并执行这个可执行文件来处理指定的数据文件,数据文件名会以命令行参数的方式传递给可执行文件。函数原型如下:

HINSTANCE ShellExecute(

  __in_opt  HWND hwnd, //指定可执行文件显示的窗口的父窗口

  __in_opt  LPCTSTR lpOperation, //指向一个表示执行方式的字符串:

                   //“open”---文件被打开,这是lpFile可以是可执行文件名、数据文件名或目录名;

//若该参数为空,函数默认执行open操作

//“print”---文件被打印,这是lpFile指定的文件名必须是数据文件。

//如果指定的是可执行文件,函数当作“open”操作。

//“explore”---浏览lpFile参数中指定的目录

  __in      LPCTSTR lpFile, //指定文件名,即可以是可执行文件也可以是数据文件

  __in_opt  LPCTSTR lpParameters, //lpFile指定可执行文件时,此参数用来指定命令行参数;

                                     //lpFile指定数据文件时,此参数必须是NULL

  __in_opt  LPCTSTR lpDirectory, //执行或打开文件时默认的目录

  __in      INT nShowCmd //如果函数执行一个可执行文件,本参数指定窗口打开方式

);

函数执行成功,返回一个大于31的值。

 

方法二:创建进程:

用WinExec和ShellExecute函数来执行文件是很方便的,调用这两个函数从某种意义上讲,相当于创建新的进程,但是函数返回以后,这些新建的进程却脱离了我们的控制,我们无法知道它们什么时候结束,也无法强制结束它们。要对进程进行后续的控制,必须使用函数CreateProcess来创建进程。

当一个进程被创建时,系统进行以下操作:

1)系统为进程创建一个内核对象,并将它的初始计数设置为1;

2)系统为进程创建一个虚拟地址空间,并将可执行文件加载到这个地址空间中。系统同时处理可执行文件的导入表,将导入表中登记的所有dll文件装入。每个dll文件被装入内存时,dll的入口函数被执行,如果入口函数返回初始化失败信息的话,进程的初始化失败。可执行文件和所有dll文件都被看作是单独的模块,都被分配了一个实例句柄(实例句柄在数值上等于模块装入到地址空间中的线性地址)。

3)系统为进程建立一个主线程,主线程从可执行文件的入口地址开始执行。

 

创建进程时需要为新进程窗口的外观指定一些属性,这是通过STARTUPINFO结构来实现的:

typedef struct _STARTUPINFO {

  DWORD  cb; //本结构的长度

  LPTSTR lpReserved; //保留字段

  LPTSTR lpDesktop; //NT下使用,指定桌面名称

  LPTSTR lpTitle; //控制台程序使用,指定控制台窗口标题

  DWORD  dwX; //当新进程使用CW_USEDEFAULT参数创建窗口时,将使用这些位置和大小属性

  DWORD  dwY; //

  DWORD  dwXSize; //

  DWORD  dwYSize; //

  DWORD  dwXCountChars; //控制台程序使用,指定控制台窗口行数

  DWORD  dwYCountChars; //

  DWORD  dwFillAttribute; //控制台程序使用,指定控制台窗口背景色

  DWORD  dwFlags; //标志

  WORD   wShowWindow; //窗口的显示方式

  WORD   cbReserved2; //

  LPBYTE lpReserved2; //

  HANDLE hStdInput; //控制台程序使用:几个标准句柄

  HANDLE hStdOutput; //

  HANDLE hStdError; //

} STARTUPINFO, *LPSTARTUPINFO;

 

在需要定制新进程的窗口的时候,才需要手工填写STARTUPINFO结构,在大部分情况下,并不需要新进程的窗口有什么特殊之处,这时只要使用GetStartupInfo获取当前进程的STARTUPINFO并使用它的默认值就可以了:

VOID WINAPI GetStartupInfo(

  __out  LPSTARTUPINFO lpStartupInfo

);

 

获得STARTUPINFO结构后,就可以调用CreateProcess函数来创建进程了:

BOOL WINAPI CreateProcess(

  __in_opt     LPCTSTR lpApplicationName, //指定可执行文件名的字符串,当该参数为NULL时,

                            //可执行文件名可以在lpCommandLine参数指定的命令行参数中包括

  __inout_opt  LPTSTR lpCommandLine, //指定命令行参数的字符串

  __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes, //指定新进程的安全属性

  __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes, //指定新线程的安全属性

  __in         BOOL bInheritHandles, //指定当前进程的句柄是否可以被新进程继承

  __in         DWORD dwCreationFlags, //创建标志,指定新进程的优先级以及其他标志

  __in_opt     LPVOID lpEnvironment, //指向新进程的环境变量块,若该参数为NULL,则让

                            //Windows拷贝当前进程的环境块当作子进程的环境块

  __in_opt     LPCTSTR lpCurrentDirectory, //指向一个路径字符串,用来指定子进程的当前驱动器

                            //和当前目录,如果为NULL,子进程将引用父进程的当前路径

  __in         LPSTARTUPINFO lpStartupInfo, //指向STARTUPINFO结构

  __out        LPPROCESS_INFORMATION lpProcessInformation //指向一个

                   //PROCESS_INFORMATION结构,这个结构用来供函数返回新建进程的相关信息

);

 

函数执行成功时,返回非0值,否则返回0值。

PROCESS_INFORMATION结构如下:

typedef struct _PROCESS_INFORMATION {

  HANDLE hProcess; //新建进程句柄

  HANDLE hThread; //进程的主线程句柄

  DWORD  dwProcessId; //新建进程ID

  DWORD  dwThreadId; //进程的主线程ID

} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;

 

创建进程的简单例子如下:

#include <windows.h>

#include <stdio.h>

#include <tchar.h>

 

void _tmain( int argc, TCHAR *argv[] )

{

    STARTUPINFO si;

    PROCESS_INFORMATION pi;

 

    ZeroMemory( &si, sizeof(si) );

    si.cb = sizeof(si);

    ZeroMemory( &pi, sizeof(pi) );

 

    if( argc != 2 )

    {

        printf("Usage: %s [cmdline]/n", argv[0]);

        return;

    }

 

    // Start the child process.

    if( !CreateProcess( NULL,   // No module name (use command line)

        argv[1],        // Command line

        NULL,           // Process handle not inheritable

        NULL,           // Thread handle not inheritable

        FALSE,          // Set handle inheritance to FALSE

        0,              // No creation flags

        NULL,           // Use parent's environment block

        NULL,           // Use parent's starting directory

        &si,            // Pointer to STARTUPINFO structure

        &pi )           // Pointer to PROCESS_INFORMATION structure

    )

    {

        printf( "CreateProcess failed (%d)./n", GetLastError() );

        return;

    }

 

    // Wait until child process exits.

    WaitForSingleObject( pi.hProcess, INFINITE );

 

    // Close process and thread handles.

    CloseHandle( pi.hProcess );

    CloseHandle( pi.hThread );

}

 

要结束当前进程,可以使用函数ExitProcess:

VOID WINAPI ExitProcess(

  __in  UINT uExitCode //进程退出码

);

 

要结束其他进程,包括当前进程的子进程时,需要使用函数TerminateProcess:

BOOL WINAPI TerminateProcess(

  __in  HANDLE hProcess, //需要结束的进程的句柄

  __in  UINT uExitCode //进程的退出码

);

注意:TerminateProcess函数不是一个推荐使用的函数,一般仅在很极端的情况下使用(如任务管理器用来结束停止响应的进程),因为它将目标进程无条件结束,被结束的进程根本没有机会进行扫尾工作。同时,目标进程使用的dll文件也不会收到结束通知,所有极有可能造成数据丢失。

 

当一个进程被正常结束时,系统将做下面的工作:

1)进程创建或打开的所有对象句柄被关闭;

2)进程中所有线程被终止;

3)进程及进程中所有线程的状态被改为置位状态,以便让WaitForSingleObject函数正确检测;

4)进程对象中退出码字段从STILL_ACTIVE被改为指定的退出码。

 

要检测一个进程的退出码,可以使用GetExitCodeProcess函数:

BOOL WINAPI GetExitCodeProcess(

  __in   HANDLE hProcess, //被检测进程的进程句柄

  __out  LPDWORD lpExitCode //用来接收函数返回的退出码

);

如果被检测进程还没有结束,那么返回到lpExitCode的是STILL_ACTIVE。

 

下面的代码演示了创建子进程,使用匿名管道来重定向子进程的标准输入和输出:

先是父进程的代码:

#include <windows.h>

#include <tchar.h>

#include <stdio.h>

#include <strsafe.h>

 

#define BUFSIZE 4096

 

HANDLE g_hChildStd_IN_Rd = NULL;

HANDLE g_hChildStd_IN_Wr = NULL;

HANDLE g_hChildStd_OUT_Rd = NULL;

HANDLE g_hChildStd_OUT_Wr = NULL;

 

HANDLE g_hInputFile = NULL;

 

void CreateChildProcess(void);

void WriteToPipe(void);

void ReadFromPipe(void);

void ErrorExit(PTSTR);

 

int _tmain(int argc, TCHAR *argv[])

{

   SECURITY_ATTRIBUTES saAttr;

 

   printf("/n->Start of parent execution./n");

 

// Set the bInheritHandle flag so pipe handles are inherited.

 

   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);

   saAttr.bInheritHandle = TRUE;

   saAttr.lpSecurityDescriptor = NULL;

 

// Create a pipe for the child process's STDOUT.

 

   if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )

      ErrorExit(TEXT("StdoutRd CreatePipe"));

 

// Ensure the read handle to the pipe for STDOUT is not inherited.

 

   if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )

      ErrorExit(TEXT("Stdout SetHandleInformation"));

 

// Create a pipe for the child process's STDIN.

 

   if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))

      ErrorExit(TEXT("Stdin CreatePipe"));

 

// Ensure the write handle to the pipe for STDIN is not inherited.

 

   if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )

      ErrorExit(TEXT("Stdin SetHandleInformation"));

 

// Create the child process.

  

   CreateChildProcess();

 

// Get a handle to an input file for the parent.

// This example assumes a plain text file and uses string output to verify data flow.

 

   if (argc == 1)

      ErrorExit(TEXT("Please specify an input file./n"));

 

   g_hInputFile = CreateFile(

       argv[1],

       GENERIC_READ,

       0,

       NULL,

       OPEN_EXISTING,

       FILE_ATTRIBUTE_READONLY,

       NULL);

 

   if ( g_hInputFile == INVALID_HANDLE_VALUE )

      ErrorExit(TEXT("CreateFile"));

 

// Write to the pipe that is the standard input for a child process.

// Data is written to the pipe's buffers, so it is not necessary to wait

// until the child process is running before writing data.

 

   WriteToPipe();

   printf( "/n->Contents of %s written to child STDIN pipe./n", argv[1]);

 

// Read from pipe that is the standard output for child process.

 

   printf( "/n->Contents of child process STDOUT:/n/n", argv[1]);

   ReadFromPipe();

 

   printf("/n->End of parent execution./n");

 

// The remaining open handles are cleaned up when this process terminates.

// To avoid resource leaks in a larger application, close handles explicitly.

 

   return 0;

}

 

void CreateChildProcess()

// Create a child process that uses the previously created pipes for STDIN and STDOUT.

{

   TCHAR szCmdline[]=TEXT("child");

   PROCESS_INFORMATION piProcInfo;

   STARTUPINFO siStartInfo;

   BOOL bSuccess = FALSE;

 

// Set up members of the PROCESS_INFORMATION structure.

 

   ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

 

// Set up members of the STARTUPINFO structure.

// This structure specifies the STDIN and STDOUT handles for redirection.

 

   ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );

   siStartInfo.cb = sizeof(STARTUPINFO);

   siStartInfo.hStdError = g_hChildStd_OUT_Wr;

   siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;

   siStartInfo.hStdInput = g_hChildStd_IN_Rd;

   siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

 

// Create the child process.

   

   bSuccess = CreateProcess(NULL,

      szCmdline,     // command line

      NULL,          // process security attributes

      NULL,          // primary thread security attributes

      TRUE,          // handles are inherited

      0,             // creation flags

      NULL,          // use parent's environment

      NULL,          // use parent's current directory

      &siStartInfo,  // STARTUPINFO pointer

      &piProcInfo);  // receives PROCESS_INFORMATION

  

   // If an error occurs, exit the application.

   if ( ! bSuccess )

      ErrorExit(TEXT("CreateProcess"));

   else

   {

      // Close handles to the child process and its primary thread.

           // Some applications might keep these handles to monitor the status

           // of the child process, for example.

 

      CloseHandle(piProcInfo.hProcess);

      CloseHandle(piProcInfo.hThread);

   }

}

 

void WriteToPipe(void)

 

// Read from a file and write its contents to the pipe for the child's STDIN.

// Stop when there is no more data.

{

   DWORD dwRead, dwWritten;

   CHAR chBuf[BUFSIZE];

   BOOL bSuccess = FALSE;

 

   for (;;)

   {

      bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);

      if ( ! bSuccess || dwRead == 0 ) break;

     

      bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);

      if ( ! bSuccess ) break;

   }

 

// Close the pipe handle so the child process stops reading.

 

   if ( ! CloseHandle(g_hChildStd_IN_Wr) )

      ErrorExit(TEXT("StdInWr CloseHandle"));

}

 

void ReadFromPipe(void)

 

// Read output from the child process's pipe for STDOUT

// and write to the parent process's pipe for STDOUT.

// Stop when there is no more data.

{

   DWORD dwRead, dwWritten;

   CHAR chBuf[BUFSIZE];

   BOOL bSuccess = FALSE;

   HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

 

// Close the write end of the pipe before reading from the

// read end of the pipe, to control child process execution.

// The pipe is assumed to have enough buffer space to hold the

// data the child process has already written to it.

 

   if (!CloseHandle(g_hChildStd_OUT_Wr))

      ErrorExit(TEXT("StdOutWr CloseHandle"));

 

   for (;;)

   {

      bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);

      if( ! bSuccess || dwRead == 0 ) break;

 

      bSuccess = WriteFile(hParentStdOut, chBuf,

                           dwRead, &dwWritten, NULL);

      if (! bSuccess ) break;

   }

}

 

void ErrorExit(PTSTR lpszFunction)

 

// Format a readable error message, display a message box,

// and exit from the application.

{

    LPVOID lpMsgBuf;

    LPVOID lpDisplayBuf;

    DWORD dw = GetLastError();

 

    FormatMessage(

        FORMAT_MESSAGE_ALLOCATE_BUFFER |

        FORMAT_MESSAGE_FROM_SYSTEM |

        FORMAT_MESSAGE_IGNORE_INSERTS,

        NULL,

        dw,

        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),

        (LPTSTR) &lpMsgBuf,

        0, NULL );

 

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,

        (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));

    StringCchPrintf((LPTSTR)lpDisplayBuf,

        LocalSize(lpDisplayBuf) / sizeof(TCHAR),

        TEXT("%s failed with error %d: %s"),

        lpszFunction, dw, lpMsgBuf);

    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

 

    LocalFree(lpMsgBuf);

    LocalFree(lpDisplayBuf);

    ExitProcess(1);

}

 

接下来是子进程的代码:

#include <windows.h>

#include <stdio.h>

 

#define BUFSIZE 4096

 

int main(void)

{

   CHAR chBuf[BUFSIZE];

   DWORD dwRead, dwWritten;

   HANDLE hStdin, hStdout;

   BOOL bSuccess;

 

   hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

   hStdin = GetStdHandle(STD_INPUT_HANDLE);

   if (

       (hStdout == INVALID_HANDLE_VALUE) ||

       (hStdin == INVALID_HANDLE_VALUE)

      )

      ExitProcess(1);

 

   // Send something to this process's stdout using printf.

   printf("/n ** This is a message from the child process. ** /n");

 

   // This simple algorithm uses the existence of the pipes to control execution.

   // It relies on the pipe buffers to ensure that no data is lost.

   // Larger applications would use more advanced process control.

 

   for (;;)

   {

   // Read from standard input and stop on error or no data.

      bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);

     

      if (! bSuccess || dwRead == 0)

         break;

 

   // Write to standard output and stop on error.

      bSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL);

     

      if (! bSuccess)

         break;

   }

   return 0;

}

 

 

 

 

Windows内核--CreateProcess的内核实现 (2.2) 不管是桌面双击应用程序(ShellExecute*)还是代码中调用创建进程API, 最终一般都会调用NT Native API CreateProcess, 具体请参阅WRK1.2 create.c, 这里不再赘述,下面会继续讨论API参数和Kernel内部比较特别的地方。 阅读详情

相关推荐

《windows核心编程》学习笔记——在同一个可执行文件或dll的多个实例间共享静态数据

默认情况下,同一个exe文件或dll的多个实例之间不会共享全局或静态数据。 windows没有提供一种简便的方法来检查用户是否在运行同一个应用程序的多个实例。如果应用程序的多个实例能够共享同一个全局变量,那么我们就可以用这个变量来保存正在运行的实例的数量。通过这个方法可以控制当前

luozhongzhu的专栏 1471

Windows内核解析-代码段执行过程

汇编代码在CPU中的执行过程如下: 一、拆分段选择子 RPL:请求特权级别 TI: TI=0查GDT表 TI=1查LDT表(Windows没有使用) INDEX: 处理器将索引值乘以8再加上GDT表的基址,就是要加载的段描述符 二、查表得到段描述符 根据段选择子的TI值为0,查询GDT表 根据段选择子的INDEX的值,找到对应的段描述符 三、权限检查 如果是非一致代码段,要求:C...

蜡笔小新的博客 373

《Windows核心编程》若干知识点实战应用分享

《Windows核心编程》若干知识点应用实践分享,希望大家能够仔细研读,在提升理论知识水平的同时,也能有效地提高分析解决问题的技能。

dvlinker的技术专栏 3万+

【Windows源码分析】(一)初始化内核与执行体子系统

标 题: 【Windows源码分析】(一)初始化内核与执行体子系统作 者: 北极星2003时 间: 2008-03-22,23:59链 接: http://bbs.pediy.com/showthread.php?t=61749对于那么没有相关经验的朋友,在阅读本文时最好对照windows源码来看,否则光看着这么多数据结构就足以头大。对于这篇文章,严格来说,应该是属于学习笔记型

求索 2317

Windows下开箱即用的C++模板排行榜程序,含源码与可执行文件

一个专为C++课程设计准备的轻量级排行榜管理程序,基于标准C++模板编写,不依赖第三方库,所有功能实现在单个cpp文件中。支持选手信息录入、按分数自动排序、结果列表显示等核心操作,逻辑清晰,注释到位,变量命名规范,适合教学演示和学生自主调试。压缩包里直接提供已编译好的8_5.exe,Windows系统解压后双击就能运行,无需安装编译环境或额外配置;同时附带原始源码8_5.cpp,兼容MinGW、MSVC等主流编译器,方便修改扩展或二次学习。配套.gitignore和项目元数据文件,结构干净,便于纳入课程作业

g5h6i的博客 228

Windows原生风格任务管理器:C++源码+双版本可执行文件+VS2019/2022工程

直接双击就能用的Windows任务管理器程序,基于纯C++和Win32 API开发,不依赖任何第三方库或运行时。压缩包里包含Debug和Release两个编译好的.exe文件,开箱即用;同时提供完整的Visual Studio 2019/2022解决方案(.sln)、项目配置(.vcxproj)、资源文件(图标.ico、对话框.rc、资源头文件resource.h)、编译中间产物(.obj、.pdb、.ilk等)以及IDE缓存文件(.VC.db、.tlog等)。程序界面采用传统Win32 GUI实现,支持查

book8的博客 206

Windows核心编程学习四:GetModuleHandle获取进程的地址空间中的可执行文件的基地址

注:源码为学习《Windows核心编程》的一些尝试,非原创。若能有助于一二访客,幸甚。 /* * File: DumpModule.cpp * Time: 2013-04-19 * 描述:学习《windows核心编程》 */ #include #include #include #include extern "C" const IMAGE_DOS_HEADE

孤舟钓客 1万+

PlutoSDR在matlab中的环境配置(包括驱动和硬件支持包)

双击下载到的驱动,除了点击接受协议和设置安装路径,其他只要点击下一步或者安装即可。登录或者注册你的账号,选择你的matlab版本(这里我的是2023b),点击下一步。设置安装路径,并选择我接受,点击下一步,即可安装完成,并在安装路径中得到。完成后,打开设备管理器,在端口和通用串行总线设备一栏,即可看见下图。点击进入,选择相应的系统安装包,这里我选择的Windows。可看到我们要安装的Pluto支持包,安装即可。右击桌面matlab,选择打开文件所在位置。右上角搜索pluto,选择并点击下一步。

weixin_53160338的博客 4864

Windows核心编程学习笔记(3)--获得可执行文件的实例和dll文件的实例句柄

Drecik学习经验分享 转载请注明出处:http://blog.youkuaiyun.com/drecik__/article/details/8079742   获得可执行或DLL文件的实例句柄,可以使用函数GetModuleHandle函数来获得 HMODULE GetModuleHandle( LPCSTR lpModuleName ); 参数为要加载的可执行文件或DLL名,当传入NULL的

Drecik__的专栏 1260

测试人必看!正交表与Allpairs工具对比:如何选择最适合你的组合测试方案?

本文深入对比了正交表、PICT与Allpairs三种主流组合测试方案,帮助测试人员根据项目场景做出最佳选择。文章分析了各自的核心原理、优劣势及适用场景,特别强调了在涉及复杂约束时PICT工具的强大功能,并提供了从原理学习到CI/CD集成的实战选型指南。

weixin_29224309的博客 267

ABAP代码检查(Code Review)

1、SLIN 扩展程序检查 也可通过(SE38工具栏->检查-扩展程序检查) 进入 2、ATC 检查 通过<SE38菜单栏-检查-ABAP测试主控室> 或事物代码SCI, 支持自定义检查变式 SCI可以配置检查变式满足特殊需求,配置对象集用于批量检查 3、通过ABAP测试主控室实现统一管理,定期检查作业和监控功能(事务代码ATC) 参考 SLIN...

u012232542的博客 6063

claude-code 安装报错排查与实战:Windows 下 Node 全局包可执行文件映射问题

在 Windows 环境下,Node.js 全局包的可执行文件映射常因 nvm 版本切换或 PATH 配置不当而失败,导致 claude.exe 无法执行。其原理在于 npm 垫片机制与系统环境变量的协同问题。解决此类问题不仅能恢复 claude-code 这一 AI 编程助手的终端能力,还能提升开发工作流中上下文感知与代码修改的效率。典型应用场景包括项目初始化、代码调试与审查。本文从报错出发,深入解析 claude-code 的安装与使用,助你避开环境陷阱,释放 AI 结对编程的生产力。

weixin_30129025的博客 274

基于simulink的能量管理策略的场景实例:混合动力电动汽车(HEV)的能量回收与分配策略

本文以混合动力电动汽车(HEV)为研究对象,通过Simulink建模详细演示了能量管理策略的设计与实现。内容涵盖HEV系统架构、核心组件工作原理,并分步骤指导搭建车辆动力学模型、动力系统组件模型和基于规则的能量管理策略。重点分析了制动能量回收与功率分配机制,通过仿真验证了逻辑门限策略在提升燃油经济性和维持电池SOC平衡方面的有效性。文章最后展望了更高级控制策略的应用前景,为HEV能量优化提供了完整的仿真研究框架。

xiaoheshang_123的博客 213

PyInstaller 完全指南:将 Python 程序打包为独立可执行文件

PyInstaller 是一个成熟的 Python 打包工具,能够把一个 Python 应用及其全部依赖(甚至包括活动的 Python 解释器本身)打包进一个文件夹或单个可执行文件中,让最终用户无需安装 Python 环境即可直接运行。本指南以当前仓库(PyInstaller 6.22.1)的官方 README 为主线,结合仓库内文档与源码,系统讲解 PyInstaller 的工作原理、安装方式、

gitblog_00234的博客 354

Python进阶005:修改Jupyter Notebook的工作路径

1、Jupyter Notebook简介 2、修改Jupyter Notebook的工作路径 如下三个步骤: 一、安装notebook 在CMD命令行中输入: pip install notebook 二、启动notebook jupyter notebook 启动后发现是在你的用户目录下,如下图所示,但是该目录下有很多的其他文件或文件夹,因此我想把这个环境切换到我单门创建的一个文件夹中。 三、创建notebook的配置文件 在CMD命令行中执行: jupyter notebook --genera

若北辰 1384

【WorkBuddy专栏53】我的WB为什么变聪明了——SOUL与USER配置管理实战(上)

【WorkBuddy专栏53】我的WB为什么变聪明了——SOUL与USER配置管理实战(上)

AI人人懂 244

Roswell脚本编程完全指南:打造可执行的Lisp应用程序

想要将你的Common Lisp代码变成真正的可执行应用程序吗?Roswell脚本编程工具正是你需要的终极解决方案!🎯 作为一款强大的Lisp环境设置工具,Roswell不仅简化了Lisp实现的安装和管理,更为开发者提供了完整的脚本编程和应用程序构建能力。本文将为你详细介绍如何利用Roswell将Lisp脚本转化为可执行程序,让你的Lisp项目焕发新生。 ## 为什么选择Roswell脚本编程

gitblog_01120的博客 894

【Zephyr存储专题】06_存储块设备驱动开发(Nand Flash)

本文介绍了基于Zephyr系统的NAND Flash驱动实现方案,重点阐述了Flash驱动模型的核心架构和关键API设计。

zero的博客 1802

Windows11 ChatGPT 无法启动:Codex CLI 可执行文件找不到,TaoToken 配置排查与修复

Windows11 上 ChatGPT 报 Codex CLI 找不到,先定位 codex.exe 真实目录,再设置 CODEX_CLI_PATH 环境变量并同步 config.toml 与 settings.json 路径,接口指向 TaoToken 官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 即可恢复启动。

weixin_35794316的博客 228

IEC 104电力规约详细解读(一) - 报文结构、报文分类、ASDU

比如,双方可以按频率发送,比如接收8帧I帧回答一帧S帧,也可以要求接收1帧I帧就应答1帧S帧,当然也可以不要求(S帧内容示例参考例1)。所以U帧格式的控制欲第一个字节就只有6种情况,分别是0x83(TESTER确认)、0x43(TESTER命令)、0x23(STOPDT确认)、0x13(STOPDT命令)、0x0B(START确认)、0x07(START命令)ASDU(应用服务数据单元),协议原文格式最好去101协议去看看,会比较详细。遥信、遥测、遥控、遥调、总召、对时等都需要使用I格式传送。

张二狗的博客 1万+

精雕软件(JDpaint) 5.5 绿色版

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

windows核心编程19

 第19章 DLL基础自从Microsoft公司推出第一个版本的Windows操作系统以来,动态链接库(DLL)一直是这个操作系统的基础。Windows API 中的所有函数都包含在DLL中。3个最重要的DLL是:Kernel32.dll,它包含用于管理内存、进程和线程的各个函数;User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;GDI32

freedom0203的专栏 801

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

windows核心编程

主要用于记录windows核心编程这本书籍的学习。

weixin_45002961的博客 1341

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

上一篇: 《Windows核心编程》---逻辑驱动器操作
下一篇: 《Windows核心编程》の“打开”和“保存”文件对话框
ACE1985
博客等级 码龄17年 1307粉丝 308原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值