I have a problem with crossdomain.xml that is located on Facebook photo servers. The first problem arises when Silverlight asks for clientaccesspolicy.xml – Facebook servers return 403 – Access Denied which is fine, since they also have crossdomain.xml deployed on their servers. Silverlight then asks for that crossdomain.xml and the response it gets is exactly this:
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain- policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*" secure="false" to-ports="*" />
<site-control permitted-cross-domain-policies="master-only" />
</cross-domain-policy>
Then I played for a while with this, deployed that crossdomain.xml to my own servers and a got the same results – a security exception. Then I started moving things out and came to a conclusion that everything will work as desired if I only remove the to-ports="*" attribute? Does anyone has an idea how to overcome this, has anyone had the same problem before or is it something that I’m doing wrong?
I have run into the same issue while trying to programmatically retrieve images from Facebook. The strange part is that if you point a Silverlight image control to the Facebook image url, the image is retrieved and displayed without error. This got me thinking and I have come up with a viable workaround that seems to work consistently for my situation. I hope you find it useful too.
var uri = new Uri("http://graph.facebook.com/mglace/picture/", UriKind.Absolute);
var bmp = new BitmapImage();
bmp.ImageOpened += (sender, e) => { /* Do something here with the sender (which is the BitmapImage) */ };
bmp.CreateOptions = BitmapCreateOptions.None;
bmp.UriSource = uri;
Create a BitmapImage object, set an event handler for the ImageOpened event and set the CreateOptions property to BitmapCreateOptions.None. Finally, set the UriSource to the Facebook image you want to retrieve. The image is downloaded immediately because we set the CreateOptions to None (the default value is DelayedCreation). You can then perform any actions you would like in the ImageOpened event handler.
I wanted to encapsulate this logic in my service layer and beef up the error handling and such so I wrapped it in a Reactive Extensions observable to make it easier to use. Here is my final code snippet:
public IObservable<BitmapImage> GetProfilePhoto(string profileId)
{
return Observable.Create<BitmapImage>(
observer =>
{
// This handler handles a successful fetch
EventHandler<RoutedEventArgs> openedHandler =
(sender, args) =>
{
try
{
observer.OnNext(sender as BitmapImage);
observer.OnCompleted();
}
catch (Exception ex)
{
observer.OnError(ex);
}
};
// This handler handle a failure
EventHandler<ExceptionRoutedEventArgs> failedHandler =
(sender, args) => observer.OnError(args.ErrorException);
var url = string.Format("http://graph.facebook.com/{0}/picture/", profileId);
var uri = new Uri(url, UriKind.Absolute);
BitmapImage bmp = null;
try
{
Deployment.Current.Dispatcher.BeginInvoke(
() =>
{
bmp = new BitmapImage();
bmp.ImageOpened += openedHandler;
bmp.ImageFailed += failedHandler;
bmp.CreateOptions = BitmapCreateOptions.None;
bmp.UriSource = uri;
});
}
catch (Exception ex)
{
observer.OnError(ex);
}
return () =>
{
// Cleanup the event handlers
if (bmp != null)
{
bmp.ImageOpened -= openedHandler;
bmp.ImageFailed -= failedHandler;
}
};
});
}
And usage:
GetProfilePhoto("mglace")
.Subscribe(image => { /* Do something with the image in here*/ },
error => { /* Handle any errors in here */ },
() => { /* Finalization code goes here */ });
I hope someone out there finds this useful.
This is still not resolved in Silverlight 5. Facebook doesn't seem concerned too.
Related
How can I use dotnetbrowser doing the following in a winform application?
Create a listener that listen for callbacks to a specific redirect url.
Open url in dotnetbrowser. The url makes the callback to the redirect url in another thread
The listener catches the response from the callback.
I can do this with an ordinary webbrowser, but I would like it to be silent. That's why I try to use dotnetbrowser instead.
Is dotNetBrowser a good choice for this, or is there a better option?
This is from my test code with a non silent webbrowser. First I create a listener that listen to a redirectUri:
var listener = new HttpListener();
listener.Prefixes.Add(redirectURI);
listener.Start();
Then I start the url in a webbrowser:
Process p = Process.Start(url);
The started url will send a callback to the redirectUri. The listener will get it.
var context = await listener.GetContextAsync(); ;
string formData = string.Empty;
using (var body = context.Request.InputStream)
{
using (var reader = new System.IO.StreamReader(body, context.Request.ContentEncoding))
{
formData = reader.ReadToEnd();
}
}
listener.Close();
I found a solution with help from dotnetbrowser support site.
This is the winform constructor in my new test project:
public Form1()
{
webView = new BrowserView() { Dock = DockStyle.Fill };
Task.Run(() =>
{
engine = EngineFactory.Create(new EngineOptions.Builder
{
RenderingMode = RenderingMode.HardwareAccelerated,
LicenseKey = "your license key here"
}
.Build());
browser = engine.CreateBrowser();
})
.ContinueWith(t =>
{
webView.InitializeFrom(browser);
var listener = new HttpListener();
listener.Prefixes.Add(redirectURI);
listener.Start();
browser.Navigation.LoadUrl(url);
var context = listener.GetContextAsync().GetAwaiter().GetResult();
//Get data from redirectUri. You find this code from test example above, but not really relevant for the problem.
var formData = GetRequestPostData(context.Request);
listener.Close();
}, TaskScheduler.FromCurrentSynchronizationContext());
InitializeComponent();
FormClosing += Form1_FormClosing;
Controls.Add(webView);
this.Visible = false;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
browser?.Dispose();
engine?.Dispose();
}
I'm using CefSharp (47) to render a webpage from a host that I have no control over, and I want to make some additional CSS tweaks to those provided by the host.
Reading up on various topics across GitHub (https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp.Example/CefSharpSchemeHandlerFactory.cs), and here (CefSharp custom SchemeHandler), I wrote a custom scheme handler accordingly:
public class CustomSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "custom";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
Console.WriteLine(request.Url);
if (schemeName.ToLower() == SchemeName.ToLower())
{
// Do some stuff
}
return null;
}
}
I attempt to bind it in my application in the following manner:
CefSettings settings = new CefSettings();
settings.CachePath = browserCachePath;
settings.RegisterScheme(new CefCustomScheme()
{
SchemeName = CustomSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomSchemeHandlerFactory()
});
Cef.Initialize(settings);
The application then browses to the appropriate website, and uses the 'LoadingStateChanged' event to then fire off some JavaScript to inject the CSS file I want to load:
string linkText = "<link rel=\u0022stylesheet\u0022 type=\u0022text/css\u0022 href=\u0022custom://custom.css\u0022>";
var jsFunctionText = string.Format("(function() {{ $('head').append('{0}'); return true;}}) ();", linkText);
var injectionTask = await _myBrowser.GetMainFrame().EvaluateScriptAsync(jsFunctionText, null);
...which succeeds.
But my custom resource handler 'Create' event is never fired.
I can only presume that the handler isn't being registered properly, so I'd appreciate any advice/help in getting this working properly!
Thanks!
I have the following simple application page that uses the phone camera to upload the taken photo to azure blob:
public partial class AddReport : PhoneApplicationPage
{
// blobs stuff
string storageAccount = "MYACCOUNT";
string storageKey = "MYKEY";
string blobServiceUri = "http://MYACCOUNT.blob.core.windows.net";
CloudBlobClient blobClient;
private Report newReport;
public AddReport()
{
InitializeComponent();
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
//base.OnNavigatedTo(e);
newReport = new Report();
var credentials = new StorageCredentialsAccountAndKey(storageAccount, storageKey);
blobClient = new CloudBlobClient(blobServiceUri, credentials);
}
private void TakePhotoClick(object sender, EventArgs eventArgs)
{
//The camera chooser used to capture a picture.
CameraCaptureTask ctask;
//Create new instance of CameraCaptureClass
ctask = new CameraCaptureTask();
//Create new event handler for capturing a photo
ctask.Completed += new EventHandler<PhotoResult>(ctask_Completed);
//Show the camera.
ctask.Show();
}
void ctask_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK && e.ChosenPhoto != null)
{
WriteableBitmap CapturedImage = PictureDecoder.DecodeJpeg(e.ChosenPhoto);
UploadToBlobContainer(e.ChosenPhoto);
}
else
{
//user decided not to take a picture
}
}
private void UploadToBlobContainer(System.IO.Stream stream)
{
string containerName = "reportsPhotos";
var container = blobClient.GetContainerReference(containerName);
container.CreateIfNotExist(true, r =>
Dispatcher.BeginInvoke(() =>
{
var blobName = "report" + newReport.ReportId.ToString();
var blob = container.GetBlobReference(blobName);
blob.Metadata["ReportId"] = newReport.ReportId.ToString();
blob.UploadFromStream(stream, r2 =>
Dispatcher.BeginInvoke(() =>
{
newReport.Photo = container.Uri + "/" + blobName;
}));
}));
}
}
This is a simple case and I am not using SAS to authenticate, instead I save the key in the app itself (this is only for testing purposes) and also my blobs are publicly available.
when I run in debug mode it seems that everything is working, but the photo doesn't get uploaded to the blob. Also, I don't know how I can debug this to see if there was any error from the blob service.
Can anyone tell me what might be wrong ?
EDIT1: it seems that the container is not being created either. i've confirmed this using azure blob explorer
EDIT2: I am getting a System.Net.WebException : "The remote server returned an error: NotFound."
After long hours I have finally discovered that the problem was with this line:
string containerName = "reportsPhotos";
According to here all letters in a container name must be lowercase.
Changing it to reportsphotos solved the issue
That was time well spent.
Can you try just doing it like this instead:
// Retrieve storage account from connection-string
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
RoleEnvironment.GetConfigurationSettingValue("StorageConnectionString"));
// Create the blob client
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
// Retrieve reference to a previously created container
CloudBlobContainer container = blobClient.GetContainerReference("mycontainer");
// Retrieve reference to a blob named "myblob"
CloudBlob blob = container.GetBlobReference("myblob");
// Create or overwrite the "myblob" blob with contents from a local file
using (var fileStream = System.IO.File.OpenRead(#"path\myfile"))
{
blob.UploadFromStream(fileStream);
}
This is from:
http://www.windowsazure.com/en-us/develop/net/how-to-guides/blob-storage/#upload-blob
I'm trying to wrap the Event Async Programming model used in RIA Services in a Task.
I have followed the standard way of using a TaskCompletionSource and implemented the following extension method:
public static Task<IEnumerable<T>> LoadAsync<T>(this DomainContext source, EntityQuery<T> query) where T : Entity
{
TaskCompletionSource<IEnumerable<T>> taskCompletionSource = new TaskCompletionSource<IEnumerable<T>>();
source.Load(
query,
loadOperation =>
{
if (loadOperation.HasError && !loadOperation.IsErrorHandled)
{
taskCompletionSource.TrySetException(loadOperation.Error);
loadOperation.MarkErrorAsHandled();
}
else if (loadOperation.IsCanceled)
{
taskCompletionSource.TrySetCanceled();
}
else
{
taskCompletionSource.TrySetResult(loadOperation.Entities);
}
},
null);
return taskCompletionSource.Task;
}
I then use this in the following way:
var task = _context.LoadAsync(_context.GetPlayersQuery());
task.Start();
task.Result;
The problem though is that I get an InvalidOperationException stating that "Start may not be called on a promise-style task". I have tried not starting the task, but then the loadOperation callback never fires.
Can anyone see what I am doing wrong here?
Thanks in advance
Problem is solved. Under the hood the DomainContext.Load() method is already operating in an asynchronous manner. There must have been some conflict with trying to wrap an already asynchronous method in a task.
However, even if I still follow the EAP correctly with the code below, I still get the InvalidOperationException of 'start cannot be called on a promise-style task'
public static Task<IEnumerable<T>> LoadAsync<T>(this DomainContext source, EntityQuery<T> query) where T : Entity
{
TaskCompletionSource<IEnumerable<T>> taskCompletionSource = new TaskCompletionSource<IEnumerable<T>>();
var loadOperation = source.Load(query);
loadOperation.Completed += (obj, args) =>
{
if (loadOperation.HasError && !loadOperation.IsErrorHandled)
{
taskCompletionSource.TrySetException(loadOperation.Error);
loadOperation.MarkErrorAsHandled();
}
else if (loadOperation.IsCanceled)
{
taskCompletionSource.TrySetCanceled();
}
else
{
taskCompletionSource.TrySetResult(loadOperation.Entities);
}
};
return taskCompletionSource.Task;
}
Try this instead
var result = await _context.LoadAsync(_context.GetPlayersQuery());
Try using
task.ContinuewWith(Action<Task<T>> continuation)
That worked for me, as I too got that exception when using task.Start
I'm tring to get an album picture from facebook using the facebook C# SDK within Silverlight app with the following code:
FacebookClient client = new FacebookClient(this.Profile.AccessToken);
client.GetAsync(string.Format("/{0}/picture?type=small", this.ID));
client.GetCompleted += (s, e) =>
{
dynamic result = e;
};
Where this.ID is the ID of the album, but I get this error:
Unexpected character encountered while parsing value: �. Line 1, position 1.
from the DeserializeObject method in the JsonSerializer. The problem is that facebook does'n return json data with the imge uri or something like this, but they actually return the image itself in a binary data. Anybody has any idea how I can handle this result or just get Uri to the image?
I have a workaround for this using this code:
var request = WebRequest.Create(string.Format("https://graph.facebook.com/{0}/picture?access_token={1}", this.ID, this.Profile.AccessToken));
request.BeginGetResponse(ar =>
{
using (var response = ((WebRequest)ar.AsyncState).EndGetResponse(ar))
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
this.Picture = new BitmapImage(new Uri(response.ResponseUri.AbsoluteUri));
}
);
}
}, request);
But I really wanted to use only Facebook C# SDK for getting the data.
Here is the solution that I'm going to use:
FacebookClient client = new FacebookClient(this.Profile.AccessToken);
client.QueryAsync(String.Format("SELECT src_small, src_big, src FROM photo WHERE pid IN (SELECT cover_pid FROM album WHERE object_id={0})", this.ID));
client.GetCompleted += (s, e) =>
{
dynamic result = e.GetResultData();
Deployment.Current.Dispatcher.BeginInvoke(() => this.Picture = result[0].src_small);
};
I was facing a similar issue. Wanted to use Facebook C# SDK only as well.
Solved it like this:
FacebookClient facebookAlbumClient = new FacebookClient(_albumAccessToken);
dynamic facebookAlbumCover = facebookAlbumClient.Get(string.Format("/{0}?fields=picture&type=thumbnail", (string)facebookAlbum["id"]));
This way you are getting the json array and not the picture
I think I found an acceptable solution for my problem. I'll just use FQL instead of Graph API. This will do the job:
FacebookClient client = new FacebookClient(this.Profile.AccessToken);
client.QueryAsync(String.Format("SELECT src_small, src_big, src FROM photo WHERE pid IN (SELECT cover_pid FROM album WHERE object_id={0})", this.ID));
client.GetCompleted += (s, e) =>
{
dynamic result = e.GetResultData();
Deployment.Current.Dispatcher.BeginInvoke(() => this.Picture = result[0].src_small);
};