-->

NullPointerException thrown when instantiating and

2019-09-09 13:42发布

问题:

I am getting a NullPointerException when trying to open excel from SWT. Sadly I can't post the full stacktrace and some of the code because it has references to the company that I work for. Hopefully someone has run into this issue before and might recognize it.

Here is the part of the stacktrace that I can post

java.lang.NullPointerException
at org.eclipse.swt.ole.win32.OleControlSite.disconnectEventSinks(OleControlSite.java:468)
at org.eclipse.swt.ole.win32.OleControlSite.releaseObjectInterfaces(OleControlSite.java:774)
at org.eclipse.swt.ole.win32.OleClientSite.onDispose(OleClientSite.java:909)
at org.eclipse.swt.ole.win32.OleClientSite.access$1(OleClientSite.java:895)
at org.eclipse.swt.ole.win32.OleClientSite$1.handleEvent(OleClientSite.java:129)

Here is the code. It's on the last line that the exception gets thrown, when a new OleControlSite is instantiated.

OleFrame frame1 = new OleFrame(shell, SWT.NONE);
if (clientSite != null && !clientSite.isDisposed()){
    clientSite.dispose();
    clientSite = null;
}
OleAutomation doc;
try{
    clientSite = new OleControlSite(frame1, SWT.NONE, file);    

This code works in windows XP, but not in windows 7 it throws the NullPointerException.

As per cubic suggestion I created a self contained example here is the code.

package com.test;

import java.io.File;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.ole.win32.OLE;
import org.eclipse.swt.ole.win32.OleAutomation;
import org.eclipse.swt.ole.win32.OleControlSite;
import org.eclipse.swt.ole.win32.OleEvent;
import org.eclipse.swt.ole.win32.OleFrame;
import org.eclipse.swt.ole.win32.OleListener;
import org.eclipse.swt.ole.win32.Variant;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Button;

public class OpenExcelExampleWindow extends Shell {
    public final static int WorkbookBeforeClose    = 0x00000622;

    /**
     * Launch the application.
     * @param args
     */
    public static void main(String args[]) {
        try {
            Display display = Display.getDefault();
            OpenExcelExampleWindow shell = new OpenExcelExampleWindow(display);
            shell.open();
            shell.layout();
            while (!shell.isDisposed()) {
                if (!display.readAndDispatch()) {
                    display.sleep();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Create the shell.
     * @param display
     */
    public OpenExcelExampleWindow(Display display) {
        super(display, SWT.SHELL_TRIM);

        Button btnOpenExcel = new Button(this, SWT.NONE);
        btnOpenExcel.setBounds(10, 10, 68, 23);
        btnOpenExcel.setText("open excel");

        btnOpenExcel.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                onOpenExcelClicked();
            }

        });
        createContents();
    }

    protected void onOpenExcelClicked() {
        openExcel("testfile.xls",this);
    }

    /**
     * Create contents of the shell.
     */
    protected void createContents() {
        setText("SWT Application");
        setSize(450, 300);

    }

    @Override
    protected void checkSubclass() {
        // Disable the check that prevents subclassing of SWT components
    }

    protected void openExcel(String fileName, Shell shell){
        OleControlSite clientSite = null;
        final File file = new File(fileName);
        if (file.exists()) {

            OleFrame frame1 = new OleFrame(shell, SWT.NONE);
            if (clientSite != null && !clientSite.isDisposed()){
                clientSite.dispose();
                clientSite = null;
            }

            clientSite = new OleControlSite(frame1, SWT.NONE, file);                        
            OleAutomation doc = new OleAutomation(clientSite);

            int [] dispInfo = doc.getIDsOfNames(new String[] {"Application"});
            Variant variant = doc.getProperty(dispInfo[0]);
            OleAutomation app = variant.getAutomation();
            variant.dispose();
            doc.dispose();
            doc = null;

            int result = clientSite.doVerb(OLE.OLEIVERB_OPEN);  
            if (result != OLE.S_OK){
                OLE.error(OLE.ERROR_CANNOT_OPEN_FILE, result);
            }

            //When user close workbook, dispose the clientSite.
            clientSite.addEventListener(app, "{00024413-0000-0000-C000-000000000046}",
                    WorkbookBeforeClose,new OleListener() {
                public void handleEvent(OleEvent event) {

                    OleControlSite oldControlSite = (OleControlSite)event.widget;
                    if ( !oldControlSite.isDisposed()){
                        //System.out.println("event in WorkbookBeforeClose");
                        oldControlSite.dispose();
                    }
                }
            });

        }
    }
}

And here is the exception

java.lang.NullPointerException
at org.eclipse.swt.ole.win32.OleControlSite.disconnectEventSinks(OleControlSite.java:468)
at org.eclipse.swt.ole.win32.OleControlSite.releaseObjectInterfaces(OleControlSite.java:774)
at org.eclipse.swt.ole.win32.OleClientSite.onDispose(OleClientSite.java:909)
at org.eclipse.swt.ole.win32.OleClientSite.access$1(OleClientSite.java:895)
at org.eclipse.swt.ole.win32.OleClientSite$1.handleEvent(OleClientSite.java:129)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1053)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1077)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1058)
at org.eclipse.swt.widgets.Widget.release(Widget.java:808)
at org.eclipse.swt.widgets.Widget.dispose(Widget.java:446)
at org.eclipse.swt.ole.win32.OleClientSite.<init>(OleClientSite.java:194)
at org.eclipse.swt.ole.win32.OleControlSite.<init>(OleControlSite.java:96)
at com.test.OpenExcelExampleWindow.openExcel(OpenExcelExampleWindow.java:93)
at com.test.OpenExcelExampleWindow.onOpenExcelClicked(OpenExcelExampleWindow.java:65)
at com.test.OpenExcelExampleWindow$1.widgetSelected(OpenExcelExampleWindow.java:57)
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:248)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1053)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4169)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3758)
at com.test.OpenExcelExampleWindow.main(OpenExcelExampleWindow.java:33)


