{*******************************************************} { } { Tocsg.Process.IPC } { } { Copyright (C) 2023 kku } { } {*******************************************************} unit Tocsg.Process.IPC; interface uses Tocsg.Obj, System.Classes, System.SysUtils, Winapi.Windows, Winapi.Messages, System.Generics.Collections, System.SyncObjs, Tocsg.Packet, Tocsg.Exception; const WM_WND_HANDSHAKE = WM_USER + 4632; W2W_SENDFILE_INIT = 10; W2W_SENDFILE_DATA = 11; W2W_SENDFILE_COMPLETE = 12; type PWndDataEnt = ^TWndDataEnt; TWndDataEnt = record llSender: LONGLONG; pBuf: Pointer; dwLen, dwData: DWORD; end; TWndDataEntQueue = TQueue; PW2wLinkProc = ^TW2wLinkProc; TW2wLinkProc = record // dwPid: DWORD; hRcvWnd: HWND; end; TW2wLinkProcList = class(TList) protected procedure Notify(const Item: PW2wLinkProc; Action: TCollectionNotification); override; end; TW2wLinkProcEnumerator = TEnumerator; TW2wConnState = (wcsConnect, wcsDisconnect); TEventWnd2WndConnection = procedure(Sender: TTgObject; aState: TW2wConnState; hRcvWnd: HWND) of object; TTgWnd2Wnd = class(TTgObject) private CS_: TCriticalSection; hRcvWnd_: HWND; qDataEnts_: TWndDataEntQueue; evW2WConnected_: TEventWnd2WndConnection; // {$IFDEF _IPC_TEST_} // LinkProcList_: TW2wLinkProcList; // procedure AddLinkProc(hRcvWnd: HWND); // {$ENDIF} procedure Lock; procedure Unlock; procedure OnWndDataNotify(Sender: TObject; const Item: PWndDataEnt; Action: TCollectionNotification); procedure SetEventW2WConnected(evVal: TEventWnd2WndConnection); procedure ProcessWindowMessage(var msg: TMessage); virtual; public Constructor Create(sClassName: String; hInst: HMODULE = 0); Destructor Destroy; override; procedure ClearQueue; function DeququeData: PWndDataEnt; function SendData(hTargetWnd: HWND; pBuf: Pointer; dwLen: DWORD; dwData: DWORD = 0): Boolean; overload; function SendData(hTargetWnd: HWND; aSend: ISendPacket; dwData: DWORD = 0): Boolean; overload; function SendData(hTargetWnd: HWND; sSend: String; dwData: DWORD = 0): Boolean; overload; // {$IFDEF _IPC_TEST_} // function GetLinkProcEnumerator: TW2wLinkProcEnumerator; // {$ENDIF} property RcWnd: HWND read hRcvWnd_; property OnW2WConnection: TEventWnd2WndConnection write SetEventW2WConnected; end; // ....\DropBox\....\네임드 파이프 (NamedPipe)\NamedPipeExchange-master.zip 참조 const MaxBuffSize = MAXWORD; BLOCK_BUF_LEN = 60000; type ETgNamedPipe = class(ETgException); TTgNpBase = class; TEventNpNotification = procedure(Sender: TTgNpBase; hPipe: THandle) of object; TTgNpBase = class(TTgObject) protected bIsServer_: Boolean; sPipeName_: String; evConnected_: TEventNpNotification; evDisconnected_: TEventNpNotification; procedure ProcessFail(hPipe: THandle); virtual; abstract; function _SendData(hTgPipe: THandle; pData: Pointer; dwLen: DWORD): Boolean; function _RcvData(hTgPipe: THandle; var pBuf: TBytes): DWORD; public Constructor Create(sPipeName: String); virtual; // function SendData(sData: UTF8String): Boolean; overload; function SendData(aSend: ISendPacket): Boolean; virtual; abstract; function RcvData(var pBuf: TBytes): DWORD; virtual; abstract; property IsServer: Boolean read bIsServer_; property PipeName: String read sPipeName_; property OnConnected: TEventNpNotification write evConnected_; property OnDisconnected: TEventNpNotification write evDisconnected_; end; TClientPipeList = TList; TTgNpServer = class(TTgNpBase) private bActive_: Boolean; hEvent_: THandle; hWaitPipe_: THandle; ConnOvrl_: TOverlapped; PipeList_: TClientPipeList; CS_: TCriticalSection; enumRcv_: TEnumerator; hLastRcvPipe_: THandle; procedure Lock; procedure Unlock; procedure OnClientPipeNotify(Sender: TObject; const Item: THandle; Action: TCollectionNotification); function CreatePipeInstance(out hPipe: THandle; bAsync: Boolean = false): Boolean; procedure SetPipeClientEnt(hPipe: THandle); protected procedure ProcessFail(hPipe: THandle); override; public Constructor Create(sPipeName: String); override; Destructor Destroy; override; function Listen: Boolean; procedure Close; function DoAcceptPipe: Boolean; function SendData(aSend: ISendPacket): Boolean; override; function RcvData(var pBuf: TBytes): DWORD; override; property Active: Boolean read bActive_; property LastRcvPipe: THandle read hLastRcvPipe_; end; TTgNpClient = class(TTgNpBase) private bConnected_: Boolean; hConnPipe_: THandle; protected procedure ProcessFail(hPipe: THandle); override; public Constructor Create(sPipeName: String); override; Destructor Destroy; override; function Connect: Boolean; procedure Disconnect; function SendData(aSend: ISendPacket): Boolean; override; function RcvData(var pBuf: TBytes): DWORD; override; end; implementation uses Tocsg.Param, Tocsg.Safe; { TW2wLinkProcList } procedure TW2wLinkProcList.Notify(const Item: PW2wLinkProc; Action: TCollectionNotification); begin case Action of cnAdded: ; cnRemoved: Dispose(Item); cnExtracted: ; end; end; { TTgWnd2Wnd } function AllocateHWnd_kku(const AMethod: TWndMethod; sClassName: String; hInst: HMODULE): HWND; var UtilWindowClass, TempClass: TWndClass; ClassRegistered: Boolean; begin Result := 0; try ZeroMemory(@UtilWindowClass, SizeOf(UtilWindowClass)); UtilWindowClass.lpfnWndProc := @DefWindowProc; UtilWindowClass.lpszClassName := PChar(sClassName); UtilWindowClass.hInstance := HInstance; // UtilWindowClass.cbWndExtra := SizeOf(TMethod); ClassRegistered := GetClassInfo(HInstance, UtilWindowClass.lpszClassName, TempClass); if not ClassRegistered or (TempClass.lpfnWndProc <> @DefWindowProc) then begin if ClassRegistered then Winapi.Windows.UnregisterClass(UtilWindowClass.lpszClassName, HInstance); Winapi.Windows.RegisterClass(UtilWindowClass); end; Result := CreateWindowEx(WS_EX_TOOLWINDOW, UtilWindowClass.lpszClassName, '', WS_POPUP {+ 0}, 0, 0, 0, 0, 0, 0, HInstance, nil); if Assigned(AMethod) then SetWindowLongPtr(Result, GWL_WNDPROC, IntPtr(MakeObjectInstance(AMethod))); except on E: Exception do ETgException.TraceException(E, 'Fail .. AllocateHWnd_kku()'); end; end; Constructor TTgWnd2Wnd.Create(sClassName: String; hInst: HMODULE = 0); begin CS_ := TCriticalSection.Create; Inherited Create; if sClassName = '' then sClassName := 'TTgWnd2Wnd'; if hInst = 0 then hInst := HInstance; evW2WConnected_ := nil; //{$IFDEF _IPC_TEST_} // LinkProcList_ := TW2wLinkProcList.Create; //{$ENDIF} qDataEnts_ := TWndDataEntQueue.Create; ChangeWindowMessageFilter(WM_WND_HANDSHAKE, MSGFLT_ADD); ChangeWindowMessageFilter(WM_COPYDATA, MSGFLT_ADD); hRcvWnd_ := AllocateHWnd_kku(ProcessWindowMessage, sClassName, hInst); if hRcvWnd_ = 0 then begin _Trace('Fail .. No Allocate HWND'); // raise ETgException.Create('Fail .. AllocateHWnd()'); end; end; Destructor TTgWnd2Wnd.Destroy; begin if hRcvWnd_ <> 0 then begin DeallocateHWnd(hRcvWnd_); hRcvWnd_ := 0; end; qDataEnts_.OnNotify := OnWndDataNotify; FreeAndNil(qDataEnts_); //{$IFDEF _IPC_TEST_} // FreeAndNil(LinkProcList_); //{$ENDIF} Inherited; FreeAndNil(CS_); end; procedure TTgWnd2Wnd.Lock; begin CS_.Acquire; end; procedure TTgWnd2Wnd.Unlock; begin CS_.Release; end; procedure TTgWnd2Wnd.OnWndDataNotify(Sender: TObject; const Item: PWndDataEnt; Action: TCollectionNotification); begin case Action of cnAdded: ; cnRemoved: begin if Item.pBuf <> nil then FreeMem(Item.pBuf, Item.dwLen); end; cnExtracted: ; end; end; procedure TTgWnd2Wnd.SetEventW2WConnected(evVal: TEventWnd2WndConnection); begin if @evW2WConnected_ <> @evVal then evW2WConnected_ := evVal; end; procedure TTgWnd2Wnd.ClearQueue; begin Lock; qDataEnts_.OnNotify := OnWndDataNotify; try qDataEnts_.Clear; finally qDataEnts_.OnNotify := nil; Unlock; end; end; function TTgWnd2Wnd.DeququeData: PWndDataEnt; begin Lock; try if qDataEnts_.Count > 0 then Result := qDataEnts_.Dequeue else Result := nil; finally Unlock; end; end; function TTgWnd2Wnd.SendData(hTargetWnd: HWND; pBuf: Pointer; dwLen: DWORD; dwData: DWORD = 0): Boolean; var CD: TCopyDataStruct; begin if hTargetWnd <> 0 then begin CD.dwData := dwData; CD.cbData := dwLen; CD.lpData := pBuf; Result := SendMessage(hTargetWnd, WM_COPYDATA, NativeUInt(hRcvWnd_), NativeInt(@CD)) = WM_COPYDATA; // Result := SendMessageTimeout(hTargetWnd, WM_COPYDATA, NativeUInt(hRcvWnd_), NativeInt(@CD), // SMTO_ABORTIFHUNG, 5000, nil) = WM_COPYDATA; end else Result := false; end; function TTgWnd2Wnd.SendData(hTargetWnd: HWND; aSend: ISendPacket; dwData: DWORD = 0): Boolean; var sJsonStr: String; begin sJsonStr := aSend.ToJsonString; Result := SendData(hTargetWnd, PChar(sJsonStr), (Length(sJsonStr) + 1) * 2, dwData); end; function TTgWnd2Wnd.SendData(hTargetWnd: HWND; sSend: String; dwData: DWORD = 0): Boolean; begin Result := SendData(hTargetWnd, PChar(sSend), (Length(sSend) + 1) * 2, dwData); end; procedure TTgWnd2Wnd.ProcessWindowMessage(var msg: TMessage); var pCD: PCopyDataStruct; pData: PWndDataEnt; begin try case msg.Msg of WM_WND_HANDSHAKE : begin // {$IFDEF _IPC_TEST_} // AddLinkProc(msg.LParam); // {$ENDIF} if Assigned(evW2WConnected_) then evW2WConnected_(Self, TW2wConnState(msg.WParam), msg.LParam); msg.Result := WM_WND_HANDSHAKE; end; WM_COPYDATA : begin pCD := PCopyDataStruct(msg.LParam); if pCD.cbData > 0 then begin New(pData); pData.dwData := pCD.dwData; pData.llSender := msg.WParam; pData.dwLen := pCD.cbData; pData.pBuf := AllocMem(pData.dwLen); CopyMemory(pData.pBuf, pCD.lpData, pData.dwLen); // {$IFDEF _IPC_TEST_} // if pData.dwData > 0 then // begin // case pData.dwData of // 1 : SendData(pData.llSender, Copy(PChar(pData.pBuf), 1, pData.dwLen), 2); // 2 : _Trace(PChar(Copy(PChar(pData.pBuf), 1, pData.dwLen) + ' : DONE')); // end; // Dispose(pData); // exit; // end; // {$ENDIF} Lock; try qDataEnts_.Enqueue(pData); finally Unlock; end; end; msg.Result := WM_COPYDATA; //DefWindowProc(hRcvWnd_, msg.Msg, msg.wParam, msg.lParam); end; else msg.Result := 0; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. ProcessWindowMessage()'); end; end; //{$IFDEF _IPC_TEST_} //procedure TTgWnd2Wnd.AddLinkProc(hRcvWnd: HWND); //var // pEnt: PW2wLinkProc; //begin // New(pEnt); //// pEnt.dwPid := dwPid; // pEnt.hRcvWnd := hRcvWnd; // LinkProcList_.Add(pEnt); //end; // //function TTgWnd2Wnd.GetLinkProcEnumerator: TW2wLinkProcEnumerator; //begin // Result := LinkProcList_.GetEnumerator; //end; //{$ENDIF} type PACE_HEADER = ^ACE_HEADER; {$EXTERNALSYM PACE_HEADER} _ACE_HEADER = record AceType: Byte; AceFlags: Byte; AceSize: Word; end; {$EXTERNALSYM _ACE_HEADER} ACE_HEADER = _ACE_HEADER; {$EXTERNALSYM ACE_HEADER} TAceHeader = ACE_HEADER; PAceHeader = PACE_HEADER; PACCESS_ALLOWED_ACE = ^ACCESS_ALLOWED_ACE; {$EXTERNALSYM PACCESS_ALLOWED_ACE} _ACCESS_ALLOWED_ACE = record Header: ACE_HEADER; Mask: ACCESS_MASK; SidStart: DWORD; end; {$EXTERNALSYM _ACCESS_ALLOWED_ACE} ACCESS_ALLOWED_ACE = _ACCESS_ALLOWED_ACE; {$EXTERNALSYM ACCESS_ALLOWED_ACE} TAccessAllowedAce = ACCESS_ALLOWED_ACE; PAccessAllowedAce = PACCESS_ALLOWED_ACE; { TTgNpBase } Constructor TTgNpBase.Create(sPipeName: String); begin Inherited Create; sPipeName_ := sPipeName; evConnected_ := nil; evDisconnected_ := nil; end; //function TTgNpBase.SendData(sData: UTF8String): Boolean; //begin // Result := SendData(@sData[1], Length(sData) + 1); //end; function TTgNpBase._SendData(hTgPipe: THandle; pData: Pointer; dwLen: DWORD): Boolean; var dwWrote: DWORD; pBuf: TBytes; dwRead, dwReaded: DWORD; Label LB_RetryWrite1, LB_RetryWrite2; begin Result := false; if hTgPipe <> 0 then begin if dwLen = 0 then exit; try // MAXBUFSIZE = 65535 { PIPEDATA_STX = 'PS!_'; PIPEDATA_STX_LEN = 4; PIPEDATA_ETX = '_!PE'; PIPEDATA_ETX_LEN = 4; } if dwLen > BLOCK_BUF_LEN then begin SetLength(pBuf, MAXWORD); // 전체 크기 미리 보내놓고 CopyMemory(@pBuf[0], @dwLen, 4); Result := WriteFile(hTgPipe, pBuf[0], 4, dwWrote, nil); if not Result then begin if Assigned(evDisconnected_) then evDisconnected_(Self, hTgPipe); ProcessFail(hTgPipe); exit; end; // 쪼개서 보낸다. dwReaded := 0; while dwReaded < dwLen do begin if (dwLen - dwReaded) > BLOCK_BUF_LEN then dwRead := BLOCK_BUF_LEN else dwRead := dwLen - dwReaded; CopyMemory(@pBuf[0], @dwRead, 4); CopyMemory(@pBuf[4], Pointer(LONGLONG(pData) + dwReaded), dwRead); LB_RetryWrite1 : Result := WriteFile(hTgPipe, pBuf[0], dwRead + 4, dwWrote, nil); if Result and (dwWrote = 0) then goto LB_RetryWrite1; if not Result then begin if Assigned(evDisconnected_) then evDisconnected_(Self, hTgPipe); ProcessFail(hTgPipe); exit; end; if dwWrote > 4 then Inc(dwReaded, dwWrote - 4); end; end else begin SetLength(pBuf, dwLen + 4); CopyMemory(@pBuf[0], @dwLen, 4); CopyMemory(@pBuf[4], pData, dwLen); LB_RetryWrite2 : Result := WriteFile(hTgPipe, pBuf[0], dwLen + 4, dwWrote, nil); if Result and (dwWrote = 0) then goto LB_RetryWrite2; if not Result then begin if Assigned(evDisconnected_) then evDisconnected_(Self, hTgPipe); ProcessFail(hTgPipe); end; end; except on E: Exception do ETgNamedPipe.TraceException(Self, E, 'Fail .. SendData()'); end; end; end; function TTgNpBase._RcvData(hTgPipe: THandle; var pBuf: TBytes): DWORD; var dwLen, dwWrote, dwRead, dwReaded: DWORD; Label LB_RetryRead1, LB_RetryRead2; begin Result := 0; if hTgPipe <> 0 then begin // ReadFile() 은 현재 XE2 문제 인지는 모르겠지만... Return 값이 항상 FALSE로 뜬다. // dwWrote 리턴으로 성공 유무를 확인한다. 19_0509 13:56:13 sunk dwWrote := 0; ReadFile(hTgPipe, dwLen, 4, dwWrote, nil); if dwWrote = 4 then begin try SetLength(pBuf, dwLen); except exit; end; if dwLen > BLOCK_BUF_LEN then begin // 쪼개서 보낸거 모아서 받자 dwReaded := 0; while dwReaded < dwLen do begin LB_RetryRead1 : ReadFile(hTgPipe, dwRead, 4, dwWrote, nil); if dwWrote = 0 then goto LB_RetryRead1; if dwWrote <> 4 then break; LB_RetryRead2 : ReadFile(hTgPipe, pBuf[dwReaded], dwRead, dwWrote, nil); if dwWrote = 0 then goto LB_RetryRead2; if dwRead <> dwWrote then break; Inc(dwReaded, dwWrote); end; Result := dwReaded; end else ReadFile(hTgPipe, pBuf[0], dwLen, Result, nil); end; // _Trace('_RcvData() .. Error=%d', [GetLastError]); case GetLastError of 234 : ; // 더 많은 데이터가 있습니다. 232 : ; // 파이프가 닫히는 중입니다. 233, // 파이프의 다른 끝에 프로세스가 없습니다. 109 : // 파이프가 끝났습니다. begin if Assigned(evDisconnected_) then evDisconnected_(Self, hTgPipe); ProcessFail(hTgPipe); end; end; end; end; { TTgNamedPipeServer } Constructor TTgNpServer.Create(sPipeName: String); begin Inherited Create(sPipeName); bIsServer_ := true; bActive_ := false; hEvent_ := 0; PipeList_ := TClientPipeList.Create; PipeList_.OnNotify := OnClientPipeNotify; enumRcv_ := nil; hLastRcvPipe_ := 0; CS_ := TCriticalSection.Create; end; Destructor TTgNpServer.Destroy; begin if enumRcv_ <> nil then FreeAndNil(enumRcv_); Close; FreeAndNil(PipeList_); Inherited; FreeAndnil(CS_); end; procedure TTgNpServer.Lock; begin CS_.Acquire; end; procedure TTgNpServer.Unlock; begin CS_.Release; end; procedure TTgNpServer.OnClientPipeNotify(Sender: TObject; const Item: THandle; Action: TCollectionNotification); begin try if Action = cnRemoved then begin if (Item <> 0) and (Item <> INVALID_HANDLE_VALUE) then begin DisconnectNamedPipe(Item); CloseHandle(Item); end; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. OnClientPipeNotify()'); end; end; procedure TTgNpServer.ProcessFail(hPipe: THandle); var nIdx: Integer; begin try Lock; try nIdx := PipeList_.IndexOf(hPipe); if nIdx <> -1 then PipeList_.Delete(nIdx); // Close; finally Unlock; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. ProcessFail()'); end; end; function TTgNpServer.Listen: Boolean; begin Result := false; if bActive_ then exit; try hWaitPipe_ := 0; ZeroMemory(@ConnOvrl_, SizeOf(ConnOvrl_)); hEvent_ := CreateEvent(nil, true, true, nil); if hEvent_ = 0 then begin _Trace('Fail .. SetActive() .. CreateEvent()'); exit; end; ConnOvrl_.hEvent := hEvent_; if not CreatePipeInstance(hWaitPipe_) then begin _Trace('Fail .. SetActive() .. CreatePipeInstance()'); Close; exit; end; bActive_ := true; Result := true; except on E: Exception do ETgNamedPipe.TraceException(Self, E, 'Fail .. Listen()'); end; end; procedure TTgNpServer.Close; begin try if not bActive_ then exit; bActive_ := false; PipeList_.Clear; // 이거 초기화 하면 크러쉬 되는 문제가 있다... // 일반적인 정책을 받아서 쓸때는 상관없는데 정책이 비어 있을때 문제가 됨... // 정확한 원인은 모르겠음.. 23_0412 16:36:40 kku if (hWaitPipe_ <> 0) and (hWaitPipe_ <> INVALID_HANDLE_VALUE) then begin DisconnectNamedPipe(hWaitPipe_); CLoseHandle(hWaitPipe_); end; hWaitPipe_ := 0; if hEvent_ <> 0 then begin CloseHandle(hEvent_); hEvent_ := 0; end; ZeroMemory(@ConnOvrl_, SizeOf(TOverlapped)); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. Close()'); end; end; function TTgNpServer.DoAcceptPipe: Boolean; var dwResult, dwTransferred: DWORD; begin Result := false; if bActive_ and (hEvent_ <> 0) and (hWaitPipe_ <> 0) then begin try try dwResult := WaitForSingleObjectEx(hEvent_, 500, True); except // .. end; case dwResult of WAIT_FAILED : begin _Trace('Fail .. DoAcceptPipe() .. WAIT_FAILED'); Close; end; WAIT_TIMEOUT : exit; WAIT_IO_COMPLETION : exit; WAIT_OBJECT_0 : begin if not GetOverlappedResult(hWaitPipe_, ConnOvrl_, dwTransferred, False) then begin _Trace('Fail .. DoAcceptPipe() .. GetOverlappedResult()'); Close; exit; end else SetPipeClientEnt(hWaitPipe_); hWaitPipe_ := 0; if not CreatePipeInstance(hWaitPipe_) then begin _Trace('Fail .. DoAcceptPipe() .. CreatePipeInstance()'); Close; exit; end; Result := true; // if Assigned(evConnected_) then // evConnected_(Self); end; end; except on E: Exception do ETgNamedPipe.TraceException(Self, E, 'Fail .. DoAcceptPipe()'); end; end; end; function TTgNpServer.SendData(aSend: ISendPacket): Boolean; var llToss: LONGLONG; pBuf: TBytes; dwLen: DWORD; i, n: Integer; enum: TEnumerator; begin Result := false; try dwLen := aSend.ToBytes(pBuf); if dwLen = 0 then exit; llToss := TTgPacket(aSend).Toss; if llToss = 0 then begin Lock; try enum := PipeList_.GetEnumerator; finally Unlock; end; n := 0; // 지정 되지 않으면 전체 전달 23_0313 18:34:50 kku while enum.MoveNext do begin if _SendData(enum.Current, pBuf, dwLen) then Inc(n); end; enum.Free; Result := n > 0; end else Result := _SendData(llToss, pBuf, dwLen); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. SendData()'); end; end; function TTgNpServer.RcvData(var pBuf: TBytes): DWORD; begin Result := 0; try if enumRcv_ = nil then begin Lock; try enumRcv_ := PipeList_.GetEnumerator; finally Unlock; end; if not enumRcv_.MoveNext then begin FreeAndNil(enumRcv_); exit; end; end; hLastRcvPipe_ := enumRcv_.Current; Result := _RcvData(hLastRcvPipe_, pBuf); if not enumRcv_.MoveNext then FreeAndNil(enumRcv_); except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. RcvData()'); end; end; procedure TTgNpServer.SetPipeClientEnt(hPipe: THandle); var dwMode: DWORD; bAdd: Boolean; begin if hPipe = 0 then exit; Lock; try bAdd := PipeList_.IndexOf(hPipe) = -1; if bAdd then PipeList_.Add(hPipe); finally Unlock; end; if not bAdd then begin _Trace('Fail .. SetPipeClientEnt() .. Already Pipe'); exit; end; // dwMode := PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_NOWAIT; // fail .. dwMode := PIPE_READMODE_MESSAGE or PIPE_NOWAIT; if not SetNamedPipeHandleState(hPipe, dwMode, nil, nil) then _Trace('Fail .. SetPipeClientEnt() .. SetNamedPipeHandleState()'); end; function TTgNpServer.CreatePipeInstance(out hPipe: THandle; bAsync: Boolean = false): Boolean; const SECURITY_WORLD_SID_AUTHORITY: TSidIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 1)); SECURITY_WORLD_RID = ($00000000); ACL_REVISION = (2); var SIA: SID_IDENTIFIER_AUTHORITY; SID: PSID; pAclBuf, pSidBuf: TBytes; nAclSize, nSidSize: Integer; ACL: PACL; Descriptor: SECURITY_DESCRIPTOR; Attributes: SECURITY_ATTRIBUTES; dwError, dwPipeMode: DWORD; begin Result := False; try SIA := SECURITY_WORLD_SID_AUTHORITY; nSidSize := GetSidLengthRequired(1); SetLength(pSidBuf, nSidSize); ZeroMemory(pSidBuf, nSidSize); SID := PSID(@pSidBuf[0]); // AllocMem(nSidSize); try Win32Check(InitializeSid(SID, SECURITY_WORLD_SID_AUTHORITY, 1)); PDWORD(GetSidSubAuthority(SID, 0))^ := SECURITY_WORLD_RID; nAclSize := SizeOf(ACL) + SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(SID); SetLength(pAclBuf, nAclSize); ZeroMemory(pAclBuf, nAclSize); ACL := PACL(@pAclBuf[0]); // AllocMem(nAclSize); try Win32Check(InitializeAcl(ACL^, nAclSize, ACL_REVISION)); Win32Check(AddAccessAllowedAce(ACL^, ACL_REVISION, GENERIC_ALL, SID)); Win32Check(InitializeSecurityDescriptor(@Descriptor, SECURITY_DESCRIPTOR_REVISION)); Win32Check(SetSecurityDescriptorDacl(@Descriptor, true, ACL, False)); Attributes.nLength := SizeOf(SECURITY_ATTRIBUTES); Attributes.lpSecurityDescriptor := @Descriptor; Attributes.bInheritHandle := False; dwPipeMode := PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE; if bAsync then dwPipeMode := dwPipeMode or PIPE_NOWAIT else dwPipeMode := dwPipeMode or PIPE_WAIT; hPipe := CreateNamedPipe(PChar('\\.\Pipe\' + sPipeName_), PIPE_ACCESS_DUPLEX or FILE_FLAG_OVERLAPPED, dwPipeMode, PIPE_UNLIMITED_INSTANCES, MaxBuffSize, MaxBuffSize, NMPWAIT_WAIT_FOREVER, @Attributes); if (hPipe <> 0) and (hPipe <> INVALID_HANDLE_VALUE) then begin if not ConnectNamedPipe(hPipe, @ConnOvrl_) then begin dwError := GetLastError; case dwError of ERROR_IO_PENDING: Result := true; ERROR_PIPE_CONNECTED: SetEvent(ConnOvrl_.hEvent); else _Trace('Fail .. CreatePipeInstance() .. ConnectNamedPipe(), Error=%d', [dwError]); end; end else SetPipeClientEnt(hPipe); end else _Trace('Fail .. CreatePipeInstance() .. CreateNamedPipe()'); finally // FreeMem(ACL, nAclSize); // 여기서 FreeMem 하면 AS 디버그 모드에서 크러쉬 오류가 발생한다.. 그래서 수정함 23_0517 16:26:57 kku end; finally // FreeMem(SID, nSidSize); // 여기서 FreeMem 하면 AS 디버그 모드에서 크러쉬 오류가 발생한다.. 그래서 수정함 23_0517 16:26:57 kku end; except on E: Exception do begin if hPipe <> INVALID_HANDLE_VALUE then begin CloseHandle(hPipe); hPipe := INVALID_HANDLE_VALUE; end; ETgNamedPipe.TraceException(Self, E, 'Fail .. CreatePipeInstance()'); end; end; end; { TTgNpClient } Constructor TTgNpClient.Create(sPipeName: String); begin Inherited Create(sPipeName); bIsServer_ := false; hConnPipe_ := 0; bConnected_ := false; end; Destructor TTgNpClient.Destroy; begin Disconnect; Inherited; end; procedure TTgNpClient.ProcessFail(hPipe: THandle); begin // _Trace('ProcessFail()'); Disconnect; end; function TTgNpClient.Connect: Boolean; var dwError, dwMode: DWORD; sPName: String; begin Result := false; // _Trace('Connect() .. 1'); if bConnected_ then exit; // _Trace('Connect() .. 2'); try Disconnect; sPName := '\\.\Pipe\' + sPipeName_; hConnPipe_ := CreateFile(PChar(sPName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0); if (hConnPipe_ = 0) or (hConnPipe_ = INVALID_HANDLE_VALUE) then begin _Trace('Fail .. Connect() .. CreateFile()', 100); Disconnect; exit; end; dwError := GetLastError; case dwError of NOERROR, ERROR_PIPE_BUSY : ; else begin _Trace('Fail .. Connect() .. Error=%d', [dwError], 9); Disconnect; exit; end; end; if not WaitNamedPipe(PChar(sPName), 10000 { NMPWAIT_WAIT_FOREVER } ) then begin _Trace('Fail .. Connect() .. WaitNamedPipe()', 9); Disconnect; exit; end; dwMode := PIPE_READMODE_MESSAGE or PIPE_NOWAIT; if not SetNamedPipeHandleState(hConnPipe_, dwMode, nil, nil) then begin _Trace('Fail .. Connect() .. SetNamedPipeHandleState()', 9); Disconnect; exit; end; // bConnected_ := true; Result := true; except on E: Exception do ETgNamedPipe.TraceException(Self, E, 'Fail .. Connect()', 4); end; end; procedure TTgNpClient.Disconnect; begin // _Trace('Disconnect() ..'); try bConnected_ := false; if hConnPipe_ <> 0 then begin CloseHandle(hConnPipe_); hConnPipe_ := 0; end; except on E: Exception do ETgException.TraceException(Self, E, 'Fail .. Disconnect()', 3); end; end; function TTgNpClient.SendData(aSend: ISendPacket): Boolean; var pBuf: TBytes; dwLen: DWORD; begin dwLen := aSend.ToBytes(pBuf); Result := _SendData(hConnPipe_, pBuf, dwLen); end; function TTgNpClient.RcvData(var pBuf: TBytes): DWORD; begin Result := _RcvData(hConnPipe_, pBuf); end; end.