Sign in to follow this  
Followers 0
Rainer2

Problem accessing COM interface via DllCall()

11 posts in this topic

Hello everybody,

I am stuck at my futile attempts to access Microsoft OneNote 2010 with the COM interface. I need the help of you AutoIt COM experts out there. :-)

Here is the situation: I want to retrieve content from OneNote in XML format. However, all of the OneNote methods that provide XML data use out-parameters, which means that I have to use DllCall() instead of ObjCreate().

This is how far I got on my own:

First, I can access OneNote from AutoIt with ObjCreate(), using this (obvious) line of code:

Local $objOneNote = ObjCreate("Onenote.Application")

This works fine; I used this line of code

_Assert(IsObj($objOneNote))

to check that $objOneNote actually points to a COM object. Furthermore, I was able to close an open notebook with this call to CloseNotebook, a method of the OneNote API:

$objOneNote.CloseNotebook("{MY_GUID}")

Basically, it seems that I can access OneNote via COM. However, when I want to retrieve OneNote content as XML data, I need to use a method like GetHierarchy(), which uses an out-parameter to pass the content as an XML string.

The GetHierarchy method has the following signature:

HRESULT GetHierarchy(

[in]BSTR bstrStartNodeID,

[in]HierarchyScope hsScope,

[out]BSTR * pbstrHierarchyXmlOut);

