使用WDM驱动实现在NT下读取物理端口,特殊寄存器,物理内存的代码(C++驱动加载代码) .

驱动写好后,要在应用程序中调用驱动还需要自己写一个驱动加载器,

以下代码能够实现在Windows中加载驱动到系统中,并能调用驱动的I/O例程。

编译成功后,把驱动sys文件与此代码编译好的exe放同一个目录。

 

cpp源文件:

  1. #include "libmio.h"   
  2.   
  3. using namespace std;  
  4.   
  5. CLibmio::CLibmio(voidthrow (CLibmioException) :  
  6.   m_hDriver(INVALID_HANDLE_VALUE)  
  7. {  
  8.   OSVERSIONINFO   
  9.     osv;  
  10.     
  11.   // check the os whether support this driver.   
  12.   osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);  
  13.   ::GetVersionEx(&osv);  
  14.   if ( osv.dwPlatformId != VER_PLATFORM_WIN32_NT )  
  15.     throw new CLibmioException(ERR_OS_NOT_SUPPORT);  
  16.   
  17.   const char  
  18.     * pChar = NULL;  
  19.   
  20.   // get the file path of driver.   
  21.   ::GetModuleFileName(NULL, szDriverPath, MAX_PATH);  
  22.   pChar = ::strrchr(szDriverPath, '\\');  
  23.   if ( !pChar )  
  24.     throw new CLibmioException(ERR_DRIVER_NOT_FOUND);  
  25.   
  26.   ::strcpy(const_cast<char*>(++pChar), DRIVER_FILE);  
  27.       
  28.   WIN32_FIND_DATA   
  29.     finddata;  
  30.   
  31.   HANDLE   
  32.     hFindDriver = ::FindFirstFile(szDriverPath, &finddata);  
  33.     
  34.   // confirm the file path is exist.   
  35.   if ( hFindDriver == INVALID_HANDLE_VALUE )  
  36.     throw new CLibmioException(ERR_DRIVER_NOT_FOUND);  
  37.   ::FindClose(hFindDriver);  
  38.   
  39.   // open the driver   
  40.   if ( !OpenDriver() )  
  41.   {  
  42.     // stop and uninstall the driver.   
  43.     StopDriver();  
  44.     UninstallDriver();  
  45.   
  46.     // install the driver.   
  47.     if ( !InstalDriver() )  
  48.       throw new CLibmioException(ERR_DRIVER_INSTALL_FAIL);  
  49.   
  50.     // startup the driver.   
  51.     if ( !StartDriver() )  
  52.       throw new CLibmioException(ERR_DRIVER_START_FAIL);  
  53.   
  54.     // open the driver.   
  55.     if ( !OpenDriver() )  
  56.       throw new CLibmioException(ERR_DRIVER_OPEN_FAIL);  
  57.   }  
  58. }  
  59.   
  60. CLibmio::~CLibmio(void)  
  61. {   
  62.   bool   
  63.     bUns = CanUninstall();  
  64.   CloseDriver();  
  65.     
  66.   if ( bUns )  
  67.   {  
  68.     StopDriver();  
  69.     UninstallDriver();  
  70.   }  
  71. }  
  72.   
  73. bool CLibmio::InstalDriver(void)  
  74. {  
  75.   SC_HANDLE   
  76.     hSCManager,  
  77.     hService;  
  78.   
  79.   hSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);  
  80.   if ( hSCManager == NULL )  
  81.     return false;  
  82.       
  83.   hService = ::CreateService(  
  84.     hSCManager,  
  85.     DRIVER_NAME,  
  86.     DRIVER_NAME,  
  87.     SERVICE_ALL_ACCESS,  
  88.     SERVICE_KERNEL_DRIVER,  
  89.     SERVICE_DEMAND_START,  
  90.     SERVICE_ERROR_NORMAL,  
  91.     szDriverPath,  
  92.     NULL, NULL, NULL, NULL, NULL);  
  93.   ::CloseServiceHandle(hSCManager);  
  94.     
  95.   if ( hService == NULL )  
  96.     return false;  
  97.   ::CloseServiceHandle(hService);  
  98.     
  99.   return true;  
  100. }  
  101.   
  102. bool CLibmio::StartDriver(void)  
  103. {  
  104.   SC_HANDLE   
  105.     hSCManager,  
  106.     hService;  
  107.       
  108.   bool   
  109.     bResult = false;  
  110.   
  111.   hSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);  
  112.   if ( hSCManager == NULL )  
  113.     return bResult;  
  114.   hService = ::OpenService(hSCManager, DRIVER_NAME, SERVICE_ALL_ACCESS);  
  115.   ::CloseServiceHandle(hSCManager);  
  116.   if ( hService == NULL )  
  117.     return bResult;  
  118.   #pragma warning(disable:4800)   
  119.   bResult = ::StartService(hService, 0, NULL) || GetLastError() == ERROR_SERVICE_ALREADY_RUNNING;  
  120.   ::CloseServiceHandle(hService);  
  121.     
  122.   return bResult;  
  123. }  
  124.   
  125. bool CLibmio::OpenDriver(void)  
  126. {  
  127.   m_hDriver = ::CreateFile(  
  128.     DEVICE_PATH, GENERIC_READ | GENERIC_WRITE,  
  129.     FILE_SHARE_READ | FILE_SHARE_WRITE,  
  130.     NULL, OPEN_EXISTING,  
  131.     FILE_ATTRIBUTE_NORMAL, NULL);  
  132.   return (m_hDriver != INVALID_HANDLE_VALUE);  
  133. }  
  134.   
  135. bool CLibmio::CanUninstall(void)  
  136. {  
  137.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  138.     return true;  
  139.       
  140.   DWORD   
  141.     dwBytes = 0,  
  142.     dwRef = 0;  
  143.     
  144.   if ( !::DeviceIoControl(  
  145.        m_hDriver,  
  146.        IOCTL_GET_REFCOUNT,  
  147.        NULL,  
  148.        0,  
  149.        &dwRef,  
  150.        sizeof(DWORD),  
  151.        &dwBytes,  
  152.        NULL) )  
  153.     return false;  
  154.   return (dwRef < 2);  
  155. }  
  156.   
  157. void CLibmio::CloseDriver(void)  
  158. {  
  159.   if ( m_hDriver != INVALID_HANDLE_VALUE )  
  160.     ::CloseHandle(m_hDriver);  
  161.   m_hDriver = INVALID_HANDLE_VALUE;  
  162. }  
  163.   
  164. bool CLibmio::UninstallDriver(void)  
  165. {  
  166.   SC_HANDLE   
  167.     hSCManager,  
  168.     hService;  
  169.       
  170.   bool   
  171.     bResult = false;  
  172.   
  173.   hSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);  
  174.   if ( hSCManager == NULL )  
  175.     return bResult;  
  176.   hService = ::OpenService(hSCManager, DRIVER_NAME, SERVICE_ALL_ACCESS);  
  177.   ::CloseServiceHandle(hSCManager);  
  178.   if ( hService == NULL )  
  179.     return bResult;  
  180.   #pragma warning(disable:4800)   
  181.   bResult = ::DeleteService(hService);  
  182.   ::CloseServiceHandle(hService);  
  183.     
  184.   return bResult;  
  185. }  
  186.   
  187. bool CLibmio::StopDriver(void)  
  188. {  
  189.   SERVICE_STATUS   
  190.     ServiceStatus;  
  191.   
  192.   SC_HANDLE   
  193.     hSCManager,  
  194.     hService;  
  195.       
  196.   bool   
  197.     bResult = false;  
  198.   
  199.   hSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);  
  200.   if ( hSCManager == NULL )  
  201.     return bResult;  
  202.   hService = ::OpenService(hSCManager, DRIVER_NAME, SERVICE_ALL_ACCESS);  
  203.   ::CloseServiceHandle(hSCManager);  
  204.   if ( hService == NULL )  
  205.     return bResult;  
  206.   #pragma warning(disable:4800)   
  207.   bResult = ::ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);  
  208.   ::CloseServiceHandle(hService);  
  209.     
  210.   return bResult;  
  211. }  
  212.   
  213. bool CLibmio::GetVersion(  
  214.     DWORD &dwVersion)  
  215. {  
  216.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  217.     return false;  
  218.       
  219.   DWORD   
  220.     dwBytes = 0;  
  221.   
  222.   return 0 != ::DeviceIoControl(  
  223.     m_hDriver,  
  224.     IOCTL_GET_VERSION,  
  225.     NULL, 0,  
  226.     &dwVersion,  
  227.     sizeof(DWORD),  
  228.     &dwBytes,  
  229.     NULL);  
  230. }  
  231.   
  232. bool CLibmio::GetVersionString(  
  233.     char *outString,   
  234.     int length)  
  235. {  
  236.   DWORD   
  237.     dwVersion = 0;  
  238.     
  239.   if ( !outString )  
  240.     return false;  
  241.       
  242.   if ( !GetVersion(dwVersion) )  
  243.     return false;  
  244.       
  245.   BYTE  
  246.     vers[4];  
  247.     
  248.   char  
  249.     szStr[18];  
  250.       
  251.   ::memcpy(vers, &dwVersion, sizeof(DWORD));  
  252.   ::sprintf(szStr, "%d.%d.%d.%d", vers[3], vers[2], vers[1], vers[0]);  
  253.   if ( length > strlen(szStr) )  
  254.     length = strlen(szStr);  
  255.   ::strncpy(outString, szStr, length);  
  256.     
  257.   return true;  
  258. }  
  259.   
  260. bool CLibmio::Rdmsr(  
  261.     DWORD index,   
  262.     ULARGE_INTEGER& out)  
  263. {  
  264.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  265.     return false;  
  266.       
  267.   DWORD   
  268.     dwBytes = 0;   
  269.   
  270.   return 0 != ::DeviceIoControl(  
  271.     m_hDriver,  
  272.     IOCTL_READ_MSR,  
  273.     &index,  
  274.     sizeof(index),  
  275.     &out,  
  276.     sizeof(out),  
  277.     &dwBytes,  
  278.     NULL);  
  279. }  
  280.   
  281. bool CLibmio::Wrmsr(  
  282.     DWORD index,   
  283.     ULARGE_INTEGER value)  
  284. {  
  285.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  286.     return false;  
  287.       
  288.   DWORD   
  289.     dwBytes = 0,  
  290.     buff[3];  
  291.   
  292.   buff[0] = index;  
  293.   ::memcpy(&buff[1], &value, sizeof(value));  
  294.     
  295.   return 0 != ::DeviceIoControl(  
  296.     m_hDriver,  
  297.     IOCTL_WRITE_MSR,  
  298.     buff,  
  299.     sizeof(buff),  
  300.     NULL,  
  301.     0,  
  302.     &dwBytes,  
  303.     NULL);  
  304. }  
  305.   
  306. bool CLibmio::Rdpmc(  
  307.     DWORD index,   
  308.     ULARGE_INTEGER& out)  
  309. {  
  310.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  311.     return false;  
  312.       
  313.   DWORD   
  314.     dwBytes = 0;   
  315.   
  316.   return 0 != ::DeviceIoControl(  
  317.     m_hDriver,  
  318.     IOCTL_READ_PMC,  
  319.     &index,  
  320.     sizeof(index),  
  321.     &out,  
  322.     sizeof(out),  
  323.     &dwBytes,  
  324.     NULL);  
  325. }  
  326.   
  327. bool CLibmio::Inp(  
  328.     WORD port,   
  329.     BYTE& out)  
  330. {  
  331.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  332.     return false;  
  333.   
  334.   DWORD   
  335.     dwBytes = 0;    
  336.   
  337.   out = 0;  
  338.   return 0 != ::DeviceIoControl(  
  339.     m_hDriver,  
  340.     IOCTL_READ_PORT,  
  341.     &port,  
  342.     sizeof(port),  
  343.     &out,  
  344.     sizeof(out),  
  345.     &dwBytes,  
  346.     NULL);  
  347. }  
  348.   
  349. bool CLibmio::Inp(  
  350.     WORD port,   
  351.     WORD& out)  
  352. {  
  353.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  354.     return false;  
  355.   
  356.   DWORD   
  357.     dwBytes = 0;  
  358.   
  359.   out = 0;  
  360.   return 0 != ::DeviceIoControl(  
  361.     m_hDriver,  
  362.     IOCTL_READ_PORT,  
  363.     &port,  
  364.     sizeof(port),  
  365.     &out,  
  366.     sizeof(out),  
  367.     &dwBytes,  
  368.     NULL);  
  369. }  
  370.   
  371. bool CLibmio::Inp(  
  372.     WORD port,   
  373.     DWORD& out)  
  374. {  
  375.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  376.     return false;  
  377.   
  378.   DWORD   
  379.     dwBytes = 0;   
  380.   
  381.   out = 0;  
  382.   return 0 != ::DeviceIoControl(  
  383.     m_hDriver,  
  384.     IOCTL_READ_PORT,  
  385.     &port,  
  386.     sizeof(port),  
  387.     &out,  
  388.     sizeof(out),  
  389.     &dwBytes,  
  390.     NULL);  
  391. }  
  392.   
  393. bool CLibmio::Outp(  
  394.     WORD port,   
  395.     BYTE data)  
  396. {  
  397.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  398.     return false;  
  399.       
  400.   WORD   
  401.     buff[2];  
  402.   DWORD   
  403.     dwBytes = 0;  
  404.   
  405.   buff[0] = port;  
  406.   buff[1] = data;  
  407.     
  408.   return 0 != ::DeviceIoControl(  
  409.     m_hDriver,  
  410.     IOCTL_WRITE_PORT,  
  411.     buff,  
  412.     sizeof(port) + sizeof(data),  
  413.     NULL,  
  414.     0,  
  415.     &dwBytes,  
  416.     NULL);  
  417. }  
  418.   
  419. bool CLibmio::Outp(  
  420.     WORD port,   
  421.     WORD data)  
  422. {  
  423.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  424.     return false;  
  425.       
  426.   WORD   
  427.     buff[2];  
  428.   DWORD   
  429.     dwBytes = 0;  
  430.   
  431.   buff[0] = port;  
  432.   buff[1] = data;  
  433.     
  434.   return 0 != ::DeviceIoControl(  
  435.     m_hDriver,  
  436.     IOCTL_WRITE_PORT,  
  437.     buff,  
  438.     sizeof(port) + sizeof(data),  
  439.     NULL,  
  440.     0,  
  441.     &dwBytes,  
  442.     NULL);  
  443. }  
  444.   
  445. bool CLibmio::Outp(  
  446.     WORD port,   
  447.     DWORD data)  
  448. {  
  449.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  450.     return false;  
  451.   
  452.   WORD   
  453.     buff[3];  
  454.   DWORD   
  455.     dwBytes = 0;   
  456.       
  457.   buff[0] = port;  
  458.   *((DWORD*)&buff[1]) = data;  
  459.     
  460.   return 0 != ::DeviceIoControl(  
  461.     m_hDriver,  
  462.     IOCTL_WRITE_PORT,  
  463.     buff,  
  464.     sizeof(port) + sizeof(data),  
  465.     NULL,  
  466.     0,  
  467.     &dwBytes,  
  468.     NULL);  
  469. }  
  470.   
  471. bool CLibmio::ReadPort(  
  472.     WORD port,   
  473.     BYTE& out)  
  474. {  
  475.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  476.     return false;  
  477.   
  478.   DWORD   
  479.     dwBytes = 0;   
  480.   
  481.   out = 0;  
  482.   return 0 != ::DeviceIoControl(  
  483.     m_hDriver,  
  484.     IOCTL_READ_PORT_BYTE,  
  485.     &port,  
  486.     sizeof(port),  
  487.     &out,  
  488.     sizeof(out),  
  489.     &dwBytes,  
  490.     NULL);  
  491. }  
  492.   
  493. bool CLibmio::ReadPort(  
  494.     WORD port,   
  495.     WORD& out)  
  496. {  
  497.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  498.     return false;  
  499.   
  500.   DWORD   
  501.     dwBytes = 0;   
  502.   
  503.   out = 0;  
  504.   return 0 != ::DeviceIoControl(  
  505.     m_hDriver,  
  506.     IOCTL_READ_PORT_WORD,  
  507.     &port,  
  508.     sizeof(port),  
  509.     &out,  
  510.     sizeof(out),  
  511.     &dwBytes,  
  512.     NULL);  
  513. }  
  514.   
  515. bool CLibmio::ReadPort(  
  516.     WORD port,   
  517.     DWORD& out)  
  518. {  
  519.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  520.     return false;  
  521.   
  522.   DWORD   
  523.     dwBytes = 0;   
  524.   
  525.   out = 0;  
  526.   return 0 != ::DeviceIoControl(  
  527.     m_hDriver,  
  528.     IOCTL_READ_PORT_DWORD,  
  529.     &port,  
  530.     sizeof(port),  
  531.     &out,  
  532.     sizeof(out),  
  533.     &dwBytes,  
  534.     NULL);  
  535. }  
  536.   
  537. bool CLibmio::WritePort(  
  538.     WORD port,   
  539.     BYTE data)  
  540. {  
  541.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  542.     return false;  
  543.   
  544.   DWORD   
  545.     dwBytes = 0,  
  546.     buff[2];  
  547.       
  548.   buff[0] = port;  
  549.   buff[1] = data;  
  550.     
  551.   return 0 != ::DeviceIoControl(  
  552.     m_hDriver,  
  553.     IOCTL_WRITE_PORT_BYTE,  
  554.     buff,  
  555.     sizeof(buff),  
  556.     NULL,  
  557.     0,  
  558.     &dwBytes,  
  559.     NULL);  
  560. }  
  561.   
  562. bool CLibmio::WritePort(  
  563.     WORD port,   
  564.     WORD data)  
  565. {  
  566.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  567.     return false;  
  568.   
  569.   DWORD   
  570.     dwBytes = 0,  
  571.     buff[2];  
  572.       
  573.   buff[0] = port;  
  574.   buff[1] = data;  
  575.     
  576.   return 0 != ::DeviceIoControl(  
  577.     m_hDriver,  
  578.     IOCTL_WRITE_PORT_WORD,  
  579.     buff,  
  580.     sizeof(buff),  
  581.     NULL,  
  582.     0,  
  583.     &dwBytes,  
  584.     NULL);  
  585. }  
  586.   
  587. bool CLibmio::WritePort(  
  588.     WORD port,   
  589.     DWORD data)  
  590. {  
  591.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  592.     return false;  
  593.   
  594.   DWORD   
  595.     dwBytes = 0,  
  596.     buff[2];  
  597.       
  598.   buff[0] = port;  
  599.   buff[1] = data;  
  600.     
  601.   return 0 != ::DeviceIoControl(  
  602.     m_hDriver,  
  603.     IOCTL_WRITE_PORT_DWORD,  
  604.     buff,  
  605.     sizeof(buff),  
  606.     NULL,  
  607.     0,  
  608.     &dwBytes,  
  609.     NULL);  
  610. }  
  611.   
  612. bool CLibmio::PciReadConfig(  
  613.     DWORD addr,   
  614.     DWORD offset,   
  615.     LPVOID out,   
  616.     DWORD cbSize)  
  617. {  
  618.   return PciReadConfig((addr >> 8) & 0xff, (addr >> 3) & 0x1f, addr & 0x7, offset, out, cbSize);  
  619. }  
  620.   
  621. bool CLibmio::PciReadConfig(  
  622.     WORD bus,   
  623.     WORD dev,   
  624.     WORD fun,   
  625.     DWORD offset,   
  626.     LPVOID out,   
  627.     DWORD cbSize)  
  628. {  
  629.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  630.     return false;  
  631.   
  632.   DWORD   
  633.     dwBytes = 0,  
  634.     buff[3];  
  635.       
  636.   buff[0] = bus;  
  637.   buff[1] = (fun << 5) | (dev & 0x1f);  
  638.   buff[2] = offset;  
  639.     
  640.   ::RtlZeroMemory(out, cbSize);  
  641.     
  642.   return 0 != ::DeviceIoControl(  
  643.     m_hDriver,  
  644.     IOCTL_PCI_READ_CONFIG,  
  645.     buff,  
  646.     sizeof(buff),  
  647.     out,  
  648.     cbSize,  
  649.     &dwBytes,  
  650.     NULL);  
  651. }  
  652.   
  653. bool CLibmio::PciWriteConfig(  
  654.     DWORD addr,   
  655.     DWORD offset,   
  656.     LPVOID out,   
  657.     DWORD cbSize)  
  658. {  
  659.   return PciWriteConfig((addr >> 8) & 0xff, (addr >> 3) & 0x1f, addr & 0x7, offset, out, cbSize);  
  660. }  
  661.   
  662. bool CLibmio::PciWriteConfig(  
  663.     WORD bus,   
  664.     WORD dev,   
  665.     WORD fun,   
  666.     DWORD offset,   
  667.     LPVOID out,   
  668.     DWORD cbSize)  
  669. {  
  670.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  671.     return false;  
  672.   
  673.   DWORD   
  674.     dwBytes = 0,  
  675.     dwSize = 0,  
  676.     * pBuff;  
  677.     
  678.   bool   
  679.     result = false;  
  680.       
  681.   dwSize = cbSize + 0x10;  
  682.   pBuff = reinterpret_cast<DWORD*>(new BYTE[dwSize]);  
  683.     
  684.   pBuff[0] = bus;  
  685.   pBuff[1] = (fun << 5) | (dev & 0x1f);  
  686.   pBuff[2] = offset;  
  687.   pBuff[3] = cbSize;  
  688.     
  689.   ::RtlMoveMemory(&pBuff[4], out, cbSize);  
  690.     
  691.   result = 0 != ::DeviceIoControl(  
  692.     m_hDriver,  
  693.     IOCTL_PCI_WRITE_CONFIG,  
  694.     pBuff,  
  695.     dwSize,  
  696.     NULL,  
  697.     0,  
  698.     &dwBytes,  
  699.     NULL);  
  700.     
  701.   delete []pBuff;  
  702.     
  703.   return result;  
  704. }  
  705.   
  706. LPVOID CLibmio::MapPhyMemory(  
  707.     LPVOID addr,   
  708.     DWORD cbSize)  
  709. {  
  710.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  711.     return NULL;  
  712.     
  713.   if ( !cbSize )  
  714.     return false;  
  715.   
  716.   DWORD   
  717.     dwBytes = 0,  
  718.     buff[2];  
  719.     
  720.   LPVOID  
  721.     out = NULL;  
  722.   
  723.   buff[0] = reinterpret_cast<DWORD>(addr);  
  724.   buff[1] = cbSize;  
  725.     
  726.   if ( !::DeviceIoControl(  
  727.     m_hDriver,  
  728.     IOCTL_MAP_PHY_MEMORY,  
  729.     buff,  
  730.     sizeof(buff),  
  731.     &out,  
  732.     sizeof(out),  
  733.     &dwBytes,  
  734.     NULL) )  
  735.     return NULL;  
  736.       
  737.   return out;  
  738. }  
  739.   
  740. bool CLibmio::UnmapPhyMemory(  
  741.     LPVOID addr)  
  742. {  
  743.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  744.     return false;  
  745.     
  746.   if ( !addr )  
  747.     return false;  
  748.   
  749.   DWORD   
  750.     dwBytes = 0;  
  751.     
  752.   return 0 != ::DeviceIoControl(  
  753.     m_hDriver,  
  754.     IOCTL_UNMAP_PHY_MEMORY,  
  755.     &addr,  
  756.     sizeof(LPVOID),  
  757.     NULL,  
  758.     0,  
  759.     &dwBytes,  
  760.     NULL);  
  761. }  
  762.   
  763. bool CLibmio::ReadPhyMemory(  
  764.     LPVOID addr,   
  765.     LPVOID buffer,   
  766.     DWORD cbSize)  
  767. {  
  768.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  769.     return false;  
  770.     
  771.   if ( !buffer )  
  772.     return false;  
  773.     
  774.   if ( !cbSize )  
  775.     return false;  
  776.   
  777.   DWORD   
  778.     dwBytes = 0,  
  779.     buff[2];  
  780.   
  781.   buff[0] = reinterpret_cast<DWORD>(addr);  
  782.   buff[1] = cbSize;  
  783.     
  784.   return 0 != ::DeviceIoControl(  
  785.     m_hDriver,  
  786.     IOCTL_READ_PHY_MEMORY,  
  787.     buff,  
  788.     sizeof(buff),  
  789.     buffer,  
  790.     cbSize,  
  791.     &dwBytes,  
  792.     NULL);  
  793. }  
  794.   
  795. bool CLibmio::WritePhyMemory(  
  796.     LPVOID addr,   
  797.     LPVOID buffer,   
  798.     DWORD cbSize)  
  799. {  
  800.   if ( m_hDriver == INVALID_HANDLE_VALUE )  
  801.     return false;  
  802.     
  803.   if ( !buffer )  
  804.     return false;  
  805.     
  806.   if ( !cbSize )  
  807.     return false;  
  808.     
  809.   bool   
  810.     result = false;  
  811.   
  812.   DWORD   
  813.     dwBytes = 0,  
  814.     dwSize = cbSize + 2 * sizeof(DWORD),  
  815.     * pBuff;  
  816.   
  817.   pBuff = reinterpret_cast<DWORD*>(new BYTE[dwSize]);  
  818.   pBuff[0] = reinterpret_cast<DWORD>(addr);  
  819.   pBuff[1] = cbSize;  
  820.     
  821.   ::RtlMoveMemory(&pBuff[2], buffer, cbSize);  
  822.     
  823.   result = 0 != ::DeviceIoControl(  
  824.     m_hDriver,  
  825.     IOCTL_WRITE_PHY_MEMORY,  
  826.     pBuff,  
  827.     dwSize,  
  828.     NULL,  
  829.     0,  
  830.     &dwBytes,  
  831.     NULL);  
  832.     
  833.   delete []pBuff;  
  834.     
  835.   return result;  
  836. }  
