随笔 - 74, 文章 - 0, 评论 - 26, 引用 - 0
数据加载中……

Write HBITMAP Object in to BMP File 转

void WriteBMPFile(HBITMAP bitmap, LPTSTR filename, HDC hDC)
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;

// create the bitmapinfo header information

if (!GetObject( (bitmap, sizeof(BITMAP), (LPSTR)&bmp)){
AfxMessageBox("Could not retrieve bitmap info");
return;
}

// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;

// Allocate memory for the BITMAPINFO structure.
if (cClrBits != 24)
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,
sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<< cClrBits));
else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));

// Initialize the fields in the BITMAPINFO structure.

pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);

// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;

// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8 * pbmi->bmiHeader.biHeight * cClrBits;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;

// now open file and save the data
pbih = (PBITMAPINFOHEADER) pbmi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

if (!lpBits) {
AfxMessageBox("writeBMP::Could not allocate memory");
return;
}

// Retrieve the color table (RGBQUAD array) and the bits
if (!GetDIBits(hDC, HBITMAP(bitmap), 0, (WORD) pbih->biHeight, lpBits, pbmi,
DIB_RGB_COLORS)) {
AfxMessageBox("writeBMP::GetDIB error");
return;
}

// Create the .BMP file.
hf = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, (DWORD) 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
if (hf == INVALID_HANDLE_VALUE){
AfxMessageBox("Could not create file for writing");
return;
}
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;

// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof (RGBQUAD);

// Copy the BITMAPFILEHEADER into the .BMP file.
if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
(LPDWORD) &dwTmp, NULL)) {
AfxMessageBox("Could not write in to file");
return;
}

// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
+ pbih->biClrUsed * sizeof (RGBQUAD),
(LPDWORD) &dwTmp, ( NULL))){
AfxMessageBox("Could not write in to file");
return;
}


// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL)){
AfxMessageBox("Could not write in to file");
return;
}

// Close the .BMP file.
if (!CloseHandle(hf)){
AfxMessageBox("Could not close file");
return;
}

// Free memory.
GlobalFree((HGLOBAL)lpBits);
}

posted @ 2009-02-16 11:32 井泉 阅读(1190) | 评论 (2)编辑 收藏

用VS2008 Feature Pack 修改您现有的Visual C++的程序界面 转

上次给大家介绍了Visual C++ 2008 的Feature Pack的界面库新特性。今天给大家介绍一下,怎样用Feature Pack把您现有的Visual C++ 程序界面修改得漂亮些。

      所需的修改环境:
      Visual C++ 2008  (Team Suite版Express版都可以,但必须是英文版,否则Feature Pack不支持)
      正确的安装了Visual C++ 2008 Feature Pack beta
      您要修改的Visual C++ 的程序源代码工程

      具备了以上三点就可以开始进行修改操作了。但是有一点提请注意,那就是您程序中是否用到了MS C++ 9.0 编译器不再支持的语法特性?如果有,那很不幸,我个人不推荐您升级您的程序界面,毕竟程序运行的稳定性才是最重要的。为了漂亮的界面修改已经测试过并稳定 运行的代码,可不是一个明智的选择。

      下面我就用一个Visual C++的入门Demo Scribble 来修改。这个Scribble您可以在MSDN网站上下载到,但请您注意,我给出的这个下载工程是VS2005 for x64的。下载后,您需要进行以下改动:
       1) 将Scribble工程属性中,C/C++编译器的Treat Warnings as error 关闭,否则您的工程将会因为一个Warning没有解决,导致整个程序编译失败。如图:
       

      2) 编译时,将Target 改为Win32;

      罗嗦了这么多,开始修改吧!

      第一步: 请确定CScribbleApp::InitialInstance() 方法中已经调用了AfxOleInit();
      第二步: 在stdafx.h文件中加入 #include"afxcontrolbars.h" ,这头文件包含了Feature Pack新增的界面类声明;
      第三步:修改CScribbleApp类继承的父类,由CWinApp改为CWinAppEx;这个CWinAppEx类比CWinApp添 加了很多的功能,说个简单的,CWinAppEx提供了一个SetRegistryBase方法,这个方法可以用来设定当前App所使用的注册表的根。
      第四步:修改主框架类,将CMainFrame的父类由CMDIFrameWnd改为CMDIFrameWndEx;这个修改设计到类声明、 IMPLEMENT_DYNAMIC宏、MESSAGE_MAP宏、OnCreate函数等调用到静态方法的地方、以及其它等等。最好是直接用 Replace all文本替换掉;
      第五步:将CMDIChildWnd类替换为CMDIChildWndEx,主框架换了,子窗体也要换;
      第六步:替换CTooBar为CMFCTooBar,替换CStatusBar为CMFCStatusBar;就是修改一下m_wndStatusBar和m_wndToolBar两个变量的声明处;
      第七步:替换CMainFrame::OnCreate()函数中m_wndToolBar 和m_wndStatusBar停靠的相关代码;将Set/Get BarStyle改为Set/Get PaneStyle 也是文本替换一下,很简单。将DockControlBar(&m_wndStatusBar);改为 DockPane(&m_wndStatusBar);
      完成以上七步,基本改造就算完成了。但是如果您现在编译您的程序,您会发现Scribble界面基本上没有任何改变。下面的才是更重要的,我们要添加RibbonBar了:

      第一步:在CMainFrame类中声明一个CMFCRibbonBar类型的变量m_wndRibbonBar。这个变量就代表Office2007界面里面那个替代了菜单的东东;
      第二步:在CMainFrame类中声明一个CMFCRibbonApplicationButton的变量m_MainButton。这个变量代表了Office2007界面左上角那个Home按钮;
      第三步:在CMainFrame::OnCreate函数中添加代码。首先是创建RibbonBar对象,老规矩:
      if(!m_wndRibbonBar.Create(this))
      {
          return -1;
      }
      第四步:设定m_MainButton对象:
      m_MainButton.SetImage(……);   //设定图标
      m_MainButton.SetToolTipText(……); //设定提示文本
      m_MainButton.SetText(……);   //设定按钮文本
      第五步:在CMainFrame::OnCreate函数中添加代码,通过RibbonBar对象添加一个Category:
      CMFCRibbonMainPanel *pMainPanel = m_wndRibbonBar.AddMainCategory(_T("File"));
      这实际上就类似于创建了一个名为File的主菜单项;
      第六步:给这个Panel添加按钮(其实就是子菜单项):
      pMainPanel->Add(new CMFCRibbonButton(ID_FILE_OPEN,_T("打开")));
      ……
      第七步:
      在CMainFrame::OnCreate()函数的最后部分,添加代码设定当前界面的风格:
      CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerOffice2007));
      CMFCVisualManagerOffice2007::SetStyle(CMFCVisualManagerOffice2007::Office2007Luna_Blue);
      代码的第一行用来设定可视化管理器为Office2007类型,可供选择的还有OfficXP、Office2003、VS2005 三种,换句话说,我们可以将我们的程序界面修改为Office2007、Office2003、OfficeXP、VS2005四种风格;
      代码的第二行用来设定Office2007界面的色调;
      完成以上步骤后,就可以编译运行啦,看看界面是不是改变了呢?
      修改前:
     

      修改后:
      

      我没有找到比较好看的图标,也没有把菜单都实现出来,仅仅是作为一个演示。相信在美工的帮助下,我们的MFC程序界面一定会漂亮起来的。

      说到最后,我要提醒大家一下,发布程序前,已经要静态链接MFC的库。在现在VC8.0 的RTM尚不普及的情况下,就别指望您的用户安装部署了支持Feature Packe的FTM库了。粗粗看了一下,一个用向导生成的支持Feature Pack的Application(是的,安装了Feature Pack在用AppWizard生新程序的时候,就可以指定Feature Pack支持了),什么代码都不加,静态编译一般在6M-8M之间(还算可以接受的说)。
      
     

posted @ 2009-01-09 13:51 井泉 阅读(2508) | 评论 (3)编辑 收藏

.net制作的wap网站在手机中的测试 转

访问asp站点的时候会根据访问的设备,输出不同的内容,如果用IE访问就输出的是html,手机访问,输出就是WML。是什么让他这么智能化呢?关键之处就在配置文件的<browserCaps>节!

在webconfig中加上这个,他可以强制输出wml,还有其他的移动设置属性都在这。

<browserCaps>
            
<result type="System.Web.Mobile.MobileCapabilities, System.Web.Mobile, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
            
