Click here to Skip to main content
13,256,716 members (52,449 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

70.7K views
6.5K downloads
112 bookmarked
Posted 15 Jun 2015

A Working TCP Client and Server With SSL

, 15 Sep 2016
Rate this:
Please Sign up or sign in to vote.
A working example of a Windows client and server using SSL over TCP.

Introduction

This is a project (two, technically) to demonstrate how to use the Microsoft implementation of SSL (called SCHANNEL). This is a working example of a multithreaded server using SSL and a client which can connect to it.

For the SSL savvy, be aware that this sample supports SNI and SAN certificates on both the server and, optionally, the client, as well as providing flexible certificate selection and acceptance. If you're unfamiliar with the world of SSL, this sample provides some code that's intended to be easy to reuse for a simple, or complex, client and server.

Background

SSL is a means by which data can be moved between two endpoints securely and reliably. Using it, you can verify the other end of a connection is who it claims to be, and/or encrypt data as it passes between the two endpoints. TLS (the successor to SSL) is what is actually used here, but I use the more familiar term SSL throughout this document.

SSL relies on public key infrastructure, and generally those keys live in a certificate store. In the Microsoft world, the usual store is some encrypted portions of the registry (Java and OpenSSL do it differently). There's a set of named stores for the whole machine and a set for each user, all viewable using an MMC snap-in.

Generally, servers get their certificate (which contains the public key and points to a private one) from the machine store called "personal" (oddly, it's called "my" in code) and clients get their certificate from the per-user store called "personal" (likewise "my" in code). Clients generally do not use certificates at all, since client certificates are optional unless the server says it requires one; this sample server does requires one by default, but the sample client can create a suitable one if necessary. The server certificate identifies the system, and the client certificate (if there is one) usually identifies a particular user. That is the most common setup, but the minimum requirement is merely that the server supplies a certificate. For more details, see Creating Digital Certificates.

Client Overview

The client tries to open a TCP socket to port number 41000 on a host you specify using a command line parameter (the name defaults to the DNS name of the local host). Once that socket is open, the client initiates an SSL handshake (an initial exchange of data to agree on the options used for communication) and validates the certificate the server provides. The server requests a client certificate so the client selects one likely to be trusted by the server if possible, otherwise it selects one (somewhat arbitrarily) from the user personal store and if it cannot find one, it creates one. The client is a console application, so it displays progress on the console. If you run a debug version in the debugger, there's also a lot of detailed information in the output window.

Server Overview

The server waits on an incoming connection on port number 41000, and when one arrives, it hands the socket for the connection to a newly initiated thread for processing (this is a common pattern for servers which do significant work for each new socket). Like the client, it is a console application, so it displays progress on the console. If you run a debug build in the debugger, there's also a lot of detailed output.

Once a thread is initiated, it waits for the client at the other end of the socket to initiate an SSL handshake (or until it eventually times out). As part of the handshake, the client will use SNI to tell the server what server name it is trying to connect to, and armed with that information, the thread will look for an appropriately named certificate in the machine store. The chosen certificate must meet other requirements too, such as having a private key and being marked as usable for server identification. It requests a certificate from the client and validates that before marking the connection as successful.

Build Environment

Most of the code compiles under Visual Studio 2010, 2012, 2013 and 2015, but when I added the SAN and wildcard certificate matching, I used modern code, requiring at least VS2013, and upgraded the project to use the VS 2015 (version 140) toolchain. It is a 32 bit Unicode build, there is no ANSI alternative. I'd expect it to run on any Windows version beyond Windows Vista.

The source uses a few parts of the STL and some handy ATL and MFC classes, but neither the client nor the server use the MFC or ATL framework as a whole.

Creating Digital Certificates

In production environments, you would get a certificate from a trusted authority (a "Certificate Authority" or CA). The CA would be responsible for ensuring that you were authorized to request the certificate you asked for. For example, if you were requesting a server certificate for SSL, the CA would ensure you were the owner of the domain the server was in. Such a certificate would usually identify the server by its fully qualified domain name, something like hostname.companyname.com. If a random person asks for a certificate in the "microsoft.com" domain, for example, a responsible CA should not issue one. Although mistakes do occasionally happen, they are rare and you can generally trust certificates issued by a legitimate CA.

Each certificate has many attributes, but the ones of most importance in this case are the "Subject Name" (what entity the certificate is for, like a specific server or a particular user), the "Key Usage" and "Enhanced Key Usage" describing how the certificate is intended to be used ("Server Authentication" for example), and whether you have a private key for the certificate or just a public one. Other attributes like the validity period or issuer name don't matter much in this sample, but would in production (because then you'd care about who issued the certificate and whether it was currently valid). Client certificates usually identify a particular user, so they often have an e-mail address as a subject name.

Newer certificate standards allow for a Subject Alternative Name (SAN) on each certificate, which allows the same certificate to be used for a list of names. Another way of allowing for multiple names is "wildcard" certificates, which allow multiple servers in the same domain to be protected by the same certificate.

For test purposes, you don't need CA issued certificates; you can use ones you generate yourself (so-called "self signed certificates"). If it doesn't find a suitable server and client certificates, this sample will make them for you using subject names it provides (the host name and user name basically), the code to do this is in CreateCertificate.cpp. If you prefer to provide your own certificate, the easiest way is to ask IIS to create one for you, or Leon Finker's article (see below) describes other alternatives I have not tried. For the client end, this sample code will use any certificate with a private key it can find, or make one if it has to.

Client Details

The client application is called StreamClient and the basic flow it uses is fairly simple:

  • declare a TCP connection
  • connect it
  • negotiate SSL over the TCP connection
  • send and receive a couple of test messages
  • shut down the connection

If all you need is a straightforward client and you are not too concerned with the details of SSL or TCP, just read "Main Program" below, then skip forward to Server Details if you need a server too.

Main Program

This is all you really need to make a connection work. It's in StreamClient.cpp and just declares a few objects, opens a TCP connection, and passes it to an object that implements SSL so that the connection can be completed. The essence of the code looks like this:

CActiveSock * pActiveSock = new CActiveSock(ShutDownEvent);
CSSLClient * pSSLClient = nullptr;
pActiveSock->SetRecvTimeoutSeconds(30);
pActiveSock->SetSendTimeoutSeconds(60);
bool b = pActiveSock->Connect(CW2A(HostName), Port);
if (b)
   {
   char Msg[100];
   pSSLClient = new CSSLClient(pActiveSock);
   b = SUCCEEDED(pSSLClient->Initialize(HostName));
   if (b)
      {
      cout << "Connected, cert name matches=" << pSSLClient->getServerCertNameMatches()
         << ", cert is trusted=" << pSSLClient->getServerCertTrusted() << endl;
      if (pSSLClient->SendPartial("Hello from client", 17) != 17)
         cout << "Wrong number of characters sent" << endl;
      int len = 0;
      while (0 < (len = pSSLClient->RecvPartial(Msg, sizeof(Msg))))
         cout << "Received " << CStringA(Msg, len) << endl;
      }
   else
      cout << "SSL client initialize failed" << endl;
   ::SetEvent(ShutDownEvent);
   pSSLClient->Close();
   }

Notice the getServerCertNameMatches and getServerCertTrusted methods; they tell the client how much trust to put in the certificate.

If you compile up the client and run it, it will connect to the server and show something like this in a console window:

Connecting to localhost:41000
Socket connected to server, initializing SSL
Client certificate requested without issuer list, none found.
Client certificate requested with issuer list, selected name: david-maw-test-ca
An untrusted server certificate was returned for the expected name: localhost
Connected, cert name matches=1, cert is trusted=0
Sending greeting
Listening for messages from server
Received Hello from server
Received Goodbye from server
Press any key to exit

A debug build can also be changed to display the details of the received server certificate. It will look something like this:

Certificate sample

To make it display the certificate, change the sample client to remove the "false" in:

if (false && debug && pCertContext)

If all you need is a simple SSL client, just alter this code so it does what you want and go no further. If you are interested in more control or how it all works, read on...

Controlling Certificates

There are a couple of things you can do to give you more control over the connection, you can provide a CertAcceptable function to evaluate the certificate provided by the server and reject it if you do not like it (which closes the connection). You can also provide a SelectClientCertificate function to allow you to control what client certificate (if any) is sent to the server. The code to use these looks like this:

pSSLClient->ServerCertAcceptable = CertAcceptable;
pSSLClient->SelectClientCertificate = SelectClientCertificate;

Examples of both of these are in the sample source, but you need not use either of them, if you do, you can assign lambda expressions, or, as the sample does, define functions with appropriate parameters, and just assign those.

TCP Client (CActiveSock)

The TCP client connection is abstracted into a class called CActiveSock defined in ActiveSock.cpp and declared in ActiveSock.h. This class implements a few simple functions (Connect, Disconnect, Send and Receive, for example) that abstract away the details of interfacing with WinSock so that the main caller need not deal with them. To use it, you declare a CActiveSock object, optionally set some timeouts on it, than ask it to connect to a particular endpoint (a hostname and port number).

SSL Client (CSSLClient)

The code to implement the SSL connection is within the CSSLClient object with some helper functions in SSLHelper.cpp. The CSSLClient constructor needs a connected CActiveSock to provide it with a channel over which to communicate. Once constructed, you call its Initialize method, and once that completes successfully, you have an open channel over which you can communicate using SendPartial and ReceivePartial. Why "partial"? That's because TCP may not send the whole message you request (it may run out of buffer space, for example) or deliver the bytes you sent in a single message (messages can be split or joined). In practice, SSL does not exhibit this property -- it sends what you request in a single message and delivers exactly what's sent in a single message.

Validating the Server Certificate

As part of the SSL negotiation, the client tells the server what server name it thinks it is connecting to using a mechanism called Server Name Indication or SNI, which is a feature of the TLS protocol (technically, it provides a list, but there's only ever one name in the list). Once the server knows what name the client is looking for (and there might not be one, in which case the local host name is used), it can provide a certificate with a matching Subject Name. This sample code does that by default, but if no matching certificate is found, it will use a self signed one with the wrong name if there is one.

The important assessments the client must make about the certificate the server provides are "is it valid" (not expired, for example), "was it issued by a trusted CA", and "does the subject name match what's expected". Once the SSL handshake completes, each CSSLClient object implements two methods the caller can use to answer these questions: getServerCertTrusted and getServerCertNameMatches. Generally, you won't want to use the connection unless both return true, because otherwise the communication channel might be compromised (that is, read, changed or even redirected before reaching the desired endpoint).

As an alternative, you can provide your own CertAcceptable function to evaluate the certificate provided by the server and reject it if you do not like it (which closes the connection). If you do decide to provide that function, it gets given a pointer to a server handle, and two boolean values to indicate if the certificate is trusted and has a subject name matching the one that the client requested.

Selecting a Client Certificate

As part of the SSL negotiation, the server sends a list of acceptable certificate issuers to the client unless a particular registry key is set (see below) . This allows the client to sort through a list of possible certificates to find one from an issuer that is acceptable to the server, and then offer that certificate to the server. If no acceptable certificate can be found, the sample uses any certificate it can find, or even creates one if it cannot find one. The server code can easily be changed to display the certificate it received from the client if it gets one, to make it display the certificate, change ClientCertAcceptable in the sample server to insert:

ShowCertInfo(pCertContext, _T("Server Received Client Certificate"));

Optionally, you can provide your own SelectClientCertificate function in the client to allow you to control what client certificate (if any) is sent to the server.

Interestingly, this function is called twice, once early in the negotiation to optionally provide a client certificate if you want to - if the server approves of this, you're done. If a client certificate is requested and you don't provide one, the connection will fail, so there may be a second call requiring a certificate to be provided - this call may come with a list of acceptable certificate issuers.

BEWARE - The server will NOT send an issuer list if the registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL has a DWORD value called SendTrustedIssuerList set to 0.

Server Details

The server is considerably more complex than the client - the SSL part is mostly the same, but there's a lot more complexity in allowing it to accept multiple simultaneous connections over TCP.

Main Program

Just as for the client, this is all you really need to make a connection work; it's in StreamServer.cpp and just declares a CListener then tells it to start listening on a particular port and define what's going to happen once a connection is made. You define that in a lambda expression, and you can use the ISocketStream interface pointer that's passed to it in order to send or receive messages across the SSL connection. The significant portion of the sample code looks like this:

Listener->Initialize(Port);
Listener->BeginListening([](ISocketStream * const StreamSock){
   // This is the code to be executed each time a socket is opened
   StreamSock->Send("Hello from server", 17);
   int len = StreamSock->Recv(MsgText, sizeof(MsgText) - 1);
   if (len > 0)
      StreamSock->Send("Goodbye from server", 19);
   });
Listener->EndListening();

If you compile up the server and run it, it will wait for connections from clients and show this console output (this example shows two clients connecting in succession):

WARNING: The server is not running as an administrator.
Starting to listen on port 41000
Listening, press any key to exit.

Server certificate requested for localhost, found IIS Express Development Certificate
A trusted client certificate was returned for david-maw-test-ca
A connection has been made, worker started, sending hello
Received Hello from client
Sending goodbye from server
Exiting worker

A trusted client certificate was returned for david-maw-test-ca
A connection has been made, worker started, sending hello
Received Hello from client
Sending goodbye from server
Exiting worker

A debug build can be easily modified to show UI to display the details of the received client certificate, if there is one, to make it display the certificate, change the sample client to remove the "false" in

if (false && debug && pCertContext)

If all you need is a fairly simple server, just amend this code to do what you need and ignore the gory detail below. If you want to exert more control or know more, read on...

Controlling Certificates

There are a couple of things you can do to give you more control over the connection, you can provide a ClientCertAcceptable function to evaluate the certificate provided by the client (if any) and reject it if you do not like it (which closes the connection). You can also provide a SelectServerCert function to allow you to control what server certificate is sent to the client. The code to use these looks like this:

Listener->SelectServerCert = SelectServerCert;
Listener->ClientCertAcceptable = ClientCertAcceptable;

Examples of both of these are in the sample source, but you need not use either of them, if you do, you can assign lambda expressions, or, as the sample does, define functions with appropriate parameters, and just assign those.

The sample SelectServerCert function makes use of CertFindServerByName in order to select a matching certificate, that, in its turn, calls MatchCertHostName in order to examine the certificate SAN or Subject fields, and then it calls HostNameMatches to validate that a name from the certificate matches the hostname being requested.

TCP Listener (CListen)

One of the first things you usually need to do in a server is figure out what port you'll be listening on for connections, and tell Winsock to start listening on that port. It's complicated a bit by what protocols you might want to listen on (TCP over IPv4 and/or IPv6 usually) and how you want to handle blocking a thread while waiting for the next socket to open (the sample handles this by running the "listen" loop on a separate thread).

That's all handled in a CListen class which implements a multi threaded listener. It can do some simple things when a connection is opened, such as start up a thread to process it, but then it needs to be told what the server actually does. The lambda expression passed into BeginListening is what provides that information, but normally there's a CSSLServer in the path to add SSL capability on top of the basic TCP transport. The lambda expression in the call on BeginListening gets passed an ISocketstream interface it can use to send and receive messages.

TCP Server (CPassiveSock)

The TCP server connection is defined by a class called CPassiveSock, defined in PassiveSock.h and declared in PassiveSock.cpp - this class implements a few simple functions (Disconnect, Send and Receive, for example) that abstract away the details of interfacing with WinSock so that the caller need not deal with them. Notice there's no "connect"; that's because this class only deals with already-open sockets delivered as a result of a connection made by a client, mediated by a CListen object.

SSL Server (CSSLServer)

The code to implement the SSL connection is within the CSSLServer object. Its constructor needs a CPassiveSock to provide it with a channel over which to communicate. Once constructed, you call its Initialize method, and once that completes successfully, you have an open channel over which you will communicate. The CSSLServer object is SSLServer.cpp is fairly heavyweight, so some of the work is offloaded to code in SSLHelper.cpp.

Tying Them Together (CTransport)

The transition from a simple TCP server to a TCP server with SSL capability is handled by a CTransport object. It takes the SOCKET object delivered by the CListener and creates a CPassiveSock from it, then passes that to a CSSLServer in order to create a server-side SSL socket. The CTransport makes available an ISocketStream interface which is passed to the lambda function that is in turn passed into BeginListening.

The ISocketStream interface exists mostly so that it is possible to choose NOT to use SSL and still have Clistener::Work use the same interface (because the server likely does not change its behavior whether the channel is encrypted or not). ISocketStream also abstracts away the details of the implementation so the lambda cannot mess with them, either accidentally or on purpose. There's no logic to support running without SSL in the example, but if you wanted to do it, it's a fairly simple change to CTransport to not use CSSLServer at all.

What About SCHANNEL

The actual interface to the Microsoft SCHANNEL implementation of SSL is a bit arcane, partly because the same interface (the Security Support Provider Interface - SSPI) is used to reach a replaceable provider which might do many different things. So pretty much the first thing you must do is get a PSecurityFunctionTable object pointed at an SSPI implementation by calling InitSecurityInterface. Once you do that, around 20 methods become available, such as EncryptMessage or QuerySecurityContext.

Many of the parameters to the various SSPI functions have no meaning in the SCHANNEL implementation and must be NULL.

The SSPI interface is designed to be consumed by C rather than requiring C++, which makes it rather cumbersome to use. It also provides pretty tight control over buffer usage, which furthers adds to the complexity.

The Basic SSL Handshake

In order to start an SSL session, the first step is to establish a communication channel between the client and the server (a TCP connection in most cases) and then perform an "SSL Handshake" over the channel (Google "SSL Handshake", there are lots of good explanations). The handshake is initiated by the client sending a message to the server, the server replies and provides a certificate, the client responds (possibly with a certificate of its own), cipher suites are negotiated, SSL/TLS levels are negotiated, and so on. After both ends have sent their messages, a secure connection is made. The details of this handshake either from a client or server perspective are what SCHANNEL implements. In this example, the server implementation is in CSSLServer and the client's is in CSSLClient.

What the Caller Provides

The SSPI (SCHANNEL) caller has to provide a means to send and receive messages, some memory buffers, and a state machine to handle stepping through the handshake until SSPI reports that the handshake is complete or has failed. You can see this logic in CSSLServer::SSPINegotiateLoop, which basically just keeps calling the SSPI AcceptSecurityContext procedure until it returns SEC_E_OK. Other return values indicate a variety of things, such as a need for more data, a response that should be sent, or a failure.

Once the handshake is complete, you have an encrypted connection that can be used for send or receive operations. Send ultimately calls SSPI EncryptMessage in a similar loop to the one in SSPINegotiate, and Receive calls SSPI DecryptMessage in the same way. The article by Leon Finker referred to below has a nice overview of this, and some further references if you're interested in more details.

Controlling SSL Protocol Versions

SSL has evolved through many protocol versions over the years: SSL 1.0, 2.0, 3.0, then TLS 1.0, 1.1 and 1.2 (which is the latest in general use at the time of writing). Do not use anything earlier than TLS 1.0 if you can possibly avoid it. SSL 3.0 and earlier have been compromised in a variety of ways. The SSL handshake is supposed to negotiate mechanisms (like the latest protocol level, or encryption algorithms) that both client and server can handle. This sample uses only TLS 1.2, but if you want to change that, look for a setting of SchannelCred.grbitEnabledProtocols (it will be something like SP_PROT_TLS1_2_CLIENT) and change it to whatever you need.

Open Issues

  1. There's a lot of old style code here using explicit memory allocation - it would be nice to replace that with more modern RAII style code.
  2. Buffer sizes are all fixed (see MaxMsgSize which is currently set to 16000 -- the current SCHANNEL limit is 16384); it would be better if they were variable.
  3. It might be helpful to make use of the proposed Async support in C++ to move the threading logic out of this code and take advantage of services provided by the system instead.
  4. Some of the character size handling makes use of TCHAR and related functions quite a bit, as well as the ATL::Cxxx conversion functions. Since only a Unicode build is supported, it probably makes sense to simplify all that in future.

Acknowledgements

The SSL portion of this code was inspired by an article by Leon Finker published on CodeProject in 2003 (it is here) which I recommend as a starting point for someone who wants to know more about SSL and SCHANNEL. It was also inspired by an old Microsoft SDK sample (which I can no longer find) showing how to call SCHANNEL.

The code to create a certificate is based on the sample "How to create a self-signed certificate with CryptoAPI" code in the blog of Alejandro Campos Magencio, it can be found here.

History

This code is maintained in GitHub at https://github.com/david-maw/StreamSSL

  • June 2015 Original article published
  • June 27. 2015 - Source added to GitHub
  • July 6, 2015 - Provided the client and server code with more control over the certificates being used as well as the ability to reject them
  • January 2016 - Added code to create a client certificate if none is available and use the host name rather than "localhost" by default
  • February 2016 - Updated the source so it compiles with the VS2015 toolchain as well as VS2010
  • April 9, 2016 - Updated the code to handle SAN and Wildcard certificates, require a VS 2015 Unicode build
  • September 15, 2016 - Fixed some grammar and typos

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

David Maw
Architect Unisys Corporation
United States United States
I've been programming for many years, first on Burroughs mainframes, then, after it became Unisys, on Windows systems, first using C++ and COM on Win32, then C# on .NET on desktop and phone platforms. My professional life doesn't involve much programming any more, it's more of a personal passion.

You may also be interested in...

Pro

Comments and Discussions

 
QuestionAcquireCredentialsHandle returns SEC_E_UNKNOWN_CREDENTIALS Pin
Member 1352106214-Nov-17 8:08
memberMember 1352106214-Nov-17 8:08 
David
 Excellent package that you have put here.
 
 As part of the work I am doing, in GetNewClientCredentials --> SelectClientCertificate, I extracted the client certificate from the Local Machine store using CertOpenStore with CERT_STORE_PROV_SYSTEM and flags CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_READONLY_FLAG. This does retrieve the correct client certificate corresponding to the issuer list that the server (openssl s_server, not your schannel server) sends.
 
 However, in CreateCredentialsFromCertificate,when the AcquireCredentialsHandle is called, it returns with SEC_E_UNKNOWN_CREDENTIALS. The PCCERT_CONTEXT correctly contains the client certificate extracted from the machine store in the previous step.
 
 One thing to note is that I am running this as a service - so could the pointer to logon ID being NULL have some implications ? Any pointers will be welcome
 
Thanks
 
PS: Excellent work

QuestionDo you have the VS2013 source? Pin
Member 1311901810-Apr-17 11:24
memberMember 1311901810-Apr-17 11:24 
AnswerRe: Do you have the VS2013 source? Pin
David Maw19-Jul-17 14:16
memberDavid Maw19-Jul-17 14:16 
QuestionHow to use with a specific certificate? Pin
shfnet29-Mar-17 10:59
membershfnet29-Mar-17 10:59 
AnswerRe: How to use with a specific certificate? Pin
David Maw19-Jul-17 14:18
memberDavid Maw19-Jul-17 14:18 
Suggestionprintf is hard Pin
glen_knowles26-Mar-17 23:39
memberglen_knowles26-Mar-17 23:39 
GeneralRe: printf is hard Pin
David Maw25-Jul-17 21:10
memberDavid Maw25-Jul-17 21:10 
QuestionErrors in g_pSSPI Pin
nepete21-Feb-17 21:12
membernepete21-Feb-17 21:12 
AnswerRe: Errors in g_pSSPI Pin
David Maw25-Jul-17 21:12
memberDavid Maw25-Jul-17 21:12 
Questiontype cast problem (VS2015) Pin
Member 129280879-Jan-17 22:06
memberMember 129280879-Jan-17 22:06 
GeneralMy vote of 5 Pin
Hatem Mostafa22-Dec-16 0:40
memberHatem Mostafa22-Dec-16 0:40 
QuestionHave you used C# for this project? Pin
guillermo grillo19-Sep-16 8:30
memberguillermo grillo19-Sep-16 8:30 
AnswerRe: Have you used C# for this project? Pin
David Maw19-Sep-16 9:19
memberDavid Maw19-Sep-16 9:19 
Praiseyou earn my vote : 5 Pin
Huzifa Terkawi16-Sep-16 4:47
memberHuzifa Terkawi16-Sep-16 4:47 
GeneralMy vote of 5 Pin
Jimmy_Olano10-Feb-16 0:38
memberJimmy_Olano10-Feb-16 0:38 
BugDoes not compile with VS2013/2015 Pin
Andreas Schniertshauer27-Jan-16 2:57
memberAndreas Schniertshauer27-Jan-16 2:57 
GeneralRe: Does not compile with VS2013/2015 Pin
David Maw7-Feb-16 20:57
memberDavid Maw7-Feb-16 20:57 
GeneralRe: Does not compile with VS2013/2015 Pin
yyl8114-Jul-16 0:14
memberyyl8114-Jul-16 0:14 
QuestionExample didnt run under and Window 7 (x64) / VS2013 Pin
Member 1149577510-Jan-16 12:21
memberMember 1149577510-Jan-16 12:21 
AnswerRe: Example didnt run under and Window 7 (x64) / VS2013 Pin
David Maw26-Jan-16 20:45
memberDavid Maw26-Jan-16 20:45 
QuestionSample does not working on Win10 Pin
Yanbing Yu18-Dec-15 6:34
memberYanbing Yu18-Dec-15 6:34 
AnswerRe: Sample does not working on Win10 Pin
David Maw19-Dec-15 16:25
memberDavid Maw19-Dec-15 16:25 
GeneralRe: Sample does not working on Win10 Pin
Yanbing Yu19-Dec-15 19:29
memberYanbing Yu19-Dec-15 19:29 
GeneralRe: Sample does not working on Win10 Pin
David Maw20-Dec-15 15:17
memberDavid Maw20-Dec-15 15:17 
QuestionSending Files as Bytes crashing Pin
Member 121171936-Nov-15 16:06
memberMember 121171936-Nov-15 16:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.171114.1 | Last Updated 16 Sep 2016
Article Copyright 2015 by David Maw
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid