Jump to content

Named Pipe


Paul Thomas
 Share

Recommended Posts

I spent all day working on this, rewrote the code a few times, a couple methods weren't reliable enough, and this is basically my last chance (before reverting to the best method so far). I'm using a named pipe from BlitzMax to my DLL. The message is sent but once I send another message it crashes the DLL loaded application (I can't specifically say what that application is, but this application loads/binds the DLL).

 

I need to this to be a continuous evaluation for sent messages only to stop once I manually close the handle. If what I'm doing isn't doing that, please let me know how to fix it. I'm not very good with C++ and I haven't had much practice having BMax use Win32 external functions, although I got this to work on the first try, I brush it up on hair pulling luck.

 

C++/DLL side:

void BeginPipe()
{
 _beginthread(BeginPipeThread, 0, NULL);
}

void BeginPipeThread(void* pParams)
{
 LPTSTR _PIPE_NAME = L"\\\\.\\pipe\\RhysPipe";
 char Received_Buffer[256];
 DWORD BytesRead = 0;

 HANDLE hPIPE = CreateNamedPipe(_PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 256, 256, 10000, 0x00000000);

if(hPIPE != INVALID_HANDLE_VALUE)
{
  int Connected = 0;

	while(!(GetAsyncKeyState(VK_F8)))
	{
	  Connected = ConnectNamedPipe(hPIPE, NULL);

		if(Connected == 1)
		{
		  BOOL bRead = ReadFile(hPIPE, Received_Buffer, 256, &BytesRead, NULL);

			if(bRead != 0)
			{
			  // write message for testing
			 ofstream myfile;
			 myfile.open("WriteTest.txt", ios::app);
			 myfile << Received_Buffer << "\n";
			 myfile.close();
			}
		}
	   else
	   {
	   		   CloseHandle(hPIPE);
	   }
	}
}

 DisconnectNamedPipe(hPIPE);
 CloseHandle(hPIPE);
}

 

I execute "BeginPipe()" as this needs to be on a second thread to not freeze the application waiting for a message.

 

And the BMax side:

Const GENERIC_WRITE:Int = $40000000
Const OPEN_EXISTING:Int = 3
Const INVALID_HANDLE_VALUE:Int = -1

Extern "Win32"
Function CreateFileA(lpFileName$z, dwDesiredAccess:Int, dwShareMode:Int, lpSecurityAttributes:Byte Ptr, dwCreationDisposition:Int, dwFlagsAndAttributes:Int, hTemplateFile:Int)
Function WriteFile(hFile:Int, lpBuffer:Byte Ptr, nNumberOfBytesToWrite:Int, lpNumberOfBytesWritten:Int, lpOverlapped:OVERLAPPED)
  Function CloseHandle:Int(hObject:Int)
  Function CreateEventA:Int(lpEventAttributes:Int, bManualReset:Int, bInitialState:Int,lpName$z)
EndExtern


' OVERLAPPED '
Type OVERLAPPED
Field internal:Int
Field internalHeight:Int
Field offset:Int
Field offsetHeight:Int
Field hEvent:Int = CreateEventA(0, 0, 0, Null)

Method Delete()
		If hEvent CloseHandle(hEvent)
EndMethod
EndType

Global hFile:Int = CreateFileA("\\.\pipe\RhysPipe", GENERIC_WRITE, 0, Null, OPEN_EXISTING, 0, Null);

Local buffer:Byte Ptr
Local bytesWritten:Int
Local MyString:String = "THIS IS A TEST FROM BMAX"
buffer = MyString.ToCString()

If hFile

	WriteFile(hFile, buffer, 256, bytesWritten, Null)

EndIf

 

Again, it works, but when sending a second message, it prints, and then crashes. Not exactly sure why but I'm too novice in C++.

 

Any help would be appreciated.

Link to comment
Share on other sites

You could use the Named Pipe Server and Client example from Microsoft: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365588(v=vs.85).aspx

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

Not sure what you are doing, but this might help. It's the code I used to communicate between Leadwerks3D and executable converters:

    Method Convert:Int(path:String,commandline:String="")
       path=RealPath(path)
       Local outfile:String = StripExt(path)+"."+outformat
       Local proc:TProcess
       Local s:String
       Local success:Int=False
       Local ba:Byte[]
       Local sarr:String[]

       Print "Converting ~q"+RelativePath(path)+"~q to ~q"+ RelativePath(outfile)+"~q..."

       Local procpath:String=RealPath(AppDir+"/"+apppath)

       proc=CreateProcess("~q"+procpath+"~q +path ~q"+path+"~q"+" "+commandline)
       If proc
           While proc.status()
               Delay 1
           Wend
           While proc.pipe.readavail()
               's=proc.pipe.ReadLine().Trim()
               ba = proc.pipe.ReadPipe()
               ba = ba[..ba.length+1]
               ba[ba.length-1]=0
               s:+String.fromcstring(ba)+"~n"
           Wend
           sarr=s.Trim().split("~n")
           For s=EachIn sarr
               s=s.Trim()
               If s
                   If s[..7].tolower()="success"
                       success=True
                   Else
                       Print s
                   EndIf
               EndIf
           Next
           proc.close()
           If FileType(outfile)<>1 success=False
           If Not success Notify outfile
           If success
               If Not filesystemwatcher EmitEvent(CreateEvent(EVENT_FILECREATED,StripExt(path)+"."+outformat))
               Return True
           Else
               DeleteFile outfile
               Return False
           EndIf
       Else
           Print "Error: Failed to create process ~q"+procpath+"~q."
           Return False
       EndIf
   EndMethod

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

The example from MSDN works pretty good. At the moment I'm just struggling working with TCHAR* so I've changed it around to use CHAR* instead. Anyone know how to use TCHAR*? For example, to print the results of a TCHAR* to a file and/or to try and compare it with a string? I haven't tried to compare it with a string yet since I couldn't even get it to print correctly.

Link to comment
Share on other sites

I guess TCHAR* is either 8 or 16 bit char*, depending if you have unicode enabled in your project settings or not.

With printf you print 8 bit chars, with wsprintf you print 16 bit chars.

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

Almost finished, now I just need to figure out why I get a ERROR_NOACCESS (998) when trying to get BMax to read a response from the pipe after the DLL had received a message and sent one back. Then I have to make sure neither are causing any memory leaks or hogging CPU or anything like that.

 

It's been a long and strange battle learning C++. If I was learning properly I would be okay but I was kind of forced into this and trying to be as quick as possible. The overall objective of this whole thing is communication between two processes. The DLL is loaded by another application (I could edit the source of that application, rebuilding it to use a named pipe instead of the DLL, but that's like editing Mount Rushmore to put an eyelash or two on George Washington) and executes specific functions for specific actions/responses by the application. I needed to build a communication layer between the DLL and my BlitzMax application. Hopefully this is the right answer.

 

For my next personal challenge I was thinking of making a C++ DLL that can give me handles/pointers to processes but at the moment I don't even know where to begin that using C++. My main purpose for that is for two reasons; 1) To deny an application from opening twice (Josh, you pointed me to a interprocesses module but it won't compile correctly; do you have a compiled version you could send?), 2) to set window focus between one process and another.

 

Thanks to everyone who helped me through this whole ordeal, greatly appreciated.

Link to comment
Share on other sites

Also, I used something similar to what you did Josh for a slightly different purpose. I also used a thread so my UI doesn't freeze up during the process:

 

field thread:tthread

method beginprocess()
 thread = createthread(runprocess, null)
endmethod

function runprocess:object(data:object)
 local line:string = ""
 local lines:string = ""
 local process:tprocess = createprocess(PROCESSLOCATION, 1)

   while process.status()
  line = process.pipe.readline().trim()
    if line <> "" lines :+ line + "~n";
   wend

 ' process lines '

 process.terminate()
endfunction

 

Technically don't need to terminate the process at the end since it'll terminate once the thread is finished but it doesn't hurt. Every time I'm using processes I'm parsing it line by line so I edited the above to add all the lines.

 

Meh, just felt like sharing code.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...