<use var="HTTP_USER_AGENT"/>

            browser=Unknown
            version=0.0
            majorversion=0
            minorversion=0
            frames=false
            tables=false
            cookies=false
            backgroundsounds=false
            vbscript=false
            javascript=false
            javaapplets=false
            activexcontrols=false
            win16=false
            win32=false
            beta=false
            ak=false
            sk=false
            aol=false
            crawler=false
            cdf=false
            gold=false
            authenticodeupdate=false
            tagwriter=System.Web.UI.Html32TextWriter
            ecmascriptversion=0.0
            msdomversion=0.0
            w3cdomversion=0.0
            platform=Unknown
            css1=false
            css2=false
            xml=false

            mobileDeviceManufacturer = "Unknown"
            mobileDeviceModel = "Unknown"

            gatewayVersion = "None"
            gatewayMajorVersion = "0"
            gatewayMinorVersion = "0"

           preferredRenderingType = "wml11"
           preferredRenderingMime = "text/vnd.wap.wml"
           preferredImageMime = "image/vnd.wap.wbmp"

            defaultScreenCharactersWidth = "12"
            defaultScreenCharactersHeight = "6"
            defaultScreenPixelsWidth = "96"
            defaultScreenPixelsHeight = "72"
            defaultCharacterWidth = "8"
            defaultCharacterHeight = "12"
            screenBitDepth = "1"
            isColor = "false"
            inputType = "telephoneKeypad"

            numberOfSoftkeys = "0"
            maximumSoftkeyLabelLength = "5"

            canInitiateVoiceCall = "false"

            canSendMail = "true"
            hasBackButton = "true"
            rendersWmlDoAcceptsInline = "true"
            rendersWmlSelectsAsMenuCards = "true"
            rendersBreaksAfterWmlAnchor = "false"
            rendersBreaksAfterWmlInput = "false"
            rendersBreakBeforeWmlSelectAndInput = "true"
            requiresAttributeColonSubstitution = "true"
            requiresPhoneNumbersAsPlainText = "false"
            requiresUrlEncodedPostfieldValues = "false"
            requiredMetaTagNameValue = ""
            rendersBreaksAfterHtmlLists = "true"
            requiresUniqueHtmlCheckboxNames = "true"
            requiresUniqueHtmlInputNames = "true"
            requiresUniqueFilePathSuffix = "true"
            supportsCss = "false"
            hidesRightAlignedMultiselectScrollbars = "false"
            canRenderAfterInputOrSelectElement = "true"
            canRenderInputAndSelectElementsTogether = "true"
            canRenderOneventAndPrevElementsTogether = "true"
            canCombineFormsInDeck = "true"
            canRenderMixedSelects = "true"
            canRenderPostBackCards = "true"
            canRenderSetvarZeroWithMultiSelectionList = "true"
            supportsImageSubmit = "true"
            supportsSelectMultiple = "true"
            requiresHtmlAdaptiveErrorReporting = "false"
            requiresContentTypeMetaTag = "false"
            requiresDBCSCharacter = "false"
            requiresOutputOptimization = "false"
            supportsAccesskeyAttribute = "false"
            supportsInputIStyle = "false"
            supportsInputMode = "false"
            supportsIModeSymbols = "false"
            supportsJPhoneSymbols = "false"
            supportsJPhoneMultiMediaAttributes = "false"
            maximumRenderedPageSize = "2000"
            requiresSpecialViewStateEncoding = "false"
            requiresNoBreakInFormatting = "false"
            requiresLeadingPageBreak = "false"
            supportsQueryStringInFormAction = "true"
            supportsCacheControlMetaTag = "true"
            supportsUncheck = "true"
            canRenderEmptySelects = "true"
            supportsRedirectWithCookie = "true"
            supportsEmptyStringInCookieValue = "true"
            cachesAllResponsesWithExpires = "false"
            requiresNoSoftkeyLabels = "false"
            defaultSubmitButtonLimit = "1"
            
            supportsBold = "false"
            supportsItalic = "false"
            supportsFontSize = "false"
            supportsFontName = "false"
            supportsFontColor = "true"
            supportsBodyColor = "true"
            supportsDivAlign = "true"
            supportsDivNoWrap = "false"
            supportsCharacterEntityEncoding = "true"

            isMobileDevice="false"
</browserCaps> 

另外通过设置
         Page.Response.Expires = -1;
   Response.CacheControl 
= "Public";
可以取消移动设备缓存,并通过RedirectToMobilePage函数进行重定向。

posted @ 2008-12-31 10:46 井泉 阅读(508) | 评论 (1)编辑 收藏

WINCE Driver and BSP Develop Blog 转

开发优秀的驱动程序

作为驱动开发工程师,我们需要在每一行代码上下功夫,因为驱动程序的效率直接影响着系统的性能.而新手往往不会注意到这些细节。以为功能实现以后就万事大吉了,其实不然,好的驱动程序不只是能实现预期的功能。它同样需要高的效率与规范的风格。用户花钱买我们系统是给他/她做事的,而不是给我们做测试的,所以我们要尽可能提高效率。同时好的代码风格能大大降低我们自己的维护成本。

高效率看似容易,但要注意到每个细节还是挺难的,我们可以从以下几点去注意这个问题:

1, 不要使用无关的代码,这点容易理解,尤其是调试代码,RELEASE时一定要去除这些代码。

2, 去掉多余的函数调用,尽可能的保存一些数据。即使是最快的函数,调用它时也会引发压栈与出栈,所以要尽量少做函数调用。当然如果一个函数返回的数据比较大,保存那些数据将占用比较多的内在空间,保存返回值就得不偿失了。比如,看到有的人每次在使用一个地址时就调用MmmapIoSpace将这个地址映射到程序地址空间,用完以后又立即Unmap这个地址,下次使用时又做MAP,这就是一种及不好的方法,每次需要多调用两个系统函数。

3.如果可行,不要在循环中使用条件判断,尤其在一个次数很多的循环中更应该如此。

比如:

For( i=0; i<1000;>

If( m==1) ..

Else if (m==2 )….

Else …..

}

这种代码,我们可以把If 写在for 之外,即,每一种不同的条件写一个循环体。

If( m==1) for ...

Else if (m==2 ) for ….

Else for …..

标签:

2007-03-10

开发DMA驱动

使用DMA的好处就是它不需要CPU的干预而直接服务外设,这样CPU就可以去处理别的事务,从而提高系统的效率,对于慢速设备,如UART,其作用只是降低CPU的使用率,但对于高速设备,如硬盘,它不只是降低CPU的使用率,而且能大大提高硬件设备的吞吐量。因为对于这种设备,CPU直接供应数据的速度太低。

CPU只能一个总线周期最多存取一次总线,而且对于ARM,它不能把内存中A地址的值直接搬到B地址。它只能先把A地址的值搬到一个寄存器,然后再从这个寄存器搬到B地址。也就是说,对于ARM,要花费两个总线周期才能将A地址的值送到B地址。而DMA就不同了,一般系统中的DMA都有突发(Burst)传输的能力,在这种模式下,DMA能一次传输几个甚至几十个字节的数据,所以使用DMA能使设备的吞吐能力大为增强。

使用DMA时我们必须要注意如下事实:

1. DMA使用物理地址,程序是使用虚拟地址的,所以配置DMA时必须将虚拟地址转化成物理地址。

2. 因为程序使用虚拟地址,而且一般使用CACHED地址,所以虚拟地址中的内容与其物理地址上的内容不一定一致辞,所以在启动DMA传输之前一定要将该地址的CACHE刷新,即写入内存。

3. OS并不能保证每次分配到的内在空间在物理上是连续的。尤其是在系统使用过一段时间而又分配了一块比较大的内存时。

所以每次都需要判断地址是不是连续的,如果不连续就需要把这段内存分成几段让DMA完成传输。

标签:

2007-03-03

WINCE下USBFN驱动程序的一些概念

USBFN,即USB客户端驱动,用来将一个WINCE设备模拟成一定的USB设备,让主机端(如PC)访问。目前WINCE提供的USB客户端有存储设备,串口设备,及RNDIS网络接口设备。

存储设备用来将WINCE设备上的存储空间,例如FLASH,当作一块存储介质给主机访问,即将WINCE设备模拟成一个U盘。

串口设备将设备与主机的USB连线模拟成串口,WINCE和主机端都认为它们之前连接上了一根串口线,它们之间可以做串口通信,典型的应用是用来实现WINCEPC机的同步连接。

RNDIS设备使两端认为它们之间建立了网络连接,通过注册表设置可以让主机通过WINCE设备上网或者使WINCE设备通过主机上网。

WINCE已经提供了以上三种设备的驱动程序,在同一时刻只能使用一个设备。而我们需要做的只是提供USBFN总线控制器的驱动程序。USBFN系统各个模块的关系如下:

USBFN总路线控制器作为一个总线驱动程序,被设备管理器加载,根据注册表设置加载相应的客户驱动程序,即存储设备,串口设备或者RNDIS设备。客户驱动程序即启动USBFN,引发主机配置设备,配置完成以后即可开始工作。

USBFN总路线控制器驱动的MDD部分WINCE本身已经提供,PDD只需初始化硬件设备,提供传输即可。MDD在初始化时调用UfnPdd_Init函数得到PDD层的函数表,之后会根据需要调用各个函数。PDD还需要提供IST,用以处理各个中断。需要注意的是USBFN有一个与其它设备不同之处,它的注册表需要这样一个设置:

"BusIoctl"=dword:2a0048,用以让系统加载完设备之后调用值为0x2a0048IOCTL代码去完成初始化,其定义为IOCTL_BUS_POSTINIT

标签:

2007-02-28

SOURCES文件详解

SOURCES文件是WINCE底层开发中最重要的文件之一,主要的配置项如下:

