-
May 15th, 2019, 11:21 AM
#1
[RESOLVED] Looking for advice on 64-Bit COM
I have a small application coded in VB6 that sits alongside my main application that acts as a bridge between my app & Microsoft Outlook. I execute the small app with some command-line parameters and extra data in a memory mapped file, and it handles the Outlook automation in the background using Outlook Redemption* then spits back results to my main app when operations are completed. This has all worked nicely for years provided the 32-bit version of Office/Outlook is installed.
It used to be that even Microsoft recommended using the 32-bit version of Office unless you had some specific reason to use the 64-bit version, but it looks like they've changed their tune on that recently and are even defaulting to 64-bit for new installs of Office 365 subscriptions. Needless to say, it's becoming a bit of a pain dealing with increasing numbers of support requests, getting people to uninstall the 64-bit version, and install the 32-bit version, etc... To top it off, more users are now insisting on using the 64-bit version, so I suspect it's only going to get more difficult to convince anyone to go back to 32-bit going forward. C'est la vie!
The good news is that Outlook Redemption has a 64-bit version of its library, so I'm hoping for a simple way to use it. AFAIK this will not be possible with VB6, but I'm happy to be corrected here (in fact I'd be ecstatic to be corrected here!).
Assuming I have to move to something non-VB6, I've been poking around the Internet looking for a solution that ideally:
- Is free (as in beer more than as in speech).
- Is a BASIC dialect for relatively painless translation.
- Compiles to a 64-bit EXE.
- Does not require .Net runtimes (or any runtimes for that matter).
- Supports COM DLLs (ideally regfree).
I've stumbled across FreeBasic which looks promising - it seems to check all the boxes above, however every included COM example fails to compile with warnings about missing declarations (even when I've included what appear to be the appropriate .BI files with the constants/variables declared).
I'm willing to continue fighting with FreeBasic until I get it to work, but before I get too deep I was wondering if any of you have had any experience migrating a VB6 app to another 64-bit BASIC? If so, what did you use? If you used FreeBasic, do you have any advice on how to get things to compile that work against the Windows APIs & a COM DLL?
Thanks in advance for any help.
* Not sure if I'm allowed to post this link since it is a commercial library, but I thought it might be useful to those who are unfamiliar with the library. Happy to remove the link if mods want.
-
May 15th, 2019, 11:24 AM
#2
Hyperactive Member
Re: Looking for advice on 64-Bit COM
why is vb.net\c# a deal breaker?
-
May 15th, 2019, 11:42 AM
#3
Re: Looking for advice on 64-Bit COM
.Net is not a necessarily a deal breaker, just not my ideal choice. My reasons being (and these may be misguided, my .Net experience is minimal):
- The helper app will launched fairly frequently and should initialize, run, and terminate as quickly as possible - I am hoping for something lightweight. It feels like the overhead of a big run-time might impact me negatively here.
- I don't want to distribute anything other than my app, or ask anyone to install anything other than my app. Now AFAIK the .Net runtime is nearly ubiquitous these days so maybe this is a non-issue, but it's just another potential point of failure/complication that I would like to try to avoid if possible.
If it turns out .Net is the best choice, so be it.
-
May 15th, 2019, 11:43 AM
#4
Re: Looking for advice on 64-Bit COM
One other thing that just came to mind about .Net/VisualStudio - can you use the "community/express/whatever they are calling it these days" edition for commercial projects, or do you have to purchase a license?
-
May 15th, 2019, 12:12 PM
#5
Re: Looking for advice on 64-Bit COM
I believe it can be used for commercial products, as long as it is a single programmer that is producing the product, but I could be misinterpreting the license agreement.
I agree that for a program that starts up, does something quick, and then exits, will be slower with .Net. The executable contains CLI code that will be converted into native code each time it is run, so you have that overhead each time you run, regardless of whether the RunTime libraries are already loaded in memory from earlier runs, or because of other applications using them.
How much impact that is from the users standpoint, ...?
Last edited by passel; May 15th, 2019 at 12:17 PM.
-
May 15th, 2019, 12:50 PM
#6
Fanatic Member
Re: Looking for advice on 64-Bit COM
Edit: Please see post #24 below for an updated code.
I have done something similar. I had a 64 Bit COM DLL that I wanted to talk to. The way it's done is by calling the API version of CreateObject(), and asks it to use the 64-Bit Dllhost.exe to host the 64-Bit COM DLL, then talk to it. This requires adding a registry entry at install time so Dllhost.exe is used. The value is of type String, and it should be left empty. Here is the registry entry that you need to create:
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\APPID\{AppID_value}
DllSurrogate=""
To use CreateObject64() below, you call it like this:
Dim o64 As Object
Set o64 = CreateObject64("Project1.Class1")
There is no error handling, but you could add Err.Raise, or add an extra ByRef parameter to return error information.
VB Code:
Public Type TGUID Data1 As Long Data2 As Integer Data3 As Integer Data4(7) As Byte End Type Public Const CLSCTX_INPROC_SERVER As Long = 1 Public Const CLSCTX_INPROC_HANDLER As Long = 2 Public Const CLSCTX_LOCAL_SERVER As Long = 4 Public Const CLSCTX_ENABLE_AAA As Long = &H10000 Public Const CLSCTX_REMOTE_SERVER As Long = 16 Public Const CLSCTX_SERVER As Long = (CLSCTX_INPROC_SERVER Or CLSCTX_LOCAL_SERVER) Public Const CLSCTX_ALL As Long = (CLSCTX_INPROC_SERVER Or CLSCTX_INPROC_HANDLER Or CLSCTX_LOCAL_SERVER) Public Const IID_IUnknown As String = "{00000000-0000-0000-C000-000000000046}" Public Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}" Public Declare Function CoCreateInstance Lib "OLE32.DLL" (ByVal rclsid As Long, ByVal punkOuter As Long, ByVal dwClsContext As Long, ByVal riid As Long, ByRef ppv As Any) As Long Public Declare Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As Long, ByRef pCLSID As TGUID) As Long Public Declare Function CLSIDFromProgID Lib "OLE32.DLL" (ByRef TSzProgID As String, ByRef pCLSID As TGUID) As Long Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long) Public Function CreateObject64(ByRef sObjectName As String) As Object Dim ret As Long Dim pCLSID As TGUID Dim IIDispatch As TGUID Dim ppv As Long ret = CLSIDFromString(StrPtr(IID_IDispatch), IIDispatch) ' Get object by GUID 'ret = CLSIDFromString(StrPtr("{11111111-2222-3333-4444-555555555555}"), pCLSID) ' Get object by ProgID ret = CLSIDFromProgID(StrPtr(sObjectName), pCLSID) If ret = 0 Then ' Success ' Create Object ret = CoCreateInstance(VarPtr(pCLSID), 0, CLSCTX_LOCAL_SERVER, VarPtr(IIDispatch), ppv) 'Debug.Print "CoCreateInstance returned = " & Hex(ret) & ", ppv = " & Hex(ppv) & ", LastDllError = " & Err.LastDllError If ppv <> 0 Then Set CreateObject64 = ObjFromPtr(ppv) End If Else Debug.Print "CLSIDFromString failed, ret = " & Hex(ret) & ", LastDllError = " & Err.LastDllError End If End Function ' Returns an object given its pointer ' This function reverses the effect of the ObjPtr function Public Function ObjFromPtr(ByVal pObj As Long) As Object Dim obj As Object ' force the value of the pointer into the temporary object variable CopyMemory obj, pObj, 4 ' assign to the result (this increments the ref counter) Set ObjFromPtr = obj ' manually destroy the temporary object variable ' (if you omit this step you'll get a GPF!) CopyMemory obj, 0&, 4 End Function
Last edited by qvb6; May 16th, 2019 at 11:17 AM.
-
May 15th, 2019, 01:08 PM
#7
Re: Looking for advice on 64-Bit COM
Hi qvb6, thanks a lot, this looks very promising!
One question I have about the App.Id - at install time do I create an registry entry for my Executable file name, e.g.
Code:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\MyAppFileName.exe
Then create a subkey of that called "AppID" with a GUID that I generate myself?
Next I would add another registry key:
Code:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{GuidIGeneratedAndStoredInMyAppFilename/AppID}
And then create a subkey of that call DllSurrograte with an empty string value. Is that correct?
Is there anything I need to do to put the AppId in my EXE, or does Windows get the AppId from the registry automatically based solely on the filename?
-
May 15th, 2019, 01:13 PM
#8
Fanatic Member
Re: Looking for advice on 64-Bit COM
No, you need to search the registry for Outlook Redemption after installing the 64-Bit version of it, and add DllSurrogate to it, not your app.
-
May 15th, 2019, 01:15 PM
#9
Re: Looking for advice on 64-Bit COM
Ahh I see, thanks for the clarification! I'll give it a shot, appreciate the help
-
May 15th, 2019, 01:42 PM
#10
Hyperactive Member
Re: Looking for advice on 64-Bit COM
Originally Posted by passel
I believe it can be used for commercial products, as long as it is a single programmer that is producing the product, but I could be misinterpreting the license agreement.
number or programmers doesn't matter. You need the pro license if you are over 1 million a year in revenue or have more than 250 computers at your business
-
May 15th, 2019, 01:45 PM
#11
Fanatic Member
Re: Looking for advice on 64-Bit COM
I should have renamed CreateObject64() to CreateObjectOutOfProcess(), because it has nothing really to do with 64-Bit. It can be used to start a 32-Bit COM DLL/OCX out of process, for example, if the DLL/OCX is buggy and crashes, it doesn't impact your EXE other than getting a runtime error. This technique is used by web servers so a bad DLL doesn't crash the server.
Also, I tweaked the function a little before posting it as I was using GUID rather than a name to instantiate the object. The commented out CLSIDFromString() call is the one I was using and testing with. I hope that my tweaks didn't break things, I only tested it to make sure it compiles.
-
May 15th, 2019, 01:54 PM
#12
Fanatic Member
Re: Looking for advice on 64-Bit COM
Originally Posted by DllHell
number or programmers doesn't matter. You need the pro license if you are over 1 million a year in revenue or have more than 250 computers at your business
This is mentioned here(Under "Supported Usage Scenarios"):
https://visualstudio.microsoft.com/vs/compare/
-
May 15th, 2019, 02:04 PM
#13
Re: Looking for advice on 64-Bit COM
Thanks for the VisualStudio licensing clarifications everyone!
Looks like Redemption64.dll doesn't have an AppId when you register it, but as per this StackOverflow thread it looks like you can just use the CLSID as the AppID. I've tried it out quickly and it looks like basic calls are working, but I have to do some more in-depth testing to ensure I'm actually using the 64-bit DLL (I have both on this system, so I might be hitting the 32-bit accidentally - must confirm so I won't mark this resolved yet).
That said, this looks great so far. Thank you very much qvb6!
-
May 15th, 2019, 03:05 PM
#14
Re: Looking for advice on 64-Bit COM
Another possibility would be to use VBScript as the "Execution-Host" for the 64Bit-Redemption-Dll...
(*.vbs-Files will be started in a 64Bit-Host-Process by default on 64Bit-Windows).
To make a given Dll regfree usable via VBScript (or also *.asp):
- one needs to place a MyDllName.manifest file besides the Dll
- and then use the following VBScript-Block, to instantiate an SxS-Helper:
Code:
Dim SxS
Set SxS = CreateObject("Microsoft.Windows.ActCtx") 'create the SxS-Helper-Obj
SxS.Manifest = "Redemption.manifest" 'specify the path to the manifest-file of the Dll
Dim Itm 'now create Objects regfree by using the CreateObject-Method of the SxS-Helper
Set Itm = SxS.CreateObject("Redemption.SafeMailItem")
The xml-content for the SxS-manifest-file of the Redemption-Dll would be the following:
Code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<file name="Redemption.dll">
<comClass clsid="{4fd5c4d3-6c15-4ea0-9eb9-eee8fc74a91b}" threadingModel="Apartment" progid="Redemption.SafeContactItem" />
<comClass clsid="{620d55b0-f2fb-464e-a278-b4308db1db2b}" threadingModel="Apartment" progid="Redemption.SafeAppointmentItem" />
<comClass clsid="{741beefd-aec0-4aff-84af-4f61d15f5526}" threadingModel="Apartment" progid="Redemption.SafeMailItem" />
<comClass clsid="{7a41359e-0407-470f-b3f7-7c6a0f7c449a}" threadingModel="Apartment" progid="Redemption.SafeTaskItem" />
<comClass clsid="{c5aa36a1-8bd1-47e0-90f8-47e7239c6ea1}" threadingModel="Apartment" progid="Redemption.SafeJournalItem" />
<comClass clsid="{fa2cbafb-f7b1-4f41-9b7a-73329a6c1cb7}" threadingModel="Apartment" progid="Redemption.SafeMeetingItem" />
<comClass clsid="{11e2bc0c-5d4f-4e0c-b438-501ffe05a382}" threadingModel="Apartment" progid="Redemption.SafePostItem" />
<comClass clsid="{4a5e947e-c407-4dcc-a0b5-5658e457153b}" threadingModel="Apartment" progid="Redemption.MAPIUtils" />
<comClass clsid="{03c4c5f4-1893-444c-b8d8-002f0034da92}" threadingModel="Apartment" progid="Redemption.MAPIFolder" />
<comClass clsid="{7ed1e9b1-cb57-4fa0-84e8-fae653fe8e6b}" threadingModel="Apartment" progid="Redemption.SafeCurrentUser" />
<comClass clsid="{7c4a630a-de98-4e3e-8093-e8f5e159bb72}" threadingModel="Apartment" progid="Redemption.SafeDistList" />
<comClass clsid="{37587889-fc28-4507-b6d3-8557305f7511}" threadingModel="Apartment" progid="Redemption.AddressLists" />
<comClass clsid="{a6931b16-90fa-4d69-a49f-3abfa2c04060}" threadingModel="Apartment" progid="Redemption.MAPITable" />
<comClass clsid="{d46ba7b2-899f-4f60-85c7-4df5713f6f18}" threadingModel="Apartment" progid="Redemption.SafeReportItem" />
<comClass clsid="{ed323630-b4fd-4628-bc6a-d4cc44ae3f00}" threadingModel="Apartment" progid="Redemption.SafeInspector" />
<comClass clsid="{29ab7a12-b531-450e-8f7a-ea94c2f3c05f}" threadingModel="Apartment" progid="Redemption.RDOSession" />
</file>
</assembly>
Not sure whether that can make your "shelled Mini-Executable" obsolete - but *.vbs Files should be "shellable" nicely...
(and - FWIW - could be generated on the fly - before shelling them).
HTH
Olaf
-
May 15th, 2019, 03:26 PM
#15
Re: Looking for advice on 64-Bit COM
Hi Olaf, that looks like a great approach too. I especially like the fact that it can run regfree (which was one of points on my "ideal" list). I suppose a potential drawback is that some sites may be blocking execution of VBS files by disabling Windows Script Host, but I'll have to check with the customers to see if that's the case. Thanks a lot for that example!
-
May 15th, 2019, 11:57 PM
#16
Re: Looking for advice on 64-Bit COM
Maybe we need to face more and more 64-Bit COMs in the future.
-
May 16th, 2019, 12:46 AM
#17
Re: Looking for advice on 64-Bit COM
Good post, as like dreammanor is saying we will face more & more this in the future and finding a generic solution will be good.
I also begin to face the same problem with Outlook or Excel
the vbs idea should be promising.
The problem is that we need a development computer with 64 bits installed
I'll start to investigate a bit too
-
May 16th, 2019, 01:18 AM
#18
Re: Looking for advice on 64-Bit COM
I was able to use Word automation from VB6 with Office Word 64 bits with normal code also used to handle 32 bits versions...
-
May 16th, 2019, 06:36 AM
#19
Fanatic Member
Re: Looking for advice on 64-Bit COM
It looks like the Redemption DLL developers were lazy and didn't add an AppID key(It's optional, but needed for out-of-process activation as listed in the first item here), which means that someone has to add it, then tell each of the 16 Objects it contains the AppID it belongs to by adding "AppID" value underneath the CLSID of each object. I try to do that if I have the time and post back.
Last edited by qvb6; May 16th, 2019 at 11:18 AM.
-
May 16th, 2019, 09:50 AM
#20
Re: Looking for advice on 64-Bit COM
Just a quick update.
I've been trying qvb6's approach re: AppId on a fresh system with Outlook 64-bit installed, and so far I haven't been able to get it to work (whereas it seemed to work on my main dev system with Outlook 32-bit installed). I still have some more experimenting to do here, it's possible I got something wrong with my registry. Long and short of this approach though for those stopping by from the future - if you getting working the plus side is that you won't have to change much of your existing VB6 code. The downside is that you will have to use the registry, so reg-free is a no-go.
I've also just got Olaf's VBS approach working. One plus sides are that I can dynamically generate a script with all the appropriate email addresses, attachments, etc... so I don't need to create a mapped file for shared storage/passing data to a separate process. Another plus is that it can work reg-free. The only downside I can think of is IF VBS scripts are blocked on an end user's machine. I don't know how common this is, though I have heard people complain about it, so it is definitely a potential concern. Another downside is that I had to rewrite some swaths of code, but the transition to VBScript is relatively painless and my helper app was quite small, so not a big deal overall.
Last thing about Olaf's VBS approach I have to figure out is if I can easily pass data back to my VB6 app from a VBS...I suppose one easy (if ugly) way would be to save the data to a file in the TMP folder and then just watch for it on the VB6 side and slurp it in. Anyway, this part is a bit off-topic so I will do some research and try to find a good solution.
Thanks again to everyone for your help and advice. This exercise turned out to be quite a bit easier than I thought it would thanks to you!
-
May 16th, 2019, 09:52 AM
#21
Re: Looking for advice on 64-Bit COM
@jpbro @qvb6
The Outlook Redemption developers are usually helpful. It may be worth contacting them http://www.dimastr.com/redemption/home.htm
-
May 16th, 2019, 10:17 AM
#22
Re: Looking for advice on 64-Bit COM
Originally Posted by dreammanor
Maybe we need to face more and more 64-Bit COMs in the future.
Originally Posted by Thierry69
Good post, as like dreammanor is saying we will face more & more this in the future and finding a generic solution will be good.I also begin to face the same problem with Outlook or Excel
Yes I think we will be seeing more of 64-Bit Office unfortunately - especially now that Microsoft appears to be endorsing it over 32-bit, which is a change from their previous stance. It used to be we could point IT to Microsoft's page that recommended 32-bit over 64-bit unless you had special needs that only the 64-bit version would support, but that's ammunition we no longer have.
-
May 16th, 2019, 10:19 AM
#23
Re: Looking for advice on 64-Bit COM
Originally Posted by VB6 Programming
Thanks VB6Programming - I've been considering contacting Dmitry (Redemption Dev) as I have a support license, but I wanted to collect some more information/run some more tests before doing so. But I agree, the support for Redemption is excellent.
-
May 16th, 2019, 11:16 AM
#24
Fanatic Member
Re: Looking for advice on 64-Bit COM
I made some progress, but I don't have Outlook to test. I am getting error 0x8004010F from CoCreateInstance(), which is an Outlook related error according to this page. I also found about a new flag called CLSCTX_ACTIVATE_64_BIT_SERVER.
To try this code, you need to add the registry entries in the attached RegAppIDRedemption.reg first(I included another file to remove these entries). Compile, and run the program from the EXE, not the IDE; because the IDE is running as Admin. I see an extra dllhost.exe process, and it's the 64-Bit version, I also see Redemption license agreement. In the registry file, I made a new GUID as the AppID, so you won't find it anywhere on the web. I used the list provided by Olaf and a spreedsheet to quickly create the registry entries. I couldn't make it work with ProgID("Redemption.RDOSession"), so I had to use GUID's, but I made constants to use instead of using GUID that you will find at the top of Module1. There are probably more objects needed, but I am not sure. All the code is in Module1.
OutlookRedemptionTest1.zip
-
May 16th, 2019, 12:30 PM
#25
-
May 17th, 2019, 08:57 AM
#26
Re: [RESOLVED] Looking for advice on 64-Bit COM
Looks like there is one entry missing from Olaf's manifest and qvb6's registry file - probably something introduced in a more recent version than Olaf had available (Redemption.SafeExplorer). Here it is in case anyone needs it:
Manifest:
Code:
<comClass clsid="{c3b05695-ae2c-4fd5-a191-2e4c782c03e0}" threadingModel="Apartment" progid="Redemption.SafeExplorer" />
Reg:
Code:
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{c3b05695-ae2c-4fd5-a191-2e4c782c03e0}]
"AppID"="{9F2804F5-ECAB-4fd7-95B2-2914B8F5A5D8}"
-
Jul 12th, 2019, 09:11 PM
#27
Re: [RESOLVED] Looking for advice on 64-Bit COM
Just following up on this thread as I recently encountered some notable events.
First, I started with Olaf's VBScript approach. I dynamically generate a .VBS file that looks an awful lot like my original VB6 code. I tried running the generated script from Windows Explorer to prove that it could work and everything went great.
From there, I tried using an old stock function of mine to execute a file using the CreateProcess API (in this case against cscript.exe) and it failed.
So I tried the same function against wscript.exe and it failed too. Made sure I had the full (and correct) 64-bit system32 path in front of my cscript.exe/wscript.exe calls and it still failed.
So a mystery! Well, it worked when I double-clicked the script in Explorer, so maybe I should try ShellExecute/ShellExecuteEx. Both failed. ***?
Turns out that when you try to use the "System32" folder from a 32-bit process on 64-bit Windows, it will redirect you to SysWow64 (and yes, before Dilettante show's up and says "what did you expect you numbskull, we knew this 15 years ago" - I was aware of redirects in the write direction against protected folders, this is the first time I experienced a problem in the "read/execute" direction...I haven't mucked about with protected folders in ages, so this one took me longer to figure out that I would have expected).
Anyway, after a long story here's the solution to getting to the 64-bit version of cscript/wscript (or any other file in the 64-bit %winsys% folder):
You can either try using the Wow64DisableWow64FsRedirection API function, or make sure you use the %windir%\SysNative folder instead of anything that resolves to what you think would be the 64-bit system folder. Hope that saves someone a bit of time!
-
Mar 3rd, 2021, 03:08 AM
#28
New Member
Re: [RESOLVED] Looking for advice on 64-Bit COM
Hi jpbro
I'm a novice programmer and I'm trying to call the 64b redemption.dll via COM from a 32b app.
I was looking at post #24 from qvb6 and in his vb file, he has this line:
Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
Could you please explain what is this for and where did it come from?
Thanks very much
-
Mar 3rd, 2021, 07:28 AM
#29
Re: [RESOLVED] Looking for advice on 64-Bit COM
http://www.yfvb.com/thread-6847.htm
visual freebasic(vfb,vb7)
The Chinese developed a programming IDE like VB6 by vfb
you can download a APP (QQ),like twitter in china,have english version.
search and join QQ talking group,id(78458582)
-
Mar 3rd, 2021, 07:49 AM
#30
Addicted Member
Re: [RESOLVED] Looking for advice on 64-Bit COM
The Microsoft OLE/COM Object Viewer provides a GUI alternative to allow a DLLSurrogate for a 32-Bit In-Process COM Server DLL to be created so as to be used from a 64-bit application.
Details on how I do this are available from Using a 32-bit In-Process COM Server DLL from 64-bit TCC
Joe
-
Mar 3rd, 2021, 09:45 AM
#31
Re: [RESOLVED] Looking for advice on 64-Bit COM
Originally Posted by s29ers
Hi jpbro
I'm a novice programmer and I'm trying to call the 64b redemption.dll via COM from a 32b app.
I was looking at post #24 from qvb6 and in his vb file, he has this line:
Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
Could you please explain what is this for and where did it come from?
Thanks very much
IDispatch is a standard COM interface that objects can implement. The string value is a GUID/unique code that identifies the IDispatch Interface. It is a published/well-known number from Microsoft. You can learn a bit more about IDispatch at the links below:
https://en.wikipedia.org/wiki/IDispatch
https://docs.microsoft.com/en-us/win...aidl-idispatch
IDispatch allows a client application to find out what properties and methods & parameters an object supports without knowing anything about the object at compile time. You can then invoke the methods/properties using the IDispatch Invoke method.
-
Mar 4th, 2021, 03:03 AM
#32
New Member
Re: [RESOLVED] Looking for advice on 64-Bit COM
@jpbro and @Joe Caverly, Many thanks for the quick replies which together with the code from @qvb6 in msg #24 has worked! Thanks again to all.
-
Mar 7th, 2021, 07:09 PM
#33
New Member
Re: [RESOLVED] Looking for advice on 64-Bit COM
Well after thinking I had got @qvb6's code in msg #24 working, it turned out not to be quite so simple.
I ran into some problems and was wondering if anyone can shed some light on what to do as I've been knocking my head against a wall all day and can't seem to find any documentation/articles/etc that can help.
Sorry for the long post, but without the context it's impossible to explain.
Firstly, after I had got it working, I later found there was one Redemption function that was not working very stably and sometimes would work and other times not. I've not been able to figure out what the issue is and in trying to isolate that issue I ran into another problem.
Now, I'm using two different development environments and test beds.
I got @qvb6's code "working" (as above) in an interpreted 32 bit development environments which uses a VB (almost compatible language, running in an Oracle Virtual Box with Redemption64 and Outlook64 installed.
There are a few key language differences between VB. In particular VarPtr and StrPtr are not supported, so passing the strings and TGUID structure to CLSIDFromString and CoCreateInstance needed to be done differently. Similarly to ensure that the strings were passed as UNICODE. But I got it to work with the following customizations to the corresponding line in original @qvb6's code, which I provide for reference:
API DECLARATIONS:
---------------------
Code:
Declare Function CLSIDFromString Lib "OLE32.DLL" (Byval lpsz As Unicode String, pCLSID As TGUID) As Long
Declare Function CoCreateInstance Lib "OLE32.DLL" (rclsid As TGUID, Byval punkOuter As Long, Byval dwClsContext As Long, riid As TGUID, ppv As Long) As Long
USING CLSIDFromString
---------------------
Code:
Const IID_IDispatchC = "{00020400-0000-0000-C000-000000000046}"
Const Redemption_RDOSession = "{29ab7a12-b531-450e-8f7a-ea94c2f3c05f}"
Dim ret As Long
Dim IID_IDispatch As String
Dim IIDispatch As TGUID
Dim pCLSID As TGUID
IID_IDispatch = IID_IDispatchC
ret = CLSIDFromString(IID_IDispatch, IIDispatch)
Temp = Redemption_RDOSession
ret = CLSIDFromString(Temp, pCLSID)
USING CoCreateInstance
----------------------
Code:
ret = CoCreateInstance (pCLSID, 0, CLSCTX_LOCAL_SERVER Or CLSCTX_ACTIVATE_64_BIT_SERVER, IIDispatch, ppv)
I then tried exactly the same code on exactly the same interpreted 32 bit development environment running on my main system with Redemption64 and Outlook64 installed, but it failed at CoCreateInstance with the error "DLL not registered" even though checking the registry everything seemed fine and matched the Oracle Virtual Box.
To try and isolate what might be causing this problem I decided to recreate got @qvb6's code in pure VB in a new install of Visual Studio 2019.
The first problem is that
Code:
Private Type TGUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
is not valid syntax anymore (Type is no longer supported, Structure must be used instead, but then Data4 can't be declared as a fixed array inside a Structure). So I end up with this:
Code:
Private Structure TGUID
Dim Data1 As Long
Dim Data2 As Integer
Dim Data3 As Integer
Dim Data4() As Byte
End Structure
To get Data4 back into an array of 8 elements (0-7) before calling CLSIDFromString I ReDim the arrays as follows:
Code:
ReDim pCLSID.Data4(7)
ReDim IIDispatch.Data4(7)
In the debugger the TGUID structures look ok after the ReDim.
The second problem I ran into is that the VarPtr and StrPtr functions are no longer available so I had to change the API declarations as follows (which is very similar to the declrations above in the 32 bit interpreted development environment):
Code:
Private Declare Function CoCreateInstance Lib "OLE32.DLL" (ByRef rclsid As TGUID, ByVal punkOuter As Long, ByVal dwClsContext As Long, ByRef riid As TGUID, ByRef ppv As Long) As Long
Private Declare Unicode Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As String, ByRef pCLSID As TGUID) As Long
For reference, @qvb6's original code was (to be used with VarPtr and StrPtr) was:
Code:
Private Declare Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As Long, ByRef pCLSID As TGUID) As Long
Private Declare Function CoCreateInstance Lib "OLE32.DLL" (ByVal rclsid As Long, ByVal punkOuter As Long, ByVal dwClsContext As Long, ByVal riid As Long, ByRef ppv As Any) As Long
Here's my test Visual Studio 2019 VB code (I'm still just trying to get CLSIDFromString to work, so have not even been able to see if CoCreateInstance is working, hence all the calls to CLSIDFromString in the code below):
Code:
Dim ret As Long
Dim pCLSID As TGUID
Dim IIDispatch As TGUID
Dim IID_IDispatchS As String
Dim sObjectS As String
' Can't declare a fixed array inside a structure so need to ReDim it before we use it so we have enough space
ReDim pCLSID.Data4(7)
ReDim IIDispatch.Data4(7)
ret = CLSIDFromString("{00020400-0000-0000-C000-000000000XXX}", IIDispatch)
Console.WriteLine("CLSIDFromString result for invalid {00020400-0000-0000-C000-000000000XXX}, ret = 0x" & Hex(ret) & ", LastDllError = " & Err.LastDllError)
ret = CLSIDFromString("{00020400-0000-0000-C000-000000000046}", IIDispatch)
Console.WriteLine("CLSIDFromString result for {00020400-0000-0000-C000-000000000XXX}, ret = 0x" & Hex(ret) & ", LastDllError = " & Err.LastDllError)
ret = CLSIDFromString(IID_IDispatch, IIDispatch)
Console.WriteLine("CLSIDFromString result for IID_IDispatch, ret = 0x" & Hex(ret) & ", LastDllError = " & Err.LastDllError)
IID_IDispatchS = IID_IDispatch
ret = CLSIDFromString(IID_IDispatchS, IIDispatch)
Console.WriteLine("CLSIDFromString result for IID_IDispatchS, ret = 0x" & Hex(ret) & ", LastDllError = " & Err.LastDllError)
ret = CLSIDFromString(Redemption_RDOSession, pCLSID)
Console.WriteLine("CLSIDFromString result for Redemption_RDOSession, ret = 0x" & Hex(ret) & ", LastDllError = " & Err.LastDllError)
sObjectS = "Redemption.RDOSession"
ret = CLSIDFromProgID(sObjectS, pCLSID)
Console.WriteLine("CLSIDFromProgID result for Redemption.RDOSession, ret = 0x" & Hex(ret) & ", LastDllError = " & Err.LastDllError)
The results from the above declrations and tests are as follows:
Code:
CLSIDFromString result for invalid {00020400-0000-0000-C000-000000000XXX}, ret = 0x1800401F3, LastDllError = 0
CLSIDFromString result for {00020400-0000-0000-C000-000000000XXX}, ret = 0x200000000, LastDllError = 0
CLSIDFromString result for IID_IDispatch, ret = 0x200000000, LastDllError = 0
CLSIDFromString result for IID_IDispatchS, ret = 0x200000000, LastDllError = 0
CLSIDFromString result for Redemption_RDOSession, ret = 0x200000000, LastDllError = 0
CLSIDFromProgID result for Redemption.RDOSession, ret = 0x2D50000800401F3, LastDllError = 0
Notice however the first error code is 0x1800401F3. There is no such 9 digit error code, because it should be the 8 digit code 0x800401F3 which is the error code for an invalid CLSID.
Similarly notice the other error codes are 0x200000000. Again there is no such 9 digit return code, and if the extra digit is removed, we have a return code of 0 (success), suggesting the CLSID was found. And as exepected (with a success return code) the TGUID structures were filled, however they were only partially and incorrectly filled (as compared to the system where the code is working)
Notice for the last call to CLSIDFromProgID (which should not have returned an error, but did, as @qvb6 also found that CLSIDFromProgID doesn't seem to work with Redemption). However, notice the return code is the 15 digit 0x2D50000800401F3 return code, but if only the lower 8 digits are used we again have the valid 8 digit 0x800401F3 which is the error code for an invalid CLSID.
THE PROBLEM
---------
So there are two issues:
a) Why are the return codes larger have extra digits?
b) Why are the TGUID structures not filled correctly when the return code seems to be 0 (when extra digits removed)?
ADJUSTMENT A
---------
In the declration if I change pCLSID from ByRef:
Code:
Private Declare Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As Long, ByRef pCLSID As TGUID) As Long
to ByVal:
Code:
Private Declare Unicode Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As String, ByVal pCLSID As TGUID) As Long
then every call to CLSIDFromString returns an 8 digit code 0x80070057 (or 0x3370000800401F3 for CLSIDFromProgID)
ADJUSTMENT B
---------
In the declration if I change lpsz from ByVal:
Code:
Private Declare Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As Long, ByRef pCLSID As TGUID) As Long
to ByRef:
Code:
Private Declare Unicode Function CLSIDFromString Lib "OLE32.DLL" (ByRef lpsz As String, ByRef pCLSID As TGUID) As Long
then every call to CLSIDFromString returns the 9 digit code 0x2800401F3 (or 0x3230000800401F3 for CLSIDFromProgID)
ADJUSTMENT C
---------
If I remove the Unicode from the declration then TGUID structures are not filled and every call to CLSIDFromString returns the 9 digit code 0x2800401F3 (or 0x3030000800401F3 for CLSIDFromProgID)
In other words, without any of these adjustments, my Visual Studio 2019 VB code almost works (and suggests the UNICODE, ByRef, ByVal operators are correct) except for the two issues above: extra digits in the return codes (which happens all the time, no matter the adjustments) and partially/incorrectly completed TGUID structures.
Any suggestions on what to do will be most appreciated as at this point I am completely stumped.
Once I figure this out I can go back to the original problem to find out why my 32 bit dev environment code works in an Oracle Virtual Box, but not on the main system. If anyone has an ideas about that, that would be much appreciated too.
Many thanks in advance.
Last edited by s29ers; Mar 7th, 2021 at 07:12 PM.
-
Mar 7th, 2021, 08:12 PM
#34
Hyperactive Member
Re: [RESOLVED] Looking for advice on 64-Bit COM
it seems twinBasic will be ok?
although it is not complete now
-
Mar 8th, 2021, 01:02 AM
#35
New Member
Re: [RESOLVED] Looking for advice on 64-Bit COM
As an update I think I see the problem: Visual Basic's Long data type is 8 bytes instead of 4. But this created new problems.
So although the return value from CLSIDFromString is 4 bytes, it's being stored in a variable of 8 so that's why are extra digits.
Similarly the Data1, Data2, Data3 Long variables in the TGUID structure are 8 bytes instead of 4 and probably why the TGUID variables look like they are not filled correctly.
Now, on this page:
https://docs.microsoft.com/en-us/dot...long-data-type
Microsoft says:
"If you are interfacing with components not written for the .NET Framework, for example Automation or COM objects, remember that Long has a different data width (32 bits) in other environments. If you are passing a 32-bit argument to such a component, declare it as Integer instead of Long in your new Visual Basic code."
So I changed all Longs to Integers, in particular
Code:
Private Declare Unicode Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As String, ByRef pCLSID As TGUID) As Long
became
Code:
Private Declare Unicode Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As String, ByRef pCLSID As TGUID) As Integer
But this started giving me the following exception:
"System.Runtime.InteropServices.SafeArrayTypeMismatchException: 'Mismatch has occurred between the runtime type of the array and the sub type recorded in the metadata.'"
When I trapped the error with a Try/Catch statement, the Exception.Message is
System.Byte[115]' from assembly 'System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' has too many dimensions.
The only way to stop this error was to change the pCLSID from ByRef to ByVal (I really have no idea why that should make a difference and get rid of the above error to do with changing Long to Integer). So it is:
Code:
Private Declare Unicode Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As String, ByVal pCLSID As TGUID) As Integer
This stopped the exception but now the error code returned from all calls to CLSIDFromString is: 0x80070057 which is "Invalid argument"
I've tried everything I can think of but still can't seem to get the call to CLSIDFromString to work.
Any suggestions would be greatly appreciated.
Thanks
-
Mar 8th, 2021, 01:41 PM
#36
New Member
Re: [RESOLVED] Looking for advice on 64-Bit COM
Update #2
On this page:
https://docs.microsoft.com/en-us/dot...ined-data-type
Microsoft states that:
"If you are interfacing with components not written for the .NET Framework, for example Automation or COM objects, keep in mind that user-defined types in other environments are not compatible with Visual Basic structure types."
I actually had the TGUID structure wrong before as Data2 and Data3 are not the same type as Data1, although that would have made no difference as the structures can't be passed anyway.
So I decided to then just make the TGUID variables an array of Integers. The TGUID requires 16 bytes (one 4 byte inter, two 2 byte integers, and an 8 byte array). So I just declared pCLSID and IIDispatch as follows (since an array of (3) is actually 4 items (0 to 3)):
Code:
Dim pCLSID(3) As Integer
Dim IIDispatch(3) As Integer
Now to pass this to CLSIDFromString I changed the pCLSID parameter as follows, and used the ParamArray per the documentation here:
https://docs.microsoft.com/en-us/dot...rameter-arrays
Without ParamArray, the pCLSID's parameter and argument types don't match. Also ParamArray requires the parameter to be ByVal.
Code:
Private Declare Unicode Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As String, ByVal ParamArray pCLSID() As Integer) As Integer
And now CLSIDFromString now works! In the debugger, if I compare the actually 16 bytes of pCLSID with the pCLSID in the other development environment I mentioned where this is all working, pCLSID is correct.
But ...
a) only one ParamArray parameter is allowed per declaration and it must be the last paramater. So this:
Code:
Private Declare Function CoCreateInstance Lib "OLE32.DLL" (ByVal ParamArray rclsid() As Integer, ByVal punkOuter As Integer, ByVal dwClsContext As Integer, ByVal ParamArray riid() As Integer, ByRef ppv As Integer) As Integer
won't compile because there are 2 ParamArrays (and the second is not even the last parameter.
Now this it would seem that this:
Code:
Private Declare Function CoCreateInstance Lib "OLE32.DLL" (ByRef rclsid() As Integer, ByVal punkOuter As Integer, ByVal dwClsContext As Integer, ByRef riid() As Integer, ByRef ppv As Integer) As Integer
Should work, and just pass the address of the first element of the arrays to CoCreateInstance but it doesn't because when I examine pCLSID's memory in the debugger it is the following:
Code:
0x049ED594 04 00 00 00 ....
0x049ED598 12 7a ab 29 .z«)
0x049ED59C 31 b5 0e 45 1µ.E
0x049ED5A0 8f 7a ea 94 .zê”
0x049ED5A4 c2 f3 c0 5f ÂóÀ_
The actual pCLSID contents starts at 0x049ED598 but the address of pCLISD is 0x049ED594, and that holds the four byte number "4", the size of the pCLSID array, and that's what CoCreateInstance thinks is the first item in the TGUID, but it isn't. The first item is 4 bytes further. That is why I'm guessing the ParamArray modifier is needed, so VB knows to skip past this when passing an array to a function. But then it doesn't allow 2 ParamArrays parameters which CoCreateInstance needs.
Any suggestions??
What am I missing here? I'm sure Microsoft Visual Basic (coded in Visual Studio 2019) can call the Microsoft Windows API functions with the parameters as the API requires: Longs as 4 bytes, structures, arrays, addresses variables/memory blocks, etc. etc.
Thanks again
-
Mar 8th, 2021, 05:34 PM
#37
New Member
Re: [RESOLVED] Looking for advice on 64-Bit COM
UPDATE 3
Well moving along, I've almost got it working.
I finally figured away around the ParamArray issue: just pass the first element of the array to ByRef parameter. So my declarations as as follows:
Code:
Private Declare Function CoCreateInstance Lib "OLE32.DLL" (ByRef rclsid As Integer, ByVal punkOuter As Integer, ByVal dwClsContext As Integer, ByRef rii As Integer, ByRef ppv As Integer) As Integer
Private Declare Unicode Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As String, ByRef pCLSID As Integer) As Integer
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef hpvDest As Integer, ByRef hpvSource As Integer, ByVal cbCopy As Integer)
Dim pCLSID(3) As Integer
Dim IIDispatch(3) As Integer
And the calls are as follows:
Code:
ret = CLSIDFromString(IID_IDispatchS, IIDispatch(0))
ret = CLSIDFromString(Redemption_RDOSession, pCLSID(0))
ret = CoCreateInstance(pCLSID(0), 0, CLSCTX.CLSCTX_LOCAL_SERVER Or CLSCTX.CLSCTX_ACTIVATE_64_BIT_SERVER, IIDispatch(0), ppv)
That all works very well ... until (sigh) the code reaches:
Code:
CreateObject64 = ObjFromPtr(ppv)
ObjFromPtr is defined as follows;
Code:
Public Function ObjFromPtr(ByVal pObj As Integer) As Object
Dim obj As Object
obj = Nothing
ObjFromPtr = obj
Try
' force the value of the pointer into the temporary object variable
CopyMemory(obj, pObj, 4)
' assign to the result (this increments the ref counter)
ObjFromPtr = obj
' manually destroy the temporary object variable
' (if you omit this step you'll get a GPF!)
' CopyMemory(obj, 0&, 4)
Catch ex As Exception
Console.WriteLine("ERROR -- ObjFromPtr -- At line: " & CStr(Err.Erl) & " -- " & ex.Message)
End Try
End Function
The problem is the call to CopyMemory. The value of pObj is copied to obj, but the moment it is, VB says that obj is an Integer Object (rather than being some untyped Object -- I can see this change take immediately in the debugger which shows type of obj change from "Object" to "Object(integer)"), so when obj is returned back through CreateObject64 to the main routine which then trys to get Redemptions's version number with o64.Version, VB throws the following error:
Code:
Public member 'Version' on type 'Integer' not found.
Because obviously Integer objects don't have the Version member.
Any suggestions?
Thanks in advance
-
Mar 9th, 2021, 04:17 AM
#38
Re: [RESOLVED] Looking for advice on 64-Bit COM
This ObjFromPtr is totally not going to work in managed environment. The pObj argument gets boxed probably on first CopyMemory and most every single line is not doing what the VB6 code impl is supposed to do.
You have to find out how to use GC and managed environment to construct COM objects from pointers. There has to be a built-in function for that task that is equivalent to ObjFromPtr because it's not possible to manually implement it -- surely not by direct translation of the VB6 code.
cheers,
</wqw>
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|