Tech Articles


Calling .Net Assemblies from PowerBuilder Win32 via PowerShell


Recently someone asked me how they could get the output from PBDOM used from a PowerBuilder Classic Win32 application formatted with white space as PBDOM doesn’t include it.  I gave them a number of options, particularly MSXML through OLE Automation or using a .Net class like XmlTextWriter.  Normally if you were going to try to access a .Net assembly from a PowerBuilder Classic Win32 application, you would do it via a COM Callable Wrapper.  However, for something this simple I thought there had to be a more lightweight way to accomplish it.  One particular lighterweight way that occurred to be would be to have the application launch a Windows PowerShell script that would then use the .Net class in question.  We’re going to look at an example of how that’s done.

The first thing we’ll need is the PowerShell script to do the work. This is what I came up with (referenced as prettyprint.ps1 in the later code):

param( [string]$filein, [string]$fileout ) [void][System.Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") [System.Xml.Linq.XDocument]::Load($filein).Save($fileout) # If running in the console, wait for input before closing. if ($Host.Name -eq "ConsoleHost") { Write-Host "Press any key to continue..." $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null }

The script declares a couple of parameters (the input xml file and the output xml file).  It then loads the System.Xml.Linq .Net assembly via reflection.  Finally, we load the file and then write it back out again with white space.  The last few lines are just for debugging from a command line prompt, as it displays a “Press any key to continue…” prompt before closing the PowerShell process window.

To test it through a batch file, I used this:

@ECHO OFF SET ThisScriptsDirectory=%~dp0 SET PowerShellScriptPath=%ThisScriptsDirectory%prettyprint.ps1 SET SourceFile=%ThisScriptsDirectory%config.xml SET DestFile=%ThisScriptsDirectory%.config.new.xml %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%PowerShellScriptPath%' -filein '%SourceFile%' -fileout '%DestFile%'"; pause

The assumption here is that the batch file, the PowerShell script and the input xml file are all in the same directory.  I’ve got a pause statement in this script, so you actually get two “Press any key to continue…” prompts.  If it works properly, that’s all you should see.  Otherwise you should see the error message from PowerShell or the batch file.

Once you know it’s working, it’s time to call it from PowerBuilder.  I’m going to cheat a bit in the following example.  I’m using the PowerScript Run function and have hard coded the location of the PowerShell executable.  In actual usage, you might query the operating system for the location of the SystemRoot, or use the ShellExecuteShellExecuteEx or CreateProcess Windows API functions to invoke the process rather than Run.  Also note that the code assumes that the PowerShell script file is located in the same directory as the PowerBuilder Classic Win32 application.

int li_rc string ls_sourcepath string ls_sourcefile string ls_destpath string ls_destfile string ls_command string ls_directory ls_directory = GetCurrentDirectory() ls_command = 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "&' ls_command += " '" + ls_directory + "\prettyprint.ps1' " li_rc = GetFileOpenName ( "Select source file", ls_sourcepath, ls_sourcefile, "XML", "XML File (*.xml),*.xml" ) IF li_rc <> 1 THEN Return ls_command += " -filein '" + ls_sourcepath + "' " li_rc = GetFileSaveName ( "Select destination file", ls_destpath, ls_destfile, "XML", "XML File (*.xml),*.xml" ) IF li_rc <> 1 THEN Return ls_command += " -fileout '" + ls_destpath + "' " li_rc = Run ( ls_command )

In this case, the application prompts the user to select an XML file to process and then provide a name for the “pretty” version of the XML to output.

Without the second argument to Run, you will see the PowerShell process window open momentarily.  If you were to continue to use the Run method, you can pass Minimized! as a second argument to that function to suppress the window.  In addition, the ShellExecute, ShellExecuteEx and CreateProcess Windows API functions have options to suppress the PowerShell process window from displaying.

Comments (2)
Monday, Mar 06 2017

Thanks for the post. I was wandering, since I am new to .net, i would like to call a c#.net window, passing some params, and receiving some back.  Your example uses a window control. Can this be done with the entire c# window?

#9
0

Tuesday, Mar 07 2017

I'm not sure where you got the impression I'm using a window control.  I'm calling a nonvisual class.  This technique would only work with nonvisual classes.

If you wanted to interop with a C# window you'd have to expose it to PowerBuilder as an ActiveX control.  It's the kind of thing that the Interop Forms Toolkit was designed for (https://www.microsoft.com/en-us/download/details.aspx?id=3264).

In fact, I've done other articles showing how to use that toolkit with PowerBuilder, that may be where you have the impression I was using a Windows control.  You may have been thinking of one of those articles.

The Interop Forms Toolkit does handle entire windows as well.

#10
0

Find Articles by Tag

RibbonBar Builder XML InfoMaker PBNI TLS/SSL Icon Database Profile PowerServer Mobile Source Control SQL PBVM Authorization Outlook C# iOS PostgreSQL ODBC driver BLOB PostgreSQL Charts Import JSON Resize Git DataType SQL Server UI Modernization Application Data RESTClient Design WinAPI SVN Linux OS Export JSON Interface Oracle JSONParser SDK Event Database Table Data Database Table Schema PowerBuilder Compiler External Functions Event Handler PDF 32-bit Source Code Installation DragDrop DataWindow JSON SnapObjects Web API Visual Studio Performance ODBC Mobile Repository Stored Procedure .NET Assembly OrcaScript DLL .NET DataStore Validation Testing Platform Text Messagging TFS CrypterObject PBDOM SOAP Trial Encoding PDFlib Deployment CoderObject Database Connection Service PowerBuilder (Appeon) SnapDevelop TreeView Elevate Conference Debugger Bug Jenkins .NET Std Framework PowerServer Web SqlModelMapper RibbonBar Expression Open Source API Filter DataWindow REST IDE JSONGenerator Debugging ActiveX GhostScript Variable Menu Encryption Android File CI/CD HTTPClient 64-bit Script UI Themes Database Object Azure Sort PFC Export COM Migration Class DevOps PowerScript (PS) Web Service Proxy NativePDF UI Transaction PowerBuilder SqlExecutor Event Handling MessageBox Graph WebBrowser OAuth Window OLE Automated Testing JSON License Database Error Excel Debug Windows OS RichTextEdit Control Branch & Merge Import Array Database Table Database Painter TortoiseGit Syntax Windows 10 Icons Model OAuth 2.0 Configuration Authentication