2010-11-16 32 views
1

Une application de génération de rapports s'exécute côté serveur qui lit un fichier BMP stocké dans ma base de données (en tant qu'octet []), le convertit en image, puis le place dans une feuille de calcul Excel qui constitue la base de ce rapport (ce rapport est finalement remis au client pour téléchargement.) Pour ce faire, j'essaie d'utiliser le presse-papier côté serveur pour gérer le «collage» de l'image dans une plage spécifique dans la feuille de calcul. Voici l'extrait de code -Problème lié à l'utilisation du Presse-papiers côté serveur dans la Webapp ASPX

System.Drawing.Image image; 
Bitmap bm; 
Graphics g; 
Excel.Range range; 

MemoryStream ms = new MemoryStream(graphRecs.ElementAt(0).Graph, 0, 
    graphRecs.ElementAt(0).Graph.Length); 
ms.Write(graphRecs.ElementAt(0).Graph, 0, graphRecs.ElementAt(0).Graph.Length); 
image = System.Drawing.Image.FromStream(ms, true); 

bm = new Bitmap(413, 130); 
g = Graphics.FromImage(bm); 
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; 
g.DrawImage(image, 0, 1, 413, 130); 

Clipboard.SetDataObject(bm, false, 4, 250); 
range = ws.get_Range(cBlkPtr[6, 2], cBlkPtr[6, 2]); 
ws.Paste(range, bm); 
Clipboard.Clear(); 

L'exécution de ce mode de débogage sous VS2008 semble fonctionner correctement - l'image est convertie, ajoutée au presse-papiers, et collé dans la plage spécifiée sans problème. Après que je publie la webapp à mon serveur IIS, cela échoue sur la mention « Clipboard.SetDataObject » à l'exception suivante -

Requested Clipboard operation did not succeed. 
at System.Windows.Forms.Clipboard.ThrowIfFailed(Int32 hr) 
at System.Windows.Forms.Clipboard.SetDataObject(Object data, Boolean copy, Int32 
     retryTimes, Int32 retryDelay) 
at ReportGenerate.buildPvsClinicSections(Worksheet ws, Object j, patientRecord p, String 
     patientStatus, String programType) 

Je suis en supposant que cette erreur doit faire de ne pas être dans un SingleThreadApartment. J'ai ajouté la directive 'AspCompat = true' à ma page ASPX sans changement (je ne pensais pas que cela aiderait car AspCompat est plus pour ASP que ASPX). Comme je ne peux pas ajouter le [STAThread] à mon "main" (ce serait IIS), je ne sais pas comment procéder. Je suis également ouvert à la modification de l'approche que j'utilise pour ajouter l'image à la feuille de calcul, tant que je peux explicitement spécifier (via la plage) où la placer. Utiliser Shape.AddPicture par exemple ne me permet pas de faire cela.

Des idées?

Merci.

Mise à jour

J'ai mis à jour l'extrait de code pour lancer un deuxième fil avec le ApartmentState correct -

range = ws.get_Range(cBlkPtr[6, 2], cBlkPtr[6, 2]); 

ClipboardModel cbm = new ClipboardModel(bm, range, ws); 
System.Threading.Thread cbThread = new System.Threading.Thread(new 
    System.Threading.ParameterizedThreadStart(DoClipboardStuff)); 
cbThread.SetApartmentState(System.Threading.ApartmentState.STA); 
cbThread.Start(cbm); 
cbThread.Join(); 

La méthode 'DoClipboardStuff' ressemble à ceci -

[STAThread] 
protected void DoClipboardStuff(object o) 
{ 
    try 
    { 
    ClipboardModel cbm = (ClipboardModel)o; 

    Clipboard.SetDataObject(cbm.bm, false, 4, 250); 
    cbm.ws.Paste(cbm.range, cbm.bm); 
    Clipboard.Clear(); 
    } 
    catch (Exception e) 
    { 
    StreamWriter sw = new StreamWriter(@"C:\Myopia\Log.txt"); 
    sw.WriteLine(e.Message); 
    sw.WriteLine(e.StackTrace); 
    sw.Flush(); 
    sw.Close(); 
    throw e; 
    } 
} 