TARGETNAME,定义模块名称.
TARGETTYPE,
模块的种类,可以是DYNLINK, LIBRARY,EXE.
如果TARGETTYPEDLL,则可以定义DLLENTRY,Dll入口定义成别的不是DLLMain的函数,如果DLL的入口是DllMain,则不需要别的定义。
如果TARGETTYPEEXE,则可以定义EXEENTRY,用于指定EXE的入口函数.

如果TARGETTYPELIBRARY,则不需要定义入口函数。


INCLUDES
,如果一个模块需要使用非标准路径下的头文件时,需要定义INCLUDES,用于包含更多的头文件路径,用法如下:

INCLUDES=$(INCLUDES);\new directory\...,
注意定义新的INCLUDES,需要包含INCLUDES原来的值,否则就需要包含所有可能的目录。

TARGETLIBS,SOURCELIBS
用于定义该模块需要链接哪些库文件.


TARGETLIBS
,如果一个库以DLL的形式提供给调用者,就需要用TARGETLIBS,它只链接一个函数地址,系统执行时会将被链接的库加载。比如coredll.lib就是这样的库文件。即动态链接。

SOURCELIBS,将库中的函数实体链接进来。即静态链接,用到的函数会在我们的文件中形成一份拷贝。


注意,内核这个执行文件是没有TARGETLIBS的,GIISR.DLL也不能有TARGETLIBS


WINCECOD,
如果将其定义为1,则编译器会为每一个文件生成.cod文件,它是一个汇编文件,调试时查看汇编代码也是一种很好的办法。

SOURCES,
定义该模块需要哪些源文件.

标签:

2007-02-17

多个设备共享同一个硬件中断

硬件中断线总是有限的,我们可能需要在已有的系统上做一些扩展,比如将串口扩展成好几个,有些硬件本身就设计成多个设备共享一条中断线,比如我的系统中两个串口就共享同一个CPU中断,任何一个串口发生中断以后都会触发CPU的同一条中断线,需要判断别的寄存器来确定是哪个串口发生了什么中断。

我们可以在OAL中分析各个中断源,然后返回不同的SYSINTR值,但这种做法扩展性不好。例如,OAL中设值某个中断源最多会产生三个SYSINTR,但以后扩展成了四个设备,有一个设备就无法正常工作了。

WINCE引入了可装载中断处理例程的概念。即在需要与别的设备共享中断的驱动程序中加载一个ISR,一般使用WINCE提供的GIISR即成满足需求。将其安装到内核。OAL中发生中断时调用NKCallIntChain来得到SYSINTR,这个函数会引起系统逐个调用在该IRQ上加载的所有可装载的ISR,当某个ISR认为这个中断是由它引发的时就返回其SYSINTR,否则就返回SYSINTR_CHAIN系统就会接着调用其它的ISR,甚至所有的ISR都被调用或者有一个ISR返回了正确的SYSINTR

驱动程序中的调用办法如下(CE帮助文档):

if (InstallIsr) {

// Install ISR handler

g_IsrHandle = LoadIntChainHandler(IsrDll, IsrHandler, (BYTE)Irq);

if (!g_IsrHandle) {

DEBUGMSG(ZONE_ERROR, (L"WAVEDEV: Couldn't install ISR handler\r\n"));

} else {

GIISR_INFO Info;

PVOID PhysAddr;

DWORD inIoSpace = 1; // io space

PHYSICAL_ADDRESS PortAddress = {ulIoBase, 0};

if (!TransBusAddrToStatic(PCIBus, 0, PortAddress, ulIoLen, &inIoSpace, &PhysAddr)) {

DEBUGMSG(ZONE_ERROR, (L"WAVEDEV: Failed TransBusAddrToStatic\r\n"));

return FALSE;

}

DEBUGMSG(ZONE_PDD, (L"WAVEDEV: Installed ISR handler, Dll = '%s', Handler = '%s', Irq = %d, PhysAddr = 0x%x\r\n", IsrDll, IsrHandler, Irq, PhysAddr));

// Set up ISR handler

Info.SysIntr = ulSysIntr;

Info.CheckPort = TRUE;

Info.PortIsIO = TRUE;

Info.UseMaskReg = FALSE;

Info.PortAddr = (DWORD)PhysAddr + ES1371_dSTATUS_OFF;

Info.PortSize = sizeof(DWORD);

Info.Mask = ES1371_INTSTAT_PENDING;

if (!KernelLibIoControl(g_IsrHandle, IOCTL_GIISR_INFO, &Info, sizeof(Info), NULL, 0, NULL)) {

DEBUGMSG(ZONE_ERROR, (L"WAVEDEV: KernelLibIoControl call failed.\r\n"));

}

}

}

这里需要注意一下,因为ISR在内核态运行,Info.PortAddr必须是系统最原始的虚拟地址,即没有用VirtualCopy映射过的,从OEMAddressTable中计算出来的虚拟地址。在这个例子中用TransBusAddrToStatic函数可以直接把物理地址转换成这种地址。而MmMapIoSpace得到是在当前程序空间中的地址,不能使用。而且GIIR要被加载到内核空间,所以在加入到OS包中时需要加上K标志,否则LoadIntChainHandler函数会失败。

posted @ 2008-12-24 15:12 井泉 阅读(732) | 评论 (0)编辑 收藏

如何使用CeLog调试Windows mobile设备驱动 转

一.     介绍
本文主要介绍在不进入KITL模式, 使用CeLog工具来调试Windows mobile设备的方法,该方法可以抓到使用DEBUGMSG打出的log信息,注意:本方法只在RETAIL版本上实验通过。
二.     工具
Platform Builder
 Readlog.exe
CeLogStopFlush.exe
Readlog.exe可以在路径:_WINCEROOT\SDK\BIN\I386下面找到,CeLogStopFlush.exe
的源代码可以在_WINCEROOT\Public\Common\SDK\Samples\CeLog\Flush\Stopflush找到,使用build –c可以生成CeLogStopFlush.exe。
三.     步骤
1.建立一个Start Log的快捷方式,具体新建一个文件,复制下面的语句:
83#\Windows\celogflush.exe -buf 0x100000 -time 60000 -n \celog.clg -z 0x00800000 -ui 1
将其重命名为Start Log.lnk
2.检查手机的\Windows目录是否有以下两个文件CeLog.dll and CeLogFlush.exe,如果没有的话,将它们拷贝到\Windows目录(release目录可以找到)
3.拷贝Start Log.lnk和CeLogStopFlush.exe到开始菜单中,修改注册表,增加或修改下面的键值。(注意ZoneCE是16进制的)
[HKEY_LOCAL_MACHINE\System\CeLog]
    "Transport"="LocalFile"
    "FileName"="celog.clg"
    "ZoneCE"=dword:800000
4.点击Start Log开始抓log
5.点击CeLogStopFlush.exe停止抓log
6.将根目录下生成的celog.clg文件拷贝到PC机与Readlog同一级目录
7.打开DOS命令行,转到Readlog.exe目录中,输入下面的命令:
Readlog.exe celog.clg celog.log
8.使用文本工具查看celog.log中的log信息,下面是一个log的实例。

posted @ 2008-12-19 14:07 井泉 阅读(835) | 评论 (0)编辑 收藏

Linux应用程序安装与管理-转

Linux应用程序安装与管理        
        
目标:
        了解linux应用程序的组成部分
        掌握使用RPM工具管理软件包的方法
        掌握应用程序源代码包的编译安装方法
        掌握图形界面下应用程序管理工具的使用
1、Linux应用程序基础
2、RPM包管理
        包管理系统初步:
        RPM:RPM Package Manager        
        
http://www.rpm.org
        RPM包管理系统:
                $ rpm
        RPPM包的文件名称:
           bash-3.0-19.2.i386.rpm
                bash:软件名称。
                3.0-19.2:软件的版本号。
                i386:软件所运行的最低硬件平台。
                rpm:文件的扩展名,用来标识当前文件是rpm格式的软件包。
        RPM包管理功能:
            rpm命令配合不同的参数可以实现以下的rpm包的管理功能:
                查询已安装在linux系统中的RPM软件包的信息。
                查询RPM软件包安装文件的信息。
                安装RPM软件包到当前linux系统。
                从当前linux系统中卸载已安装的RPM软件包。
                从当前linux系统中升级已安装的RPM软件包。
