VB6 COM Object - only works when IDE is running? - dll

I have an ancient COM object in VB6 that has been working fine until recently. (Don't they all). The only code change that has been made (as verified by svn) is the inclusion of a new string literal in an array.
The VB6 IDE compiles the object fine. When I hit Run|Start... and execute the following test vb script from the command prompt, the object works fine and I see the dialog boxes I expect:
dim o
set o = CreateObject("MyDll.MyClassName")
wscript.Echo "Testing object"
wscript.Echo o.HelloWorld ' runs a test method that returns "Hello World"
wscript.Echo "Done"
However, when I stop debugging in the IDE and attempt to run the same vbscript from the same command prompt, I get the error:
(ProgID removed for security reasons, but it is the same as in the script.)
Things I've tried:
I suspected that the DLL may have been registered and unregistered by VB when I start and stop the debugger, so I also tried registering the object with regsvr32 before running the test script. This has had no effect.
I also removed all references to the DLL from the registry, and re-registered the object. Same error.
I deleted the DLL and re-built it from VB (File|Make...) and re-registered the DLL. Same error.
Machine is Win7 Ultimate x64, object built with VB6.
Any suggestions?
And, no, unfortunately, rewriting the object in C# isn't an option.

Microsoft says it's some sort of dependency issue: http://support.microsoft.com/kb/194801
Because it's working when you run the object in the IDE this leaves you with four possibilities:
The ActiveX dll itself is not on the system path.
The ActiveX dll depends on something else which is not on the system path.
After registering the dll, it is somehow marked as requiring elevated security to run
After registering the dll, something it depends on requires elevated security to run.
I would try opening a command prompt as administrator then run your vbscript file that starts the object. If that works then it means the problem is either #3 or #4. If it doesn't, then it means #1 or #2.
You can eliminate #2 and #4 if the ActiveX dll has no external dependencies.
Next, I'd look in my event log to see if any other errors were logged by windows about this.
UPDATE
Just found another possible cause. If the ActiveX dll is 32-bit, then the script has to use the 32-bit version of the script engine to run; otherwise it will give this error because the default script engine (x64 on that machine) literally can't find the dll.
I believe if you use \windows\system32\cscript.exe to run your vbscript then you'll be good.

Well it definitely sounds like the issue because of 32bit dll.. The suggestion mentioned above is correct but the path is wrong.. try using the CSCRIPT from C:\Windows\SysWOW64..

Try registering the DLL with regsvr32.exe from %Windows%\SysWOW64. It is different than the regsvr32.exe in %Windows%\System32 (on a 64-bit OS).
See this SO posting.

Related

GetWindowsDirectory() API returns wrong (vba\vb6)

on my Windows-Terminal user, I'm trying to have two application point to the same Windows directory, one written in VBA one in VB6.
When calling the GetWindowsDirectory() API from VB6 it returns the correct path
C:\documents and settings\%user%\Windows
When calling it from VBA macro, it returns
C:\Windows
Notice that same result goes for GetSystemWindowsDirectory()
Thiking may be the VBA code wasn't aware its a Terminal station, I called the GetSystemMetrics(SM_REMOTESESSION) API which returned 1, meaning it is aware that it is a terminal.
The exact same code was used in both VB6 and VBA
Windows 2003R2 , Office version is 2010 64bit (which as i'm typing this, makes me wonder if it's related, knowing vb6 is 32bit ...)
Any ideas ?
EDIT: as explained by IInspectable below, the difference between vba and vb6 is because vb6 is not Terminal-Service-Aware like Office is.
The behavior you are observing is documented. See the Remarks section for GetWindowsDirectory:
Terminal Services: If the application is running in a Terminal Services environment, each user has a private Windows directory. There is also a shared Windows directory for the system. If the application is Terminal-Services-aware (has the IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE flag set in the image header), this function returns the path of the system Windows directory, just as the GetSystemWindowsDirectory function does. Otherwise, it retrieves the path of the private Windows directory for the user.
The DUMPBIN tool with the /HEADERS option can be used to verify, whether the IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE flag is set for a binary.
dumpbin /HEADERS WINWORD.EXE
produces the following output (Microsoft Office 2013 32-bit):
OPTIONAL HEADER VALUES
10B magic # (PE32)
...
2 subsystem (Windows GUI)
8140 DLL characteristics
Dynamic base
NX compatible
Terminal Server Aware
...
In other words: Microsoft Office is Terminal-Services-aware, and calling GetWindowsDirectory from a VBA script hosted inside Microsoft Office will return the shared Windows directory for the system.
If you checked your VB6 application with DUMPBIN, you'll see that it isn't Terminal-Services-aware, and calling GetWindowsDirectory will return the private Windows directory for the user.
Additional resources:
/TSAWARE (Create Terminal Server Aware Application)

AnyCPU COM library (.tlb file) not working when called from external VB6 macro in x64

Okay my title might not be clear enough. I work with an application for which we can develop VB6 macros. This macro needs to work with a .NET dll that I also developped. When I launch the application in x86 mode and I try to run the macro, it's working fine, I can access the methods of the class and everything.
But when I do the same in x64 mode I get the error : "Automation server can't create object"
What's really strange is that I'm not using any x86 unnmanaged COM dll which would be a problem with an x64 architecture. When I do : "Dim myClass as new MyClass.App" it's working but when I call for example a myClass.Start() method, I get this error. And I tried just having a MsgBox('hello world') in my Start method, to be sure it had nothing to do with my code, still no luck. I'm linking my macro to my class with a TLB file.
I tried registering my DLL with RegAsm, but it's still not working in x64. Is there a way to generate both x86 and x64 versions of my TLB? Then I would add both references and if the x64 version throws me an error on Start method, I simply call the x86 method (not a perfect solution, but still..).
Any idea how to solve this problem?
As Hans said, I needed to user the 64-bit version of Regasm.exe. Even though I had already tried launching RegAsm manually after my installation, it was not working. First I added that in the post-build event of my project :
"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regasm.exe" "$(TargetPath)"
That did the trick for the Debug version. What's strange is that the AnyCPU version was working fine at first on my x64 application, in debug mode, it started crashing when I created my setup project (no idea why...). But this change doesn't solve my problem when installing the application, so I added this in the "Commit" of my custom action (based on this article : Regasm- 64bit or 32bit though windows installer in visual studio) :
Public Overrides Sub Commit(savedState As System.Collections.IDictionary)
MyBase.Commit(savedState)
'Check if we're on a x64 OS
If Environment.Is64BitOperatingSystem Then
'Get the Windows dir on the current computer
Dim winDir As String = New IO.DirectoryInfo(Environment.SystemDirectory).Parent.FullName
'Define the path to regasm for x64 machines
Dim regasmPath As String = IO.Path.Combine(winDir, "Microsoft.NET", "Framework64", System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion, "RegAsm.exe")
If File.Exists(regasmPath) Then 'Si le chemin existe, on enregistre la DLL
'Get the location of our DLL
Dim componentPath As String = GetType(Installer).Assembly.Location
'Executes regasm
System.Diagnostics.Process.Start(regasmPath, "/codebase """ & componentPath & """")
End If
End If
End Sub
What I changed from the original article is the path to RegAsm. Since my setup is x86, the RuntimeEnvironnment's directory was pointing to the "Microsoft.NET\Framework..." path not the "Framework64" which was defeating the purpose of my action (it was already registered as a 32 bit COM interop), so I try to get the x64 version of RegAsm and run it.

VB.NET Process.Start failing

A problem that has been plaguing me for nearly a week now.
I am trying to get an install of IIS to take place through the command line in VB. I understand that i need to be setting up an Unattended xml script to call, but here is a fundamental bit that is confusing me:
If i run the command : ' start /w pkgmgr /iu:IIS-WebServerRole;IIS-WebServer; ' it executes perfectly within CMD.exe.
If i add the command to a batch file and run the batch file, it runs perfectly.
If i call the command using : Dim myProcess As Process = Process.Start("cmd.exe", "/k start /w pkgmgr /iu:IIS-WebServerRole;IIS-WebServer;"), then it fails with an error of:
////////////////////////////////////////////////////////////////////////////////////////////////////////////
Operation failed with 0x8007000B ////
////
An attempt was made to run the program in an incorrect format ////
////////////////////////////////////////////////////////////////////////////////////////////////////////
If i call the batch file mentioned earlier, then i get the exact same error.
How can it work perfectly with the two first examples but fail when it is called through VB?
Thanks for any help!
Your VB.NET program is very likely to be running in 32-bit mode and will start the 32-bit version of cmd.exe. The one from c:\windows\syswow64 instead of the one from c:\windows\system32 that you used before. Getting BadImageFormatException starts to become likely.
Project + Properties, Compile tab, set the Target CPU to AnyCPU and untick the "Prefer 32-bit" option. On older versions of VS click the Advanced Compile Options button to get to the setting.

VBS - Register and use Dsofile Windows 7 64 bit

I'm trying to use Dsofile with a VBS script on a Windows 7 64-bit machine. I believe my issue is with registering the DLL.
I used this to register the DLL
regsvr32 D:\Desktop\dsofile.dll
and am told that
"DllRegisterServer in D:\Desktop\dsofile.dll succeeded."
leading me to believe that everything worked. When I try to run the test code that Microsoft gives
Set objFile = CreateObject("DSOFile.OleDocumentProperties")
objFile.Open("D:\Desktop\test.xls")
Wscript.Echo "Author: " & objFile.SummaryProperties.Author
it fails on the first line, stating that
"ActiveX component can't create object: 'DOSFile.OLEDocumentProperties"
which leads me to believe that the DLL did not actually register correctly.
I then tried registering the DLL, based off this solution, by doing the following
cd \Windows\SysWOW64
regsvr32 C:\dsofile.dll
It also informs me that it registered correctly, but once again the script fails on the first line.
How can I fix this, so that I can use Dsofile?
Thanks.

Windows Scripting can't find reference

I have a windows script file that doesn't work anymore.
The script look like this
<job>
<reference object="Some.Component.1" />
<script language="VBScript">
x = CreateObject("Some.Component.1")
MsgBox TypeName(x)
</script>
</job>
When I run the script with cscript or wscript I get the error
Windows Script Host: Cannot find the type library for this reference :
Some.Component.1
The error code is 0x80040068 which means "Invalid index". (I tried to removed the .1 index but it didn't work)
The strange thing is that if I remove the line <reference object="Some.Component.1"/>, the CreateObject line works and the object is created.
I know the script worked about a year ago. The "Some.Component" library has been updated but since CreateObject works I have no idea what is wrong with it.
I have tested the script on Server 2008 R2 (64bit), Server 2003 (32bit) and Windows 7 (64bit) with the same error.
Some.Component is 32 bit. For the 64bit machines I used cscript, wscript in the SysWow64 folder.
(I need the <reference../> to access enums within the component)
The type library is not registered. This is a different and separate thing from the object being registered. Usually the control will do both.
I suggest you first try unregistering and re-registering the DLL.
regsvr32.exe /u c:\path\to\control.dll
regsvr32.exe c:\path\to\control.dll
If that doesn't work, try using RegTLib.exe to register the type library (*.tlb) directly.
regtlib.exe c:\path\to\control.dll
OR
regtlib.exe c:\path\to\control.tlb
Note that the type library may be a separate file or may be embedded in the DLL.
For more on regTLib.exe see here:
http://support.microsoft.com/kb/292744