I J'obtiens maintenant exactement la même erreur qu'avant, seulement maintenant dans cette méthode. Je commence à soupçonner que ce n'est pas l'appartement, mais plutôt l'absence d'une «interface utilisateur». Je ne sais pas si l'interface Win32 normale serait mieux, mais c'est mon approche suivante (à moins que quelqu'un d'autre a un plus, la solution .NET'ish.)

Mise à jour # 2

Alors que havre de paix I » J'ai été capable de résoudre le problème avec IIS 6 et le presse-papiers, j'ai réussi à contourner ce problème en écrivant le BMP reconstruit dans un fichier temporaire, puis en utilisant le Shapes.AddPicture pour le placer là où j'en ai besoin -

g.DrawImage(image, 0, 1, 400, 75); 

bm.Save(@"c:\Myopia\temp.bmp"); 

Excel.Shape xlShape = ws.Shapes.Item("Rectangle 2"); 
float left = xlShape.Left; 
float top = Convert.ToSingle(ws.get_Range("A1", cBlkPtr[5, 2]).Height); 
float width = xlShape.Width; 
float height = xlShape.Height; 

xlShape = ws.Shapes.AddPicture(@"c:\Myopia\temp.bmp", 
    Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoCTrue, 
     left, top, width, height); 

Pas une solution idéale, mais celle qui fonctionne pour l'instant. Le seul problème avec cette approche est que je semble avoir une perte de résolution entre reconstruire le BMP de l'octet [], l'enregistrer dans le fichier temp.bmp, puis l'ajouter - le bmp a l'air 'flou'. Peut-être devoir chercher un format moins 'lossy' à utiliser.

Répondre

0

Si STA est en effet le problème puis essayez d'effectuer l'opération dans un nouveau thread que vous définissez à STA avant de commencer comme indiqué dans la réponse de 'Skeet (tm) à cette question:

in .NET, How do I set STAThread when I'm running a form in an additional thread?

Quelque part dans le fond de mon esprit si une petite voix suggère que le presse-papiers pourrait être disponible uniquement pour les applications avec une interface utilisateur (le serveur web dev par exemple) ... J'espère que je me trompe!

+0

Question d'origine mise à jour. Modifié le code pour déclencher un deuxième thread avec l'ApartmentState correct, mais réussi à déplacer seulement l'erreur vers ma méthode d'aide. – JJalenak

+0

JJanelak: Dans ce cas, je ne suis pas entièrement sûr. Je viens de configurer une nouvelle application Web avec une page qui définit du texte dans le presse-papiers, puis le supprime du presse-papiers. Tout ce que j'avais à faire était de placer aspcompat sur la page comme vous l'avez fait. Cela fonctionne à la fois sur DevWebServer et IIS7.5 (avec le pool d'applications utilisant l'application ApplicationPoolIdentity par défaut). Dans ce cas, il peut s'agir de l'identité du pool d'applications sur le serveur en ligne, car l'accès au presse-papiers est un privilège de sécurité et est donc contrôlé par le type de contexte de sécurité dont dispose l'utilisateur. –

+0

Andras - Malheureusement, je suis toujours coincé avec IIS 6, et franchement, je ne suis pas vraiment informé sur IIS. D'après ce que je peux dire, le DefaultAppPool est ce que j'utilise, avec une identité «prédéfinie» du service réseau. L'application de site Web est définie pour utiliser DefaultAppPool et sous l'onglet ASP.Net, Application, les paramètres d'identité sont l'emprunt d'identité local avec le nom d'utilisateur/mot de passe administrateur. Que devrais-je vérifier? Merci..... – JJalenak