使用rpm命令查询软件包:
        1、查询系统中安装的所有RPM包
                $ rpm  -qa                                      查询当前linux系统中已经安装的软件包。
                例:$ rpm -qa | grep -i x11 | head -3  察看系统中包含x11字符串的前3行软件包。
        2、查询软件包是否安装
                $ rpm –q rpm包名称                        察看系统中指定软件包是否安。
                例: $ rpm -q bash                            察看系统中bash软件包是否安装。
           "rpm -q"命令中指定的软件包名称需要准确的拼写,该命令不会在软件包的名称中进行局部匹配的查询。
        3、查询已安装软件包详细信息
                $ rpm –qi RPM包名称                       查询linux系统中指定名称软件包的详细信息。
                例:$ rpm -qi bash                          察看bash软件包的详细信息。
           "rpm -qi"命令的执行结果中包含较详细的信息,包括:软件名称,版本信息,包大小,描述,等。
        4、查询已安装软件包中的文件列表
                $ rpm –ql RPM包名称                       查询已安装软件包在当前系统中安装了哪些文件。
                例:$ rpm -ql bash | head -3            查看bash软件在系统中已安装文件的前3行文件列表。
                    $ rpm -ql bash | grep bin             用过滤方式察看bash中包含bin字符串的文件列表。
        5、查询系统中文件所属的软件包
                $ rpm –qf 文件名称    查询linux系统中指定文件所属的软件包。
                例:$ rpm -qf /bin/bash                   察看bash文件所属的软件包。
                    bash-3.0-19.2      显示结果。
        6、查询RPM安装包文件中的信息
                $ rpm –qpi RPM包文件名                  察看RPM包未安装前的详细信息。
                $ rpm –qpl RPM包文件名                  察看RPM包未安装前的文件列表。
           "rpm -qpi和rpm -qpl 这两条命令可作为在安装软件包之前对其的了解。
        7、rpm命令查询实例
                $ which mount                               获得mount命令的可执行文件路径。
                $ rpm –qf  /bin/mount                     查询/bin/mount所属的软件包。
                $ rpm –qi util-linux                           查询/bin/mount所属软件包的详细信息。
                $ rpm –qf util-linux | grep mount       查询/bin/mount所属软件包中包括mount相关所有文件。
使用rpm命令安装软件包
        1、rpm软件包地基本安装
                $ rpm –i  rpm安装包文件名                安装该软件包中的文件到当前系统,安装过程不提示任何信息。
        2、在安装软件包的同时显示详细信息
                $ rpm –ivh rpm安装包文件                 安装该软件包中的文件到当前系统,安装过程会以百分比的形式
                                                                    显示安装的进度和一些其他信息。
        3、RPM软件包安装的依赖关系
                强制安装:$ rpm  --force  –i  rpm包文件名
            注:要先满足软件包的依赖关系后再进行软件包的安装,使用强制安装命令安装不能保证软件安装到系统后一定能
                  正常运行,因此建议慎重使用。
使用rpm命令卸载软件包:
        1、RPM软件包的卸载
                $ rpm  -e  软件包名称                       软件包的卸载,在卸载时不显示任何信息。
            注:RPM软件包的卸载同样存在依赖关系,只有在没有依赖关系存在时才能对其进行卸载。
        2、rpm软件包卸载的依赖关系
                  在使用RPM命令进行卸载时,RPM命令会分析要卸载的软件包的依赖关系,当存在依赖关系时会自动停止,并显由   
                  哪个软件造成的卸载失败。根据RPM提示的错误信息,确定先卸载的软件包,再卸载被依赖的软件包。
使用rpm命令升级软件包:
        $ rpm  - U  rpm安装包文件名
            注:"rpm -u"命令中使用的升级软件包文件最好使用RED HAT公司针对当前的linux版本官方推出的升级文件,建议不要  
                  使用第三方提供的升级包。
   
应用程序编译
        开放源代码应用程序的编译安装
        (下面以多线程下载软件"prozilla"的源代码编译安装为例来说明源代码编译安装的整个过程)
        编译应用程序前的准备工作:
        1、确认系统中已经安装了编译环境
                $ rpm  -qa  | grep gcc                       确定当前系统中安装了gcc编译器环境。
        2、下载prozilla程序的源代码安装包文件
                 略
        3、释放已下载的源代码软件包文件
                $ tar jxf prozilla-2.0.4.tar.bz2              释放以下载的源代码软件包文件到当前目录。解压后的文件
                                                                     名:prozilla-2.0.4
           扩展:tar的xzvf参数用于释放以tar.gz格式的压缩包。
        4、进入源代码目录
                $ cd prozilla-2.0.4                             进入目录。
                $ pwd                                            显示当前目录路径。
                /home/teacher/download/prozilla-2.0.4      显示结果。
           编译软件安装的路径:
                $ ./configure --prefix=/home/teacher/proz  
                在prozilla程序的配置中,使用"--prdfix"选项可以指定应用程序编译后的安装路径,如果不使用"--prefix"
                选项指定安装路径,configure程序将配置prozilla的默认安装路径为"/usr/local/bin"目录。
         5、程序编译过程
                $ make                                           使用make命令进行程序的二进制编译。
        6、程序安装过程
                $ make install
                "make install"命令将按照configuer命令的"--prefix"选项中设定的安装路径将已编译完成的应用程序安装
                 到目标目录。
        7、验证编译安装的程序
                $ ls /home/teacher/proz                    察看proz文件夹中的文件。
                bin  include lib man share
        
        
        编译前的配置
                $ ./configure  - - help                    
        编译与安装:
        1、程序编译过程
                $ make
        2、程序安装过程
                $ make install
        3、验证编译安装的程序
使用图形界面系统工具完成RPM保的安装
         略

posted @ 2008-12-16 11:34 井泉 阅读(246) | 评论 (0)编辑 收藏

Windows CE usb驱动程序

上述讲了堆理论,可能读者脑袋都已经大了,为此,我们举个简单的例子来详细说明一下驱动程序的开发过程。

例如我们有个USB Mouse设备,设备信息描述如下:
Device Descriptor:
bcdUSB: 0x0100
bDeviceClass: 0x00
bDeviceSubClass: 0x00
bDeviceProtocol: 0x00
bMaxPacketSize0: 0x08 (8)
idVendor: 0x05E3 (Genesys Logic Inc.)
idProduct: 0x0001
bcdDevice: 0x0101
iManufacturer: 0x00
iProduct: 0x01
iSerialNumber: 0x00
bNumConfigurations: 0x01

ConnectionStatus: DeviceConnected
Current Config value: 0x01
Device Bus Speed: Low
Device Address: 0x02
Open Pipes: 1

Endpoint Descriptor:
bEndpointAddress: 0x81
Transfer Type: Interrupt
wMaxPacketSize: 0x0003 (3)
bInterval: 0x0A

可以看出上述设备有一个中断PIPE,包的最大值为3。可能有人问上述的值怎么得到的,win2k 的DDK中有个usbview的例程,编译一下,将你的USB设备插到PC机的USB口中,运行usbview.exe即可看得相应的设备信息。

有了这些基本信息,就可以编写USB设备了,首先声明一下,下面的代码取自微软的USB鼠标样本程序,版权归微软所有,此处仅仅借用来描述一下USB鼠标驱动的开发过程,读者如需要引用此代码,需要得到微软的同意。

首先,必须输出USBD要求调用的三个函数,首先到设备插入到USB端口时,USBD会调用USBDeviceAttach()函数,相应的代码如下:
extern "C" BOOL
USBDeviceAttach(
USB_HANDLE hDevice, // USB设备句柄
LPCUSB_FUNCS lpUsbFuncs, // USBDI的函数集合
LPCUSB_INTERFACE lpInterface, // 设备接口描述信息
LPCWSTR szUniqueDriverId, // 设备ID描述字符串。
LPBOOL fAcceptControl, // 返回TRUE,标识我们可以控制此设备, 反之表示不能控制
DWORD dwUnused)
{
*fAcceptControl = FALSE;
// 我们的鼠标设备有特定的描述信息,要检测是否是我们的设备。
if (lpInterface == NULL)
return FALSE;
// 打印相关的USB设备接口描述信息。
DEBUGMSG(ZONE_INIT,(TEXT("USBMouse: DeviceAttach, IF %u, #EP:%u, Class:%u, Sub:%u,Prot:%u\r\n"), lpInterface->Descriptor.bInterfaceNumber,lpInterface->Descriptor.bNumEndpoints, lpInterface->Descriptor.bInterfaceClass,lpInterface->Descriptor.bInterfaceSubClass,lpInterface->Descriptor.bInterfaceProtocol));
// 初试数据USB鼠标类,产生一个接受USB鼠标数据的线程
CMouse * pMouse = new CMouse(hDevice, lpUsbFuncs, lpInterface);
if (pMouse == NULL)
return FALSE;

if (!pMouse->Initialize())
{
delete pMouse;
return FALSE;
}

// 注册一个监控USB设备事件的回调函数,用于监控USB设备是否已经拔掉。
(*lpUsbFuncs->lpRegisterNotificationRoutine)(hDevice,
USBDeviceNotifications, pMouse);

*fAcceptControl = TRUE;
return TRUE;
}

第二个函数是 USBInstallDriver()函数,
一些基本定义如下:
const WCHAR gcszRegisterClientDriverId[] = L"RegisterClientDriverID";
const WCHAR gcszRegisterClientSettings[] = L"RegisterClientSettings";
const WCHAR gcszUnRegisterClientDriverId[] = L"UnRegisterClientDriverID";
const WCHAR gcszUnRegisterClientSettings[] = L"UnRegisterClientSettings";
const WCHAR gcszMouseDriverId[] = L"Generic_Sample_Mouse_Driver";

函数接口如下:
extern "C" BOOL
USBInstallDriver(
LPCWSTR szDriverLibFile) // @parm [IN] - Contains client driver DLL name
{
BOOL fRet = FALSE;
HINSTANCE hInst = LoadLibrary(L"USBD.DLL");

// 注册USB设备信息
if(hInst)
{
LPREGISTER_CLIENT_DRIVER_ID pRegisterId = (LPREGISTER_CLIENT_DRIVER_ID)
GetProcAddress(hInst, gcszRegisterClientDriverId);

LPREGISTER_CLIENT_SETTINGS pRegisterSettings =
(LPREGISTER_CLIENT_SETTINGS) GetProcAddress(hInst,
gcszRegisterClientSettings);

if(pRegisterId && pRegisterSettings)
{
USB_DRIVER_SETTINGS DriverSettings;

DriverSettings.dwCount = sizeof(DriverSettings);

// 设置我们的特定的信息。
DriverSettings.dwVendorId = USB_NO_INFO;
DriverSettings.dwProductId = USB_NO_INFO;
DriverSettings.dwReleaseNumber = USB_NO_INFO;

DriverSettings.dwDeviceClass = USB_NO_INFO;
DriverSettings.dwDeviceSubClass = USB_NO_INFO;
DriverSettings.dwDeviceProtocol = USB_NO_INFO;

DriverSettings.dwInterfaceClass = 0x03; // HID
DriverSettings.dwInterfaceSubClass = 0x01; // boot device
DriverSettings.dwInterfaceProtocol = 0x02; // mouse

fRet = (*pRegisterId)(gcszMouseDriverId);

if(fRet)
{
fRet = (*pRegisterSettings)(szDriverLibFile,
gcszMouseDriverId, NULL, &DriverSettings);

if(!fRet)
{
//BUGBUG unregister the Client Driver’s ID
}
}
}
else
{
RETAILMSG(1,(TEXT("!USBMouse: Error getting USBD function pointers\r\n")));
}
FreeLibrary(hInst);
}
return fRet;
}
上述代码主要用于产生USB设备驱动程序需要的注册表信息,需要注意的是:USB设备驱动程序不使用标准的注册表函数,而是使用RegisterClientDriverID()和RegisterClientSettings来注册相应的设备信息。

另外一个函数是USBUninstallDriver()函数,具体代码如下:
extern "C" BOOL
USBUnInstallDriver()
{
BOOL fRet = FALSE;
HINSTANCE hInst = LoadLibrary(L"USBD.DLL");

if(hInst)
{
LPUN_REGISTER_CLIENT_DRIVER_ID pUnRegisterId =
(LPUN_REGISTER_CLIENT_DRIVER_ID)
GetProcAddress(hInst, gcszUnRegisterClientDriverId);

LPUN_REGISTER_CLIENT_SETTINGS pUnRegisterSettings =
(LPUN_REGISTER_CLIENT_SETTINGS) GetProcAddress(hInst,
gcszUnRegisterClientSettings);

if(pUnRegisterSettings)
{
USB_DRIVER_SETTINGS DriverSettings;

DriverSettings.dwCount = sizeof(DriverSettings);
// 必须填入与注册时相同的信息。
DriverSettings.dwVendorId = USB_NO_INFO;
DriverSettings.dwProductId = USB_NO_INFO;
DriverSettings.dwReleaseNumber = USB_NO_INFO;

DriverSettings.dwDeviceClass = USB_NO_INFO;
DriverSettings.dwDeviceSubClass = USB_NO_INFO;
DriverSettings.dwDeviceProtocol = USB_NO_INFO;

DriverSettings.dwInterfaceClass = 0x03; // HID
DriverSettings.dwInterfaceSubClass = 0x01; // boot device
DriverSettings.dwInterfaceProtocol = 0x02; // mouse

fRet = (*pUnRegisterSettings)(gcszMouseDriverId, NULL,
&DriverSettings);
}

if(pUnRegisterId)
{
BOOL fRetTemp = (*pUnRegisterId)(gcszMouseDriverId);
fRet = fRet ? fRetTemp : fRet;
}
FreeLibrary(hInst);
}
return fRet;
}
此函数主要用于删除USBInstallDriver()时创建的注册表信息,同样的它使用自己的函数接口UnRegisterClientDriverID()和UnRegisterClientSettings()来做相应的处理。

另外一个需要处理的注册的监控通知函数USBDeviceNotifications():
extern "C" BOOL USBDeviceNotifications(LPVOID lpvNotifyParameter, DWORD dwCode,
LPDWORD * dwInfo1, LPDWORD * dwInfo2, LPDWORD * dwInfo3,
LPDWORD * dwInfo4)
{
CMouse * pMouse = (CMouse *)lpvNotifyParameter;

switch(dwCode)
{
case USB_CLOSE_DEVICE:
//删除相关的资源。
delete pMouse;
return TRUE;
}
return FALSE;
}


USB鼠标的类的定义如下:
class CMouse
{
public:
CMouse::CMouse(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs,
LPCUSB_INTERFACE lpInterface);
~CMouse();

BOOL Initialize();
private:
// 传输完毕调用的回调函数
static DWORD CALLBACK MouseTransferCompleteStub(LPVOID lpvNotifyParameter);
// 中断处理函数
static ULONG CALLBACK CMouse::MouseThreadStub(PVOID context);
DWORD MouseTransferComplete();
DWORD MouseThread();

BOOL SubmitInterrupt();
BOOL HandleInterrupt();

BOOL m_fClosing;
BOOL m_fReadyForMouseEvents;

HANDLE m_hEvent;
HANDLE m_hThread;

USB_HANDLE m_hDevice;
USB_PIPE m_hInterruptPipe;
USB_TRANSFER m_hInterruptTransfer;

LPCUSB_FUNCS m_lpUsbFuncs;
LPCUSB_INTERFACE m_pInterface;

BOOL m_fPrevButton1;
BOOL m_fPrevButton2;
BOOL m_fPrevButton3;

// 数据接受缓冲区。
BYTE m_pbDataBuffer[8];
};

具体实现如下:

// 构造函数,初始化时调用
CMouse::CMouse(USB_HANDLE hDevice, LPCUSB_FUNCS lpUsbFuncs,
LPCUSB_INTERFACE lpInterface)
{
m_fClosing = FALSE;
m_fReadyForMouseEvents = FALSE;
m_hEvent = NULL;
m_hThread = NULL;

m_hDevice = hDevice;
m_hInterruptPipe = NULL;
m_hInterruptTransfer = NULL;

m_lpUsbFuncs = lpUsbFuncs;
m_pInterface = lpInterface;

m_fPrevButton1 = FALSE;
m_fPrevButton2 = FALSE;
m_fPrevButton3 = FALSE;

memset(m_pbDataBuffer, 0, sizeof(m_pbDataBuffer));
}

// 析构函数,用于清除申请的资源。
CMouse::~CMouse()
{
// 通知系统去关闭相关的函数接口。
m_fClosing = TRUE;

// Wake up the connection thread again and give it time to die.
if (m_hEvent != NULL)
{
// 通知关闭数据接受线程。
SetEvent(m_hEvent);

if (m_hThread != NULL)
{
DWORD dwWaitReturn;

dwWaitReturn = WaitForSingleObject(m_hThread, 1000);
if (dwWaitReturn != WAIT_OBJECT_0)
{
TerminateThread(m_hThread, DWORD(-1));
}
CloseHandle(m_hThread);
m_hThread = NULL;
}
CloseHandle(m_hEvent);
m_hEvent = NULL;
}

if(m_hInterruptTransfer)
(*m_lpUsbFuncs->lpCloseTransfer)(m_hInterruptTransfer);

if(m_hInterruptPipe)
(*m_lpUsbFuncs->lpClosePipe)(m_hInterruptPipe);
}


// 初始化USB鼠标驱动程序
BOOL CMouse::Initialize()
{
LPCUSB_DEVICE lpDeviceInfo = (*m_lpUsbFuncs->lpGetDeviceInfo)(m_hDevice);

// 检测配置:USB鼠标应该只有一个中断管道
if ((m_pInterface->lpEndpoints[0].Descriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) != USB_ENDPOINT_TYPE_INTERRUPT)
{
RETAILMSG(1,(TEXT("!USBMouse: EP 0 wrong type (%u)!\r\n"),
m_pInterface->lpEndpoints[0].Descriptor.bmAttributes));
return FALSE;
}
DEBUGMSG(ZONE_INIT,(TEXT("USBMouse: EP 0:MaxPacket: %u, Interval: %u\r\n"),
m_pInterface->lpEndpoints[0].Descriptor.wMaxPacketSize,
m_pInterface->lpEndpoints[0].Descriptor.bInterval));

m_hInterruptPipe = (*m_lpUsbFuncs->lpOpenPipe)(m_hDevice,
&m_pInterface->lpEndpoints[0].Descriptor);

if (m_hInterruptPipe == NULL) {
RETAILMSG(1,(TEXT("Mouse: Error opening interrupt pipe\r\n")));
return (FALSE);
}
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (m_hEvent == NULL)
{
RETAILMSG(1,(TEXT("USBMouse: Error on CreateEvent for connect event\r\n")));
return(FALSE);
}
// 创建数据接受线程
m_hThread = CreateThread(0, 0, MouseThreadStub, this, 0, NULL);
if (m_hThread == NULL)
{
RETAILMSG(1,(TEXT("USBMouse: Error on CreateThread\r\n")));
return(FALSE);
}

return(TRUE);
}

