來源:代碼實驗室
該例子演示了利用微軟.Net框架的Web服務與天氣預報站點的硬件進行數據交換來實現Web天氣預報的服務。
微軟力推.Net的目的,用他自己的話來說就是“使人們能夠在任何時候、任何地點以及任何設備上通過我們開發的軟件發揮最大的潛能”。在這裏大多數人可能認爲微軟所講的“任何設備”是指袖珍PC、手持設備、臺式電腦及筆記本電腦等。下面的例子我們將向大家展示.Net如何使那些電腦硬件設備驅動程序開發人員發揮自己的潛能,這些設備驅動程序可能和一些專用的PC控制器或PC的標準端口直接通訊。這些驅動程序開發人員大多依靠Windows的驅動開發工具包(DDK)及其其他各種工具,如可安裝文件系統開發工具包(IFS)等。雖然這些微軟的工具包並沒有隨着.Net的推出而改變,但是.Net構架仍然給這些硬件設備開發帶來了新的機遇。
我們將要構造的示例方案的目的就是通過專用的PC卡自動採集來自於不同天氣情報採集點的天氣信息。PC卡可以通過每個採集點的標識ID來同時控制每個採集點的數據,按要求每個採集點可以提供該點的溫度、溼度和氣壓等數據。
我們的目標是讓用戶通過互聯網(Web)獲得指定採集點的天氣預報信息。接下來我們開始結構設計。首先需要爲PC卡寫一個Windows的驅動程序以便PC卡可以讀取指定採集點的天氣信息,另外我們還需要利用.Net構造一個網絡服務(Web Service)以便互聯網用戶可以通過Internet訪問採集到的天氣信息數據。
.Net的託管代碼是不能直接訪問Windows的內核的。所有我們必須先利用非託管代碼寫一個用戶級(User level)的模塊以便網絡服務和PC卡的驅動程序之間可以相互交流數據。
PC卡的驅動程序接口
假設我們PC採集卡已安裝到專用PC上,而且採集點到PC採集開的信號電纜也連接好。接下來的任務就是驅動程序開發人員開始開發硬件驅動程序。
我們並不打算深入研究如何開發PC卡的硬件驅動程序。其實網上有很多介紹開發驅動程序的工具和資源,也有一些介紹在NT和XP下開發驅動程序差異的文章。我們的主要目的是關心採集數據的封裝形式以及用戶模塊和驅動程序通訊的方法。
我們準備有下面定義的結構來封裝採集到的數據:
typedef struct {
unsigned long stationID;
unsigned long state; // for management purposes
unsigned long timeStamp;
double temperature; // celcius
double humidity; // percent
double airPressure; // millibar
} WEATHER_DATA, *PWEATHER_DATA;
從PC卡讀取數據我們可以利用Win32 API - DeviceIOControl(...)。爲了調用該函數設備驅動程序和用戶程序必須共享用戶定義的IOCTL碼。爲了簡單起見,我們用WEATHER_DATA結構表示用戶程序和驅動程序共享的數據緩衝區。但是在實際應用中,應該小心謹慎,您可以參考微軟的技術文章Q126416和Q186775。
用戶要求得到指定的採集點的天氣信息的過程如下:
1.用戶程序分配WEATHER_DATA結構,設置 stationID 和 dataTag。
2.用戶程序利用Win32 API - CreateFile(...)打開與PC卡相關聯的設備句柄。
3.用戶程序調用DeviceIOControl(),並將結構 WEATHER_DATA 作爲參數。
4.驅動程序處理調用,利用用戶選擇的數據採集點的數據填充WEATHER_DATA數據緩衝區。(在我們的例子中,用虛擬數據以模仿顯示中的數據採集。)
5.驅動程序返回數據個用戶程序,結束DeviceIOControl()調用。
6.用戶程序處理得到的數據。
以下的頭定義文件爲驅動程序和用戶程序共用:
// weather_common.h
// Common definitions used by both user level module and
// kernel driver
//
#define WEATHER_TYPE 40000
// The IOCTL function codes from 0x800 to 0xFFF
// are for customer use.
#define IOCTL_GET_WEATHER_DATA
CTL_CODE( WEATHER_TYPE, 0x900, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
// Define common used weather data structure
typedef struct {
unsigned long stationID;
unsigned long state;
unsigned long timeStamp;
double temperature;
double humidity;
double airPressure;
} WEATHER_DATA, *PWEATHER_DATA;
接下來的代碼是用戶端的實現:
// UserLevelModule.cpp
//
// Implementation of user level module
...
DWORD dwBytesReturned;
// Create structure and fill initial data
WEATHER_DATA dataStruct;
// specify station to request
dataStruct.stationID = stationID;
...
// Open driver
HANDLE hDriver;
hDriver = CreateFile(“//./Device/WeatherDevice”,...);
...
// Issue control call to driver passing prepared structure
DeviceIOControl(hDriver,IOCTL_WEATHER_DATA,
(void*)&dataStruct,sizeof(dataStruct),
(void*)&dataStruct,sizeof(dataStruct),
&dwBytesReturned,NULL);
// If succeeded the structure now contains
// the requested data in data
...
// Close driver
CloseHandle(hDriver);
接下來是驅動程序的部分代碼:
// WeatherDrv.c
//
// Implementation of kernel driver
NTSTATUS
WeatherDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
...
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
// Dispatch based on major fcn code.
switch (pIrpStack->MajorFunction)
{
...
case IRP_MJ_DEVICE_CONTROL:
// Dispatch on IOCTL
switch (pIrpStack->Parameters.DeviceIoControl
.IoControlCode)
{
case IOCTL_GET_WEATHER_DATA:
pWeatherData = (PWEATHER_DATA)
pIrp->AssociatedIrp.SystemBuffer;
if(pWeatherData != NULL)
{
// Fill time stamp in data structure
KeQuerySystemTime(&sysTime);
RtlTimeToTimeFields(&sysTime,&tFields);
pWeatherData->timeStamp = tFields.Second
+ 100 * tFields.Minute
+ 10000 * tFields.Hour;
// Emulate controller card work with
// sample data
pWeatherData->temperature = 25;
pWeatherData->humidity = 60;
pWeatherData->airPressure = 1015;
}
pIrp->IoStatus.Information =
sizeof(WEATHER_DATA);
break;
default:
break;
}
Status = STATUS_SUCCESS;
break;
...
// Complete request
}
用戶端代碼
驅動程序以及驅動程序和用戶程序的通訊協議定義好了以後,接下來就是開發用戶端模塊了。
我們可以開發一個應用程序或者動態連接庫。在這裏我們實現一個動態連接庫。以下代碼演示瞭如何輸出一個函數供外部調用:
// weatherDrvCtrl.h
//
// Declaration of GetWeatherData()
DWORD WINAPI GetWeatherData(WEATHER_DATA* pData);
// weatherDrvCtrl.cpp
//
// Definition of GetWeatherData()
DWORD WINAPI GetWeatherData(
WEATHER_DATA* pData)
{
HANDLE hDriver;
DWORD dwErr = ERROR_SUCCESS;
DWORD dwBytesReturned;
// Open driver
HANDLE hDriver;
hDriver = CreateFile(DRIVER_DEVICE_NAME,...);
if(hDriver != INVALID_HANDLE_VALUE)
{
// Issue control call to driver
// passing prepared structure
dwErr = DeviceIOControl(hDriver,IOCTL_WEATHER_DATA,
(void*)&dataStruct,sizeof(dataStruct),
(void*)&dataStruct,sizeof(dataStruct),
&dwBytesReturned,NULL);
if(!dwErr)
{
// If succeeded the structure now contains
// requested data in data field
}
// Close driver
CloseHandle(hDriver);
}
else
{
// Handle device open error;
}
return dwErr;
}
附件的weatherDrvCtrl項目是用Visual Studio .Net構造的,我們還提供了一個Win32的應用程序以供你測試應用程序(weatherDrvCtrl)和驅動程序(weatherDrv)。
到現在爲止我們已經完成了驅動程序和用戶端調用的所有工作。任何用戶程序都可以通過調用動態庫輸出函數得到指定採集點的天氣信息數據。
網絡服務Web Service
下面我們來實現.Net的網絡服務,以便互聯網用戶可以享受我們的服務。利用Visual Studio .Net創建Web Service很簡單:
1.啓動Visuao Studio .Net;
2.新健一個新項目,選擇 C# ASP .Net Web服務。
3.編寫數據結構:
[ StructLayout( LayoutKind.Sequential )]
public struct WeatherData
{
public UInt32 stationID;
public UInt32 state;
public UInt32 timeStamp;
public Double temperature;
public Double humidity;
public Double airPressure;
}
4.利用屬性DllImport綁定動態庫:weatherDrvCtrl.DLL。
[DllImport("weatherDrvCtrl.dll")]
static extern unsafe uint GetWeatherData
(ref WeatherData data);
5.創建一個Web方法:
[WebMethod(Description=”Get weather data and station state from specified weather station”)]
public unsafe WeatherData
QueryWeatherData(uint stationID)
{
// Create and initialize data structure
WeatherData weatherData = new WeatherData();
weatherData.stationID = stationID;
// Call user level wrapper for device driver
uint dwRet = GetWeatherData(ref weatherData);
// If call failed, hqndle error here...
return weatherData;
}
6.生成並測試。
如果您按照上述過程正確操作,應該得到結果。這是一個重要的里程碑。因爲你已經創建了一個完整的Web服務,您可以註冊您的服務讓更多的人使用它。