在 Windows CE 下自帶有無線網卡的配置和連接程序,可是我的系統剪裁掉了資源管理器和任務欄,導致自帶的無線網卡配置程序不能再使用了,只好自力更生。
我的環境是 S3C2440 + WinCE 5.0 + VNUWCL5(威盛無線網卡)及驅動程序。使用 Automatic Configuration Functions API
一、枚舉系統中可用的無線網絡設備
下面的函數可以枚舉出系統中所有可用的無線網卡設備的GUID,為了簡化,我選擇第一塊可用的無線網卡來操作
BOOL GetFirstWirelessCard(PTCHAR pCard)
{
if (!pCard)
{
return FALSE;
}
INTFS_KEY_TABLE IntfsTable;
IntfsTable.dwNumIntfs = 0;
IntfsTable.pIntfs = NULL;
_tcscpy(pCard, TEXT(""));
// 枚舉系統中可用的無線網卡
DWORD dwStatus = WZCEnumInterfaces(NULL, &IntfsTable);
if (dwStatus != ERROR_SUCCESS)
{
RETAILMSG(DBG_MSG, (TEXT("WZCEnumInterfaces() error 0x%08X\n"),dwStatus));
return FALSE;
}
// 判斷無線網卡的數量,可以根據無線網卡數量來枚舉出所有可用的無線網卡
if (!IntfsTable.dwNumIntfs)
{
RETAILMSG(DBG_MSG, (TEXT("System has no wireless card.\n")));
return FALSE;
}
_tcscpy(pCard, IntfsTable.pIntfs[0].wszGuid);
LocalFree(IntfsTable.pIntfs);
return TRUE;
}
二、獲取無線網絡信息
獲取到了系統可用的無線網卡后,我們就可以利用它的 GUID 號來進行進一步的操作了,首先要做的事情就是得到該無線網卡的信息以及該無線網卡掃描到的 WIFI 網關信息。
以下函數可以獲取到該無線網卡及掃描的到的無線 AP 信息
//////////////////////////////////////////////////////////////////////////
// pCard: 無線網卡GUID
// pIntf: 無線網卡配置信息結果體
// pOutFlags: 網卡配置信息掩碼標志
//////////////////////////////////////////////////////////////////////////
BOOL GetWirelessCardInfo(PTCHAR pCard, PINTF_ENTRY_EX pIntf, PDWORD pOutFlags)
{
TCHAR *szWiFiCard = NULL;
// 參數校驗
if (!pCard || !pIntf || !pOutFlags)
{
RETAILMSG(DBG_MSG, (TEXT("Param Error.\n")));
return FALSE;
}
szWiFiCard = pCard;
*pOutFlags = 0;
// 初始化無線網卡信息
ZeroMemory(pIntf, sizeof(INTF_ENTRY_EX));
// 設置GUID 號
pIntf->wszGuid = szWiFiCard;
// 查詢無線網卡信息
DWORD dwStatus = WZCQueryInterfaceEx(NULL, INTF_ALL, pIntf, pOutFlags);
if (dwStatus != ERROR_SUCCESS)
{
RETAILMSG(DBG_MSG, (TEXT("WZCQueryInterfaceEx() error 0x%08X\n"), dwStatus));
return FALSE;
}
return TRUE;
}
三、判斷連接狀態
我們可以通過無線網卡的狀態來判斷當前無線網卡是否已經和無線AP建立了連接
BOOL IsAssociated(const INTF_ENTRY_EX Intf, const DWORD dwOutFlags)
{
if (dwOutFlags & INTF_BSSID)
{
PRAW_DATA prdMAC = (PRAW_DATA)(&Intf.rdBSSID);
// 判斷BSSID 的MAC 地址是否有效來判斷是否和無線AP建立了連接
if (prdMAC == NULL || prdMAC->dwDataLen == 0 ||
(!prdMAC->pData[0] && !prdMAC->pData[1] && !prdMAC->pData[2] &
!prdMAC->pData[3] && !prdMAC->pData[4] && !prdMAC->pData[5]))
{
RETAILMSG(DBG_MSG, (TEXT("(This wifi card is not associated to any)\n")));
return FALSE;
}
else
{
RETAILMSG(DBG_MSG, (TEXT("(This wifi card is associated state)\n")));
return TRUE;
}
}
else
{
return FALSE;
}
}
四、獲取無線AP信息
獲取了無線網卡的信息后,可以通過無線網卡枚舉出當前所有可用的無線AP的SSID名稱以及加密模式等等所有可用信息,一下函數可以實現該功能
void GetWirelseeListSSID(const PRAW_DATA prdBSSIDList, HWND hListCtlWnd)
{
if (prdBSSIDList == NULL || prdBSSIDList->dwDataLen == 0)
{
RETAILMSG(DBG_MSG, (TEXT("<null> entry.\n")));
}
else
{
PWZC_802_11_CONFIG_LIST pConfigList = (PWZC_802_11_CONFIG_LIST)prdBSSIDList->pData;
//RETAILMSG(DBG_MSG, (TEXT("[%d] entries.\n"), pConfigList->NumberOfItems));
uint i;
// 枚舉所有無線AP
for (i = 0; i < pConfigList->NumberOfItems; i++)
{
PWZC_WLAN_CONFIG pConfig = &(pConfigList->Config[i]);
RAW_DATA rdBuffer;
rdBuffer.dwDataLen = pConfig->Ssid.SsidLength;
rdBuffer.pData = pConfig->Ssid.Ssid;
TCHAR tSsid[MAX_PATH];
// 將SSID 的ASCII 碼轉化成字符串
PrintSSID(&rdBuffer, tSsid);
if (hListCtlWnd)
{
if (ListBox_FindString(hListCtlWnd, 0, tSsid) == LB_ERR)
{
ListBox_AddString(hListCtlWnd, tSsid);
}
}
//RETAILMSG(DBG_MSG, (TEXT("\n")));
}
}
}
五、連接到指定的無線AP
//////////////////////////////////////////////////////////////////////////
// pCard: 無線網卡GUID
// pSSID: 無線AP SSID號
// bAdhoc: 是否點對點的WIFI 連接
// ulPrivacy: 加密模式(WEP/WPA....)
// ndisMode: 認證模式(Open/Share)
// iKeyIndex: 密鑰索引(1-4)
// pKey: 密碼
// iEapType: 802.11 認證模式
//////////////////////////////////////////////////////////////////////////
BOOL WirelessConnect(PTCHAR pCard, PTCHAR pSSID, BOOL bAdhoc, ULONG ulPrivacy, NDIS_802_11_AUTHENTICATION_MODE ndisMode, int iKeyIndex, PTCHAR pKey, int iEapType)
{
BOOL bRet = FALSE;
if (!pSSID)
{
RETAILMSG(DBG_MSG, (TEXT("Param Error.\n")));
return FALSE;
}
else
{
WZC_WLAN_CONFIG wzcConfig;
ZeroMemory(&wzcConfig, sizeof(WZC_WLAN_CONFIG));
wzcConfig.Length = sizeof(WZC_WLAN_CONFIG);
wzcConfig.dwCtlFlags = 0;
wzcConfig.Ssid.SsidLength = _tcslen(pSSID);
for (UINT i = 0; i < wzcConfig.Ssid.SsidLength; i++)
{
wzcConfig.Ssid.Ssid[i] = (CHAR)pSSID[i];
}
if (bAdhoc)
{
wzcConfig.InfrastructureMode = Ndis802_11IBSS;
}
else
{
wzcConfig.InfrastructureMode = Ndis802_11Infrastructure;
}
wzcConfig.AuthenticationMode = ndisMode;
wzcConfig.Privacy = ulPrivacy;
if (pKey == NULL || _tcslen(pKey) == 0)
{
// 對密鑰進行轉換
bRet = InterpretEncryptionKeyValue(wzcConfig, 0, NULL, TRUE);
wzcConfig.EapolParams.dwEapType = iEapType;
wzcConfig.EapolParams.dwEapFlags = EAPOL_ENABLED;
wzcConfig.EapolParams.bEnable8021x = TRUE;
wzcConfig.EapolParams.dwAuthDataLen = 0;
wzcConfig.EapolParams.pbAuthData = 0;
}
else
{
RETAILMSG(DBG_MSG, (TEXT("WirelessConnect iKeyIndex = %d.\n"), iKeyIndex));
bRet = InterpretEncryptionKeyValue(wzcConfig, iKeyIndex, pKey, FALSE);
}
// 連接到指定的無線AP,并將該AP添加到首先無線AP中
AddToPreferredNetworkList(pCard, wzcConfig, pSSID);
}
return bRet;
}
六、密鑰轉換
輸入的密鑰需要通過加密方式進行一定的轉化,以下函數可以完成改功能
static void EncryptWepKMaterial(IN OUT WZC_WLAN_CONFIG* pwzcConfig)
{
BYTE chFakeKeyMaterial[] = { 0x56, 0x09, 0x08, 0x98, 0x4D, 0x08, 0x11, 0x66, 0x42, 0x03, 0x01, 0x67, 0x66 };
for (int i = 0; i < WZCCTL_MAX_WEPK_MATERIAL; i++)
pwzcConfig->KeyMaterial[i] ^= chFakeKeyMaterial[(7*i)%13];
}
BOOL InterpretEncryptionKeyValue(IN OUT WZC_WLAN_CONFIG& wzcConfig, IN int iKeyIndex, IN PTCHAR pKey, IN BOOL bNeed8021X)
{
if(wzcConfig.Privacy == Ndis802_11WEPEnabled)
{
if(!bNeed8021X && pKey)
{
wzcConfig.KeyIndex = iKeyIndex;
wzcConfig.KeyLength = _tcslen(pKey);
if((wzcConfig.KeyLength == 5) || (wzcConfig.KeyLength == 13))
{
for(UINT i=0; i<wzcConfig.KeyLength; i++)
wzcConfig.KeyMaterial[i] = (UCHAR)pKey[i];
}
else
{
if((pKey[0] != TEXT('0')) || (pKey[1] != TEXT('x')))
{
RETAILMSG(DBG_MSG, (TEXT("Invalid key value.\n")));
return FALSE;
}
pKey += 2;
wzcConfig.KeyLength = wcslen(pKey);
if((wzcConfig.KeyLength != 10) && (wzcConfig.KeyLength != 26))
{
RETAILMSG(DBG_MSG, (TEXT("Invalid key value.\n")));
return FALSE;
}
wzcConfig.KeyLength >>= 1;
for(UINT i=0; i<wzcConfig.KeyLength; i++)
{
wzcConfig.KeyMaterial[i] = (HEX(pKey[2 * i]) << 4) | HEX(pKey[2 * i + 1]);
}
}
EncryptWepKMaterial(&wzcConfig);
wzcConfig.dwCtlFlags |= WZCCTL_WEPK_PRESENT;
}
}
else if(wzcConfig.Privacy == Ndis802_11Encryption2Enabled
|| wzcConfig.Privacy == Ndis802_11Encryption3Enabled)
{
if(!bNeed8021X)
{
wzcConfig.KeyLength = wcslen(pKey);
if((wzcConfig.KeyLength < 8) || (wzcConfig.KeyLength > 63))
{
RETAILMSG(DBG_MSG, (TEXT("WPA-PSK/TKIP key should be 8-63 char long string.\n")));
return FALSE;
}
char szEncryptionKeyValue8[64]; // longest key is 63
memset(szEncryptionKeyValue8, 0, sizeof(szEncryptionKeyValue8));
WideCharToMultiByte(CP_ACP,
0,
pKey,
wzcConfig.KeyLength + 1,
szEncryptionKeyValue8,
wzcConfig.KeyLength + 1,
NULL,
NULL);
WZCPassword2Key(&wzcConfig, szEncryptionKeyValue8);
EncryptWepKMaterial(&wzcConfig);
wzcConfig.dwCtlFlags |= WZCCTL_WEPK_XFORMAT
| WZCCTL_WEPK_PRESENT
| WZCCTL_ONEX_ENABLED;
}
wzcConfig.EapolParams.dwEapFlags = EAPOL_ENABLED;
wzcConfig.EapolParams.dwEapType = DEFAULT_EAP_TYPE;
wzcConfig.EapolParams.bEnable8021x = TRUE;
wzcConfig.WPAMCastCipher = Ndis802_11Encryption2Enabled;
}
return TRUE;
}
通過以上操作,完全可以連接到可用的無線AP了,再加上些適當的UI程序,完全可以用來替代 Windows CE 自帶的無線配置程序了,我再連接中放置了一個簡單的而完整的測試程序,相信大家看了以后都知道怎么操作無線網卡了
http://download.csdn.net/source/927575
Note:需CE4.0或更高版本(兼容NDIS5.1)支持
一、加入頭文件
#include <winioctl.h>
#include <ntddndis.h>
#include <nuiouser.h>
二、Attach to NDISUIO
HANDLE hNdis = ::CreateFile( NDISUIO_DEVICE_NAME, GENERIC_ALL, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
(HANDLE)INVALID_HANDLE_VALUE );
三、獲取設備名稱
UCHAR cbQueryBuffer[ 1024 ];
PNDISUIO_QUERY_BINDING pQueryBinding;
DWORD dwBytesReturned = 0;
pQueryBinding = (PNDISUIO_QUERY_BINDING)&cbQueryBuffer[ 0 ];
pQueryBinding->BindingIndex = 0;
if ( ::DeviceIoControl( hNdis,
IOCTL_NDISUIO_QUERY_BINDING,
(LPVOID)&cbQueryBuffer[ 0 ],
sizeof(NDISUIO_QUERY_BINDING),
(LPVOID)&cbQueryBuffer[ 0 ],
sizeof(cbQueryBuffer),
&dwBytesReturned,
NULL ) == TRUE )
{
TCHAR* pDeviceName = (TCHAR*)( cbQueryBuffer + pQueryBinding->DeviceNameOffset );
}
四、檢測連接狀態
NIC_STATISTICS nicStatistics = { 0 };
DWORD dwBytesReturned = 0;
BOOL bConnected = FALSE;
nicStatistics.ptcDeviceName = pDeviceName;
if ( ::DeviceIoControl( hNdis,
IOCTL_NDISUIO_NIC_STATISTICS,
NULL,
0,
&nicStatistics,
sizeof(NIC_STATISTICS),
&dwBytesReturned,
NULL ) == TRUE )
{
bConnected = ( nicStatistics.MediaState == MEDIA_STATE_CONNECTED );
}
五、獲取信號強度
// example.
// < -90 : No Signal
// < -81 : Very Low
// < -71 : Low
// < -67 : Good
// < -57 : Very Good
// ... : Excellent
NDISUIO_QUERY_OID ndisQueryOid = { 0 };
DWORD dwBytesReturned = 0;
int nDb = 0;
ndisQueryOid.Oid = OID_802_11_RSSI;
ndisQueryOid.ptcDeviceName = pDeviceName;
if ( ::DeviceIoControl( hNdis,
IOCTL_NDISUIO_QUERY_OID_VALUE,
(LPVOID)&ndisQueryOid,
sizeof(ndisQueryOid),
(LPVOID)&ndisQueryOid,
sizeof(ndisQueryOid),
&dwBytesReturned,
NULL ) == TRUE )
{
::CopyMemory( &nDb, &ndisQueryOid.Data[ 0 ], sizeof(ULONG) );
}
http://msdn.microsoft.com/en-us/library/ms706716(VS.85).aspx
The WlanEnumInterfaces function enumerates all of the wireless LAN interfaces currently enabled on the local computer.
Syntax
DWORD WINAPI WlanEnumInterfaces(
__in HANDLE hClientHandle,
__reserved PVOID pReserved,
__out PWLAN_INTERFACE_INFO_LIST *ppInterfaceList
);
Parameters
- hClientHandle [in]
-
The client's session handle, obtained by a previous call to the WlanOpenHandle function.
- pReserved [in]
-
Reserved for future use. This parameter must be set to NULL.
- ppInterfaceList [out]
-
A pointer to storage for a pointer to receive the returned list of wireless LAN interfaces in a WLAN_INTERFACE_INFO_LIST structure.
The buffer for the WLAN_INTERFACE_INFO_LIST returned is allocated by the WlanEnumInterfaces function if the call succeeds.
Return Value
If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value may be one of the following return codes.
Return code |
Description |
- ERROR_INVALID_PARAMETER
|
A parameter is incorrect. This error is returned if the hClientHandle or ppInterfaceList parameter is NULL. This error is returned if the pReserved is not NULL. This error is also returned if the hClientHandle parameter is not valid.
|
- ERROR_INVALID_HANDLE
|
The handle hClientHandle was not found in the handle table.
|
- RPC_STATUS
|
Various error codes.
|
- ERROR_NOT_ENOUGH_MEMORY
|
Not enough memory is available to process this request and allocate memory for the query results.
|
Remarks
The WlanEnumInterfaces function allocates memory for the list of returned interfaces that is returned in the buffer pointed to by the ppInterfaceList parameter when the function succeeds. The memory used for the buffer pointed to by ppInterfaceList parameter should be released by calling the WlanFreeMemory function after the buffer is no longer needed.
Examples
The following example enumerates the wireless LAN interfaces on the local computer and prints values from the retrieved WLAN_INTERFACE_INFO_LIST structure and the enumerated WLAN_INTERFACE_INFO structures.
Note This example will fail to load on Windows Server 2008 and Windows Server 2008 R2 if the Wireless LAN Service is not installed and started.
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <wlanapi.h>
#include <objbase.h>
#include <wtypes.h>
#include <stdio.h>
#include <stdlib.h>
// Need to link with Wlanapi.lib and Ole32.lib
#pragma comment(lib, "wlanapi.lib")
#pragma comment(lib, "ole32.lib")
int wmain()
{
// Declare and initialize variables.
HANDLE hClient = NULL;
DWORD dwMaxClient = 2; //
DWORD dwCurVersion = 0;
DWORD dwResult = 0;
int iRet = 0;
WCHAR GuidString[40] = {0};
int i;
/* variables used for WlanEnumInterfaces */
PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
PWLAN_INTERFACE_INFO pIfInfo = NULL;
dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwCurVersion, &hClient);
if (dwResult != ERROR_SUCCESS) {
wprintf(L"WlanOpenHandle failed with error: %u\n", dwResult);
// FormatMessage can be used to find out why the function failed
return 1;
}
dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList);
if (dwResult != ERROR_SUCCESS) {
wprintf(L"WlanEnumInterfaces failed with error: %u\n", dwResult);
// FormatMessage can be used to find out why the function failed
return 1;
}
else {
wprintf(L"Num Entries: %lu\n", pIfList->dwNumberOfItems);
wprintf(L"Current Index: %lu\n", pIfList->dwIndex);
for (i = 0; i < (int) pIfList->dwNumberOfItems; i++) {
pIfInfo = (WLAN_INTERFACE_INFO *) &pIfList->InterfaceInfo[i];
wprintf(L" Interface Index[%d]:\t %lu\n", i, i);
iRet = StringFromGUID2(pIfInfo->InterfaceGuid, (LPOLESTR) &GuidString, 39);
// For c rather than C++ source code, the above line needs to be
// iRet = StringFromGUID2(&pIfInfo->InterfaceGuid, (LPOLESTR) &GuidString, 39);
if (iRet == 0)
wprintf(L"StringFromGUID2 failed\n");
else {
wprintf(L" InterfaceGUID[%d]: %ws\n",i, GuidString);
}
wprintf(L" Interface Description[%d]: %ws", i,
pIfInfo->strInterfaceDescription);
wprintf(L"\n");
wprintf(L" Interface State[%d]:\t ", i);
switch (pIfInfo->isState) {
case wlan_interface_state_not_ready:
wprintf(L"Not ready\n");
break;
case wlan_interface_state_connected:
wprintf(L"Connected\n");
break;
case wlan_interface_state_ad_hoc_network_formed:
wprintf(L"First node in a ad hoc network\n");
break;
case wlan_interface_state_disconnecting:
wprintf(L"Disconnecting\n");
break;
case wlan_interface_state_disconnected:
wprintf(L"Not connected\n");
break;
case wlan_interface_state_associating:
wprintf(L"Attempting to associate with a network\n");
break;
case wlan_interface_state_discovering:
wprintf(L"Auto configuration is discovering settings for the network\n");
break;
case wlan_interface_state_authenticating:
wprintf(L"In process of authenticating\n");
break;
default:
wprintf(L"Unknown state %ld\n", pIfInfo->isState);
break;
}
wprintf(L"\n");
}
}
if (pIfList != NULL) {
WlanFreeMemory(pIfList);
pIfList = NULL;
}
return 0;
}
Requirements
Minimum supported client
|
Windows Vista, Windows XP with SP3 |
Minimum supported server
|
Windows Server 2008 |
Redistributable
|
Wireless LAN API for Windows XP with SP2 |
Header
|
Wlanapi.h (include Wlanapi.h) |
Library
|
Wlanapi.lib |
DLL
|
Wlanapi.dll |
用native wifi api吧。
VS2008下:
加入:
#include "Wlanapi.h"
#pragma comment(lib, "Wlanapi.lib")
變量:
DWORD pdwNegotiatedVersion;
HANDLE phClientHandle;
PWLAN_INTERFACE_INFO_LIST wiiList;
然后用下面語句打開handle.
WlanOpenHandle (1,NULL,&pdwNegotiatedVersion,&phClientHandle);
用WlanEnumInterfaces來枚舉interfaces到一個WLAN_INTERFACE_INFO_LIST結構。如下:
WlanEnumInterfaces(phClientHandle,NULL,&wiiList);
然后wiiList->dwNumberOfItems的值就是無線網卡的數量。
VC6的時候沒有Wlanapi.h頭文件。可以直接LoadLibrary("wlanapi.dll"),然后GetProcAddress取得以上兩個函數指針。
#include <windows.h>
#include <stdio.h>
int main()
{
HKEY bKey,hKey;
LONG retVal;
DWORD dwBuf = 1;
DWORD dwLen;
char SubKey[] = "System\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
char SubKeyValueName[] = "MediaSubType";
bKey = HKEY_LOCAL_MACHINE;
retVal = RegOpenKeyEx(bKey,SubKey,0,KEY_ALL_ACCESS,&hKey);
if(retVal!=ERROR_SUCCESS)
{
return -1;
}
RegQueryValueEx( bKey, SubKeyValueName, 0, 0, (LPBYTE)&dwBuf, &dwLen );
if ( dwBuf == 1 )
printf( "普通網卡\n " );
if ( dwBuf == 2 )
printf( "無線網卡\n " );
return 0;
}
轉載文章一:http://www.cnblogs.com/shanhaobo/articles/1065380.html
[3D基礎]理解計算機3D圖形學中的坐標系變換
要談坐標系變換,那么坐標系有哪些呢?依次有:物體坐標系,世界坐標系,相機坐標系,投影坐標系以及屏幕坐標系.我要討論的就是這些坐標系間的轉換。
這些坐標系不是憑空而來,他們都是為了完成計算機3D圖形學最最最基本的目標而出現.
計算機3D圖形學最最最基本的目標就是:將構建好的3D物體顯示在2D屏幕坐標上.
初看好像就是將最初的物體坐標系轉換到屏幕坐標系就可以了呀,為什么多出了世界坐標系,相機坐標系,投影坐標 系。這是因為:在一個大世界里有多個物體,而每個物體都有自己的坐標系,如何表述這些物體間相對的關系,這個多出了世界坐標系;如果只需要看到這個世界其 中一部分,這里就多出了相機坐標系;至于投影坐標系那是因為直接將3D坐標轉換為屏幕坐標是非常復雜的(因為它們不僅維度不同,度量不同(屏幕坐標一般都 是像素為單位,3D空間中我們可以現實世界的米,厘米為單位),XY的方向也不同,在2D空間時還要進行坐標系變換),所以先將3D坐標降維到2D坐標, 然后2D坐標轉換到屏幕坐標。
理解3D圖形學的第一步:理解左手坐標系與右手坐標系
為什么會有左手坐標系與右手坐標系之分?
在3D空間(沒錯!就是3D)中,所有2D坐標系是等價的(就是通過一系列的仿射變換,可以互相轉換)
而3D坐標系不是等價的,通過仿射變換,是無法將左手坐標系轉換到右手坐標系;也就是說,物體坐標系用的就是左手坐標系,世界坐標系用的是右手 坐標系,那么物體可能就是不會是我們所希望的樣子了,可能是倒立的,也可能是背對著我們的,所以我們要區分左手坐標系與右手坐標系。也許在4D空間,左右 手坐標系就可以互相變換了吧。
進入正題吧:
首先討論的是物體坐標系->世界坐標系
前面說了為了描述多個物體間相對的關系,這里引進了世界坐標系,所以世界坐標系是個參考坐標系。
這一步的目的將所有的物體的點都轉移到世界坐標系,這里主要涉及的是旋轉,縮放,平移等。
不過我將詳細說明為何及如何用矩陣來描述這些變換。
例:如果有兩個坐標系C與C`, C`是C繞Z軸旋轉θ得到的。下面是各坐標軸的變換:
如果是C坐標系的點P(x, y, z),而在C`的表示就是

這時該如何建立矩陣呢? 答案就是區分你用的是行向量還是列向量.也許有人會問為什么不區分是左手坐標系還是右手坐標系呢?因為C可以變換到C`,那么他們一定是同在左手坐標系或右手坐標系,變換只能在可以互相轉換的坐標系之間進行。
如果你用的是行向量:由于行向量只能左乘矩陣(注意乘與乘以的區別)
所以矩陣形式應該是這樣