Update:

I created an entirely new project with maven and just added one dependency for eclipse sdk 4.2 here is my pom file you can see there is only one dependency. The code in the project is the same as the class above.

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.test</groupId>
  <artifactId>excelopen</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>excelopen</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
<build>
<plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.8</version>
            </plugin>
</plugins>
</build>
  <dependencies>
        <dependency>
            <groupId>org.eclipse.swt</groupId>
            <artifactId>swt-win32-x86_64</artifactId>
            <version>4.2_3.100.0.v4233d</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
  </dependencies>
</project>

I noticed that the source code for the OleControlSite class is the same from 3.8 sdk to the 4.2 sdk, which makes it unlikely that it's the SDK version that's causing the issue for me.


Update:

I download the SWT Source code and the exception get's thrown at line 392 of OleClientSite at this line.

if (result != COM.S_OK) OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result)

And the hidden exception is

org.eclipse.swt.SWTException: Failed to create Ole Client. result = -2147221164
at org.eclipse.swt.ole.win32.OLE.error(OLE.java:302)
at org.eclipse.swt.ole.win32.OleClientSite.OleCreate(OleClientSite.java:392)
at org.eclipse.swt.ole.win32.OleClientSite.<init>(OleClientSite.java:192)
at org.eclipse.swt.ole.win32.OleControlSite.<init>(OleControlSite.java:96)
at com.test.OpenExcelExampleWindow.openExcel(OpenExcelExampleWindow.java:93)
at com.test.OpenExcelExampleWindow.onOpenExcelClicked(OpenExcelExampleWindow.java:65)
at com.test.OpenExcelExampleWindow$1.widgetSelected(OpenExcelExampleWindow.java:57)
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:248)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1053)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4169)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3758)
at com.test.OpenExcelExampleWindow.main(OpenExcelExampleWindow.java:33)


Update 09/05 9:35am

Thanks to favonius's information I was able to find out some more information. I found this discussion about 32bit com's on a 64bit windows OS. This is where the 32bit com on 64bit is discussed. http://www.eclipse.org/forums/index.php/mv/msg/264018/763345/ This might be my issue as we are running Office 2007 which only comes in 32bit and it's running on a 64bit OS. I would like to check the registry but I don't know what key's I should look up. Does anyone know what the registry key is?


Update 09/05 9:45am

Here are the results of the TestCode class that favonius provided.

Run 1 with line 57 commented

{00020820-0000-0000-C000-000000000046}
Excel.Sheet.8
org.eclipse.swt.SWTException: Failed to create Ole Client. result = -2147221164
    at org.eclipse.swt.ole.win32.OLE.error(OLE.java:302)
    at com.test.TestCode.check(TestCode.java:62)
    at com.test.TestCode.main(TestCode.java:23)

