20170918-20170924
例程:
In computer programming, routine and subroutine are general and nearly synonymous terms for any sequence of code that is intended to be called and used repeatedly during the executable of a program. This makes the program shorter and easier to write (and also to read when necessary). The main sequence of logic in a program can branch off to a common routine when necessary. When finished, the routine branches back to the next sequential instruction following the instruction that branched to it. A routine may also be useful in more than one program and save other programmers from having to write code than can be shared.(参考routine)
关于方法和函数的异同:
Note that “method” still has an important distinction from a function: methods are associated with a class or type, whereas functions are not. A language that forces you to define classes for everything, such as Java, only has methods. A language with no class system at all, such as C, only has functions. And a language that makes classes optional, such as Python, has both methods and functions.
例程和函数及方法的异同:
Nowadays the terms can be used interchangeably. But originally the distinction was this: a routine doesn’t return anything, whereas a function returns something.
When talking about object oriented programming, we use the word methods to mean functions or routines that are part of an object - also called instance methods.
结论
不必要区分得很精确,大体上是指可重复调用的一段代码,用来实现某一小部分功能。
InitCommonControls
是注册并初始化通用控件窗口类
void InitCommonControls(void);
INT_PTR WINAPI DialogBoxParam(
_In_opt_ HINSTANCE hInstance,
_In_ LPCTSTR lpTemplateName,
_In_opt_ HWND hWndParent,
_In_opt_ DLGPROC lpDialogFunc,
_In_ LPARAM dwInitParam
);
This structure stores information about each serial device that can be handled by a particular serial device driver.
typedef struct __DEVICE_LIST {
LPSTR DllName;
ULONG NumberOfDevices;
PHWOBJ* DeviceArray;
} DEVICE_LIST, *PDEVICE_LIST;
Allocates the specified number of bytes from the heap.
HLOCAL WINAPI LocalAlloc(
_In_ UINT uFlags,
_In_ SIZE_T uBytes
);
Retrieves a handle to a control in the specified dialog box.
HWND WINAPI GetDlgItem(
_In_opt_ HWND hDlg,
_In_ int nIDDlgItem
);
char AddNewDeviceNode(const GUID guid,
const char* szName,
const char* szInstallID,
const char* szPath,
const int wIndex,
const short wOrder)
{
DEVICE_LIST* pAdd = AllocNewDeviceNode();
//
if (!pAdd)
return 0;
memcpy(&pAdd->guid, &guid, sizeof(GUID));
memcpy(pAdd->szInstallID, szInstallID, strlen(szInstallID));
memcpy(pAdd->szName, szName, strlen(szName));
memcpy(pAdd->szPath, szPath, strlen(szPath));
pAdd->wIndex = wIndex;
pAdd->wOrder = wOrder;
pAdd->pNext = _pHead->pNext;
_pHead->pNext = pAdd;
return 1;
};
char EnumWDMDriver(const UINT nIdTree, const UINT nIdBmp)
{
HDEVINFO hDevInfo = 0L;
SP_DEVINFO_DATA spDevInfoData = {0};
short wIndex = 0;
HTREEITEM hTreeChild = 0L;
//
hTreeChild = MakeDeviceRootTree(_spImageData, nIdTree, nIdBmp);
if (!hTreeChild)
return 0;
//
hDevInfo = SetupDiGetClassDevs(0L, 0L, _hDlg, DIGCF_PRESENT |
DIGCF_ALLCLASSES | DIGCF_PROFILE);
if (hDevInfo == (void*)-1)
{
ShowErrorMsg(_hDlg, GetLastError(), "SetupDiGetClassDevs");
return 0;
};
//
wIndex = 0;
spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
SendMessage(GetDlgItem(_hDlg, nIdTree), TVM_SETIMAGELIST,
TVSIL_NORMAL, (LPARAM)_spImageData.ImageList);
//
while (1)
{
if (SetupDiEnumDeviceInfo(hDevInfo,
wIndex,
&spDevInfoData))
{
char szBuf[MAX_PATH] = {0};
short wImageIdx = 0;
short wItem = 0;
//
if (!SetupDiGetDeviceRegistryProperty(hDevInfo,
&spDevInfoData,
SPDRP_CLASS, //SPDRP_DEVICEDESC,
0L,
(PBYTE)szBuf,
2048,
0))
{
wIndex++;
continue;
};
//
if (SetupDiGetClassImageIndex(&_spImageData,
&spDevInfoData.ClassGuid,
(int*)&wImageIdx))
{
TVINSERTSTRUCT tvStruct = {0};
HWND hTree = GetDlgItem(_hDlg, nIdTree);
char szName[64] = {0};
char szID[LINE_LEN] = {0};
char szPath[MAX_PATH] = {0};
HTREEITEM hItem;
DWORD dwRequireSize;
short wOrder;
//
if (!SetupDiGetClassDescription(&spDevInfoData.ClassGuid,
szBuf,
MAX_PATH,
&dwRequireSize))
{
wIndex++;
continue;
};
wOrder = FindDeviceOrder(szBuf);
if (!AddNewDeviceOrderNode(szBuf))
{
wIndex++;
continue;
};
hItem = TreeViewFindChild(hTree, hTreeChild, szBuf);
if (!hItem)
{
tvStruct.hParent = hTreeChild;
tvStruct.hInsertAfter = TVI_LAST;
tvStruct.item.mask = TVIF_IMAGE | TVIF_TEXT | TVIF_SELECTEDIMAGE;
tvStruct.item.mask |= TVIF_PARAM;
tvStruct.item.lParam = 1;
tvStruct.item.pszText = szBuf;
tvStruct.item.iImage = wImageIdx;
tvStruct.item.iSelectedImage = wImageIdx;
hItem = (HTREEITEM)SendMessage(hTree, TVM_INSERTITEM, 0, (LPARAM)&tvStruct);
wOrder = 0;
};
GetDeviceInstanceID(hDevInfo, &spDevInfoData, szID);
GetDeviceInterfaceInfo(hDevInfo, spDevInfoData, szPath);
//
if (SetupDiGetDeviceRegistryProperty(hDevInfo,
&spDevInfoData,
SPDRP_FRIENDLYNAME,
0L,
(PBYTE)szName,
63,
0))
{
DisplayDriverDetailInfo(hItem, nIdTree, szName, wImageIdx, wImageIdx);
AddNewDeviceNode(spDevInfoData.ClassGuid, szName, szID, szPath, wIndex, wOrder);
}
else if (SetupDiGetDeviceRegistryProperty(hDevInfo,
&spDevInfoData,
SPDRP_DEVICEDESC,
0L,
(PBYTE)szName,
63,
0))
{
DisplayDriverDetailInfo(hItem, nIdTree, szName, wImageIdx, wImageIdx);
AddNewDeviceNode(spDevInfoData.ClassGuid, szName, szID, szPath, wIndex, wOrder);
// if (!GetFirmwareEnvironmentVariable(szName, (LPCSTR)&spDevInfoData.ClassGuid, szBuf, 127))
// ShowErrorMsg(_hDlg, GetLastError(), "GetFirmwareEnvironmentVariable");
};
};
// SetupDiDestroyDriverInfoList(hDevInfo, &spDevInfoData, SPDIT_COMPATDRIVER);
}
else
break;
wIndex++;
};
SendMessage(GetDlgItem(_hDlg, nIdTree), TVM_EXPAND,
TVE_EXPAND, (LPARAM)hTreeChild);
SendMessage(GetDlgItem(_hDlg, nIdTree), TVM_SORTCHILDREN,
0, (LPARAM)hTreeChild);
TreeView_SetItemState(GetDlgItem(_hDlg, nIdTree), hTreeChild, TVIS_SELECTED, TVIS_SELECTED);
// SendMessage(GetDlgItem(_hDlg, nIdTree), TVM_SELECTITEM,
// TVGN_FIRSTVISIBLE, (LPARAM)hTreeChild);
SetupDiDestroyDeviceInfoList(hDevInfo);
return 1;
};
short FindDeviceOrder(const char* szName)
{
DEVICE_ORDER *pList = _pOrderHead->pNext;
short wOrder = 0;
//
while (pList)
{
if (!strcmp(pList->szDevName, szName))
wOrder++;
pList = pList->pNext;
};
return wOrder;
};
DEVICE_ORDER* AllocNewDeviceOrderNode()
{
DEVICE_ORDER* pNew = (DEVICE_ORDER*)LocalAlloc(LPTR, sizeof(DEVICE_ORDER));
//
if (!pNew)
{
ShowErrorMsg(_hDlg, GetLastError(), "LocalAlloc");
return 0;
};
RtlZeroMemory(pNew->szDevName, sizeof(char)*LINE_LEN);
pNew->pNext = 0L;
return pNew;
};
HTREEITEM TreeViewFindChild(HWND hTreeView, HTREEITEM hRoot,
const char *szBuf)
{
HTREEITEM hChildItem = 0L;
HTREEITEM hParentItem = hRoot;
char szText[128] = {0};
TVITEM tvItem = {0};
int iLoop;
int nIndex;
//
tvItem.mask = TVIF_TEXT|TVIF_HANDLE;
tvItem.pszText = szText;
tvItem.cchTextMax = sizeof(char)*127;
nIndex = (int)SendMessage(hTreeView, TVM_GETCOUNT, 0, 0);
RtlZeroMemory(szText, sizeof(char)*128);
for (iLoop = 0; iLoop < nIndex; iLoop++)
{
hChildItem = (HTREEITEM)SendMessage(hTreeView,
TVM_GETNEXTITEM,
(iLoop) ? TVGN_NEXT : TVGN_CHILD,
(iLoop) ? (LPARAM)hParentItem : (LPARAM)hRoot);
tvItem.hItem = hChildItem;
SendMessage(hTreeView, TVM_GETITEM, 0, (LPARAM)&tvItem);
if (*szText)
{
if (!strcmp(szBuf, szText))
return hChildItem;
};
hParentItem = hChildItem;
};
return 0;
};
void GetDeviceInstanceID(HDEVINFO hDevInfo,
SP_DEVINFO_DATA* pspDevInfoData,
char *szInstanceID)
{
if (!SetupDiGetDeviceInstanceId(hDevInfo,
pspDevInfoData,
szInstanceID,
LINE_LEN,
0))
ShowErrorMsg(_hDlg, GetLastError(), "SetupDiBuildDriverInfoList");
};
void GetDeviceInterfaceInfo(HDEVINFO hDevInfo, SP_DEVINFO_DATA spDevInfoData,
char *szPath)
{
SP_DEVICE_INTERFACE_DATA spDevInterfaceData = {0};
//
spDevInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
if (!SetupDiCreateDeviceInterface(hDevInfo,
&spDevInfoData,
&spDevInfoData.ClassGuid,
0L,
0L,
&spDevInterfaceData))
ShowErrorMsg(_hDlg, GetLastError(), "SetupDiBuildDriverInfoList");
else
{
SP_DEVICE_INTERFACE_DETAIL_DATA *pspDevInterfaceDetail = 0L;
DWORD dwRequire = 0L;
//
if (!SetupDiGetDeviceInterfaceDetail(hDevInfo,
&spDevInterfaceData,
0L,
0,
&dwRequire,
0L))
{
DWORD dwError = GetLastError();
//
if (dwError != ERROR_INSUFFICIENT_BUFFER)
{
ShowErrorMsg(_hDlg, dwError, "SetupDiBuildDriverInfoList");
return;
};
};
//
pspDevInterfaceDetail = (SP_DEVICE_INTERFACE_DETAIL_DATA*)LocalAlloc(LPTR,
sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA)*dwRequire);
pspDevInterfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (!SetupDiGetDeviceInterfaceDetail(hDevInfo,
&spDevInterfaceData,
pspDevInterfaceDetail,
dwRequire,
&dwRequire,
0L))
{
DWORD dwError = GetLastError();
//
if (dwError != ERROR_INSUFFICIENT_BUFFER)
ShowErrorMsg(_hDlg, dwError, "SetupDiBuildDriverInfoList");
}
else
{
memcpy(szPath, pspDevInterfaceDetail->DevicePath,
strlen(pspDevInterfaceDetail->DevicePath));
// switch(spDevInterfaceData.
};
//
if (pspDevInterfaceDetail)
LocalFree(pspDevInterfaceDetail);
};
};
void DisplayDriverDetailInfo(HTREEITEM hTreeChild, const UINT nID,
const char *szBuf, const int iImageIdx, const int iSelectImage)
{
TVINSERTSTRUCT tvStruct = {0};
HWND hTree = GetDlgItem(_hDlg, nID);
//
tvStruct.hParent = hTreeChild;
tvStruct.hInsertAfter = TVI_LAST;
tvStruct.item.mask = TVIF_IMAGE | TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_HANDLE;
tvStruct.item.mask |= TVIF_PARAM;
tvStruct.item.lParam = 1;
tvStruct.item.pszText = (char*)szBuf;
tvStruct.item.iImage = iImageIdx;
tvStruct.item.iSelectedImage = iImageIdx;
SendMessage(hTree, TVM_INSERTITEM, 0, (LPARAM)&tvStruct);
};
注:同一类设备的实例GUID相同。
查找某一实例的GUID用到函数SetupDiGetDeviceInstanceId function.
SetupDiGetClassDescription function
The SetupDiGetClassDescription function retrieves the class description associated with the specified setup class GUID.
设备列举
PnP管理器从一个称为root的虚拟总线型驱动程序开始设备列举过程,root代表整个计算机系统,也担当了所有非即插即用驱动程序和HAL的总线型驱动程序。HAL也被认为是一个总线型驱动程序,可以列举所有直接负载在主板上的设备,以及诸如电池之类的系统部件。实际上HAL并不是真的执行列举操作,相反,它依赖于Setup进程记录在注册表中的硬件描述来检测系统总主线(绝大多数情况下是PCI总线)和诸如电池和风扇之类的设备。
Microsoft Windows NT(New Technology)是Microsoft在1993年推出的面向工作站、网络服务器和大型计算机的网络操作系统,也可做PC操作系统。[参考 百度百科]
C run-time library里面含有初始化代码,还有错误处理代码(例如divide by zero处理)。你写的程序可以没有math库,程序照样运行,只是不能处理复杂的数学运算,不过如果没有了C run-time库,main()就不会被调用,exit()也不能被响应。因为C run-time library包含了C程序运行的最基本和最常用的函数。
图形用户界面,亦称为图形窗口环境或虚界面。
早期的Windows的多任务系统是“非抢占型”(Non-Preemptive)的。也就是说,那时候的Windows还不能用系统时钟把系统中正在运行的各个程序的处理时间分段,这些程序必须自觉地放弃对处理器的控制,给予其他程序运行的机会。
Windows工作原理的中心思想就是“动态链接”概念。Windows自身带有一大套函数,应用程序就是通过调用这些函数来实现它的用户界面和在屏幕上显示文本与图形的。这些函数都是在动态链接库里实现的。这些文件的名称都带有后缀.dll
或有时带有后缀.exe
.
在Windows程序中,调用Windows函数与调用C语言的库函数(如Strlen)没什么不同。最主要的区别是C语言库函数的机器代码会直接链接到你的程序代码中,而Windows函数则是放在程序之外的dll里。
Windows是一个非常复杂的系统,在API之上加一层编程语言并不能消除其复杂性,最多不过是把复杂性隐藏起来了而已。说不定什么时候,Windows复杂的那一面迟早会蹦出来拖后腿,懂得API能让你到时候更快地挣脱困境。
在基本Windows API之上的任何软件层或多或少都会限制你使用Windows的全部功能。比如,你或许发现采用VB来编写你的应用程序非常理想,但就是有那么一两个非常基本的功能VB无法支持。往往这种时候,就必须要调用基本API。作为Windows程序员,我们的活动空间完全由API来规范,再没有什么其他方式能比直接调用API更有效、灵活多样了。
WinMain的第一个参数一般叫做“实例句柄”(Instance Handle)。在Windows程序中句柄无非就是一个数值,程序里用它来标识某些东西。(Window程序设计 p15)
#include
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hprevInstance,PSTR szCmdLine,int iCmdShow)
{
MessageBox(
NULL,
(LPCWSTR)L"Resource not available\nDo you want to try again?",
(LPCWSTR)L"Account Details",
MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2
);
return 0;
}
在MSDN中,如下描述:
//C++
int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
);
Unicode是ASCII字符编码的一个扩展,用的是16为字符编码。
ps:原来英文也有很多带有发音的词,resume和cooperate居然也是,长知识了。。
void SetCharSet(const UINT nIDList1, const UINT nIDList2)
{
HWND hListView1 = GetDlgItem(_hDlg, nIDList1);
HWND hListView2 = GetDlgItem(_hDlg, nIDList2);
//
if (GetACP() == 950)
{
ListViewInsertColumnText(hListView1, 0, 0, "欄位(Field)", 0);
}
else
{
ListViewInsertColumnText(hListView1, 0, 0, "Field", 0);
};
};
关于950的说明
Windows支持四种不同的双字节字符集(DBCS):代码页932(日文)、936(简体中文)、949(韩文)、以及950(繁体中文)。DBCS只有在为这些国家制造的Windows版本上才会被支持。
前缀N和L代表着“近”(near)和”远”(long),指的是16位Windows系统中的两种大小不同的指针,但在win32中没有区别。
“windows向应用程序发送了一条消息。”这句话是什么意思?
——Windows调用了该程序内部的一个函数,这个函数是你写的,而且是程序的核心,此函数的参数描述了由Windows所发送并由你的程序所接收的特定消息。这个函数被称为“窗口过程”。
应用程序所创建的每一个窗口都有一个与之相关联的窗口过程。这个窗口过程可以是应用程序中的某一个函数,也可以位于一个动态链接库中。Windows正是通过调用该窗口过程来向窗口传递消息的。窗口过程则依据这些消息作出相应的处理,然后将控制权返还给Windows。
当Windows程序开始执行时,Windows首先为该程序创建一个“消息队列”。该消息队列中存放着应用程序可能创建的所有窗口的消息。Windows应用程序中一般都包含一小段称为“消息循环”的代码,该段代码用于从消息队列中检索消息,并将其分发给相应的窗口过程。其他消息则不经过消息队列直接发送给窗口过程。
要创建窗口,首先要注册一个窗口类,而窗口类又需要窗口过程来处理窗口消息。这些事几乎所有的Windows程序都会包括的一些常规而繁琐的步骤。
通常Windows程序员都是将已有的程序代码复制到新程序中,然后加以修改。
ps:没有可视化工具之前的程序员门编译程序是真的吃力啊。。。致敬!
/*------------------------------------------------------------
HELLOWIN.C -- Displays "Hello, Windows 98!" in client area
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("HelloWin") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, // window class name
TEXT ("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
RECT rect ;
switch (message)
{
case WM_CREATE:
//PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
MessageBox(
NULL,
(LPCWSTR)L"Resource not available\nDo you want to try again?",
(LPCWSTR)L"Account Details",
MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2
);
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
GetClientRect (hwnd, &rect) ;
DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
对于各种类型的句柄,有下表所示的三种大写标识符:
标识符 | 含义 |
---|---|
HINSTANCE | 实例句柄——程序本身 |
HWND | 窗口句柄 |
HDC | 设备环境句柄 |
在Windows中,句柄的使用非常频繁。句柄本质上是引用某个对象的数值(通常为32位)。Windows中的句柄非常类似于传统的C或MS-DOS程序中使用的文件句柄。一般情况下,应用程序几乎总是通过调用Windows函数来获取句柄。应用程序通过在其他Windows函数中使用句柄来引用相应对象。句柄的实际取值对你的程序来说并不重要,而将句柄传递给你的程序的Windows模块则知道如何通过句柄来引用对象。(p45)
关于Windows API的调用等等方面的知识学习就此打住,随见随学吧,接下来主要谈谈,这个简易版设备管理器到底怎么做才好,或者说,不仅仅是设备管理器,这一类关于新操作的探索,到底应该怎样才好。
这个题目有点广,我们往小了说,主要是指要如何完成之前未接触过的新任务。所谓新任务,新在思路、方法和内容。一般来说,按照认识的发展规律来讲,实践—感性认识—理性认识—实践是必不可少的过程,对此我亦深信不疑,而且严格执行。当然,现实中我们遇到的问题,已经没有了那么多非得探索到本质才能解决的问题,我们看到某种新事物,首先是了解,点到为止,不求甚解而知其缘,会用,是所有结论的必然结果,也是我们这个时代所需要的一种精神。不需要理论方面的研究,亦不需要亲自实践去证明既定事实的合理性,说白了,接受即可,社会是对的。是的,这是属于我们的认识规律,毕竟,深层次理论挖掘虽不是科研人员的特权,但由于太过耗时,必须争分夺秒才能实现的功能,不允许我们有太多的思考时间,我们是要完成任务的,而不是科研人员。任务完成了,若有兴趣,可返回头将之前的经过梳理一遍,将经历转换为经验,才是价值所在,而这部分,是任何教科书都不能给出的答案,需要自己探索发现。
我一直习惯于参透某个知识后再去应用,从小到大一直这样,不懂就不会去做,深信万丈高楼平地起,地基的重要性,太过浮躁走不远的道理。这个过程在时间充裕的情况下是没有问题的,知其然而知其所以然。但是我们现在的问题与状态,不得不转换思考方式,亟待改变。
于是乎,我们的认识发展不得不转变为:感性认识—实践—理性认识—实践的过程,可以明显地看出其中的优缺点。没有什么事能够系统地学完再开始做,条件不允许,现实不允许。
放弃之前的习惯,好的坏的都算,是一个痛苦而漫长的过程,并不是手心手背翻转那么简单的问题。需要时间,需要栽跟头,需要经历痛苦来磨合。我正经历着。
从任务管理器到前端学习再到设备管理器,无一不重复着这个过程,习惯的力量。从任务管理器的任务中,深刻学习到了分解、拆解问题的重要性,尽管很多问题具有粘连性,不容易拆封为小问题去解决,但在大多数问题上,思路是正确的,能帮助后期不返工,少返工。也是从任务管理器开始,误打误撞接触到了Windows编程,而在这之前,已经拜读过MFC相关书籍,当然那个时候不大懂,只是有个模糊的概念,到现在也是模糊的概念,甚至觉得更不懂不了,Windows真是一个庞大的工程,系统学习迟早的事,理论层面的东西,什么时候都不能丢,理论才能指导实践,实践反过来修正理论。其实呢,现在这种亦懂非懂的状态最为可怕,若是认为懂了,则不会去深挖某些概念,从而错过很多精彩内容;若是认为没懂,则会一直去深挖,而偏离最初要解决问题的方向与意愿,世界这么大,哪儿能挖完呢?如老大所言,编程追根溯源,要纠结沙子是如何提炼成硅的吗?当然不需要。不过玩笑话,作为理工科生,知道这些为什么,不是应该的吗?世界由理工科负责解释。关于前端学习,用了一个周的时间,把html的标签和CSS大概看了一些,还有一部分js,用来实现页面上的动画和函数处理,之后做了模仿报纸的排版,还算不错吧效果,做一点学一点再进步一点。
这次的任务是一个做一个任务管理器,同样的,把之前学过的好多内容综合起来了,有涉及到Windows API的调用,这部分总是最令人头痛的,不过对Windows编程有了更深刻的理解。Windows的操作系统是用C语言写的,所以如果要调用内核态的一些功能,必须转到C语言来实现,而我们现在需要用C#封装,所以必然涉及到C语言到C#翻译的一个过程,这里有一个难点:将C风格的Windows API声明转换为C#下的数据格式封装,而这个过程需要对CPU数据位有清楚的认识,上一次做任务管理器也是在这方面栽了跟头,什么时候64位debug,什么时候用32位debug,又在什么时候用AnyCPU,都需要注意,不然就会在后边出现一些很奇怪的问题,内存溢出啦什么的找死你都找不到到底哪儿出现了问题,所以切记一定注意!!!在做Windows API翻译的过程中,发现了两个不错的国外网站,有现成的封装,可以做参考:
PINVOKE.NET
netomatix
而后有前人在C#中应用过的传参都可以参考一下。涉及到Windows API的问题都会特别的棘手,因为在官网找不到任何可供参考的有意义的实例,传什么参数会得到什么结果都没有讲清楚,所以应该去论坛找参考而后验证。再一个难点就是,这个过程中其实是有codeProject上的C语言版的简易版设备管理器可以供参考,虽然bug很多,但是还是有很大的参考价值的。关于启用和禁用设备的功能实现,参考开源代码很快就实现了,而怎么把设备树列出来,占用了大部分的时间,差不多有三四天,可以这么说,之前的思路都是错的,认为底层逻辑上也必然分为父节点和子节点,或者系统里边就有专门的函数能把这棵树提取出来,不用自己去写,这个问题本来就是一个矛盾的反映:说明对函数库不熟悉,而这也验证了之前学习html方面的问题——不知道语义化标签,用自己的方式实现起来会很笨拙,磨刀不误砍柴工,该看的、了解和学习的知识点还是要学的。因为一直坚信系统中会提供这么一个接口函数,然后就花很长时间去拜读MSDN网站,试图从少的可怜的官方说明文档中寻找出一点点蛛丝马迹,来解决这棵树的呈现问题,但是并没有。事实证明,寻找失败,需要及时换一种思路来求解,别忘记当初是为什么找这些资料的。在读别人的C语言版的源码时,陷入很多细节里出不来,比如sendMessage等一些和事件相关的操作,之后应该有必要把Windows消息循环队列和事件再看看,还是不能熟练运用,毕竟解决方案里目前还不会出现这些名目。
最后的最后,跳出死循环圈,只是借鉴了原作者的C代码下如何将父节点找出,将逻辑上存在的父节点串成一串(链表),子节点也串成一串(链表),再利用C#下的字典和treeView,成功将问题解决。以上。
该看的书还是要看的,以任务为导向,不要陷得太深!