#include "libmio.h"

using namespace std;

CLibmio::CLibmio(void) throw (CLibmioException) :
  m_hDriver(INVALID_HANDLE_VALUE)
{
  OSVERSIONINFO 
    osv;
  
  // check the os whether support this driver.
  osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  ::GetVersionEx(&osv);
  if ( osv.dwPlatformId != VER_PLATFORM_WIN32_NT )
    throw new CLibmioException(ERR_OS_NOT_SUPPORT);

  const char
    * pChar = NULL;

  // get the file path of driver.
  ::GetModuleFileName(NULL, szDriverPath, MAX_PATH);
  pChar = ::strrchr(szDriverPath, '\\');
  if ( !pChar )
    throw new CLibmioException(ERR_DRIVER_NOT_FOUND);

  ::strcpy(const_cast<char*>(++pChar), DRIVER_FILE);
    
  WIN32_FIND_DATA 
    finddata;

  HANDLE 
    hFindDriver = ::FindFirstFile(szDriverPath, &finddata);
  
  // confirm the file path is exist.
  if ( hFindDriver == INVALID_HANDLE_VALUE )
    throw new CLibmioException(ERR_DRIVER_NOT_FOUND);
  ::FindClose(hFindDriver);

  // open the driver
  if ( !OpenDriver() )
  {
    // stop and uninstall the driver.
    StopDriver();
    UninstallDriver();

    // install the driver.
    if ( !InstalDriver() )
      throw new CLibmioException(ERR_DRIVER_INSTALL_FAIL);

    // startup the driver.
    if ( !StartDriver() )
      throw new CLibmioException(ERR_DRIVER_START_FAIL);

    // open the driver.
    if ( !OpenDriver() )
      throw new CLibmioException(ERR_DRIVER_OPEN_FAIL);
  }
}

