Showing posts with label Interop. Show all posts
Showing posts with label Interop. Show all posts

Tuesday, September 20, 2016

IPC with SendMessage between WPF and WinForms apps

Communicating between processes is essential part of every big systems which we generally call as Inter Process Communication. If its large distributed systems the processes will be in different computers which we classify as servers and clients. Often we use http / http(s) based or other socket based communication mechanisms to do the job. In .Net world most of the people use WCF.

But in some cases there will be big systems running on same computer as separate processes and would like to communicate each other. We can use WCF here also with net.pipe binding. Since its same machine there are some more available options. Lets see what are those options. This post is about such an alternative which uses SendMessage API provided by Windows OS.

Clipboard

One of the dangerous forms of communication. Dangerous as user or any other application can copy new data to clipboard before our intended process reads data. But I have seen enterprise systems which uses this technique and working well for years.

EventLog

The sender can write to event log and the receiver subscribe to the event log entries to receive message. This will leave a trace of what has send back and forth between apps. But we should never use this for secure communication unless encrypted.

Standard In and Out of processes

One process can write to StdIn stream of another process and other can write to sender's StdId for two way communications. I have used this technique in one of my recent projects as it spawns processes frequently where publishing a unique WCF end endpoint was little difficult to achieve.

FileWatcher

Still many systems uses this technique. If there is a designated folder for messages and files are unique, it will work. in .Net works we can use FileSystemWatcher APIs to achieve the functionality.

SendMessage

Here we leverage the SendMessage windows API function to send a message to another process. That message then will be available to the message pump of receiving application. As we know the windows apps are running based on the messaging infrastructure. In simple words, the OS sends appropriate messages to apps about what is going on (eg:MouseClicked) with required contextual data and apps just process them. Its same for WPF as well though its hidden for normal apps. But there is a way to capture the message pump in WPF apps. 

A sample has been uploaded to github which shows how communication can be established between WinForms app and WPF app using SendMessage. Since the code is the true documentation, there is no more explanation required in this post.

https://github.com/joymon/Win32IPCDemo

Happy coding.

Tuesday, July 19, 2016

Communication between WPF Windows App & HTML Page hosted in WebBrowser control

Context

This can be considered as a continuation of one old post in this same blog. In that post we can see the technique to establish communication between HTML Page inside WebBrowser controls and and .Net application. In this post we are going to see the approach in detail. Code is available for download and is self explanatory.

Technical details


  • For WPF to JS communication
    • WebBrowser control has a method called InvokeScript.
    • This can be used to invoke any JavaScript function present in the rendered web page and pass parameters.
  • For Web page/JS to WPF .Net communication
    • WebBrowser has a property called ObjectForScripting which will be available in JS as window.external
    • If we set an object to ObjectForScripting property, that .Net object will be available in JS and from JS we can call any method on that object which will in turn call the .Net code.

What’s not done

This just proves the technique. Performance testing is not part of this.

Source code

  • The HTML page is inside the WPF.
  • WebBrowser loads the HTML page by using pack: uri syntax.
  • WebBrowser's ObjectForScripting Object is set when the window loads
  • A class which is comvisible is used for the above property
  • From JavaScript the SetAValue() is called to pass data from JS to WPF
  • For WPF to JS myWebOC.InvokeScript() is invoked
  • In JS the data is received in the setSomeText() function.
Download from the below url.

Tuesday, May 24, 2016

Embed Google V8 JavaScript engine in .Net application

Background

JavaScript is everywhere whether we accept or not. Now a days the trend in new languages is to transpile to JavaScript. That's the way they don't need to worry about runtime. Thanks to fast JavaScript engines such as V8. People are sure once their code is in JavaScript, the engines will run it in the highest speed, even it works in server side with the help if NodeJS technology. For client side desktop development also we can rely on JavaScript using the Electron technology. In all these places JavaScript engines power the execution.

So what about DSLs? Why can't we write our DSL in JavaScript and execute via JavaScript engines? The only constraint here is from our technology / runtime we should be able to send and receive objects to and from JavaScript engine. In simple terms, in our C#.Net application there is a salary calculation which is dynamic and needs to be customizable by user. If we can pass the Employee object to and from JavaScript engine, we can use JavaScript as DSL for our app.

The problem narrows down to
  • Starting JavaScript engine from our technology of choice. Send the JavaScript code to the engine.
  • Passing objects from our application JS engine to manipulate.
Since JS is dynamic and not strictly typed, we can pass any string as program into the engine. Engine will fail the code only during the execution. In this post, we can see how such an interaction can be done between .Net and Chrome V8 JavaScript engine.

Selecting Chrome V8 Engine

There are many implementations out there for Chrome V8 engine. Most of those are compatible directly with native code. From .Net its little difficult to interface with those.

We are going to use one out there named JavaScriptDotNet.

Setting up the environment

Download the binaries from the below repository. They moved the code to Github. But unfortunately the ready made binaries are not there.

