I suspect the problem may be too low to access any of the public APIs.
As a starting point, I realized that we have at our disposal (and this will not require changes on the user side), which allows us to detect where the & signs are. From this, we can determine if & part of the value or query string delimiter. If the latter, we replace it with a random symbol and break it into this symbol.
Regex rx = new Regex(@"(\b&.*?)=");
The above regex matches only & , followed by = (so it will match &test= below, but not Macklemore & Ryan Lewis ).
Then we replace all instances of & that are matched by the above expression with a random character that will not be used elsewhere. In this example, I just used | .
string mapperInput = @"Protocol?encodedLaunchUri=my-app://do/stuff/?artist=Macklemore & Ryan Lewis&test=1"; string final = rx.Replace(mapperInput, new MatchEvaluator( new Func<Match, string>(x => x.Value.Replace('&', '|')) ));
Then we take this result and put it in the collection.
//skip 2 because the first two matches include the protocol section var values = final.Split(new char[] { '?', '|' }).Skip(2).ToArray();
The values array now contains two elements (which can be iterated and placed in the Key-Value dictionary for access)
artist=Macklemore & Ryan Lewis test=1
This had to be tested with various inputs that include characters, but from a quick test, it seemed like normal.