CLibmio::~CLibmio(void)
{ 
  bool 
    bUns = CanUninstall();
  CloseDriver();
  
  if ( bUns )
  {
    StopDriver();
    UninstallDriver();
  }
}

bool CLibmio::InstalDriver(void)
{
  SC_HANDLE 
    hSCManager,
    hService;

  hSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  if ( hSCManager == NULL )
    return false;
    
  hService = ::CreateService(
    hSCManager,
    DRIVER_NAME,
    DRIVER_NAME,
    SERVICE_ALL_ACCESS,
    SERVICE_KERNEL_DRIVER,
    SERVICE_DEMAND_START,
    SERVICE_ERROR_NORMAL,
    szDriverPath,
    NULL, NULL, NULL, NULL, NULL);
  ::CloseServiceHandle(hSCManager);
  
  if ( hService == NULL )
    return false;
  ::CloseServiceHandle(hService);
  
  return true;
}

bool CLibmio::StartDriver(void)
{
  SC_HANDLE 
    hSCManager,
    hService;
    
  bool 
    bResult = false;

  hSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  if ( hSCManager == NULL )
    return bResult;
  hService = ::OpenService(hSCManager, DRIVER_NAME, SERVICE_ALL_ACCESS);
  ::CloseServiceHandle(hSCManager);
  if ( hService == NULL )
    return bResult;
  #pragma warning(disable:4800)
  bResult = ::StartService(hService, 0, NULL) || GetLastError() == ERROR_SERVICE_ALREADY_RUNNING;
  ::CloseServiceHandle(hService);
  
  return bResult;
}

