10

UPDATE: Looking through the protocol here, I can't figure out what goes into the Unsized Envelope Record. I can't find any examples online.

ORIGINAL:

I have the following WCF service

    static void Main(string[] args)
    {
        var inst = new PlusFiver();
        using (ServiceHost host = new ServiceHost(inst,
            new Uri[] { new Uri("net.pipe://localhost") }))
        {
            host.AddServiceEndpoint(typeof(IPlusFive), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "PipePlusFive");
            host.Open();
            Console.WriteLine("Service is Available. Press enter to exit.");
            Console.ReadLine();
            host.Close();
        }
    }

   [ServiceContract]
   public interface IPlusFive
   {
       [OperationContract]
       int PlusFive(int value);
   }


   [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
   public class PlusFiver : IPlusFive
   {
      public int PlusFive(int value)
      {
          Console.WriteLine("Adding 5 to " + value);
          return value + 5;
      }      
   }

I output the adding 5 line so I know if the server processed the request or not.

I have a .NET client that I used to test this and everything works as expected.

Now I want to make an unmanaged C++ client for this.

I figured out how to get the name of the pipe, and write to it.
I've downloaded the protocol from here

I can write to the pipe but I can't read to it. Whenever I try to read from it I get a ERROR_BROKEN_PIPE 109 (0x6D) The pipe has been ended. error. If I replace the read with a write, the write is successful, so I don't think that the pipe is closed, at least not until I try to do a read.

Here is how I'm connecting to the pipe.

HANDLE OpenPipe(OLECHAR* bstrGuid)
{
    wstring pipeName = L"\\\\.\\pipe\\";
    wstring strGuid = bstrGuid;
    pipeName.append(strGuid.substr(1,36));
    wcout << "Pipe Name " << endl;
    wcout << pipeName.c_str() << endl;

    HANDLE hPipe = CreateFile(pipeName.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
    if(hPipe == INVALID_HANDLE_VALUE)
    {
        wcout << "failed to create pipe" << endl;
        system("pause");
        return NULL;
    }
    return hPipe;
}

this is how i'm creating the first message that I'm sending

std::list<wchar_t> GetFirstMessage()
{
    std::list<wchar_t> message;
    message.push_back(0x00);// version record
    message.push_back(0x01);// major version
    message.push_back(0x00);// minor version
    message.push_back(0x01);// mode record
    message.push_back(0x01);// singleton-unsized mode
    message.push_back(0x02);// via record

    wstring url = L"net.pipe://localhost/PipePlusFive";
    message.push_back(url.length());// via length
    for(int x= 0;x<url.length();x++)
    {
        message.push_back(url[x]); // via
    }
    message.push_back(0x03);
    message.push_back(0x08);
    return message;
}

This is how I'm writing it to the file.

int WriteMessage(HANDLE hPipe, LPVOID message, int size)
{
    DWORD bytesWritten;

    BOOL bWrite = WriteFile(hPipe, &message, size, &bytesWritten, NULL);
    wcout << "Bytes Written: " << bytesWritten << endl;
    if(bWrite == false)
    {
        wcout << "fail"<<endl;
        CloseHandle(hPipe);
        system("pause");
        return 1;
    }
    return 0;
}

    list<wchar_t> full_message = GetFirstMessage();
int result = WriteMessage(hPipe, &full_message, full_message.size());   
if (result == 1)
{ return 1;}

Here is how I'm writing the end message

    wchar_t message = 12;
result = WriteMessage(hPipe, &message, 1);
if (result == 1)
{ return 1;}

here is how I'm trying to read the response

    char buffer[10];
DWORD bytesRead;
BOOL bRead = ReadFile(hPipe, buffer, 1, &bytesRead, NULL);
if(bRead == false)
{
    wcout << "fail read"<<endl;
    wcout << "error: " << GetLastError() << endl;
    CloseHandle(hPipe);
    system("pause");
    return 1;
}

I'm new to c++, so I don't know if I'm not following the protocol correctly or making a stupid mistake in the way I'm trying to do this?

UPDATE: The problem was that I was writing the pointer address to the named pipe instead of the contents of the list. I've fixed that And I'm now able to read the Preamble Ack Record. Now I have to figure out what needs to be sent for the next part of the protocol.

scott
  • 2,991
  • 5
  • 36
  • 47
  • Can't you use managed C++ or another easier WCF binding (REST, etc.)? – Simon Mourier Aug 10 '12 at 14:10
  • I can't use managed C++. I think REST would be to slow. – scott Aug 10 '12 at 14:11
  • Here is a solution here: http://stackoverflow.com/questions/686452/create-wcf-service-for-unmanaged-c-clients but it uses some pieces of managed C++ – Simon Mourier Aug 10 '12 at 14:53
  • 1
    that used the basic http binding. I need to use the NetNamedPipeBinding – scott Aug 10 '12 at 16:14
  • MSFT recommend using Windows Web Services - http://msdn.microsoft.com/en-us/windowsserver2008r2trainingcourse_nativewebserviceswiththewindowswebservicesapi_topic10 Alternatively, you can make your WCF interface COMVisible and create a tlb from it http://www.developerfusion.com/article/2134/com-interoperability-in-net-part-2/3/ – leon.io Aug 15 '12 at 12:57
  • You may find this blog entry of interest: http://blogs.charteris.com/blogs/chrisdi/archive/2010/11/04/talking-to-wcf-over-named-pipes-1-the-framing-protocol.aspx In particular note that the server is probably expecting a Sized Envelope record in the next phase of the protocol, and you will have to implement the special "binary encoding with inband dictionary" encoding scheme for the envelope content. – Chris Dickson Aug 20 '12 at 20:49
  • @Chris - thanks, I have read that blog entry. My main problem is that I don't know what needs to be in that sized envelope record. How do I specify what method I want called? – scott Aug 21 '12 at 14:48
  • The content of the sized envelope record is a SOAP message, encoded using the "binary encoding with in-band dictionary" encoding scheme. If your service is using the default operation invoker, the service method called is determined by the Action header of the SOAP message. – Chris Dickson Aug 22 '12 at 09:16
  • It looks like you have given yourself a lot of work for no good reason. Why must it be WCF over named pipes? Why not REST? If it must be named pipes why not DCOM? Why must it be C++ not C++/CLR? Perhaps it would be better to choose an easier path. – Ben Oct 10 '15 at 09:41
  • Do you mind to try RPC frameworks such as ICE, thrift, avro, grpc, dcom, corba ? Your architecture will be more flexible, you can deploy you c++ apps and wcf apps on different devices. – neohope Mar 10 '16 at 03:46

1 Answers1

0

Check if this works for you

  1. Try to open a named pipe. (CreateFile)
  2. Set the read mode and the blocking mode of the specified named pipe. (SetNamedPipeHandleState)
  3. Send a message to the pipe server and receive its response. (WriteFile, ReadFile)
  4. Close the pipe. (CloseHandle)