// 从USB鼠标设备中读出数据,产生相应的鼠标事件。
BOOL CMouse::SubmitInterrupt()
{
if(m_hInterruptTransfer)
(*m_lpUsbFuncs->lpCloseTransfer)(m_hInterruptTransfer);

// 从USB鼠标PIPE中读数据
m_hInterruptTransfer = (*m_lpUsbFuncs->lpIssueInterruptTransfer)
(m_hInterruptPipe, MouseTransferCompleteStub, this,
USB_IN_TRANSFER | USB_SHORT_TRANSFER_OK, // 表示读数据
min(m_pInterface->lpEndpoints[0].Descriptor.wMaxPacketSize,
sizeof(m_pbDataBuffer)),
m_pbDataBuffer,
NULL);

if (m_hInterruptTransfer == NULL)
{
DEBUGMSG(ZONE_ERROR,(L "!USBMouse: Error in IssueInterruptTransfer\r\n"));
return FALSE;
}
else
{
DEBUGMSG(ZONE_TRANSFER,(L"USBMouse::SubmitInterrupt,Transfer:0x%X\r\n",
m_hInterruptTransfer));
}
return TRUE;
}

// 处理鼠标中断传输的数据
BOOL CMouse::HandleInterrupt()
{
DWORD dwError;
DWORD dwBytes;

DWORD dwFlags = 0;
INT dx = (signed char)m_pbDataBuffer[1];
INT dy = (signed char)m_pbDataBuffer[2];

BOOL fButton1 = m_pbDataBuffer[0] & 0x01 ? TRUE : FALSE;
BOOL fButton2 = m_pbDataBuffer[0] & 0x02 ? TRUE : FALSE;
BOOL fButton3 = m_pbDataBuffer[0] & 0x04 ? TRUE : FALSE;

if (!(*m_lpUsbFuncs->lpGetTransferStatus)(m_hInterruptTransfer, &dwBytes,&dwError))
{
DEBUGMSG(ZONE_ERROR,(TEXT("!USBMouse: Error in GetTransferStatus(0x%X)\r\n"),
m_hInterruptTransfer));
return FALSE;
}
else
{
DEBUGMSG(ZONE_TRANSFER,(TEXT("USBMouse::HandleInterrupt, hTransfer 0x%X complete (%u bytes, Error:%X)\r\n"),
m_hInterruptTransfer,dwBytes,dwError));
}

if (!SubmitInterrupt())
return FALSE;

if(dwError != USB_NO_ERROR)
{
DEBUGMSG(ZONE_ERROR,(TEXT("!USBMouse: Error 0x%X in interrupt transfer\r\n"),dwError));
return TRUE;
}

if(dwBytes < 3)
{
DEBUGMSG(ZONE_ERROR,(TEXT("!USBMouse: Invalid byte cnt %u from interrupt transfer\r\n"),dwBytes));
return TRUE;
}

if(dx || dy)
dwFlags |= MOUSEEVENTF_MOVE;

if(fButton1 != m_fPrevButton1)
{
if(fButton1)
dwFlags |= MOUSEEVENTF_LEFTDOWN;
else
dwFlags |= MOUSEEVENTF_LEFTUP;
}

if(fButton2 != m_fPrevButton2)
{
if(fButton2)
dwFlags |= MOUSEEVENTF_RIGHTDOWN;
else
dwFlags |= MOUSEEVENTF_RIGHTUP;
}

if(fButton3 != m_fPrevButton3)
{
if(fButton3)
dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
else
dwFlags |= MOUSEEVENTF_MIDDLEUP;
}

m_fPrevButton1 = fButton1;
m_fPrevButton2 = fButton2;
m_fPrevButton3 = fButton3;

DEBUGMSG(ZONE_EVENTS,
(TEXT("USBMouse event: dx:%d, dy:%d, dwFlags:0x%X (B1:%u, B2:%u, B3:%u)\r\n"),
dx,dy,dwFlags,fButton1,fButton2,fButton3));

// 通知系统产生鼠标事件
if (m_fReadyForMouseEvents)
mouse_event(dwFlags, dx, dy, 0, 0);
else
m_fReadyForMouseEvents = IsAPIReady(SH_WMGR);

return TRUE;
}


DWORD CALLBACK CMouse::MouseTransferCompleteStub(LPVOID lpvNotifyParameter)
{
CMouse * pMouse = (CMouse *)lpvNotifyParameter;
return(pMouse->MouseTransferComplete());
}

// 数据传输完毕回调函数
DWORD CMouse::MouseTransferComplete()
{
if (m_hEvent)
SetEvent(m_hEvent);
return 0;
}


ULONG CALLBACK CMouse::MouseThreadStub(PVOID context)
{
CMouse * pMouse = (CMouse *)context;
return(pMouse->MouseThread());
}

// USB鼠标线程
DWORD CMouse::MouseThread()
{
DEBUGMSG(ZONE_INIT,(TEXT("USBMouse: Worker thread started\r\n")));
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

if (SubmitInterrupt())
{
while (!m_fClosing)
{
WaitForSingleObject(m_hEvent, INFINITE);

if (m_fClosing)
break;

if ((*m_lpUsbFuncs->lpIsTransferComplete)(m_hInterruptTransfer))
{
if (!HandleInterrupt())
break;
}
else
{
RETAILMSG(1,(TEXT("!USBMouse: Event signalled, but transfer not complete\r\n")));
// The only time this should happen is if we get an error on the transfer
ASSERT(m_fClosing || (m_hInterruptTransfer == NULL));
break;
}
}
}
RETAILMSG(1,(TEXT("USBMouse: Worker thread exiting\r\n")));
return(0);
}

看 到了没有,其实USB的驱动程序编写就这么简单,类似的其他设备,例如打印机设备,就有Bulk OUT PIPE,需要Bulk传输,那就需要了解一下IssueBulkTransfer()的应用。当然如果是开发USB Mass Storage Disk的驱动,那就需要了解更多的协议,例如Bulk-Only Transport协议等。

微软的Windows CE.NET的Platform Build中已经带有USB Printer和USB Mass Storage Disk的驱动的源代码了,好好研究一下,你一定回受益非浅的。



参考资料:
1. 微软出版社 <<Windows Ce Device Driver Kit>>
2. <<Universal Serial Bus Specification 1.1>> 来自http:://www.usb.org

posted @ 2008-12-03 10:35 井泉 阅读(1290) | 评论 (0)编辑 收藏

Windows CE 6.0中断处理过程 by ningling

这里我们主要讨论的是CE的中断建立和中断相应的大概流程以及所涉及的代码位置。这里所讲述的,是针对ARM平台的。在CE的中断处理里面,有一部分工作是CE Kernel完成的,有一部分工作是要由OEM完成的。

Kernel代码工作

ExVector.s:中断向量定义,里面定义的是armtrap.s的函数地址

Armtrap.s:中断处理定义,最重要是里面的IRQHandler函数,而其中最重要的是CALL OEMInterruptHandler

Mdarm.c:中断向量加载

Kdriver.cNKCallIntChain函数:把IRQ转换为SysIntr,值得留意的是pIntChainTable[],是IRQ所对应的ISR处理程序的入口,其中最主要的是其成员函数pfnHandlerpfnHandler的填充,是在HookIntChain里面,这个函数是ISR在初始化的时候调用的。在这个函数里面,如果pIntChainTable为空,则返回SYSINTR_CHAIN,如果pIntChainTable[]不为空,则调用pfnHandler得到一个sysintr值,然后返回之。

 

OEM定义工作:Oalintr.cOEMInterruptHandler函数,通过查询硬件的中断寄存器,得到硬件IRQ号。对于EINT04-23的中断,通过EINTMASK寄存器,得到相对应的系统IRQ。注意,这里的IRQCE定义的IRQ,是系统硬件IRQ的扩展。然后调用NKCallIntChain看看这个IRQ是否是一个ChainInterrupt。如果函数返回SYSINTR_CHAIN或者返回一个不合法的sysintr,则通过OALIntrTranslateIrqIRQ转化为sysintr。如果是一个合法的sysintr,则返回该值。

 

单一ISRDevice,主要通过OEMInterruptHandler处理,在OEMInterruptHandler没有定义的IRQ,可以在OAL里面或者驱动的加载里面,通过HookInterrupt函数进行关联。

多个ISRDevice,通常这是总线设备的需求,因为总线设备上面通常串有几个设备。这些总线上的设备,需要有一个ISR判断究竟是哪个设备发出的中断。这个ISR,是一个DLL的程序,设备驱动必须在初始化的时候通过LoadIntChainHandler(文件名,函数名,irq)加载这个DLL程序。LoadIntChainHandler的定义在kdriver.cNKLoadIntChainHandler里面。对于大多数的总线设备,可以利用微软已经写好的giisr.dll来实现。giisr的实现代码在Public\common\oak\drivers下面。

 

