Paul Thomas Posted February 13, 2012 Share Posted February 13, 2012 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. Quote Link to comment Share on other sites More sharing options...
Canardia Posted February 13, 2012 Share Posted February 13, 2012 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 Quote ■ Ryzen 9 ■ RX 6800M ■ 16GB ■ XF8 ■ Windows 11 ■ ■ Ultra ■ LE 2.5 ■ 3DWS 5.6 ■ Reaper ■ C/C++ ■ C# ■ Fortran 2008 ■ Story ■ ■ Homepage: https://canardia.com ■ Link to comment Share on other sites More sharing options...
Josh Posted February 13, 2012 Share Posted February 13, 2012 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 Quote 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 More sharing options...
Paul Thomas Posted February 14, 2012 Author Share Posted February 14, 2012 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. Quote Link to comment Share on other sites More sharing options...
Canardia Posted February 14, 2012 Share Posted February 14, 2012 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. Quote ■ Ryzen 9 ■ RX 6800M ■ 16GB ■ XF8 ■ Windows 11 ■ ■ Ultra ■ LE 2.5 ■ 3DWS 5.6 ■ Reaper ■ C/C++ ■ C# ■ Fortran 2008 ■ Story ■ ■ Homepage: https://canardia.com ■ Link to comment Share on other sites More sharing options...
Paul Thomas Posted February 14, 2012 Author Share Posted February 14, 2012 Yeah, I've read that during my long extensive search trying to convert TCHAR* to std::string or std::wstring but it never prints anything. Quote Link to comment Share on other sites More sharing options...
Paul Thomas Posted February 14, 2012 Author Share Posted February 14, 2012 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. Quote Link to comment Share on other sites More sharing options...
Paul Thomas Posted February 14, 2012 Author Share Posted February 14, 2012 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. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.