简体   繁体   中英

Calling .NET Assembly from PowerBuilder (Exposing .NET Framework Components to COM)

Here is the programming environment.

  • Framework: ASP.NET Framework 4
  • Language: Visual C# 2010, PowerBuilder 12.5.2 Build 5609 Built on Jan 2 2014 at 01:29:51

Here is the scenario.

I'm creating a PowerBuilder application that fires up a simple window with a multi-line editing textbox where you can type something in there and click a button to load a C# COM class that checks for spelling errors and returns the value back to the PowerBuilder application's textbox.

C# ClassLibrary.cs

using System;
using System.Runtime.InteropServices;

namespace InteropServices
{
    [Guid("Let's just assume that I have a correct Guid string here.")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface ISpellChecker
    {
        [(DispId(1)]
        string CheckText(string inputMsg);

        [(DispId(2)]
        void Dispose();
    }

    [ClassInterface(ClassInterfaceType.None)]
    [Guid("Let's just assume that I have a correct Guid string here.")]
    [ProgId("InteropServices.SpellChecker")]
    public class SpellChecker: ISpellChecker
    {
        private string newInputMsg

        public string inputMsg
        {
            get
            {
                return newInputMsg;
            }
            set
            {
                newInputMsg = value;
            }
        }

        private App spellCheckerApp;
        private MainWindow spellCheckerMainWindow;

        public SpellChecker() { }

        public string CheckText(string inputMsgBase)
        {
            inputMsg = inputMsgBase;
            spellCheckerApp = new App();
            spellCheckerMainWindow = new MainWindow(inputMsg);
            spellCheckerApp.Run(spellCheckerMainWindow);
            txtCorrected = MainWindow.TextReturned;

            return txtCorrected;
        }

        public string txtCorrected { get; set; }

        // This function was my futile attempt to resolve this issue, but it seemingly has no effect whatsoever.
        public void Dispose()
        {
            spellCheckerMainWindow.Close();
            spellCheckerApp.Shutdown();
            spellCheckerMainWindow = null;
            spellCheckerApp = null;
        }
    }
}

C# MainWindow.xaml

<Window x:Class="InteropServices.MainWindow"
        xmlns="http://schemas.microsft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsft.com/winfx/2006/xaml"
        Title="Spell Checker .NET" Height="350" Width="525" Loaded="Window_Loaded">
    <Grid>
        <TextBox Name="TextBoxSpellCheck" SpellCheck.IsEnabled="True" AcceptsReturn="True" TextWrapping="Wrap" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True" Margin="50" />
        <Button Name="ButtonAccept" Margin="229,267,146,12" Width="128" Height="32" Content="Accept" IsDefault="True" Click="ButtonAccept_Click" />
        <Button Name="ButtonCancel" Margin="364,267,12,12" Width="128" Height="32" Content="Cancel" IsDefault="True" Click="ButtonAccept_Click" />
    </Grid>
</Window>

C# MainWindow.xaml.cs

using System;
// Omitting the rest of the default Usings

namespace InteropServices
{
    public partial class MainWindow : Window
    {
        private string txtChecked;
        private int caretIdx;
        private SpellingError spellingErr;
        private string p;

        public MainWindow(string p)
        {
            this.p = p;
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            txtChecked = p;
            TextBoxSpellCheck.Text = txtChecked;
            caretIdx = TextBoxSpellCheck.CaretIndex;
            spellingErr = TextBoxSpellCheck.GetSpellingError(caretIdx);

            if (spellingErr == null)
            {
                MessageBox.Show("No spelling error was found. Click OK to continue.", "Congratulations!");
                txtReturned = p;
                Application.Current.Shutdown();
            }
        }

        private void ButtonAccept_Click(object sender, RoutedEventArg e)
        {
            txtReturned = TextBoxSpellCheck.Text;
            Application.Current.Shutdown();
        }

        private void ButtonCancel_Click(object sender, RoutedEventArg e)
        {
            txtReturned = p;
            Application.Current.Shutdown();
        }

        public static string txtReturned
    }
}

PowerBuilder event clicked for type cb_1 from commandbutton within main

mle_1.Text = "Teh quik brownn fox junps ober teh lazy dgo!" // Too lazy to copy and paste this into the app's textbox, so here it is...
txtChecked = mle_1.Text
myOLEObject = CREATE OLEObject
result = myOLEObject.ConnectToNewObject("InteropServices.SpellChecker")

IF result < 0 THEN
    DESTROY myOLEObject
    MessageBox("Connecting to COM Object Failed", "Error: " + String(result))
    RETURN
ELSE
    txtCorrected = myOLEObject.CheckText(txtChecked) // This is the line that causes an error.
    mle_1.Text = txtCorrected
END IF

myOLEObject.Dispose() // This function was my futile attempt to resolve this issue, but it seemingly has no effect whatsoever.
myOLEObject.DisconnectObject()
DESTROY myOLEObject

PowerBuilder Instance Variables for main

String txtChecked
String txtCorrected
Int result
OLEObject myOLEObject

The solution that I came up with so far has been acting very weird. Clicking the button from the PowerBuilder application works only once after launching PowerBuilder or the deployed executable, and it would give the following message on further attempts at clicking the button again:

PowerBuilder application execution error (R0035)

Application terminated.

Error: Error calling external object function checktext at line 11 in clicked event of object cb_1 of main.

What should I change from these codes to make it work every time?

The diagnostic you get from PowerBuilder is entirely too inadequate to ever have a shot at debugging the problem. Best thing to do here is to use the Visual Studio debugger:

  • Project + Properties, Debug tab. Select the "Start external program" radio button and select your PowerBuilder test program.
  • Debug + Exceptions, tick the Thrown checkbox for CLR Exceptions. This makes the debugger stop when your program throws an exception.
  • Set breakpoints at the start of CheckText() and on the statement after the Run() call. One in the Loaded event handler ought to be useful.
  • Press F5 to start debugging.

When you click the PowerBuilder button, your first breakpoint should hit. Check if you like the inputMsgBase value, press F5 to continue. Press the button again, good odds that the debugger now stops and tells you what is wrong. I have some guesses, nothing I'd risk right now without knowing what you see.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM