Results 1 to 38 of 38

Thread: [RESOLVED] Looking for advice on 64-Bit COM

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    Resolved [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.

  2. #2
    Hyperactive Member
    Join Date
    Mar 2018
    Posts
    436

    Re: Looking for advice on 64-Bit COM

    why is vb.net\c# a deal breaker?

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    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.

  4. #4

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    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?

  5. #5
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,290

    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.

  6. #6
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    706

    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:
    1. Public Type TGUID
    2.    Data1 As Long
    3.    Data2 As Integer
    4.    Data3 As Integer
    5.    Data4(7) As Byte
    6. End Type
    7.  
    8. Public Const CLSCTX_INPROC_SERVER As Long = 1
    9. Public Const CLSCTX_INPROC_HANDLER As Long = 2
    10. Public Const CLSCTX_LOCAL_SERVER As Long = 4
    11. Public Const CLSCTX_ENABLE_AAA As Long = &H10000
    12. Public Const CLSCTX_REMOTE_SERVER As Long = 16
    13. Public Const CLSCTX_SERVER As Long = (CLSCTX_INPROC_SERVER Or CLSCTX_LOCAL_SERVER)
    14. Public Const CLSCTX_ALL As Long = (CLSCTX_INPROC_SERVER Or CLSCTX_INPROC_HANDLER Or CLSCTX_LOCAL_SERVER)
    15. Public Const IID_IUnknown As String = "{00000000-0000-0000-C000-000000000046}"
    16. Public Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
    17.  
    18. 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
    19. Public Declare Function CLSIDFromString Lib "OLE32.DLL" (ByVal lpsz As Long, ByRef pCLSID As TGUID) As Long
    20. Public Declare Function CLSIDFromProgID Lib "OLE32.DLL" (ByRef TSzProgID As String, ByRef pCLSID As TGUID) As Long
    21. Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)
    22.  
    23. Public Function CreateObject64(ByRef sObjectName As String) As Object
    24.     Dim ret As Long
    25.     Dim pCLSID As TGUID
    26.     Dim IIDispatch As TGUID
    27.     Dim ppv As Long
    28.  
    29.     ret = CLSIDFromString(StrPtr(IID_IDispatch), IIDispatch)
    30.     ' Get object by GUID
    31.     'ret = CLSIDFromString(StrPtr("{11111111-2222-3333-4444-555555555555}"), pCLSID)
    32.     ' Get object by ProgID
    33.     ret = CLSIDFromProgID(StrPtr(sObjectName), pCLSID)
    34.    
    35.     If ret = 0 Then
    36.         ' Success
    37.        
    38.         ' Create Object
    39.         ret = CoCreateInstance(VarPtr(pCLSID), 0, CLSCTX_LOCAL_SERVER, VarPtr(IIDispatch), ppv)
    40.        
    41.         'Debug.Print "CoCreateInstance returned = " & Hex(ret) & ", ppv = " & Hex(ppv) & ", LastDllError = " & Err.LastDllError
    42.        
    43.         If ppv <> 0 Then
    44.             Set CreateObject64 = ObjFromPtr(ppv)
    45.         End If
    46.     Else
    47.         Debug.Print "CLSIDFromString failed, ret = " & Hex(ret) & ", LastDllError = " & Err.LastDllError
    48.     End If
    49.  
    50. End Function
    51.  
    52. ' Returns an object given its pointer
    53. ' This function reverses the effect of the ObjPtr function
    54. Public Function ObjFromPtr(ByVal pObj As Long) As Object
    55.     Dim obj As Object
    56.     ' force the value of the pointer into the temporary object variable
    57.     CopyMemory obj, pObj, 4
    58.     ' assign to the result (this increments the ref counter)
    59.     Set ObjFromPtr = obj
    60.     ' manually destroy the temporary object variable
    61.     ' (if you omit this step you'll get a GPF!)
    62.     CopyMemory obj, 0&, 4
    63. End Function
    Last edited by qvb6; May 16th, 2019 at 11:17 AM.

  7. #7

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    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?

  8. #8
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    706

    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.

  9. #9

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    Re: Looking for advice on 64-Bit COM

    Ahh I see, thanks for the clarification! I'll give it a shot, appreciate the help

  10. #10
    Hyperactive Member
    Join Date
    Mar 2018
    Posts
    436

    Re: Looking for advice on 64-Bit COM

    Quote Originally Posted by passel View Post
    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

  11. #11
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    706

    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.

  12. #12
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    706

    Re: Looking for advice on 64-Bit COM

    Quote Originally Posted by DllHell View Post
    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/

  13. #13

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    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!

  14. #14
    PowerPoster
    Join Date
    Jun 2013
    Posts
    5,368

    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

  15. #15

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    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!

  16. #16
    PowerPoster
    Join Date
    Sep 2012
    Posts
    2,083

    Re: Looking for advice on 64-Bit COM

    Maybe we need to face more and more 64-Bit COMs in the future.

  17. #17
    Hyperactive Member
    Join Date
    Jan 2015
    Posts
    429

    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

  18. #18
    PowerPoster
    Join Date
    Feb 2017
    Posts
    3,049

    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...

  19. #19
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    706

    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.

  20. #20

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    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!

  21. #21
    Addicted Member
    Join Date
    Feb 2015
    Posts
    224

    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

  22. #22

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    Re: Looking for advice on 64-Bit COM

    Quote Originally Posted by dreammanor View Post
    Maybe we need to face more and more 64-Bit COMs in the future.
    Quote Originally Posted by Thierry69 View Post
    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.

  23. #23

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    Re: Looking for advice on 64-Bit COM

    Quote Originally Posted by VB6 Programming View Post
    @jpbro @qvb6

    The Outlook Redemption developers are usually helpful. It may be worth contacting them http://www.dimastr.com/redemption/home.htm
    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.

  24. #24
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    706

    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

  25. #25

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    Re: Looking for advice on 64-Bit COM

    Hi @qvb6 - nice work I think the CLSCTX_ACTIVATE_64_BIT_SERVER flag might have been the trick because I was getting errors with a same/similar REG file that I put together, and now with your latest demo everything appears to be working on a clean Windows 10 install with Office 64-bit.

    So I think we now have 2 great options for automating 64-bit Office from our 32-bit VB6 apps thanks to you and Olaf! Thanks to you both!

  26. #26

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    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}"

  27. #27

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    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!

  28. #28
    New Member
    Join Date
    Mar 2021
    Posts
    6

    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

  29. #29
    Fanatic Member
    Join Date
    Jan 2020
    Posts
    927

    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)

  30. #30
    Member
    Join Date
    May 2012
    Location
    42.787034,-81.176367
    Posts
    35

    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

  31. #31

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2010
    Location
    Canada
    Posts
    1,729

    Re: [RESOLVED] Looking for advice on 64-Bit COM

    Quote Originally Posted by s29ers View Post
    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.

  32. #32
    New Member
    Join Date
    Mar 2021
    Posts
    6

    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.

  33. #33
    New Member
    Join Date
    Mar 2021
    Posts
    6

    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.

  34. #34
    Addicted Member
    Join Date
    Jan 2015
    Posts
    244

    Re: [RESOLVED] Looking for advice on 64-Bit COM

    it seems twinBasic will be ok?
    although it is not complete now

  35. #35
    New Member
    Join Date
    Mar 2021
    Posts
    6

    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

  36. #36
    New Member
    Join Date
    Mar 2021
    Posts
    6

    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

  37. #37
    New Member
    Join Date
    Mar 2021
    Posts
    6

    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

  38. #38
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    2,912

    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
  •  



Click Here to Expand Forum to Full Width