BSOne.SFC/Tocsg.Module/BlueMon/DBlueMonMain.pas

757 lines
23 KiB
Plaintext

unit DBlueMonMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.ComCtrls,
VirtualTrees, Tocsg.Bluetooth, EM.jwabluetoothapis, Vcl.Menus, Vcl.Buttons;
type
PBlueEnt = ^TBlueEnt;
TBlueEnt = record
sName,
sMajor,
sMinor,
sAddress: String;
nClassofDevice: Integer;
bAuth,
bConnected: Boolean;
dtSeen,
dtRecent: TDateTime;
Address: BLUETOOTH_ADDRESS;
end;
TPreventBlue = record
bPreventAll,
bPvIncName,
bPvMajor,
bPvMinor: Boolean;
// sExcepDev,
// sPvIncName,
// sPvMajor,
// sPvMinor: String;
end;
TDlgBlueMon = class(TForm)
pcMain: TPageControl;
tabBlueList: TTabSheet;
pnTop: TPanel;
btnRefresh: TButton;
tabBlueMon: TTabSheet;
vtList: TVirtualStringTree;
popFun: TPopupMenu;
miPreventBlue: TMenuItem;
Panel1: TPanel;
btnBlueMon: TButton;
N1: TMenuItem;
miCopyCB: TMenuItem;
chPreventBtAll: TCheckBox;
Label1: TLabel;
mmExcept: TMemo;
chPB_Name: TCheckBox;
edPB_Name: TEdit;
cbPB_MJ: TComboBox;
btnPB_MJAdd: TSpeedButton;
chPB_MJ: TCheckBox;
cbPB_MN: TComboBox;
btnPB_MNAdd: TSpeedButton;
chPB_MN: TCheckBox;
lxPB_MJ: TListBox;
btnPB_MJDel: TSpeedButton;
btnPB_MNDel: TSpeedButton;
lxPB_MN: TListBox;
procedure btnRefreshClick(Sender: TObject);
procedure vtListGetNodeDataSize(Sender: TBaseVirtualTree;
var NodeDataSize: Integer);
procedure vtListFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
procedure vtListHeaderClick(Sender: TVTHeader; HitInfo: TVTHeaderHitInfo);
procedure vtListGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
procedure miPreventBlueClick(Sender: TObject);
procedure vtListContextPopup(Sender: TObject; MousePos: TPoint;
var Handled: Boolean);
procedure miCopyCBClick(Sender: TObject);
procedure btnBlueMonClick(Sender: TObject);
procedure btnPB_MJAddClick(Sender: TObject);
procedure btnPB_MNAddClick(Sender: TObject);
procedure btnPB_MJDelClick(Sender: TObject);
procedure btnPB_MNDelClick(Sender: TObject);
private
{ Private declarations }
ThdBlueMon_: TThdBtDevNotify;
PreventBlue_: TPreventBlue;
PvBtNameList_,
PvMajorList_,
PvMinorList_,
ExcepBtDevList_: TStringList;
procedure RefreshBtList;
function ProcessPreventBT(pEnt: PBtDevEnt): Boolean;
procedure OnBtDevEntNotify(pEnt: PBtDevEnt; csBT: TBTChangeStates; var bPrevent: Boolean);
public
{ Public declarations }
Constructor Create(aOwner: TComponent); override;
Destructor Destroy; override;
end;
var
DlgBlueMon: TDlgBlueMon;
implementation
uses
Tocsg.Safe, VirtualTrees.Types, Tocsg.Convert, Tocsg.VTUtil,
Tocsg.Strings, Define, GlobalDefine, superobject, Tocsg.Driver,
Tocsg.Exception, DNoticeBT;
{$R *.dfm}
Constructor TDlgBlueMon.Create(aOwner: TComponent);
begin
Inherited Create(aOwner);
Caption := APP_TITLE;
ThdBlueMon_ := nil;
PvBtNameList_ := TStringList.Create;
PvMajorList_ := TStringList.Create;
PvMinorList_ := TStringList.Create;
ExcepBtDevList_ := TStringList.Create;
RefreshBtList;
pcMain.ActivePageIndex := 0;
end;
Destructor TDlgBlueMon.Destroy;
begin
if ThdBlueMon_ <> nil then
FreeAndNil(ThdBlueMon_);
FreeAndNil(ExcepBtDevList_);
FreeAndNil(PvMinorList_);
FreeAndNil(PvMajorList_);
FreeAndNil(PvBtNameList_);
Inherited;
end;
procedure TDlgBlueMon.RefreshBtList;
var
BTDevice: TBluetoothDevice;
i: Integer;
pData: PBlueEnt;
sMajor, sMinor: String;
begin
Guard(BTDevice, TBluetoothDevice.Create);
BTDevice.RefreshBTDevice;
vtList.BeginUpdate;
try
vtList.Clear;
for i := 0 to BTDevice.Count - 1 do
begin
BtDevTypeToStr(BTDevice[i].dInfo.ulClassofDevice, sMajor, sMinor);
pData := VT_AddChildData(vtList);
pData.sName := BTDevice[i].dInfo.szName;
pData.sMajor := sMajor;
pData.sMinor := sMinor;
pData.nClassofDevice := BTDevice[i].dInfo.ulClassofDevice;
pData.sAddress := BTDevice[i].sAddress;
pData.bConnected := BTDevice[i].dInfo.fConnected;
pData.bAuth := BTDevice[i].dInfo.fAuthenticated;
pData.dtSeen := BTDevice[i].dtLastSeen;
pData.dtRecent := BTDevice[i].dtLastUsed;
pData.Address := BTDevice[i].dInfo.Address;
end;
finally
vtList.EndUpdate;
end;
end;
procedure PopMsg(pEnt: PBtDevEnt; bPrevent: Boolean);
var
O: ISuperObject;
sMajor, sMinor, sData: String;
begin
BtDevTypeToStr(pEnt.dInfo.ulClassofDevice, sMajor, sMinor);
O := SO;
O.I['T'] := TYPE_MSG_PREVENT_BLUETOOTH;
sData := String(pEnt.dInfo.szName) + '|' + Format('%s (%s)', [sMajor, sMinor]) + '|' + pEnt.sAddress;
if bPrevent then
sData := sData + '|PV';
O.S['D'] := sData;
TDlgNoticeBT.Create(nil).PopupMessage(O.AsString);
end;
function TDlgBlueMon.ProcessPreventBT(pEnt: PBtDevEnt): Boolean;
var
sTemp1, sTemp2: String;
i: Integer;
begin
Result := false;
if not pEnt.dInfo.fConnected then
exit;
if ExcepBtDevList_.IndexOf(pEnt.sAddress) <> -1 then
exit;
if PreventBlue_.bPreventAll then
begin
Result := BluetoothRemoveDevice(pEnt.dInfo.Address) = 0;
Result := true; // 차단 대상이면 위 작업과 별개로 장치 차단을 하도록 함 22_0630 09:15:10 kku
exit;
end;
if PreventBlue_.bPvIncName then
begin
sTemp1 := UpperCase(pEnt.dInfo.szName);
for i := 0 to PvBtNameList_.Count - 1 do
begin
if sTemp1.Contains(PvBtNameList_[i]) then
begin
Result := BluetoothRemoveDevice(pEnt.dInfo.Address) = 0;
Result := true; // 차단 대상이면 위 작업과 별개로 장치 차단을 하도록 함 22_0630 09:15:10 kku
exit;
end;
end;
end;
if PreventBlue_.bPvMajor or PreventBlue_.bPvMinor then
begin
BtDevTypeToStr(pEnt.dInfo.ulClassofDevice, sTemp1, sTemp2);
if PreventBlue_.bPvMajor and (PvMajorList_.IndexOf(sTemp1) <> -1) then
begin
Result := BluetoothRemoveDevice(pEnt.dInfo.Address) = 0;
Result := true; // 차단 대상이면 위 작업과 별개로 장치 차단을 하도록 함 22_0630 09:15:10 kku
exit;
end;
if PreventBlue_.bPvMinor and (PvMinorList_.IndexOf(sTemp2) <> -1) then
begin
Result := BluetoothRemoveDevice(pEnt.dInfo.Address) = 0;
Result := true; // 차단 대상이면 위 작업과 별개로 장치 차단을 하도록 함 22_0630 09:15:10 kku
end;
end;
end;
procedure TDlgBlueMon.OnBtDevEntNotify(pEnt: PBtDevEnt; csBT: TBTChangeStates; var bPrevent: Boolean);
var
bPreventBtEnt: Boolean;
begin
if (csConnected in csBT) or (csAuthenticated in csBT) then
begin
bPreventBtEnt := ProcessPreventBT(pEnt);
bPrevent := bPrevent or bPreventBtEnt;
PopMsg(pEnt, bPreventBtEnt);
end;
end;
procedure TDlgBlueMon.btnBlueMonClick(Sender: TObject);
var
BTDevice: TBluetoothDevice;
i: Integer;
bPvreventCurBT: Boolean;
begin
if ThdBlueMon_ = nil then
begin
mmExcept.Text := Trim(mmExcept.Text);
edPB_Name.Text := Trim(edPB_Name.Text);
if chPB_Name.Checked and (edPB_Name.Text = '') then
begin
MessageBox(Handle, PChar('차단 문구를 입력해 주십시오.'), PChar(Caption), MB_ICONWARNING or MB_OK);
edPB_Name.SetFocus;
exit;
end;
if chPB_MJ.Checked and (lxPB_MJ.Count = 0) then
begin
MessageBox(Handle, PChar('차단 Major 정보를 하나이상 추가해 주십시오.'), PChar(Caption), MB_ICONWARNING or MB_OK);
exit;
end;
if chPB_MN.Checked and (lxPB_MN.Count = 0) then
begin
MessageBox(Handle, PChar('차단 Minor 정보를 하나이상 추가해 주십시오.'), PChar(Caption), MB_ICONWARNING or MB_OK);
exit;
end;
ZeroMemory(@PreventBlue_, SizeOf(PreventBlue_));
PreventBlue_.bPreventAll := chPreventBtAll.Checked;
PreventBlue_.bPvIncName := chPB_Name.Checked;
PreventBlue_.bPvMajor := chPB_MJ.Checked;
PreventBlue_.bPvMinor := chPB_MN.Checked;
SplitString(mmExcept.Text, ';', ExcepBtDevList_);
SplitString(UpperCase(edPB_Name.Text), ';', PvBtNameList_);
SplitString(lxPB_MJ.Items.CommaText, ',', PvMajorList_);
SplitString(lxPB_MN.Items.CommaText, ',', PvMinorList_);
bPvreventCurBT := false;
Guard(BTDevice, TBluetoothDevice.Create);
BTDevice.RefreshBTDevice;
for i := 0 to BTDevice.Count - 1 do
bPvreventCurBT := bPvreventCurBT or ProcessPreventBT(BTDevice[i]);
ThdBlueMon_ := TThdBtDevNotify.Create(true);
ThdBlueMon_.PreventBtDevs := bPvreventCurBT;
ThdBlueMon_.OnChangeBTDevice := OnBtDevEntNotify;
ThdBlueMon_.StartThread;
btnBlueMon.Caption := '감시/차단 중지';
end else begin
FreeAndNil(ThdBlueMon_);
btnBlueMon.Caption := '감시/차단 시작';
end;
chPreventBtAll.Enabled := ThdBlueMon_ = nil;
tabBlueList.TabVisible := chPreventBtAll.Enabled;
Label1.Enabled := chPreventBtAll.Enabled;
mmExcept.Enabled := chPreventBtAll.Enabled;
chPB_Name.Enabled := chPreventBtAll.Enabled;
edPB_Name.Enabled := chPreventBtAll.Enabled;
chPB_MJ.Enabled := chPreventBtAll.Enabled;
cbPB_MJ.Enabled := chPreventBtAll.Enabled;
btnPB_MJAdd.Enabled := chPreventBtAll.Enabled;
btnPB_MJDel.Enabled := chPreventBtAll.Enabled;
lxPB_MJ.Enabled := chPreventBtAll.Enabled;
chPB_MN.Enabled := chPreventBtAll.Enabled;
cbPB_MN.Enabled := chPreventBtAll.Enabled;
btnPB_MNAdd.Enabled := chPreventBtAll.Enabled;
btnPB_MNDel.Enabled := chPreventBtAll.Enabled;
lxPB_MN.Enabled := chPreventBtAll.Enabled;
if tabBlueList.TabVisible then
RefreshBtList;
Application.ProcessMessages;
end;
procedure TDlgBlueMon.btnPB_MJAddClick(Sender: TObject);
var
sAdd: String;
begin
sAdd := cbPB_MJ.Text;
if lxPB_MJ.Items.IndexOf(sAdd) = -1 then
lxPB_MJ.Items.Add(sAdd);
end;
procedure TDlgBlueMon.btnPB_MJDelClick(Sender: TObject);
begin
if lxPB_MJ.SelCount = 0 then
begin
MessageBox(Handle, PChar('삭제할 항목을 선택해 주십시오.'), PChar(Caption), MB_ICONWARNING or MB_OK);
exit;
end;
lxPB_MJ.DeleteSelected;
end;
procedure TDlgBlueMon.btnPB_MNAddClick(Sender: TObject);
var
sAdd: String;
begin
sAdd := cbPB_MN.Text;
if lxPB_MN.Items.IndexOf(sAdd) = -1 then
lxPB_MN.Items.Add(sAdd);
end;
procedure TDlgBlueMon.btnPB_MNDelClick(Sender: TObject);
begin
if lxPB_MN.SelCount = 0 then
begin
MessageBox(Handle, PChar('삭제할 항목을 선택해 주십시오.'), PChar(Caption), MB_ICONWARNING or MB_OK);
exit;
end;
lxPB_MN.DeleteSelected;
end;
procedure TDlgBlueMon.btnRefreshClick(Sender: TObject);
begin
RefreshBtList;
end;
function FindFirstBluetoothDevicePath(out ADevicePath: string): Boolean;
const
GUID_BLUETOOTH_DEVICE_INTERFACE: TGUID = '{E0CBF06C-CD8B-4647-BB8A-263B43F0F974}'; // 클래식
GUID_BLUETOOTHLE_DEVICE_INTERFACE: TGUID = '{781AEE18-7733-4CE4-ADD0-91F41C67B592}'; // BLE
type
TGuidArray = array[0..1] of TGUID;
var
Guids: TGuidArray;
FlagsArr: array[0..1] of DWORD;
g, f: Integer;
hDev: HDEVINFO;
DevIfData: SP_DEVICE_INTERFACE_DATA;
Required: DWORD;
Detail: PSPDeviceInterfaceDetailData;
Index: DWORD;
begin
Result := False;
ADevicePath := '';
Guids[0] := GUID_BLUETOOTH_DEVICE_INTERFACE; // 클래식
Guids[1] := GUID_BLUETOOTHLE_DEVICE_INTERFACE; // BLE
// PRESENT 유무 두 번 시도: (1) PRESENT 포함, (2) PRESENT 제외
FlagsArr[0] := DIGCF_DEVICEINTERFACE or DIGCF_PRESENT;
FlagsArr[1] := DIGCF_DEVICEINTERFACE;
for g := Low(Guids) to High(Guids) do
begin
for f := Low(FlagsArr) to High(FlagsArr) do
begin
hDev := SetupDiGetClassDevs(@Guids[g], nil, 0, FlagsArr[f]);
if hDev = INVALID_HANDLE_VALUE then
Continue;
try
Index := 0;
while True do
begin
ZeroMemory(@DevIfData, SizeOf(DevIfData));
DevIfData.cbSize := SizeOf(DevIfData);
if not SetupDiEnumDeviceInterfaces(hDev, nil, Guids[g], Index, DevIfData) then
begin
if GetLastError = ERROR_NO_MORE_ITEMS then
Break
else
Break; // 다른 오류라면 필요시 로그
end;
// 1차 호출: 필요한 버퍼 크기 얻기 (여기서는 실패 + ERROR_INSUFFICIENT_BUFFER가 정상)
Required := 0;
SetupDiGetDeviceInterfaceDetail(hDev, @DevIfData, nil, 0, Required, nil);
if (Required = 0) then
begin
Inc(Index);
Continue;
end;
Detail := PSPDeviceInterfaceDetailData(AllocMem(Required));
try
Detail.cbSize := SizeOf(TSPDeviceInterfaceDetailData);
if SetupDiGetDeviceInterfaceDetail(hDev, @DevIfData, Detail, Required, Required, nil) then
begin
{$IFDEF UNICODE}
ADevicePath := Detail.DevicePath;
{$ELSE}
ADevicePath := string(Detail.DevicePath); // 필요시 변환
{$ENDIF}
Result := True;
Exit; // 첫 번째 것만 반환. 모두 수집하려면 리스트에 Add하고 계속.
end;
finally
FreeMem(Detail);
end;
Inc(Index);
end;
finally
SetupDiDestroyDeviceInfoList(hDev);
end;
end;
end;
end;
function GetBluetoothDevicePath: string;
const
GUID_BLUETOOTH_DEVICE_INTERFACE: TGUID = '{E0CBF06C-CD8B-4647-BB8A-263B43F0F974}';
GUID_BLUETOOTHLE_DEVICE_INTERFACE: TGUID = '{781AEE18-7733-4CE4-ADD0-91F41C67B592}'; // BLE
GUID_BTHPORT_DEVICE_INTERFACE: TGUID = '{0850302A-B344-4FDA-9BE9-90576B8D46F0}';
var
hDInfo: HDEVINFO;
DevInfData: TSPDeviceInterfaceData;
pBuf: TBytes;
dwReqSize: DWORD;
begin
Result := '';
// FindFirstBluetoothDevicePath(Result);
// exit;
// DeviceInfoSet := SetupDiGetClassDevs(nil, 'BTHENUM', 0, DIGCF_PRESENT or DIGCF_ALLCLASSES);
// DeviceInfoSet := SetupDiGetClassDevs(@GUID_DEVCLASS_BLUETOOTH, nil, 0, DIGCF_PRESENT or DIGCF_ALLCLASSES);
// DeviceInfoSet := SetupDiGetClassDevs(@GUID_BLUETOOTH_DEVICE_INTERFACE, nil, 0, DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);
hDInfo := SetupDiGetClassDevs(@GUID_BTHPORT_DEVICE_INTERFACE, nil, 0, DIGCF_PRESENT or DIGCF_DEVICEINTERFACE);
if hDInfo = INVALID_HANDLE_VALUE then
exit;
try
ZeroMemory(@DevInfData, SizeOf(DevInfData));
DevInfData.cbSize := SizeOf(SP_DEVICE_INTERFACE_DATA);
if SetupDiEnumDeviceInterfaces(hDInfo, nil, GUID_BTHPORT_DEVICE_INTERFACE, 0, DevInfData) then
begin
SetupDiGetDeviceInterfaceDetail(hDInfo, @DevInfData, nil, 0, dwReqSize, nil);
// DeviceInterfaceDetailData := AllocMem(RequiredSize);
SetLength(pBuf, dwReqSize);
ZeroMemory(pBuf, dwReqSize);
PSPDeviceInterfaceDetailData(@pBuf[0]).cbSize := SizeOf(TSPDeviceInterfaceDetailData);
try
if SetupDiGetDeviceInterfaceDetail(hDInfo, @DevInfData, PSPDeviceInterfaceDetailData(@pBuf[0]), dwReqSize, dwReqSize, nil) then
Result := PChar(@pBuf[4]);
finally
//
end;
end;
finally
SetupDiDestroyDeviceInfoList(hDInfo);
end;
end;
function BluetoothDisconnectDevice(aBtAddr: BLUETOOTH_ADDRESS): Boolean;
const
IOCTL_BTH_DISCONNECT_DEVICE = (
(FILE_DEVICE_BLUETOOTH shl 16) or (METHOD_BUFFERED shl 14) or
($0301 shl 2) or FILE_ANY_ACCESS);
// CTL_CODE(FILE_DEVICE_BLUETOOTH, 6, METHOD_BUFFERED, FILE_ANY_ACCESS);
var
BtFindParams: BLUETOOTH_FIND_RADIO_PARAMS;
hRadio: THandle;
hDevice: THandle;
sDevPath: String;
ulllBuf: ULONGLONG;
begin
Result := false;
// 블루투스 라디오 핸들 가져오기
// hRadio := 0;
hDevice := 0;
// ZeroMemory(@BtFindParams, SizeOf(BtFindParams));
// BtFindParams.dwSize := SizeOf(BtFindParams);
// if BluetoothFindFirstRadio(@BtFindParams, hRadio) <> 0 then
// begin
// try
// Bluetooth 장치 핸들 열기
sDevPath := GetBluetoothDevicePath;
if sDevPath = '' then
exit;
// hDevice := CreateFile(PChar('\\.\BTHENUM#' + IntToStr(aBtAddr.ullLong)),
// hDevice := CreateFile(PChar('\\.\BTHLE\DEV_BC87FA05FBF7\7&17003C4D&0&BC87FA05FBF7'),
hDevice := CreateFile(PChar(sDevPath),
GENERIC_WRITE or GENERIC_READ,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil,
OPEN_EXISTING,
0,
0);
if hDevice <> INVALID_HANDLE_VALUE then
begin
try
ulllBuf := aBtAddr.ullLong;
// DeviceIoControl을 사용하여 연결 해제
Result := DeviceIoControl(hDevice,
IOCTL_BTH_DISCONNECT_DEVICE,
@ulllBuf,
SizeOf(ulllBuf),
nil,
0,
DWORD(nil^),
nil);
finally
CloseHandle(hDevice);
end;
end;
// finally
// CloseHandle(hRadio);
// end;
// end;
end;
function BluetoothDisconnectDevice2(aBtAddr: BLUETOOTH_ADDRESS): Boolean;
const
// 표준 Bluetooth 서비스 GUID
BLUETOOTH_SERVICE_GUID: TGUID = '{00001124-0000-1000-8000-00805F9B34FB}';
// 서비스 GUID 목록
SERVICE_GUID_A2DP: TGUID = '{0000110D-0000-1000-8000-00805F9B34FB}'; // Advanced Audio Distribution
SERVICE_GUID_AVRCP: TGUID = '{0000110E-0000-1000-8000-00805F9B34FB}'; // Audio/Video Remote Control
SERVICE_GUID_HFP: TGUID = '{0000111E-0000-1000-8000-00805F9B34FB}'; // Hands-Free Profile
SERVICE_GUID_HID: TGUID = '{00001124-0000-1000-8000-00805F9B34FB}'; // Human Interface Device
SERVICE_GUID_PANU: TGUID = '{00001115-0000-1000-8000-00805F9B34FB}'; // Personal Area Networking
SERVICE_GUID_SPP: TGUID = '{00001101-0000-1000-8000-00805F9B34FB}'; // Serial Port Profile
var
hFindRadio: HBLUETOOTH_RADIO_FIND;
hFindDev: HBLUETOOTH_DEVICE_FIND;
hRadio: THandle;
BtRadiFindParam: BLUETOOTH_FIND_RADIO_PARAMS;
// BtRadiInfo: BLUETOOTH_RADIO_INFO;
BtDevInfo: BLUETOOTH_DEVICE_INFO;
searchParams: BLUETOOTH_DEVICE_SEARCH_PARAMS;
pInfo: PBtRdiEnt;
begin
Result := false;
ZeroMemory(@BtRadiFindParam, SizeOf(BtRadiFindParam));
BtRadiFindParam.dwSize := SizeOf(BtRadiFindParam);
// ZeroMemory(@BtRadiInfo, SizeOf(BtRadiInfo));
// BtRadiInfo.dwSize := SizeOf(BtRadiInfo);
hFindRadio := 0;
hFindDev := 0;
hRadio := 0;
hFindRadio := BluetoothFindFirstRadio(@BtRadiFindParam, hRadio);
if hFindRadio = 0 then
exit;
try
ZeroMemory(@searchParams, SizeOf(searchParams));
searchParams.dwSize := SizeOf(searchParams);
searchParams.fReturnAuthenticated := True;
searchParams.fReturnRemembered := True;
searchParams.fReturnConnected := True;
searchParams.fReturnUnknown := False;
searchParams.hRadio := hRadio;
ZeroMemory(@BtDevInfo, SizeOf(BtDevInfo));
BtDevInfo.dwSize := SizeOf(BtDevInfo);
hFindDev := BluetoothFindFirstDevice(searchParams, BtDevInfo);
if hFindDev = 0 then
exit;
repeat
// 주소 비교
if CompareMem(@BtDevInfo.Address, @aBtAddr, SizeOf(aBtAddr)) then
begin
// 장치 연결 해제 시도
Result := BluetoothSetServiceState(hRadio, BtDevInfo, SERVICE_GUID_A2DP, BLUETOOTH_SERVICE_DISABLE) = ERROR_SUCCESS;
if not Result then
Result := BluetoothSetServiceState(hRadio, BtDevInfo, SERVICE_GUID_AVRCP, BLUETOOTH_SERVICE_DISABLE) = ERROR_SUCCESS;
if not Result then
Result := BluetoothSetServiceState(hRadio, BtDevInfo, SERVICE_GUID_HFP, BLUETOOTH_SERVICE_DISABLE) = ERROR_SUCCESS;
if not Result then
Result := BluetoothSetServiceState(hRadio, BtDevInfo, SERVICE_GUID_HID, BLUETOOTH_SERVICE_DISABLE) = ERROR_SUCCESS;
if not Result then
Result := BluetoothSetServiceState(hRadio, BtDevInfo, SERVICE_GUID_PANU, BLUETOOTH_SERVICE_DISABLE) = ERROR_SUCCESS;
if not Result then
Result := BluetoothSetServiceState(hRadio, BtDevInfo, SERVICE_GUID_SPP, BLUETOOTH_SERVICE_DISABLE) = ERROR_SUCCESS;
exit;
end;
until not BluetoothFindNextDevice(hFindDev, BtDevInfo);
finally
if hFindDev <> 0 then
BluetoothFindDeviceClose(hFindDev);
if hFindRadio <> 0 then
BluetoothFindRadioClose(hFindRadio);
end;
end;
procedure TDlgBlueMon.miPreventBlueClick(Sender: TObject);
var
pNode: PVirtualNode;
pData: PBlueEnt;
dwResult: DWORD;
begin
pNode := vtList.GetFirstSelected;
if pNode = nil then
exit;
if MessageBox(Handle, PChar('연결을 해제 하시겠습니까?'),
PChar(Caption), MB_ICONQUESTION or MB_YESNO) = IDNO then exit;
pData := vtList.GetNodeData(pNode);
// dwResult := BluetoothRemoveDevice(pData.Address);
// if dwResult = 0 then
if BluetoothDisconnectDevice2(pData.Address) then
vtList.DeleteNode(pNode)
else
ShowMessage(Format('Fail .. %d', [dwResult]));
end;
procedure TDlgBlueMon.miCopyCBClick(Sender: TObject);
begin
VT_CopyToClipboardSelectedInfo(vtList);
MessageBox(Handle, PChar('클립보드로 복사되었습니다.'), PChar(Caption), MB_ICONINFORMATION or MB_OK);
end;
procedure TDlgBlueMon.vtListContextPopup(Sender: TObject; MousePos: TPoint;
var Handled: Boolean);
begin
Handled := vtList.GetNodeAt(MousePos) = nil;
end;
procedure TDlgBlueMon.vtListFreeNode(Sender: TBaseVirtualTree;
Node: PVirtualNode);
var
pData: PBlueEnt;
begin
pData := Sender.GetNodeData(Node);
Finalize(pData^);
end;
procedure TDlgBlueMon.vtListGetNodeDataSize(Sender: TBaseVirtualTree;
var NodeDataSize: Integer);
begin
NodeDataSize := SizeOf(TBlueEnt);
end;
procedure TDlgBlueMon.vtListGetText(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
var CellText: string);
var
pData: PBlueEnt;
begin
pData := Sender.GetNodeData(Node);
case Column of
0 : CellText := IntToStr(Node.Index + 1);
1 : CellText := BooleanToStr(pData.bConnected, '연결됨', '연결안됨');
2 : CellText := pData.sName;
3 : CellText := Format('0x%.6x', [pData.nClassofDevice]);
4 : CellText := pData.sMajor;
5 : CellText := pData.sMinor;
6 : CellText := BooleanToStr(pData.bAuth, 'O', 'X');
7 : CellText := pData.sAddress;
8 : if pData.dtRecent <> 0 then CellText := DateTimeToStr(pData.dtRecent);
9 : if pData.dtSeen <> 0 then CellText := DateTimeToStr(pData.dtSeen);
end;
end;
procedure TDlgBlueMon.vtListHeaderClick(Sender: TVTHeader;
HitInfo: TVTHeaderHitInfo);
begin
if HitInfo.Button = mbLeft then
begin
if HitInfo.Column < 0 then
exit;
with Sender, Treeview do
begin
if SortColumn > NoColumn then
Columns[SortColumn].Options := Columns[SortColumn].Options + [coParentColor];
if HitInfo.Column = 0 then
SortColumn := NoColumn
else
begin
if (SortColumn = NoColumn) or (SortColumn <> HitInfo.Column) then
begin
SortColumn := HitInfo.Column;
SortDirection := sdAscending;
end else
if SortDirection = sdAscending then
SortDirection := sdDescending
else
SortDirection := sdAscending;
Columns[SortColumn].Color := $00EFEFEF;
vtList.BeginUpdate;
try
vtList.SortTree(SortColumn, SortDirection, False);
finally
vtList.EndUpdate;
end;
end;
end;
end;
end;
end.