bool CLibmio::OpenDriver(void)
{
  m_hDriver = ::CreateFile(
    DEVICE_PATH, GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL, OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL, NULL);
  return (m_hDriver != INVALID_HANDLE_VALUE);
}

bool CLibmio::CanUninstall(void)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return true;
    
  DWORD 
    dwBytes = 0,
    dwRef = 0;
  
  if ( !::DeviceIoControl(
       m_hDriver,
       IOCTL_GET_REFCOUNT,
       NULL,
       0,
       &dwRef,
       sizeof(DWORD),
       &dwBytes,
       NULL) )
    return false;
  return (dwRef < 2);
}

void CLibmio::CloseDriver(void)
{
  if ( m_hDriver != INVALID_HANDLE_VALUE )
    ::CloseHandle(m_hDriver);
  m_hDriver = INVALID_HANDLE_VALUE;
}

bool CLibmio::UninstallDriver(void)
{
  SC_HANDLE 
    hSCManager,
    hService;
    
  bool 
    bResult = false;

  hSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  if ( hSCManager == NULL )
    return bResult;
  hService = ::OpenService(hSCManager, DRIVER_NAME, SERVICE_ALL_ACCESS);
  ::CloseServiceHandle(hSCManager);
  if ( hService == NULL )
    return bResult;
  #pragma warning(disable:4800)
  bResult = ::DeleteService(hService);
  ::CloseServiceHandle(hService);
  
  return bResult;
}

bool CLibmio::StopDriver(void)
{
  SERVICE_STATUS 
    ServiceStatus;

  SC_HANDLE 
    hSCManager,
    hService;
    
  bool 
    bResult = false;

  hSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  if ( hSCManager == NULL )
    return bResult;
  hService = ::OpenService(hSCManager, DRIVER_NAME, SERVICE_ALL_ACCESS);
  ::CloseServiceHandle(hSCManager);
  if ( hService == NULL )
    return bResult;
  #pragma warning(disable:4800)
  bResult = ::ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
  ::CloseServiceHandle(hService);
  
  return bResult;
}

bool CLibmio::GetVersion(
    DWORD &dwVersion)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;
    
  DWORD 
    dwBytes = 0;

  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_GET_VERSION,
    NULL, 0,
    &dwVersion,
    sizeof(DWORD),
    &dwBytes,
    NULL);
}

bool CLibmio::GetVersionString(
    char *outString, 
    int length)
{
  DWORD 
    dwVersion = 0;
  
  if ( !outString )
    return false;
    
  if ( !GetVersion(dwVersion) )
    return false;
    
  BYTE
    vers[4];
  
  char
    szStr[18];
    
  ::memcpy(vers, &dwVersion, sizeof(DWORD));
  ::sprintf(szStr, "%d.%d.%d.%d", vers[3], vers[2], vers[1], vers[0]);
  if ( length > strlen(szStr) )
    length = strlen(szStr);
  ::strncpy(outString, szStr, length);
  
  return true;
}