http://javascriptdotnet.codeplex.com/

Create our .Net project and refer corresponding binaries. There are .Net 3.5 & 4.0 folders and each folder has 32 and 64 bit versions. Since Chrome is a native unmanaged library and JavaScriptDotNet only provides a managed wrapper over that, we need to be careful in selecting which version. The assembly we need to refer to our .Net project is Noesis.JavaScript.The other dlls should be present in the \bin or running directly of our ,Net application.

How to run JavaScript

Its simple as creating JavaScriptContext and calling Run().

using (JavascriptContext context = new JavascriptContext())
{
    var result = context.Run("3+2");
    MessageBox.Show(result.ToString());
}

How to pass .Net objects to JavaScript and vice versa

Now lets see how can we pass .Net objects into JavaScript. If we can't pass the objects there is no meaning in embedding JavaScript engine inside .Net

Lets take one simple Employee class
public class Employee : INotifyPropertyChanged
{
    public int Id { get; set; }
    public string Name { get; set; }
    public double Basic { get; set; }
    public double HRA { get; set; }
}
Employee emp = new Employee() {Basic=100, Name = "Joy" };
Object is also created with one property 'Basic' set to 100. Now lets see what the below code will do on this object.
using (JavascriptContext context = new JavascriptContext())
{
    // Setting the externals parameters to the context
    context.SetParameter("emp", emp, SetParameterOptions.RejectUnknownProperties);
    try
    {
        // Running the script
        context.Run("emp.Total = emp.Basic * 2; emp.HRA = emp.Basic / 2;");
        MessageBox.Show(emp.HRA.ToString());
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error");
    }
}

It will show 50 as the emp.HRA is calculated when JavaScript engine run. Yes now we have embedded JavaScript engine in .Net and JavaScript can now be used as DSL in our C# application.

http://stackoverflow.com/questions/172753/embedding-javascript-engine-into-net

Tuesday, July 30, 2013

Office Interop - ComExcetion HRESULT: 0x800A03EC when large text values in formulas

Office Interop in .Net

Office interop cannot be avoided, if you are developing applications for Windows platform. It is 90% sure that your customers will ask for MS Office integration when the project grows or your marketing people will add office integration regardless the client actually needs or not.

Before Open XML era (docx,xlsx etc...) developers had only one way to interact with office files from .Net or any other applications. Yes the Office interop APIs. In .Net it can be done by Adding reference to the interop assemblies.Only issue I had faced is the non type safe APIs provided. Another issue is the memory leak due to unmanaged objects but that can be avoided by using Marshal.ReleaseComObject method.

If you are working in .Net for 2 years and don't know what is Office interop and how to use it to create an Excel file programatically, just google for it and try at least once. Let me know, if you are not able to get any result in google

Adding formula to Excel cells from C#.Net

Formula in Excel should be known to everyone who uses Windows operating system seriously. It can be added from the Excel application by prefixing a '=' symbol to mathematical or logical expressions when we edit cells. For example if you want to display the sum of values in cells E1 to E9 in E10 cell, write the below into E10 cell

=SUM(E1:E9)

The same can be done from our .Net application as well. Below is an code which adds little more complicated formulae to the cell


Application app = new Application();
Workbook wb= app.Workbooks.Open(excelFilePath);
Worksheet firstSheet = wb.Sheets[1] as  Worksheet;
string message = "There is a 'No' in the range B1-B10";
string formula = @"=IF(COUNTIF(B1:B10,""No"") >0,"""+message+@""","""")";
firstSheet.Cells[1, 1]= formula;//This is cell A1
wb.Save();
wb.Close();
Marshal.ReleaseComObject(app);


This means

  1. It will create an excel file in the path mentioned in the variable excelFilePath
  2. It will add a formula in the cell A1.
  3. The formula will be '=IF(COUNTIF(B1:B10,"No") > 0,"There is a No in the range B1-B10","")
  4. The formula means, if any cell in the range B1 to B10 contains "No" string it will show the message "There is a No in the range B1-B10" in the cell A1
This is simple right. But what about the below ie I want to set a big text to the A1 cell when there is a "No" in the range.

      

Application app = new Application();
Workbook wb= app.Workbooks.Open(excelFilePath);
Worksheet firstSheet = wb.Sheets[1] as  Worksheet;
 
string message = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur";

string formula = @"=IF(COUNTIF(B1:B10,""No"") >0,"""+message+@""","""")";
firstSheet.Cells[1, 1]= formula;
wb.Save();
wb.Close();
Marshal.ReleaseComObject(app);

This simply fails with a message

A first chance exception of type 'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll
Additional information: Exception from HRESULT: 0x800A03EC

Any idea? It doesn't talk about what happened inside the code.Or at least what is the actual error?

This is the time to show the your practical debugging skills. Most of the developers complain here that, its just not working. But the intelligent developers think in a different way. Is it because of long message? Can't I assign long text via interop? or Excel itself just don't accept this much big message?