Run 2 line 57 uncommented

{00020820-0000-0000-C000-000000000046}
Excel.Sheet.8


Update 09/05 1:51pm

Our app outputs an xls file for excel to open. I just tried reading in an xlsx file and it works. This is a reasonable change to fix the problem with this specific app. Is it not supposed to open xls files?

回答1:

Dissecting the Problem

As per your last update, where you mentioned the origin of the exception, the problem lies in the following lines.

int /*long*/[] ppv = new int /*long*/[1];
result = COM.CoCreateInstance(appClsid, 0, COM.CLSCTX_INPROC_HANDLER | COM.CLSCTX_INPROC_SERVER, COM.IIDIUnknown, ppv);
if (result != COM.S_OK) OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result);

Now the second line in the above code is trying create a single uninitialized object of the class associated with a specified CLSID. Now in your case you are directly providing the XLSX file path, and therefore it uses the GetClassFile() which returns the CLSID associated with the specified file name. Now if it would have failed then you should have gotten the exception at line 186 of OleClientSite. Also, you are able to get a proper program id from the file class id otherwise an exception at line 189 would have resulted.

Checking the Error Code

In your stacktrace you got an error code -2147221164. Now to check what it really means. Start up your Visual Studio and select from menu Tools -> Error Lookup. Put the error code there and click lookup. And it gives...

Now the question is if the class is not registered then you should have got this problem while getting the program id from the class id at line 189. And this makes thing weird. Therefore, before proceeding ahead follow the Second Check.

As per MSDN entry of CoCreateInstance, the above error message could also mean thatthe type of server you requested in the CLSCTX enumeration is not registered or the values for the server types in the registry are corrupt.

Second Check

As a second check and to nail the real problem, just try this SWT Snippet and update the result in your question.

Third Check

I have written this simple test class. Try to run it and post the console output. Also, uncomment the line 57 and run again and post its output too.

Note - You have to change the File constructor parameter.

import java.io.File;

import org.eclipse.swt.internal.ole.win32.COM;
import org.eclipse.swt.internal.ole.win32.GUID;
import org.eclipse.swt.internal.win32.OS;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.ole.win32.OLE;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class TestCode 
{
    public static void main(String[] args)
    {
        Display display = new Display();
        Shell shell = new Shell(display);

        shell.setText("Excel Example");
        shell.setLayout(new FillLayout());

        try{
            check(new File("output.xlsx"));
        }catch(Exception e){
            e.printStackTrace();
        }

        shell.open();

        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        display.dispose();
    }

    static void check(File file)
    {
        GUID fileClsid = new GUID();
        char[] fileName = (file.getAbsolutePath()+"\0").toCharArray();
        int result = COM.GetClassFile(fileName, fileClsid);
        if (result != COM.S_OK) 
            OLE.error(OLE.ERROR_INVALID_CLASSID, result);

        System.out.println(fileClsid);

        String progID = getProgID(fileClsid); 
        if (progID == null) 
            OLE.error(OLE.ERROR_INVALID_CLASSID, result);

        System.out.println(progID);


        int [] ppv = new int[1];

        int server_type = COM.CLSCTX_INPROC_HANDLER | COM.CLSCTX_INPROC_SERVER;

        //server_type |= COM.CLSCTX_LOCAL_SERVER; 

        result = COM.CoCreateInstance(fileClsid, 0, server_type, COM.IIDIUnknown, ppv);
        if (result != COM.S_OK) 
            OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result);
    }

    static String getProgID(GUID clsid)
    {
        if (clsid != null)
        {
            int [] lplpszProgID = new int [1];
            if (COM.ProgIDFromCLSID(clsid, lplpszProgID) == COM.S_OK)
            {
                int  hMem = lplpszProgID[0];
                int length = OS.GlobalSize(hMem);
                int  ptr = OS.GlobalLock(hMem);
                char[] buffer = new char[length];
                COM.MoveMemory(buffer, ptr, length);
                OS.GlobalUnlock(hMem);
                OS.GlobalFree(hMem);

                String result = new String(buffer);
                int index = result.indexOf("\0");
                return result.substring(0, index);
            }
        }
        return null;
    }
}