对于总线设备,如果利用GIISR的话,原理如下:

总线设备驱动在初始化的时候,通过LoadIntChainHandler加载GIISR,而加载的时候,LoadIntChainHandler会调用GIISRCreateInstance创建一个实例,GIISR会返回一个index值给LoadIntChainHandler,以标示实例,LoadIntChainHandler则会返回一个Handle给驱动,驱动则根据这个Handle存取GIISR。得到这个handle之后,初始化还需要包括从reg表里面读出相关的初始化参数,对GIISR进行赋值,譬如Port AddressMask AddressSysIntr等。

 

驱动程序在初始化的时候:

1、创建一个EventCreateEvent

2、然后用InterruptInitialize函数把sysintr和这个Event相关联

3Kick-off一个ThreadIST

4、这个Thread最终是WaitForSingleObjectEventID

具体的例子,可以参阅USBFN的例子:sc2410pdd.cpp里面,UfnPdd_Start函数;

Published Sunday, June 10, 2007 7:13 PM by ningling

posted @ 2008-12-02 17:00 井泉 阅读(544) | 评论 (0)编辑 收藏

Windows CE下驱动开发基础 付林林

这是我从1月6日开始主持天极网论坛嵌入式开发版以来第一次发表文章,加上以前琐碎的文章共 计30篇。研究的越多就越感觉自己懂的太少,其实在驱动开发方面我还是个菜鸟,我是想再次抛砖引玉,让做驱动有N年经验的人奉献一点出来,让大家减少一些 研究驱动源码而又缺少注释所带来的痛苦。
  我想即使读者看过微软的关于驱动开发的培训教材和CE帮助文档中的驱动部分,头脑中仍然一片茫然。要想真正了解驱动程序必须结合一些驱动程序源码,在此我以串口驱动程序(COM16550)中初始化过程为线索简单讲一讲驱动开发的基础知识。
  Windows CE下的串口驱动程序能够处理所有I/O行为类似串口的设备,包括基于16450、16550 UART(通用异步收发芯片)的设备和一些采用DMA的设备,常见的有9针串口、红外I/O口、Modem等。在%_WINCEROOT%\Public \Common\OAK\Drivers\Serial目录下,COM_MDD2子目录包含新的串口驱动MDD层函数代码。COM16550子目录包含串 口驱动PDD层代码。SER16550子目录包含的一系列函数专用于控制与16550兼容的UART,这样PDD层的主要工作就是调用SER16550中 的函数。还有一个ISR16550子目录包含的是串口驱动程序专用的可安装ISR(中断服务例程),而很多硬件设备驱动程序采用CE默认的可安装ISR giisr.dll。一般串口设备相应的注册表设置例子及意义如下:
意义
"SysIntr"=dword:13 串口1的中断ID为十进制13
"IoBase"=dword:02F8 串口1的IO空间首地址为十六进制2F8
"IoLen"=dword:8 串口1的IO空间长度为8个字节
"DeviceArrayIndex"=dword:0 串口1的索引,是1的由来
"Order"=dword:0 串口1驱动的加载顺序
"DeviceType"=dword:0 串口1的设备类型
"DevConfig"=hex: 10,00 .... 串口1在与Modem设备通讯时的配置,如波特率、奇偶校检等
"FriendlyName"="COM1:" 串口1在拨号程序中显示的名字
"Tsp"="Unimodem.dll" 串口1 被用于与Modem设备通讯的时候要加载的TSP(TAPI Service provider)DLL
"Prefix"="COM" 串口1的流接口的前缀
"Dll"="com16550.Dll" 串口1的驱动程序DLL

  SysIntr 由CE在文件Nkintr.h中预定义,用于唯一标识中断设备。OEM可以在文件Oalintr.h中定义自己的SysIntr。常见的预定义 SysIntr有SYSINTR_NOP(中断只由ISR处理,IST不再处理),SYSINTR_RESCHED(重新调度线 程),SYSINTR_DEVICES(由CE预定义的设备中断ID的基值),SYSINTR_PROFILE、SYSINTR_TIMING、 SYSINTR_FIRMWARE等都是基于SYSINTR_DEVICES定义的。IoBase是串口1的IO地址空间的首地址,IoLen是IO空间 的大小。IO地址空间只存在于x86平台,如果在其它平台硬件寄存器必须映射到物理地址空间,那子键的名称为MemBase和MemLen。在x86平台 更多硬件的寄存器由于IO空间的局限也映射到物理地址空间。DeviceArrayIndex是设备的索引,用于区分同类型的设备。Prefix是流驱动 程序的前缀,当应用程序调用CreateFile函数传递COM1:参数时,文件系统负责与串口驱动程序通信,串口驱动程序是在CE启动时由 device.exe加载的。

  下面从MDD 层函数COM_Init开始探索串口驱动的初始化过程。COM_Init是在串口设备被检测后由设备管理器device.exe调用的,主要的作用是初始 化设备,它的唯一参数Identifier是由device.exe传递的,其类型是一个字符串指针,字符串的内容是HLM\Drivers \Active\xx,xx是一个十进制数(device.exe会跟踪系统中每个驱动程序,把加载的驱动程序记录在Active键下)。 COM_Init先分配一个HW_INDEP_INFO结构体,这个结构体是独立于串口硬件的头信息(MDD、PDD、SER16550都包含自己独特的 结构体,具体的结构体定义请参见串口驱动源码),分配之后再初始化结构体中每个成员,初始化结构体后调用 OpenDeviceKey((LPCTSTR)Identifier)打开HLM\Drivers\Active\xx\Key包含的注册表路径,在这 里路径一般为HLM\Drivers\BuiltIn\Serial,即串口的驱动程序信息在注册表中所处的位置。COM_Init接着在HLM \Drivers\BuiltIn\Serial下查询DeviceArrayIndex、Priority256的值,Priority256指定了驱 动程序的优先级,如果没有就用默认的优先级。接下来调用GetSerialObject(DeviceArrayIndex),这个函数由PDD层定义, 返回HWOBJ结构体,这个结构体主要包含PDD层和SER16550定义的函数的指针。也就是说MDD通过调用这个函数才能调用底层实现的函数。接下来 的大多数工作都是调用底层函数实现初始化。第一个调用的底层函数SerInit主要设置由用户设置的硬件配置,例如线路控制、波特率。它调用 Ser_GetRegistryData函数得到保存在注册表中的硬件信息,Ser_GetRegistryData在内部调用系统提供的 DDKReg_GetIsrInfoDDK和DDKReg_GetWindowInfo函数得到在HLM\Drivers\BuiltIn\Serial 下保存的IRQ、SysIntr、IsrDll、IsrHandler、IoBase、IoLen。IRQ是逻辑中断号,IsrDll表示当前驱动程序的 可安装ISR所在的DLL名称,IsrHandler 表示可安装ISR的函数名称。在这里顺便提一下可安装ISR,读者在我以前发表的关于OAL的文章中可以了解到OEM在OEMInit函数中关联IRQ和 SysIntr,当硬件设备发生中断时,ISR会禁止同级和低级中断,然后根据IRQ返回关联的SysIntr,内核根据ISR返回的SysIntr唤醒 相应的IST(SysIntr与IST创建的Event关联),IST处理中断之后调用InterruptDone解除中断禁止。在OEMInit中关联 的缺点是一旦编译了CE内核后就无法添加这种关联了,而一些硬件设备会随时插拔或者共享中断,要关联这样的硬件设备解决方法就是可安装ISR,可安装 ISR专用于处理指定的硬件设备发出的中断,所以如果硬件设备需要可安装ISR必须在注册表中添加IsrDll、IsrHandler。多数硬件设备采用 CE默认的可安装ISR giisr.dll,格式如下:

 "IsrDll"="giisr.dll"
"IsrHandler"="ISRHandler"

  如果一个硬件驱动程序需要可安装ISR而开发者又不想自己写一个,那么可以利用giisr.dll来实现。除了在注册表中添加如上所示外,还要在驱动程序中调用相关函数注册可安装ISR。伪代码如下:

