title: C# pinvoking CredWrite issue on Windows XP Pro
body: <p>I currently have a program written in c# invoking <a href="http://www.pinvoke.net/default.aspx/advapi32.credwrite" rel="nofollow">CredRead/CredWrite from Advapi32.dll</a> to make it easy for users that are not joined to the domain to save terminal server/WebDAV server credentials. (<a href="http://msdn.microsoft.com/en-us/library/aa374788%28v=vs.85%29.aspx" rel="nofollow">http://msdn.microsoft.com/en-us/library/aa374788%28v=vs.85%29.aspx</a> for credential structure information)</p>
  
  <p>This is all working fine, but I ran into an issue the other day that persists on all XP operating systems; I cannot save the terminal server credentials.  The native CredWrite method is returning an error code 87 (ERROR_INVALID_PARAMETER). </p>
  
  <p>After trying numerous different combinations of credential persistence and types, I realized the problem was the TargetName itself.  To save terminal servers in the windows key ring, the TargetName is TERMSRV/server.domain.com.  On Windows Vista or higher, this works fine with my code, but on Windows XP this does not.</p>
  
  <p>Oddly enough, when you run the Remote Desktop Application on Windows XP and have it save the credentials, it does so without fuss.  When I created a method to enumerate saved credentials after using RDP (obviously no password blobs were returned), I could see that the target was identical to the one my program attempted to write.  </p>
  
  <p>To confirm the problem was in the TargetName, I removed the "/" and it worked fine.</p>
  
  <p>Below is the relevant portion of my code:</p>
  
  <pre><code>var writeInt = NativeCredMan.WriteCred("TERMSRV/host.server.com", samAccountName, password, CRED_TYPE.DOMAIN_PASSWORD, CRED_PERSIST.LOCAL_MACHINE);
  </code></pre>
  
  <p>...
  ...</p>
  
  <pre><code>public enum CRED_TYPE : uint
  {
      GENERIC = 1,
      DOMAIN_PASSWORD = 2,
      DOMAIN_CERTIFICATE = 3,
      DOMAIN_VISIBLE_PASSWORD = 4,
      GENERIC_CERTIFICATE = 5,
      DOMAIN_EXTENDED = 6,
      MAXIMUM = 7,      // Maximum supported cred type
      MAXIMUM_EX = (MAXIMUM + 1000),  // Allow new applications to run on old OSes
  }
  
  public enum CRED_PERSIST : uint
  {
      SESSION = 1,
      LOCAL_MACHINE = 2,
      ENTERPRISE = 3,
  }
  </code></pre>
  
  <p>...
  ...</p>
  
  <pre><code>    [DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
      static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr CredentialPtr);
  
      [DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
      static extern bool CredWrite([In] ref NativeCredential userCredential, [In] UInt32 flags);
  
      [DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]
      static extern bool CredFree([In] IntPtr cred);
  
      [DllImport("Advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode)]
      static extern bool CredDelete(string target, CRED_TYPE type, int flags);
  
      //[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
      //static extern bool CredEnumerateold(string filter, int flag, out int count, out IntPtr pCredentials);
  
      [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
      public static extern bool CredEnumerate(string filter, uint flag, out uint count, out IntPtr pCredentials);
  
      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
      private struct NativeCredential
      {
          public UInt32 Flags;
          public CRED_TYPE Type;
          public IntPtr TargetName;
          public IntPtr Comment;
          public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
          public UInt32 CredentialBlobSize;
          public IntPtr CredentialBlob;
          public UInt32 Persist;
          public UInt32 AttributeCount;
          public IntPtr Attributes;
          public IntPtr TargetAlias;
          public IntPtr UserName;
  
          internal static NativeCredential GetNativeCredential(Credential cred)
          {
              var ncred = new NativeCredential
                              {
                                  AttributeCount = 0,
                                  Attributes = IntPtr.Zero,
                                  Comment = IntPtr.Zero,
                                  TargetAlias = IntPtr.Zero,
                                  Type = CRED_TYPE.DOMAIN_PASSWORD,
                                  Persist = (UInt32) cred.Persist,
                                  CredentialBlobSize = (UInt32) cred.CredentialBlobSize,
                                  TargetName = Marshal.StringToCoTaskMemUni(cred.TargetName),
                                  CredentialBlob = Marshal.StringToCoTaskMemUni(cred.CredentialBlob),
                                  UserName = Marshal.StringToCoTaskMemUni(cred.UserName)
                              };
              return ncred;
          }
      }
  
      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
      public struct Credential
      {
          public UInt32 Flags;
          public CRED_TYPE Type;
          public string TargetName;
          public string Comment;
          public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
          public UInt32 CredentialBlobSize;
          public string CredentialBlob;
          public CRED_PERSIST Persist;
          public UInt32 AttributeCount;
          public IntPtr Attributes;
          public string TargetAlias;
          public string UserName;
      }
  </code></pre>
  
  <p>...
  ...</p>
  
  <pre><code>        public static int WriteCred(string key, string userName, string secret, CRED_TYPE type, CRED_PERSIST credPersist)
      {
  
          var byteArray = Encoding.Unicode.GetBytes(secret);
          if (byteArray.Length &gt; 512)
              throw new ArgumentOutOfRangeException("The secret message has exceeded 512 bytes.");
  
          var cred = new Credential
                         {
                             TargetName = key,
                             CredentialBlob = secret,
                             CredentialBlobSize = (UInt32) Encoding.Unicode.GetBytes(secret).Length,
                             AttributeCount = 0,
                             Attributes = IntPtr.Zero,
                             UserName = userName,
                             Comment = null,
                             TargetAlias = null,
                             Type = type,
                             Persist = credPersist
                         };
          var ncred = NativeCredential.GetNativeCredential(cred);
  
          var written = CredWrite(ref ncred, 0);
          var lastError = Marshal.GetLastWin32Error();
          if (written)
          {
              return 0;
          }
          var message = "";
          if (lastError == 1312)
          {
              message = (string.Format("Failed to save " + key + " with error code {0}.", lastError) + "  This error typically occurrs on home editions of Windows XP and Vista.  Verify the version of Windows is Pro/Business or higher.");
          }
          else
          {
              message = string.Format("Failed to save " + key + " with error code {0}.", lastError);
          }
          MessageBox.Show(message);
          return 1;
      }
  </code></pre>
  
  <p>Can any one point in the right direction as to how I can get these types of credentials to save?</p>
  
  <p>Thanks</p>