只有這樣,在左乘矩陣時才能得到上面P`的形式。

如果你用的是列向量: 由于列向量只能右乘矩陣(注意乘與乘以的區別)
所以矩陣形式應該是這樣

只有這樣,在右乘矩陣時才能得到上面P`的形式。

至于如何旋轉,縮放,平移我不在多說。
…………………………………覺得自己好像跑題了
.還好這兩個坐標系變換很簡單。
我們再討論世界坐標系->相機坐標系
引進相機的目的就是只需看到世界的一部分,而哪些是可以在相機里看到的,就需要進行篩選。將物體轉換到相機坐標系,這樣相機坐標系進行篩選時就會簡單很多。這里的重點是構建相機坐標系。
物體坐標系,世界坐標系是美工在繪制時就定義好了的。而相機坐標系是需要程序實時構建的。(當然這是通常情況下,如果你要建立一個世界,這個世界都是圍繞 你轉,要實時改變所有物體坐標系,固定相機坐標系(其實這時候相機坐標系就是世界坐標系),建立一個地心說的世界,我也沒辦法,你的思維也太不一樣了。)
如何構建相機坐標系呢?首先我們要明確目標:我們是要構建3D坐標系(好像是廢話),三個坐標軸要互相垂直(也好像是廢話).
我們一般用UVN相機。例如:D3D的D3DXMatrixLookAtLH,D3DXMatrixLookAtRH,OGL的gluLookAt(右手坐標系).
如何建立呢UVN相機呢? 我們就要利用叉積這個工具了:兩個不平行,不重疊的向量的叉積可以得到與這兩個向量互相垂直的向量。
如果有了相機的位置與目標的位置那么我們可以確定一個Z軸(有人問為什么是Z軸,因為物體的遠與近我們就習慣用Z值來表示的)。求Z軸時要注意 是左手坐標系還是右手坐標系,左右手坐標系XY軸方向相同時,Z軸的方向相反。所以左手坐標系是目標位置減去相機位置,而右手坐標系則是相機位置減去目標 位置。記得normalize
這是我們要得到X與Y軸了。如何求X,Y軸呢?
一般方法是:
1、選擇一個臨時Y軸,
2、對臨時Y 與Z 軸進行叉積求得一個X軸
3、X軸再與Z軸進行叉積,得到一個Y軸。
有了XYZ就可以求出旋轉的相機矩陣了。
如何選擇一個Y軸呢?大多數情況下是(0,1,0),但是如果是相機位置E與目標位置T垂直,即(E-T=(0,+/-1,0)時),這時就不能用(0,1,0)了, 因為兩個平行向量的叉積是零向量,所以我們就要另選一個Y軸。
但是我覺得我們可以改變方法。
如果不能選Y軸,我們就選擇一個臨時X軸,這個臨時X軸就是(1,0,0)。
然后再對臨時X軸與Z軸進行叉積求得一個Y軸。
最后Y軸再與Z軸進行叉積,得到X軸。
這樣可以得到XYZ軸。
最后再根據行向量與列向量建立相機矩陣,再進行平移。
相機坐標系->投影坐標系.
投影的目的就是:降維.
兩種投影方式:正交投影與透視投影.
在我們TEAM中易穎已經寫了,我就不多說了,大家去看他的文章。
投影坐標系->屏幕坐標系
這是最簡單的。2D坐標變換。也不多說。
轉載文章2:http://www.xingousi.com/computer/computergraphics.htm
計算機圖形學筆記(Part 1 ):計算機圖形學透視投影變換原理及一點和兩點透視
一、平行互分法
吳英凡所寫的《透視作圖的新方法——交點法體系》,其中談到的平行互分法,還是有道理的。
其實簡單點說,就是透視圖上的兩條“原來空間中的平行線”(在畫面上透視投影為相交于滅點),通過其中一條透視投影直線的端點畫另一條透視投影直線的平行線,必平行于畫面;這第三條線在畫面的透視投影的滅點必然在另一條透視投影線上。
二、透視投影變換學習總結
1、用多維數列表示低維空間坐標,加深理解齊次坐標表示法。
齊次坐標表示法可以方便地運算,同時形狀不變。[x,y,z,0]表示一個無窮的點。
2、透視投影變換公式可以看成兩個矩陣的乘積,其中一個做透視變換,另外一個作正投影
保留的z'值的確切含義:指的是在完全作完透視投影變換之前,僅作透視投影之后的一條線.
它的幾何意義見李建平《計算機圖形學原理教程》第44頁。
3、左手和右手坐標系的坐標轉換
“視點坐標系與一般的物體所在的世界坐標系不同,它遵循左手法則,即左手大拇指指向Z正軸,與之垂直的四個手指指向X正軸,四指彎曲90度的方向是Y正軸。而世界坐標系遵循右手法則的。”
4、視點坐標系的透視變換公式很重要!!王飛著計算機圖形學書65頁
5、z'值的確切含義:指的是在完全作完透視投影變換之前,僅作透視投影之后的一條線
三、兩點透視的變換矩陣:
王飛編著《計算機圖形學基礎》的道理是:
從平面圖形的平移、旋轉、錯切開始推導,兩點透視的變換矩陣可以看成是:
物體本身有一個物體坐標系——xw,yw,zw,視點作為原點又構成一個視點坐標系——xe,ye,ze,物體坐標系z軸朝上,y軸朝向遠處;而視點坐標系y軸朝上,z軸朝向遠處。
這樣,最終的二點透視狀態可以這樣取得,首先把物體的位置的物體坐標系表示法轉化為視點坐標系的表示法(第一個矩陣),然后圍繞視點坐標系的y軸旋轉(第二個矩陣),然后在x,y,z方向上平移(第三個矩陣),最后做透視變換(第四個矩陣),它的原文是把平移放在第二步,我在平移之前轉動,目的是保證了物體旋轉的軸在離它不遠的地方:
我使用的矩陣變換如下,原文是是把平移放在第二步:
[xw,yw,zw,1]*
*
*
*
最后所得結果是一個新的矩陣,
[xe ye ze 1]=[cos
*xw-sin
*yw+l zw+m 2sin
*xw+2cos
*yw+2n-d (sin
*xw+cos
*yw+n)/d]
把最后一項變成1,可得
=[(cos
*xw-sin
*yw+l)*d/(sin
*xw+cos
*yw+n (zw+m)*d/(sin
*xw+cos
*yw+n) (2sin
*xw+2cos
*yw+2n-d)*d/(sin
*xw+cos
*yw+n) 1 ]
即:
Xe= (cos
*xw-sin
*yw+l)*d/(sin
*xw+cos
*yw+n)
Ye=(zw+m)*d/(sin
*xw+cos
*yw+n)
Ze=(2sin
*xw+2cos
*yw+2*n-d)*d/(sin
*xw+cos
*yw+n)
實際上我的delphi程序里面是這樣的:
xe:=trunc((cos(angle)*eee[ii][k].X-sin(angle)*eee[ii][k].Y+l)*d/(sin(angle)*eee[ii][k].X+cos(angle)*eee[ii][k].Y+n));
ye:=trunc((hhh[ii][k]+m)*d/(sin(angle)*eee[ii][k].X+cos(angle)*eee[ii][k].Y+n)); //透視變換
//ze可以考慮使用作為消隱
Ze:=trunc((2*sin(angle)*xw+2*cos(angle)*yw+2*n-d)*d/(sin(angle)*xw+cos(angle)*yw+n));
四、通過變換過的兩點透視的結果xe,ye,zw和
*,反求原來的物體坐標xw,yw
即:xe,ye,zw和
已知,求出xw,yw
根據:
Xe= (cos
*xw-sin
*yw+l)*d/(sin
*xw+cos
*yw+n) (1)
Ye=(zw+m)*d/(sin
*xw+cos
*yw+n) (2)
Ze=(2sin
*xw+2cos
*yw+2n-d)*d/(sin
*xw+cos
*yw+n) (3)
(1)除以(2)得
Xe/Ye= (cos
*xw-sin
*yw+l)/ (zw+m)
(Xe*(zw+m))/Ye=cos
*xw-sin
*yw+l
(Xe*(zw+m))/(Ye* cos
)=xw-tan
*yw+l/cos
Xw=(Xe*(zw+m))/ (Ye* cos
) - (ye*l)/(Ye* cos
)+( sin
*yw*ye)/ (Ye* cos
)
Xw=(Xe*(zw+m)+ sin
*yw*ye- ye*l)/ (Ye* cos
) (4)
由(2)得
Ye*(sin
*xw+cos
*yw+n)= (zw+m)*d
Ye*(sin
*xw)+ye* cos
*yw+n*ye=(zw+m)*d
把(4)代入上式得
Ye* sin
*(Xe*(zw+m)+ sin
*yw*ye- ye*l)/ (Ye* cos
)+ye* cos
*yw+n*ye=(zw+m)*d
約去ye,得
sin
*(Xe*(zw+m)+ sin
*yw*ye- ye*l)/ cos
+ye* cos
*yw+n*ye=(zw+m)*d
tan
*(Xe*zw+xe*m+ sin
*yw*ye- ye*l) +ye* cos
*yw+n*ye=(zw+m)*d
tan
*Xe*zw+xe*m *tan
- ye*l*tan
+ sin
*tan
*yw*ye+ye* cos
*yw+n*ye=(zw+m)*d
(sin
*tan
*ye+ye* cos
)*yw+ tan
*Xe*zw+xe*m *tan
- ye*l*tan
+n*ye=(zw+m)*d
最后得
Yw=[(zw+m)*d- tan
*Xe*zw- xe*m *tan
+ ye*l*tan
- n*ye]/ (sin
*tan
*ye+ye* cos
) (5)
而前面已經得到
Xw=(Xe*(zw+m)+ sin
*yw*ye- ye*l)/ (Ye* cos
) (4)
實際上相當于opengl里面的逆變換,從鼠標選中的屏幕位置來確定對應的三維空間中位置,opengl使用gluUnProject和gluUnProject4來計算。
摘要: 1. API之網絡函數
WNetAddConnection 創建同一個網絡資源的永久性連接
WNetAddConnection2 創建同一個網絡資源的連接
WNetAddConnection3 創建同一個網絡資源的連接
WNetCancelConnection 結束一個網絡連接
WNetCancelConnection2 結束一個網絡連接
WNetCloseEnum 結束一次枚舉操作
...
閱讀全文
摘要: //本機網絡連接類型(成功)
#define NET_TYPE_RAS_DIAL_UP_CONNECT_NET 0x01 //上網類型:采用RAS撥號連接上網 ...
閱讀全文
和所有初學者一樣,剛開始接觸新的東西,總想把畫面做的漂亮些,可是在vc中很難做到,比如對話框中按鈕等控件的字體設置,就頗費了我一番功夫。
一。做成一個函數,改變字體大小,方法如下:
1。在最開頭聲明一個全局的字體指針 CFont *my_font=new CFont();//注意初始化,不能為空
2。在需要改變字體的地方調用函數:
set_font(60,my_font,"隸書"); //字體大小、指針、名稱
GetDlgItem(IDC_anniu)->SetFont(my_font);//改變字體
3。對與不同的字體,你需要設置不同的字體指針就可以了。
4。注意在退出時要刪除字體,否則多次調用出現問題
BOOL CMyDlg::DestroyWindow()
{
if (my_font) my_font->DeleteObject();
return CDialog::DestroyWindow();
}
5。 以下是函設置字體函數的詳細內容:
void set_font(int height,CFont *font,char *name)
{
// font=new CFont();//不在此,要在外部初始化,否則找不到指針
LOGFONT lf;
lf.lfHeight=20; lf.lfWidth= 0;
lf.lfEscapement=0; lf.lfOrientation= 0;
lf.lfWeight= 760; lf.lfItalic= 0;
lf.lfUnderline =0; lf.lfStrikeOut =0;
lf.lfCharSet =134; lf.lfOutPrecision =3;
lf.lfClipPrecision =2; lf.lfQuality= 1;
lf.lfPitchAndFamily =2; lstrcpy(lf.lfFaceName, "宋體");
lf.lfOutPrecision =OUT_TT_ONLY_PRECIS;//OUT_TT_PRECIS;
lf.lfHeight= height; //字體大小
lstrcpy(lf.lfFaceName, name);//名稱
if (font!=NULL)
{
font->DeleteObject();
font->CreateFontIndirect(&lf);
}
}
#define say(ch) AfxMessageBox(ch)//自己使用的提示函數
#define bt(ch) SetWindowText(ch)//自己使用的提示函數
二。改變字體顏色,要加入系統函數
HBRUSH CMyDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
// return hbr;
switch(nCtlColor)
{
case CTLCOLOR_STATIC://靜態文本
TCHAR lpszClassName[255];
GetClassName(pWnd->m_hWnd, lpszClassName, 255);
if(_tcscmp(lpszClassName, TRACKBAR_CLASS) == 0)//類名是拉動條
return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
else if (IDC_tishi==pWnd->GetDlgCtrlID())//指定的控件ID提示
{
pDC ->SetTextColor(RGB(255,100,100));//RGB(100,255,100)
return hbr;
}
else
{
pWnd->GetWindowText(ch,40);
if ( strcmp(ch,"測試數據")==0 )//指定的標題
{
pDC ->SetTextColor(RGB(255,0,200));
return hbr;
}
pDC->SetBkColor(RGB(255,255,255));
return hbr;//(HBRUSH) GetStockObject(HOLLOW_BRUSH);
}
}
break;
case CTLCOLOR_BTN://按鈕,好象不行
// pDC ->SetBkMode(OPAQUE);//背景不透明
// pDC->SetBkMode(TRANSPARENT);//背景透明
// say("button");
CString str;
//owen draw //注意,右擊按鈕屬性改為自繪式
pWnd->GetWindowText(str); //得到標題內容
RECT rect;
pWnd->GetClientRect(&rect);//得到矩形范圍大小
pDC->SelectStockObject(BLACK_PEN);
pDC->Rectangle(&rect);//黑筆畫外邊矩形
rect.left+=2;rect.top+=2;
rect.right-=2 ;rect.bottom-=2;
pDC->SelectStockObject(WHITE_PEN);
pDC->Rectangle(&rect);//白筆畫內矩形
pDC->SelectObject(font);//選擇字體,大小
pDC->SetTextColor(RGB(0,255,0)); //字體得前景顏色
pDC->SetBkColor(RGB(255,0,255)); //字體的背景顏色
pDC->DrawText(str, &rect, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
return (HBRUSH) GetStockObject(HOLLOW_BRUSH);
break;
case CTLCOLOR_EDIT://編輯框
pDC ->SetTextColor(RGB(255,0,0));//文本顏色
pDC ->SetBkColor(RGB(255,255,255));//文本背景
break;
case CTLCOLOR_LISTBOX://列表框
// pDC ->SetTextColor(RGB(255,255,0));
// pDC ->SetBkColor(RGB(160,60,0));
break;
}
// TODO: Return a different brush if the default is not desired
return hbr;
}
2008-06-23 日 星期一 天氣 晴
很久沒有來這里寫東西了,感覺真有點對不起這個空間.
過年到現在一直都忙于工作.(項目一個接一個.報告一個接一個)
驅動開發我關注很久了.就是沒有實際行動.終于有一天不知道怎么了下定了決心搞了.
首先要搞的是編譯和編輯環境,個人感覺網上有很多DDK 與Visual Studio6的配置很多都是有問題的,而且都是幾年前寫的.不知道是我理解能力有問題還是作者表達能力有問題老是弄不好.最后弄雖然弄好也是轉了一個大圈.但是用起來實在是不敢恭謹.也許是我用慣了delphi 和vs2005的緣故.vs2005的代碼編輯和智能排版功能可以說是very Good ! 最后我還是決定把ddk和vs2005給掛上關系.花了我一個上午的時間也弄出來了.用起來比vs6.0爽了很多.效果也達到了之前設想的.我寫這些出來是為了減少步我后塵的同志們對驅動的恐懼.
步驟:
1.首先前期準備: 安裝xp ddk 和vs2005 (vs2005只要安裝c/c++就可以了) 安裝的步驟我就不在這里說了.
2.準備一個驅動源代碼(用ddk內的例子也可以)
3.vs2005建立一個makeFile 工程.
4.把源碼拷貝倒vs2005目錄內(包括*.c , *.h,還有source 和makefile)
5.在vs2005工程目錄建立一個MakeDriver.bat 文件里面內容是:
@echo off
if "%1"=="" goto usage1
if "%3"=="" goto usage2
if not exist %1\bin\setenv.bat goto usage3
echo params1: %1
echo params2: %2
echo params3: %3
echo call %1\BIN\setenv %1 %3
call %1\BIN\setenv %1 %3
echo cd /d %2
cd /d %2
build
goto ok
:usage1
echo Error: the first parameter is NULL!
goto exit
:usage2
echo Error: the third parameter is NULL!
goto exit
:usage3
echo Error: %1\bin\setenv.bat not exist!
goto exit
:ok
echo MakeDriver %1 %2 %3
:exit
5. 設置 vs2005 工程的屬性
編譯分 debug 和 release 兩個版本
在 NMke 設置里面設置 ( 我用的是 vs2005 繁體版 )
releasee 版本.只要將” 建置命令列 ” 內容改成 MakeDriver %DDKROOT% $(ProjectDir) fre 就可以了.
我的 ddk 是安裝在 c 盤 .IntelliSense 是為了能在編輯代碼的時候彈出輸入的列表 . 比如結構體內的成員等等
建置命令列 : 是調用 MakeDriver.bat 文件編譯源代碼 .
如果加上 vss 代碼管理 . 一個驅動代碼工程管理就算完善了 .
寫到這里了 .
順便提下要了解 MakeDriver.bak 內的作用就要了解一些批處理的 dos 命令 . 上網找吧 . 網上什么都有關鍵是看你怎么找 .
http://harborwan.blog.sohu.com/39762230.html
通常驅動程序的調試都是用ddk在cmd中完成的。這部分我暫時略過。下面先介紹如何設置vc++6.0在Visual Studio 6.0集成環境中開發設備驅動程序的方法。
在Windows上,Windows DDK提供的開發環境是基于命令行的,操作起來極為不便,而Visual Studio 6.0給我們提供了非常友好易用的集成環境,讓我們有如虎添翼之感。
那么,能否利用Visual Studio的集成環境來開發驅動程序呢?答案是可以的。通過對Visual Studio集成環境的簡單設置,創建好自己的驅動開發集成環境就可以了。
1,
第一:安裝Vc++6.0,我裝的是英文版,中文版應該也可以,不過我沒試。
第二:安裝winXP DDK,注意,安裝目錄中間不能有空格,比如D:\Program Files不行,
我直接裝在了D盤,裝完后設置環境變量DDKROOT為安裝目錄D:\WINDDK\2505。
2,創建一個目錄,作為開發目錄,我是利用<<PCI設備開發寶典>>的光盤中的工程文件PCI9052Demo,直接考到E盤,所以,工作目錄下是E:\PCI9052Demo
3,工作目錄下創建一個批處理文件MakeDrvr.bat,內容如下:
@echo off
if "%1"=="" goto usage
if "%3"=="" goto usage
if not exist %1\bin\setenv.bat goto usage
call %1\bin\setenv %1 %4
%2
cd %3
build -b -w %5 %6 %7 %8 %9
goto exit
:usage
echo usage MakeDrvr DDK_dir Driver_Drive Driver_Dir free/checked [build_options]
echo eg MakeDrvr %%DDKROOT%% F: %%WDMWorkshop%% free -cef
:exit
解釋以下:
1% 是DDK_dir,也就是ddk的安裝目錄
2% 是Driver_Drive,是你工作目錄所在的盤符,這里是E:
3% 是Driver_Dir,是你工作目錄的路徑,這里是E:\PCI9052Demo
4% 是編譯模式,checked表示調試模式,free表示發行模式,這里是出問題的地方,后面再說。
該批處理首先對傳遞的參數作一些檢查,然后調用ddk的setenv命令設置環境變量,然后改變目錄為源程序所在驅動器和目錄,并最后調用build,-b保證顯示完全的錯誤信息,-w保證在屏幕上輸出警告,在vc ide里的output窗口中可以看到這些錯誤和警告。
4,建立一個空白工程
選File的new菜單項,然后選project欄的makefile,然后輸入路徑,一路next下去即可,visual studio提供兩種配置win32 debug和win32 release. 按照cant的《windows wdm 設備驅動程序開發指南》方法,可以刪除掉,添加Win32 Checked和 Win32 Free
5,VC的Project->settings里面改變設置
修改這兩種配置
=============================================================
Free
=============================================================
build command line:
e:\PCI9052Demo\MakeDrvr %DDKROOT% e: e:\PCI9052Demo free
rebuild all opinions:
-nmake /a
output filename:
PCI9052Demo.sys
browse info file name:
objfre\i386\PCI9052Demo.bsc
=============================================================
Checked
=============================================================
build command line:
e:\PCI9052Demo\MakeDrvr %DDKROOT% e: e:\PCI9052Demo checked
rebuild all opinions:
-nmake /a
output filename:
PCI9052Demo.sys
browse info file name:
objfre\i386\PCI9052Demo.bsc
6.添加源文件到工程
可以新建,也可