g_IsrHandle = LoadIntChainHandler(IsrDll, IsrHandler, (BYTE)Irq);
GIISR_INFO Info;
PHYSICAL_ADDRESS PortAddress = {PhysAddr, 0};
TransBusAddrToStatic(BusType, dwBusNumber, PortAddress, dwAddrLen, &dwIOSpace, &(PVOID)PhysAddr)
Info.SysIntr = dwSysIntr;
Info.CheckPort = TRUE;
Info.PortIsIO = (dwIOSpace) ? TRUE : FALSE;
Info.UseMaskReg = TRUE;
Info.PortAddr = PhysAddr + 0x0C;
Info.PortSize = sizeof(DWORD);
Info.MaskAddr = PhysAddr + 0x10;
KernelLibIoControl(g_IsrHandle, IOCTL_GIISR_INFO, &Info, sizeof(Info), NULL, 0, NULL);

  LoadIntChainHandler 函数负责注册可安装ISR,参数1为DLL名称,参数2为ISR函数名称,参数3为IRQ。TransBusAddrToStatic函数在后面讲。如果 要利用giisr.dll作为可安装ISR,必须先填充GIISR_INFO结构体,CheckPort=TRUE表示giisr要检测指定的寄存器来确 定当前发出中断的是否是这个设备。PortIsIO表示寄存器地址属于哪个地址空间,FALSE表示是内定空间,TRUE表示IO空间。 UseMaskReg=TRUE表示设备有一个掩码寄存器,专用于指定当前设备是否是中断源,也就是发出中断,而MaskAddr表示掩码寄存器的地址。 如果对Info.Mask赋值,那么PortAddr表示一个特殊的寄存器地址,这个寄存器的值与Mask的值&运算的结果如果为真,则证明当前 设备是中断源,否则返回SYSINTR_CHAIN(表示当前ISR没有处理中断,内核将调用ISR链中下一个ISR),如果 UseMaskReg=TRUE,那么MaskReg寄存器的值与PortAddr指定的寄存器的值&运算的结果如果为真,则证明当前设备是中断 源。
  函数SerInit接着调用函数 Ser_InternalMapRegisterAddresses转换IO地址并且映射地 址,Ser_InternalMapRegisterAddresses在内部调用系统提供的HalTranslateBusAddress(Isa, 0, ioPhysicalBase, &inIoSpace, &ioPhysicalBase)函数将与总线相关的地址转换为系统地址,参数1为总线类型,参数2为总线号,参数3为要转换的地址 (PHYSICAL_ADDRESS类型,实际是LARGE_INTEGER型),参数4指定寄存器地址属于IO地址空间还是物理地址空间,参数5返回转 换后的物理地址。观察HalTranslateBusAddress的源码得知如果是在x86平台,这个函数除了把参数3赋给了参数5其余什么都没有做, 而非x86平台将inIoSpace的值置为0,表示一定是物理地址。在调用HalTranslateBusAddress前要确定从注册表中得到的寄存 器地址到底是属于哪个地址空间的,例如:

ULONG inIoSpace = 1; ///1表示是IO空间
PHYSICAL_ADDRESS ioPhysicalBase = {iobase, 0}; ///相当于ioPhysicalBase.LowPart = iobase

  在地址转换后就要将转换后的地址映射到驱动程序(一般IST和应用程序一样运行在用户模式)能够访问的虚拟地址空间(0x80000000以下)和ISR能够访问的静态虚拟地址空间中(0x80000000以上)。例如:

////如果地址属于物理地址空间
ioPortBase = (PUCHAR)MmMapIoSpace(ioPhysicalBase, Size, FALSE);
TransBusAddrToStatic(Isa, 0, ioPhysicalBase, Size, &inIoSpace, ppStaticAddress);

  MmMapIoSpace函数负责将物理地址映射到驱动程序能够访问的虚拟地址空间中,通过源码分析MmMapIoSpace在内部分别调用:

pVirtualAddress =VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS);
VirtualCopy(pVirtualAddress, (PVOID)(SourcePhys >> 8), SourceSize, PAGE_PHYSICAL |
PAGE_READWRITE | (CacheEnable ? 0 : PAGE_NOCACHE));

  VirtualAlloc 分配一块和MemLen一样大小的虚拟地址空间,因为参数1为0,所以内核自动分配。一般MemLen小于2MB,所以会在应用程序的地址空间中分配。 VirtualCopy负责将硬件设备寄存器的物理地址与VirtualAlloc分配的虚拟地址做一个映射关系,这样驱动程序访问 PvirtualAddress实际上就是访问第一个寄存器。因为硬件设备寄存器的物理地址一定是在512MB(CE支持RAM的最大值)以上,所以除了 最后的参数要加PAGE_PHYSICAL外,第二个参数物理地址也要右移8位(或者除以256)。映射硬件寄存器当然PAGE_NOCACHE是必须加 的。TransBusAddrToStatic函数负责将物理地址映射到ISR能够访问的静态虚拟地址空间中,当出现中断共享时,ISR要负责访问硬件设 备的某一个寄存器来判断中断源,所以将寄存器的物理地址映射到静态虚拟地址空间中是必要的(ISR只能访问静态的虚拟地址空间)。所谓静态虚拟地址空间是 指在OEMAddressTable中定义的虚拟地址空间(当然是0x80000000以上)。在x86平台一般这个表只定义RAM的物理地址与虚拟地址 对应关系,而硬件设备的寄存器地址并不在该表中定义,所以如果要创建一块静态的虚拟地址空间供ISR访问,必须在此之前调用 CreateStaticMapping函数在0xC4000000到0xE0000000虚拟地址空间中分配。 TransBusAddrToStatic函数在内部就是调用了CreateStaticMapping函数。注:硬件设备的寄存器地址也可以在 OEMAddressTable中定义。

////如果地址属于IO空间
ioPortBase = (PUCHAR)ioPhysicalBase.LowPart;
*ppStaticAddress=ioPortBase

这种情况只属于x86平台,是IO空间就可以直接访问,即使是用户模式。
  SerInit 函数接着初始化SER_INFO结构体成员,之后调用SL_Init函数,这个函数在ser16550中定义,负责初始化SER16550_INFO结构 体,在这个结构体中保存串口8个寄存器的地址。SerInit函数执行完毕后COM_Init函数创建接收缓冲区,然后调用 StartDispatchThread函数初始化中断并且创建IST。StartDispatchThread函数在内部调用 InterruptInitialize函数关联SysIntr和Event,然后调用InterruptDone函数告诉内核当前串口可以中断处理,接 着调用CreateThread函数创建IST线程。(over吧,再往下说就和串口硬件有关了,看多了没注释的代码我也烦!!)

posted @ 2008-11-28 16:59 井泉 阅读(567) | 评论 (0)编辑 收藏

Enumhandle

here it is .. you need the DDK for that , Gary Nebbett is the author:

#include "ntdll.h"
#include <stdlib.h>
#include <stdio.h>
#include "ntddk.h"

#define DUPLICATE_SAME_ATTRIBUTES 0x00000004

#pragma comment(lib,"ntdll.lib")

BOOL EnablePrivilege(PCSTR name)
{
TOKEN_PRIVILEGES priv = {1, {0, 0, SE_PRIVILEGE_ENABLED}};
LookupPrivilegeValue(0, name, &priv.Privileges[0].Luid);

HANDLE hToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);

AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof priv, 0, 0);
BOOL rv = GetLastError() == ERROR_SUCCESS;

CloseHandle(hToken);
return rv;
}

int main(int argc, char *argv[])
{
if (argc == 1) return 0;

ULONG pid = strtoul(argv[1], 0, 0);

EnablePrivilege(SE_DEBUG_NAME);

HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);

ULONG n = 0x1000;
PULONG p = new ULONG[n];

while (NT::ZwQuerySystemInformation(NT::SystemHandleInformation, p, n * sizeof *p, 0)
== STATUS_INFO_LENGTH_MISMATCH)

delete [] p, p = new ULONG[n *= 2];

NT::PSYSTEM_HANDLE_INFORMATION h = NT::PSYSTEM_HANDLE_INFORMATION(p + 1);

for (ULONG i = 0; i < *p; i++) {

if (h[i].ProcessId == pid) {
HANDLE hObject;

if (NT::ZwDuplicateObject(hProcess, HANDLE(h[i].Handle), NtCurrentProcess(), &hObject,
0, 0, DUPLICATE_SAME_ATTRIBUTES)
!= STATUS_SUCCESS) continue;

NT::OBJECT_BASIC_INFORMATION obi;

NT::ZwQueryObject(hObject, NT::ObjectBasicInformation, &obi, sizeof obi, &n);

printf("%p %04hx %6lx %2x %3lx %3ld %4ld ",
h[i].Object, h[i].Handle, h[i].GrantedAccess,
int(h[i].Flags), obi.Attributes,
obi.HandleCount - 1, obi.PointerCount - 2);

n = obi.TypeInformationLength + 2;

NT::POBJECT_TYPE_INFORMATION oti = NT::POBJECT_TYPE_INFORMATION(new CHAR[n]);

NT::ZwQueryObject(hObject, NT::ObjectTypeInformation, oti, n, &n);

printf("%-14.*ws ", oti[0].Name.Length / 2, oti[0].Name.Buffer);

n = obi.NameInformationLength == 0
? MAX_PATH * sizeof (WCHAR) : obi.NameInformationLength;

NT::POBJECT_NAME_INFORMATION oni = NT::POBJECT_NAME_INFORMATION(new CHAR[n]);

NTSTATUS rv = NT::ZwQueryObject(hObject, NT::ObjectNameInformation, oni, n, &n);
if (NT_SUCCESS(rv))
printf("%.*ws", oni[0].Name.Length / 2, oni[0].Name.Buffer);

printf("\n");

CloseHandle(hObject);
}
}
delete [] p;

CloseHandle(hProcess);

return 0;
}

posted @ 2008-11-28 16:30 井泉 阅读(360) | 评论 (0)编辑 收藏

仅列出标题
共8页: 1 2 3 4 5 6 7 8