bool CLibmio::Rdmsr(
    DWORD index, 
    ULARGE_INTEGER& out)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;
    
  DWORD 
    dwBytes = 0; 

  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_READ_MSR,
    &index,
    sizeof(index),
    &out,
    sizeof(out),
    &dwBytes,
    NULL);
}

bool CLibmio::Wrmsr(
    DWORD index, 
    ULARGE_INTEGER value)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;
    
  DWORD 
    dwBytes = 0,
    buff[3];

  buff[0] = index;
  ::memcpy(&buff[1], &value, sizeof(value));
  
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_WRITE_MSR,
    buff,
    sizeof(buff),
    NULL,
    0,
    &dwBytes,
    NULL);
}

bool CLibmio::Rdpmc(
    DWORD index, 
    ULARGE_INTEGER& out)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;
    
  DWORD 
    dwBytes = 0; 

  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_READ_PMC,
    &index,
    sizeof(index),
    &out,
    sizeof(out),
    &dwBytes,
    NULL);
}

bool CLibmio::Inp(
    WORD port, 
    BYTE& out)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  DWORD 
    dwBytes = 0;  

  out = 0;
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_READ_PORT,
    &port,
    sizeof(port),
    &out,
    sizeof(out),
    &dwBytes,
    NULL);
}

bool CLibmio::Inp(
    WORD port, 
    WORD& out)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  DWORD 
    dwBytes = 0;

  out = 0;
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_READ_PORT,
    &port,
    sizeof(port),
    &out,
    sizeof(out),
    &dwBytes,
    NULL);
}