(source: http://msdn.microsoft.com/en-us/library/ms788684(office.12).aspx)

Since out-parameters don't work with ObjCreate, I have to use DllCall. I am trying to do something like this (Warning: The following line of code is wrong):

Local $intRetVal = DllCall("Onenote.Application", "long", "GetHierarchy", "str", "", "int", 4, "str *", $sXml)

$sXml - the out-parameter - comes back empty. I have two questions about this:

First, I am fully aware that "Onenote.Application" has to be replaced with the correct name of the dll. How can I find the name of a dll, when I only know that "Onenote.Application" is the correct string to work with ObjCreate()?

Second, are there any other problems or errors with my attempt to translate the signature of GetHierarchy() into a DllCall()? I apologize if my questions are stupid; I am not really a COM expert. (In other words: Please be gentle, it's my first time with DllCall :-)

Any help will be greatly appreciated.

Share this post


Link to post
Share on other sites



Find UDF called AutoItObject. It comes with nice help file and plenty of examples in and around it. Everything that you ask for in your post is covered there.

That's not in domain of DllCall function.

Good luck and may the COM Go(o)d be on your side.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Hello trancexx,

thank you for the hint. I will check AutoItObject out right now.

Kind regards,

Rainer

P.S.: I must confess that I'm a bit surprised that my problem is not within the reach of "normal" AutoIt; does that mean that everybody who wants to do out-parameters with a COM-dll needs to work with AutoItObject?

Share this post


Link to post
Share on other sites

Hello trancexx,

thank you for the hint. I will check AutoItObject out right now.

Kind regards,

Rainer

P.S.: I must confess that I'm a bit surprised that my problem is not within the reach of "normal" AutoIt; does that mean that everybody who wants to do out-parameters with a COM-dll needs to work with AutoItObject?

Normal AutoIt, as you call it, is usually used by people with Send(), Run() and WinActivate() functions as their upper programming limit. Some devs (I don't dare say all) are satisfied with that fact. Chances for AutoIt3 to be more than just another little game were lost few months ago. Sadly.

Use AutoItObject and you will be just fine.

...But hey, miracles happen. Who knows what tomorrow brings.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Normal AutoIt, as you call it, is usually used by people with Send(), Run() and WinActivate() functions as their upper programming limit. Some devs (I don't dare say all) are satisfied with that fact. Chances for AutoIt3 to be more than just another little game were lost few months ago. Sadly.

Use AutoItObject and you will be just fine.

...But hey, miracles happen. Who knows what tomorrow brings.

I know, since it's tomorrow now: Happy new year! :P

I have taken a first look at AutoItObject. Looks like a great tool to create and access objects with AutoIt. I have been working with object-oriented languages on and off since the late 1980s when Smalltalk was the "hot" OO language and C++ was still a C preprocessor named "C with classes"; I'm more than willing to work with AutoItObject, but unfortunately I can't see how this is going to solve my problem from above, as I am trying to manipulate an existing COM object called "OneNote.Application". I want to get XML content from a call to GetHierarchy(). Could you point me in the right direction? Which AutoItObject function(s) should I give a closer look in order to retrieve the out-parameters from GetHierarchy()? This is my only problem, as AutoIt cannot access out-parameters via COM. (As I said before, I am not very proficient with COM, so forgive me if the question is dumb.)

Thanks in advance for any hints, and "Happy new year" again! :x

Rainer

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

Thanks. Happy New Year to you too!

It doesn't matter if Object is "existing" one. There are more than one way to process objects with AutoItObject. That include both existing object as well as those that needs to be created. Main thing to do is to find vtable definition for the object (I've posted a tool to do that in examples forum, btw). That link you gave conveniently doesn't list methods in vtable appearance order.

I can write something like this:

#AutoIt3Wrapper_UseX64=n

#include <AutoItObject.au3>

;===============================================================================
#interface "OneNoteIApplication"
Global Const $sCLSID_OneNoteApplication = "{0039FFEC-A022-4232-8274-6B34787BFC27}"
Global Const $sIID_IOneNoteApplication = "{2DA16203-3F58-404F-839D-E4CDE7DD0DED}"
; Definition
Global $dtagIOneNoteApplication = $dtagIDispatch & _
        "GetHierarchy hresult(bstr;dword;bstr*);" & _
        "UpdateHierarchy hresult(bstr);" & _
        "OpenHierarchy hresult(bstr;bstr;bstr*;dword);" & _
        "DeleteHierarchy hresult(bstr;double);" & _
        "CreateNewPage hresult(bstr;bstr*;dword);" & _
        "CloseNotebook hresult(bstr);" & _
        "GetHierarchyParent hresult(bstr;bstr*);" & _
        "GetPageContent hresult(bstr;bstr*;dword);" & _
        "UpdatePageContent hresult(bstr;double);" & _
        "GetBinaryPageContent hresult(bstr;bstr;bstr*);" & _
        "DeletePageContent hresult(bstr;bstr;dword);" & _
        "NavigateTo hresult(bstr;bstr;bool);" & _
        "Publish hresult(bstr;bstr;dword;bstr);" & _
        "OpenPackage hresult(bstr;bstr;bstr*);" & _
        "GetHyperlinkToObject hresult(bstr;bstr;bstr*);" & _
        "FindPages hresult(bstr;bstr;bstr*;bool;bool);" & _
        "FindMeta hresult(bstr;bstr;bstr*;bool);" & _
        "GetSpecialLocation hresult(dword;bstr*);"
; List
Global $ltagIOneNoteApplication = $ltagIDispatch & _
        "GetHierarchy;" & _
        "UpdateHierarchy;" & _
        "OpenHierarchy;" & _
        "DeleteHierarchy;" & _
        "CreateNewPage;" & _
        "CloseNotebook;" & _
        "GetHierarchyParent;" & _
        "GetPageContent;" & _
        "UpdatePageContent;" & _
        "GetBinaryPageContent;" & _
        "DeletePageContent;" & _
        "NavigateTo;" & _
        "Publish;" & _
        "OpenPackage;" & _
        "GetHyperlinkToObject;" & _
        "FindPages;" & _
        "FindMeta;" & _
        "GetSpecialLocation;"
;===============================================================================

_AutoItObject_StartUp()

#cs
    ;You can create object like this:
    Global $objOneNote = ObjCreate("Onenote.Application")
    Global $pOneNote = _AutoItObject_IDispatchToPtr($objOneNote)
    _AutoItObject_IUnknownAddRef($objOneNote)
    Global $oOneNote = _AutoItObject_WrapperCreate($pOneNote, $dtagIOneNoteApplication)
#ce

; But it's easier like this:
Global $oOneNote = _AutoItObject_ObjCreate("Onenote.Application", Default, $dtagIOneNoteApplication) ; or $sIID_IOneNoteApplication instead of "Default"

; Check if object is created:
If Not IsObj($oOneNote) Then
    MsgBox(48, "Error", "Object not created. Something is wrong.")
    Exit
EndIf

; Work with object now
;...
$aCall = $oOneNote.GetHierarchy("", 4, "")
$sHierarchy = $aCall[3]
ConsoleWrite("Hierarchy = " & $sHierarchy & @CRLF)
;...

I can't test it because I don't have it installed on my system. It's written upon the link you provided and quick google search.

edit: don't forget to try commented part of the code if _AutoItObject_ObjCreate would fail in creating this object (that function currently uses CLSCTX_INPROC_SERVER as context for CoCreateInstance).

Edited by trancexx

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

[...] Chances for AutoIt3 to be more than just another little game were lost few months ago. Sadly.

To feed my curiosity, may I ask what are you referring to?

Thanks.

Best wishes for the now and then!

hench

Share this post


Link to post
Share on other sites

I can't test it because I don't have it installed on my system. It's written upon the link you provided and quick google search.

edit: don't forget to try commented part of the code if _AutoItObject_ObjCreate would fail in creating this object (that function currently uses CLSCTX_INPROC_SERVER as context for CoCreateInstance).

Thank you for your help, Trancexx. I tried out your code. With the commented part, the object gets created - at least the message box doesn't complain. Unfortunately, the script doesn't write anything on the console. After a while, the console says:

!>12:46:44 AutoIT3.exe ended.rc:-1073741819
>Exit code: -1073741819    Time: 23.192

Also, "CloseNotebook" is not working anymore. I experimented with the code you gave me and changed it like this:

; Work with object now
;...
;Close a notebook (no out-parameters necessary, used to work without using "AutoItObject")
msgbox(0, "CloseNotebook", "before CloseNotebook")
$oOneNote.CloseNotebook("{MyGuid}")
msgbox(0, "CloseNotebook", "after CloseNotebook")

$aCall = $oOneNote.GetHierarchy("", 4, "")
MsgBox(0, "GetHierarchy", "after GetHierarchy")
$sHierarchy = $aCall[3]
ConsoleWrite("Hierarchy = " & $sHierarchy & @CRLF)
;...

This piece of code shows the message boxes before and after the call to "CloseNotebook" - but doesn't actually close the notebook. I also don't get an error message that something went wrong; the messagebox "after CloseNotebook" shows up immediately. The message box "after GetHierarchy" never gets shown. Instead, the aforementioned return code -1073741819 shows up on the console after a few seconds.

Any ideas what I'm doing wrong?

Share this post


Link to post
Share on other sites

I can only speculate on why your app crashes...

Indicative is the fact that _AutoItObject_ObjCreate() can't create the object (did I understood right?). If that's the case than can you check in task manager if new process gets created when you create the object with the code that works for you. That .Application suffix suggest it. CLSCTX_INPROC_SERVER is used as precaution to avoid situations you have with the object. Methods of that interface are fed with BSTRs. That means pointers, and that pointers are valid only in this process' scope. Passing that pointer to another process can lead only to a crash (and apparently that's the case with you).

Try calling some method that take and return some other data type; integer:

$aCall = $oOneNote.GetTypeInfoCount(0)
$iTypeInfoCount = $aCall[1]
ConsoleWrite("$iTypeInfoCount = " & $iTypeInfoCount & @CRLF)

If that works then I'm likely right about the cause of the crashes.

Unfortunately, I don't know how to send BSTR to another process.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

Hello trancexx,

sorry for the delay, but I was really busy with other projects. I just tried out your piece of code:

$aCall = $oOneNote.GetTypeInfoCount(0)
$iTypeInfoCount = $aCall[1]
ConsoleWrite("$iTypeInfoCount = " & $iTypeInfoCount & @CRLF)

If that works then I'm likely right about the cause of the crashes.

Unfortunately, I don't know how to send BSTR to another process.

The console says:

$iTypeInfoCount = 1

Since this worked, you are probably right about the cause of the crashes.

Anyway, I consider the problem solved for now, since I was able to access OneNote with Windows PowerShell. If you have any ideas how to do the same thing with AutoIt, here is my code (Warning: PowerShell syntax, not AutoIt! Uses a .NET assembly and a reference parameter!)

Add-Type -AssemblyName Microsoft.Office.Interop.OneNote
$global:OneNote = New-Object -type "Microsoft.Office.Interop.OneNote.ApplicationClass"
$PowerShellFlashCardPage = "{MyGUID}"
$myXml = ""
$OneNote.GetHierarchy($PowerShellFlashCardPage, [int][Microsoft.Office.Interop.OneNote.HierarchyScope]::hsChildren, [ref] $myXml)

$myXml now contains precisely the Xml data I am after. If you have any idea how to do this with AutoIt, don't hesitate to let me know. (But that's very low priority, since my problem is solved now.)

Thank you for your help with this, I sure learned a lot in the process!

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

Hey guys. I run into the exact same issue , so I update a little this topic in any case situation has changed since this time. I tried with the new ObjCreateInterface without having more success.

_AutoItObject_StartUp()

;~ Global $oOnenote = ObjCreateInterface($sCLSID_OneNoteApplication,$sIID_IOneNoteApplication,$dtagIOneNoteApplication,True)


Global $objOneNote = ObjCreate("Onenote.Application")
Global $pOneNote = _AutoItObject_IDispatchToPtr($objOneNote)
_AutoItObject_IUnknownAddRef($objOneNote)
Global $oOneNote = _AutoItObject_WrapperCreate($pOneNote, $dtagIOneNoteApplication)


; Check if object is created:
If Not IsObj($oOneNote) Then
    MsgBox(48, "Error", "Object not created. Something is wrong.")
    Exit
EndIf

$aCall = $oOneNote.GetTypeInfoCount(0)
$iTypeInfoCount = $aCall[1]
ConsoleWrite("$iTypeInfoCount = " & $iTypeInfoCount & @CRLF)

This works well. ($iTypeInfoCount = 1)

However, Issue is : how to be able to retrieve (and send) BSTR in this situation?

Meaning, how to get this code work without Autoit crashing ?

$aCall = $oOneNote.GetHierarchy("", 3, "")
;$sHierarchy = $aCall[3]
;ConsoleWrite("Hierarchy = " & $sHierarchy & @CRLF)

 

Edited by timmalos

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0