Most of them ends up in trying out the same scenario in Excel. Technically this is called as "reproducing the issue in a stripped down environment". Whoever try it in Excel will get the actual problem.

Text values in formulas are limited to 255 characters. To create text values longer than 255 characters in a formula, use the CONCATENATE function or the concatenation operator (&)

Yes there is a limitation in Excel and it itself giving the solution. So lets modify our code to have concatenation.

Application app = new Application();
Workbook wb = app.Workbooks.Open(excelFilePath);
Worksheet firstSheet = wb.Sheets[1] as Worksheet;

string message = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ";
message+=@"""&"""+"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur";
string formula = @"=IF(COUNTIF(B1:B10,""No"") >0,""" + message + @""","""")";
firstSheet.Cells[1, 1] = formula;
wb.Save();
wb.Close();
Marshal.ReleaseComObject(app);

Looks good. But in real scenario things will not be easy like this. Your application may be getting the message from some other sources which requires a splitting on the fly.

Happy debugging.

Monday, July 22, 2013

DSOFile in 64bit to deal with Office 2013 custom properties

DSOFile.dll is the way to edit Microsoft office document properties without the office installed. Microsoft claims that it is independent, but I had came across scenarios where it requires one more file installed in the machine which is msoshext.dll at the location.

C:\Program Files\Common Files\Microsoft Shared\OFFICE<version>\msoshext.dll
eg: C:\Program Files (x86)\Common Files\microsoft shared\OFFICE15\msoshext.dll

This post is about an issue which you may face, if you run your application which uses DSOFile.dll in 64 bit machines with 32bit Office 2013 installed. Mainly when you run 32 bit apps in 64 bit.The issue occurs when ever the supporting file msoshext.dll is not present for target version of the applications which uses the DSOFile.dll. Lets see the scenario in deep.

The below code snippet is something which adds a custom property to the word document. If you are not familiar about the custom properties in office document just open a word file in MS word, go to properties page and select the custom tab.


                OleDocumentPropertiesClass doc = new OleDocumentPropertiesClass();
                doc.Open("test.docx"falsedsoFileOpenOptions.dsoOptionOpenReadOnlyIfNoWriteAccess);
                object val = "joysvalue";
                doc.CustomProperties.Add("joyskey"ref val);
                doc.Save();


You may experience as it just stopped updating the custom properties in the above environment (64Bit machines which have 32bit office). ie if you look at the custom properties dialog after opening the docx file you cannot see it there.

But interesting thing is, if you again try to add the same custom property you will get an exception. I tried as much as I can to identify where the custom property is being stored but I can't find out. We can verify it as well by executing the below code


                OleDocumentPropertiesClass oleDocument = new OleDocumentPropertiesClass();
                oleDocument.Open("test.docx"falsedsoFileOpenOptions.dsoOptionOpenReadOnlyIfNoWriteAccess);
                foreach (CustomProperty property in oleDocument.CustomProperties)
                {
                    Console.WriteLine("Pro Name {0},Value{1}", property.Name, property.get_Value());
                }


Simply saying, the first code snippet added the custom property to the document. Its accessible, if we iterate the CustomProperties collection but cannot visible in the office application's custom properties page.

Alternative

When you put this question in developer forums you will get an advise to go with open xml sdk. This is the solution to the problem but the drawback is it will work only with the open xml office documents such as docx,pptx,xlsx etc...The old office documents such as .doc, .ppt, .xls etc... are not at all supported by open xml sdk.So if your application needs to deal with both the formats using same code base you need to find the solution to the DSOFile issues in 64 bit. 

Solution

Install all the latest possible office 2013 updates and make sure you have the msoshext.dll in the right location. If your application is 32 bit you need to have the file in the c:\program files (x86)\ folder. Also make sure that the DSOFile.dll is registered properly for 32 bit (Using C:\Windows\SysWOW64\regsvr32.exe) and 64 bit in the 64 bit environment.

The above updating is applicable to all versions of office, if its installed in 64 bit machines.

Monday, February 4, 2013

MS Word interop and {"Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))"}

When you first see below exception your Word interop code execution in production environment, you think that there is something directly related with security. It normally results in spending more time on verifying the security of files and com dlls.

System.UnauthorizedAccessException {"Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))"}

But is that the actual problem?
No.When we got this exception, we spent sometime on the security aspects. But when we looked at the below code we easily found that this is thrown because a com object is not supporting TextFrame property. The code was as follows.

For Each oShape As Word.Shape In wDoc.Shapes
                    If (oShape.TextFrame IsNot NothingThen
                        If (oShape.TextFrame.HasText <> 0 AndAlso oShape.TextFrame.TextRange IsNot NothingThen
                            'Processing code
                        End If
                    End If
...

Actually when we iterate through the Shapes, there may be some shapes which may not support the TextFrame property .We need to avoid those shapes from the TextFrame processing. This confusion would have been avoided if MSFT provides a meaningful exception message.

Happy debugging...