bool CLibmio::Inp(
    WORD port, 
    DWORD& out)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  DWORD 
    dwBytes = 0; 

  out = 0;
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_READ_PORT,
    &port,
    sizeof(port),
    &out,
    sizeof(out),
    &dwBytes,
    NULL);
}

bool CLibmio::Outp(
    WORD port, 
    BYTE data)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;
    
  WORD 
    buff[2];
  DWORD 
    dwBytes = 0;

  buff[0] = port;
  buff[1] = data;
  
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_WRITE_PORT,
    buff,
    sizeof(port) + sizeof(data),
    NULL,
    0,
    &dwBytes,
    NULL);
}

bool CLibmio::Outp(
    WORD port, 
    WORD data)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;
    
  WORD 
    buff[2];
  DWORD 
    dwBytes = 0;

  buff[0] = port;
  buff[1] = data;
  
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_WRITE_PORT,
    buff,
    sizeof(port) + sizeof(data),
    NULL,
    0,
    &dwBytes,
    NULL);
}

bool CLibmio::Outp(
    WORD port, 
    DWORD data)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  WORD 
    buff[3];
  DWORD 
    dwBytes = 0; 
    
  buff[0] = port;
  *((DWORD*)&buff[1]) = data;
  
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_WRITE_PORT,
    buff,
    sizeof(port) + sizeof(data),
    NULL,
    0,
    &dwBytes,
    NULL);
}

bool CLibmio::ReadPort(
    WORD port, 
    BYTE& out)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  DWORD 
    dwBytes = 0; 

  out = 0;
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_READ_PORT_BYTE,
    &port,
    sizeof(port),
    &out,
    sizeof(out),
    &dwBytes,
    NULL);
}

bool CLibmio::ReadPort(
    WORD port, 
    WORD& out)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  DWORD 
    dwBytes = 0; 

  out = 0;
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_READ_PORT_WORD,
    &port,
    sizeof(port),
    &out,
    sizeof(out),
    &dwBytes,
    NULL);
}

bool CLibmio::ReadPort(
    WORD port, 
    DWORD& out)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  DWORD 
    dwBytes = 0; 

  out = 0;
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_READ_PORT_DWORD,
    &port,
    sizeof(port),
    &out,
    sizeof(out),
    &dwBytes,
    NULL);
}

bool CLibmio::WritePort(
    WORD port, 
    BYTE data)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  DWORD 
    dwBytes = 0,
    buff[2];
    
  buff[0] = port;
  buff[1] = data;
  
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_WRITE_PORT_BYTE,
    buff,
    sizeof(buff),
    NULL,
    0,
    &dwBytes,
    NULL);
}

bool CLibmio::WritePort(
    WORD port, 
    WORD data)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  DWORD 
    dwBytes = 0,
    buff[2];
    
  buff[0] = port;
  buff[1] = data;
  
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_WRITE_PORT_WORD,
    buff,
    sizeof(buff),
    NULL,
    0,
    &dwBytes,
    NULL);
}

bool CLibmio::WritePort(
    WORD port, 
    DWORD data)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  DWORD 
    dwBytes = 0,
    buff[2];
    
  buff[0] = port;
  buff[1] = data;
  
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_WRITE_PORT_DWORD,
    buff,
    sizeof(buff),
    NULL,
    0,
    &dwBytes,
    NULL);
}

bool CLibmio::PciReadConfig(
    DWORD addr, 
    DWORD offset, 
    LPVOID out, 
    DWORD cbSize)
{
  return PciReadConfig((addr >> 8) & 0xff, (addr >> 3) & 0x1f, addr & 0x7, offset, out, cbSize);
}

bool CLibmio::PciReadConfig(
    WORD bus, 
    WORD dev, 
    WORD fun, 
    DWORD offset, 
    LPVOID out, 
    DWORD cbSize)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  DWORD 
    dwBytes = 0,
    buff[3];
    
  buff[0] = bus;
  buff[1] = (fun << 5) | (dev & 0x1f);
  buff[2] = offset;
  
  ::RtlZeroMemory(out, cbSize);
  
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_PCI_READ_CONFIG,
    buff,
    sizeof(buff),
    out,
    cbSize,
    &dwBytes,
    NULL);
}

bool CLibmio::PciWriteConfig(
    DWORD addr, 
    DWORD offset, 
    LPVOID out, 
    DWORD cbSize)
{
  return PciWriteConfig((addr >> 8) & 0xff, (addr >> 3) & 0x1f, addr & 0x7, offset, out, cbSize);
}

