Solved

Calling COM DLL from ASP

Posted on 2003-10-24
16
912 Views
Last Modified: 2013-12-14
I need to call some C code from ASP but cant get it working - I get an error "Invalid Class String". I know I need to create a COM based DLL - I used the ATL COM AppWizard project.

The only class name seems to be _Module but as you can probably tell Im not a C++ person.

Ive tried the suggestions in http://beta.experts-exchange.com/Web/Q_20650212.html. I can register the DLL ok but I still cant find the name of the class.
0
Comment
Question by:MrNed
16 Comments
 
LVL 1

Expert Comment

by:Sean_Doherty
ID: 9620083
What does your ASP code look like?

<%
dim CHandler

set CHandler = server.CreateObject("PHandlerSub.CHandler")
lErrorCode = CHandler.DoPost()
Response.Write "Test=" & lErrorCode & "<br>"
%>
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9620480
You need to create the class object yourself.  
Use the ClassWizard pane.  Right-click on the object and choose New Class and then rightclick on the added class and use Add Method to add member fns.

-- Dan
0
 
LVL 7

Author Comment

by:MrNed
ID: 9621832
My ASP code is the same as what Sean has posted. The error is on the server.createobject line.

Im trying Server.CreateObject ("TQLDAP.TQLDAP") where TQLDAP is the name of the Dll and the class i just added.

I created a new class and added a member function but it still doesnt work when referencing that class.

The ClassView tab shows

TQLDAP classes
- TQLDAP (the new class i just added)
   - test(int a) (the new function i just added)
   - TQLDAP()
   - ~TQLDAP()
- Globals
   - DllCanUnloadNow()
   - DLLGetClassObject(blah)
   - DllMain(blah)
   - DllRegisterServer()
   - DllUnregisterServer()
   - _Module

Am I doing this right?
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9621851
I have only a little experience here.  But I did create a COM object accessible by VB and JScript.

In the one I created, there was a cooresponding Interface object, representing the IDL file entries

- MyTest classes
  + CMyTools
  + IMyTools
  + Globals

And when I open them up

- MyTest classes
  - CMyTools
     - IMyTools
          - MyFnt1(int)    <<---- click here and look at the CPP file
          - MyTest(int)
        - CMyTools
  - IMyTools
     - MyFn1(int)     <<---- click here and look at the IDL file
     - MyTest(int)
0
 
LVL 7

Author Comment

by:MrNed
ID: 9624373
Sorry but im still not getting it. I got it to recognise the class but then it failed to recognise the function when i called it.

Using Visual C++ 6 I do the following:
New ATL COM AppWizard Project called MyTest.
Select the DLL Server Type and Finish.
In the ClassView window, right click on "MyTest classes" and choose "New Class"
Class type = "ATL Class", name = CMyTools, OK.
Right click the CMyTools class and choose "Add Member Function"
Function Type = int, Decl = MyTest(int a), OK
Give the MyTest function a body (return a+5;)
Run the ASP with CreateObject("MyTest.MyTools") works ok but dies on calling MyTest(1).

My class tree doesnt look quite the same as yours.
0
 
LVL 7

Author Comment

by:MrNed
ID: 9624730
OK, I sort of got something going where I add a Method to the IMyTools interface but it cant return anything other than a HRESULT. I tried (int a, int *b) but i get a type mismatch from ASP when i try to pass in a variable for b. It works if I pass in two numbers but then i cant get a return value.

:(
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9625764
If you want to get a return value, you need to use
   Add Property
rather than Add Method.  All COM properties are realy accessor functions.  THey *all* return only an HRESULT, but properties allow the caller to pass in the pointer to a place to store something else (and call it a retval).  FOr now, just try an int.

-- Dan
0
 
LVL 9

Expert Comment

by:_ys_
ID: 9626044
Automation clients never see the return type (HRESULTs). However you can simulate a return value using the retval attribute.

>> It works if I pass in two numbers but then i cant get a return value.
Your method: HRESULT foo1([in] int a, [out] int *b) has a couple of flaws when used by automation clients.

HRESULT foo1([in] int a, [in, out] int *b);    // VBScript should like this

JScript does not like [out] parameters at all.

Declaring the method as this:
HRESULT foo1([in] int a, [out, retval] int *b);

Should be accepted for both scripting languages.

>> it cant return anything other than a HRESULT
Using the previous  declaration above:
HRESULT foo1([in] int a, [out, retval] int *b);

Automation clients see it as:
int foo1(int a);

For all purposes it takes an int and returns an int.

And just for the record
>> THey *all* return only an HRESULT
There are exceptions to this rule, IUnknown::AddRef and IUnknown::Release return unsigned long values.
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 7

Author Comment

by:MrNed
ID: 9630312
Im slowly getting somewhere. The property can take ints and return an int fine now. Of course my next question is why doesnt it work when I want to pass a string (char *)?
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9630369
char* is not allowed as a parameter -- COM is all UNICODE (I'm not at all sure why they made this limitation).   All COM interfaces I've ever seen use BSTR and VARIANT to passing strings.  I think you need to to set the property as a
    BSTR*
then in your calling app, you need to convert back and forth between char strings and UNICODE strings.   I suggest using the
     _bstr_t
utility class for that purpose.

-- Dan
0
 
LVL 7

Author Comment

by:MrNed
ID: 9630420
No wonder I prefer VB for Windows programming.

How can I use that to extra an ASCII character array for using in strcmp() etc?

eg

STDMETHODIMP MyClass::get_MyTest(BSTR *a, int *pVal)
{
      *pVal = strcmp(_bstr_t::operator char *(a), "testing");
      return S_OK;
}
0
 
LVL 7

Author Comment

by:MrNed
ID: 9630497
Sorry, shouldnt have posted so quickly. Not that I really understand any of what I just wrote:

#include <comdef.h>

STDMETHODIMP MyClass::get_MyTest(BSTR *a, int *pVal)
{
      _bstr_t bstrWrapper = *a;
      *pVal = strcmp(static_cast<char*>(bstrWrapper), "testing");
      return S_OK;
}
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9630545
What is your goal?
    Do you want the ASP program to pass in a string to your C++ COM utility
or
    Do you want to return a string from the C++ COM utility to the ASP program?  
or
   Do you want ASP to pass in a string to the C++ COM utility where that utility can modify it 'in place' so that the ASP program can then do something with the 'same string' ?

=-=-=-=-=-=-=-=-=-=-=-=
One other question for you:
Why are you doing this?  You probably started this task with a specific reason in mind.  What was that reason?

=-=-=-=-=-=-=-=-=-=-=-=
Regarding UNICODE:
One way to avoid conversion headaches is to modify your COM object so that it uses UNICODE -- the same as your ASP program.  If so, you may not need to do any conversions.   In the Settings/ C/C++ /Preprocessor/Preprocessor definitions,  change
      _MBCS
to
     _UNICODE

then fns like strcmp() will work automatically.  There are a few things to watch out for when using UNICODE, for instance,  when defining literasl strings, ratherr than

   char szMyString[]= "Hi there";
you should use:
   TCHAR szMyString[]= _T("Hi there");
or
   TCHAR szMyString[]= L"Hi there";

-- Dan
0
 
LVL 7

Author Comment

by:MrNed
ID: 9630669
Thanks, ill try out the UNICODE settings.

I want an ASP program to call a function, passing in strings and returning an int.

A reason? The C++ DLL is going to be talking to an LDAP directory to change/set passwords and needs to be called from ASP (not ASP.NET sigh). Since ASP can only do simple LDAP authentication and doesnt seem to be able to do more advanced stuff like changing passwords, Im writing a C++ DLL to do it (utilising Novell's C libraries). I thought it would be easy :(
0
 
LVL 49

Accepted Solution

by:
DanRollins earned 100 total points
ID: 9631159
Here is a little JScript file for testing :

    var oMyObject= new ActiveXObject("MyComTest.MyObject");
    var nRet= oMyObject.SomeValue("The MESSAGE","this is the title" );
    WScript.Echo( nRet );

save those lines to a file on your desktop named Tester.js  then you can double-click it to verify that the COM object is working.

Steps to create the COM object:

1) I created an ActiveX named MyComTest
2) I added a "Simple Object" named MyObject
3) I right-clicked on IMyObject and chose Add Property
4) I filled is as follows:
    Property type: int
    PropertyName: SomeValue
    Parameters: BSTR sz1, BSTR sz2
    Function Type: I unchecked Put function (leaving Get Fn checked)

5) Now in my CPP file I have this:

STDMETHODIMP CMyObject::get_SomeValue2(BSTR sz1, BSTR sz2, int *pVal)
{
      // TODO: Add your implementation code here
      return S_OK;
}

and I modified it as follows:

#include <comdef.h>
STDMETHODIMP CMyObject::get_SomeValue(BSTR szMsg, BSTR szTitle, int *pVal)
{
      _bstr_t s1(szMsg);   // this is *not* a UNICODE build so I use
      _bstr_t s2(szTitle);  // this to provide char* values for MessageBox
      *pVal= strlen(s1);
      ::MessageBox( 0, s1, s2, MB_OK );
      return S_OK;
}
=-=-=-=-=-=-=-=-=-=-=-
6) I build and the Output window shows
   Performing registration
(etc).

7) Now I can double-click the JS file described above.  It pops up a window with a title and message as specified and upon return the value 11 is returned (the strlen() of the first parm).

=-=-=-=-=-=-=-=-=-=-=-
That's the Reader's Digest Condensed version of the complete tutorial on creating COM objects with VC++

-- Dan
0
 
LVL 7

Author Comment

by:MrNed
ID: 9631862
Thanks Dan, your the Man.
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

Entering time in Microsoft Access can be difficult. An input mask often bothers users more than helping them and won't catch all typing errors. This article shows how to create a textbox for 24-hour time input with full validation politely catching …
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
THe viewer will learn how to use NetBeans IDE 8.0 for Windows to perform CRUD operations on a MySql database.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now