bool CLibmio::PciWriteConfig(
    WORD bus, 
    WORD dev, 
    WORD fun, 
    DWORD offset, 
    LPVOID out, 
    DWORD cbSize)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;

  DWORD 
    dwBytes = 0,
    dwSize = 0,
    * pBuff;
  
  bool 
    result = false;
    
  dwSize = cbSize + 0x10;
  pBuff = reinterpret_cast<DWORD*>(new BYTE[dwSize]);
  
  pBuff[0] = bus;
  pBuff[1] = (fun << 5) | (dev & 0x1f);
  pBuff[2] = offset;
  pBuff[3] = cbSize;
  
  ::RtlMoveMemory(&pBuff[4], out, cbSize);
  
  result = 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_PCI_WRITE_CONFIG,
    pBuff,
    dwSize,
    NULL,
    0,
    &dwBytes,
    NULL);
  
  delete []pBuff;
  
  return result;
}

LPVOID CLibmio::MapPhyMemory(
    LPVOID addr, 
    DWORD cbSize)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return NULL;
  
  if ( !cbSize )
    return false;

  DWORD 
    dwBytes = 0,
    buff[2];
  
  LPVOID
    out = NULL;

  buff[0] = reinterpret_cast<DWORD>(addr);
  buff[1] = cbSize;
  
  if ( !::DeviceIoControl(
    m_hDriver,
    IOCTL_MAP_PHY_MEMORY,
    buff,
    sizeof(buff),
    &out,
    sizeof(out),
    &dwBytes,
    NULL) )
    return NULL;
    
  return out;
}

bool CLibmio::UnmapPhyMemory(
    LPVOID addr)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;
  
  if ( !addr )
    return false;

  DWORD 
    dwBytes = 0;
  
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_UNMAP_PHY_MEMORY,
    &addr,
    sizeof(LPVOID),
    NULL,
    0,
    &dwBytes,
    NULL);
}

bool CLibmio::ReadPhyMemory(
    LPVOID addr, 
    LPVOID buffer, 
    DWORD cbSize)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;
  
  if ( !buffer )
    return false;
  
  if ( !cbSize )
    return false;

  DWORD 
    dwBytes = 0,
    buff[2];

  buff[0] = reinterpret_cast<DWORD>(addr);
  buff[1] = cbSize;
  
  return 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_READ_PHY_MEMORY,
    buff,
    sizeof(buff),
    buffer,
    cbSize,
    &dwBytes,
    NULL);
}

bool CLibmio::WritePhyMemory(
    LPVOID addr, 
    LPVOID buffer, 
    DWORD cbSize)
{
  if ( m_hDriver == INVALID_HANDLE_VALUE )
    return false;
  
  if ( !buffer )
    return false;
  
  if ( !cbSize )
    return false;
  
  bool 
    result = false;

  DWORD 
    dwBytes = 0,
    dwSize = cbSize + 2 * sizeof(DWORD),
    * pBuff;

  pBuff = reinterpret_cast<DWORD*>(new BYTE[dwSize]);
  pBuff[0] = reinterpret_cast<DWORD>(addr);
  pBuff[1] = cbSize;
  
  ::RtlMoveMemory(&pBuff[2], buffer, cbSize);
  
  result = 0 != ::DeviceIoControl(
    m_hDriver,
    IOCTL_WRITE_PHY_MEMORY,
    pBuff,
    dwSize,
    NULL,
    0,
    &dwBytes,
    NULL);
  
  delete []pBuff;
  
  return result;
}


libmio.h中的部分内容:

  1. class CLibmioException;  
  2.   
  3. class CLibmio  
  4. {  
  5. public:  
  6.   CLibmio(voidthrow (CLibmioException);  
  7.   virtual ~CLibmio(void);  
  8.     
  9.   bool GetVersion(DWORD &);  
  10.   bool GetVersionString(char *, int);  
  11.     
  12.   bool Rdmsr(DWORD, ULARGE_INTEGER&);  
  13.   bool Wrmsr(DWORD, ULARGE_INTEGER);  
  14.     
  15.   bool Rdpmc(DWORD, ULARGE_INTEGER&);  
  16.     
  17.   bool Inp(WORDBYTE&);  
  18.   bool Inp(WORDWORD&);  
  19.   bool Inp(WORDDWORD&);  
  20.     
  21.   bool Outp(WORDBYTE);  
  22.   bool Outp(WORDWORD);  
  23.   bool Outp(WORDDWORD);  
  24.     
  25.   bool ReadPort(WORDBYTE&);  
  26.   bool ReadPort(WORDWORD&);  
  27.   bool ReadPort(WORDDWORD&);  
  28.     
  29.   bool WritePort(WORDBYTE);  
  30.   bool WritePort(WORDWORD);  
  31.   bool WritePort(WORDDWORD);  
  32.   
  33.   bool PciReadConfig(DWORDDWORDLPVOIDDWORD);  
  34.   bool PciReadConfig(WORDWORDWORDDWORDLPVOIDDWORD);  
  35.   
  36.   bool PciWriteConfig(DWORDDWORDLPVOIDDWORD);  
  37.   bool PciWriteConfig(WORDWORDWORDDWORDLPVOIDDWORD);  
  38.     
  39.   LPVOID MapPhyMemory(LPVOIDDWORD);  
  40.   bool UnmapPhyMemory(LPVOID);  
  41.     
  42.   bool ReadPhyMemory(LPVOIDLPVOIDDWORD);  
  43.   bool WritePhyMemory(LPVOIDLPVOIDDWORD);  
  44.     
  45. private:  
  46.   bool StartDriver(void);  
  47.   bool StopDriver(void);  
  48.   bool InstalDriver(void);  
  49.   bool UninstallDriver(void);  
  50.     
  51.   bool CanUninstall(void);  
  52.   
  53.   bool OpenDriver(void);  
  54.   void CloseDriver(void);  
  55.     
  56. protected:  
  57.   HANDLE m_hDriver;  
  58.   char szDriverPath[MAX_PATH];  
  59.   
  60. };  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值