Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Referencing a WebService from C through a C# Library (.NET Interop)

0.00/5 (No votes)
10 May 2005 1  
Gives a step by step procedure to access a Web Service from a C project through a C# library.

Introduction

In this article (modified from my last posted article!) I will give you an example on how to access a web service from a C project using a C# Library. Moreover, the web service's URL can also be dynamically read from an initialization file (.ini) in the C project.

So this is a typical .NET Interop scenario. The C project would communicate with the C# library which in turn would access the web service and retrieve the necessary data into the C# library and in return the C# library should parse the returned XML and then serialize the returned data into corresponding structures so that the C project could consume it.

Solution

To begin with, this is the step by step procedure to get the task done:

  1. Create a new C# library Solution Explorer->Add->Add New Project->Visual C#->Class Library. Give the project a name (let's call it Library).
  2. Add reference to the web service, Solution Explorer->C# Class Library�s add Reference->Add Web Reference and then key in the URL of the exposed WebService (example :http://localhost:8080/axis/service1/GmsWs?wsdl) and click on Go. Once the WSDL shows up, give a descriptive name for the WebService and click on Add Reference. (Let's call the reference WebService.)
  3. Now, the WebService is added and a wrapper class is created to interact with the service. Right click the web reference of the service and change the URL behavior to dynamic from static, as our program would read the URL of the service dynamically from the initialization file and the constructor also has to be modified to take a string (URL of a WebService).
  4. Click on the Show all files icon on the Solution Explorer tab and navigate to the Reference.cs file found under the reference section of the service reference, replace the constructor of the WebService with a custom constructor designed to read the URL dynamically and create wrappers.
    [System.Web.Services.WebServiceBindingAttribute(Name="WsSoapBinding",
     Namespace="urn:GmsWs")]
    public class WsAPIService : System.Web.Services.Protocols.
      SoapHttpClientProtocol {
    /// <REMARKS />
    
    /*public WsAPIService() {
    string urlSetting = 
    System.Configuration.ConfigurationSettings.AppSettings
     ["Library.WebService.WsAPIService"];
    if ((urlSetting !=null)) {
        this.Url = string.Concat(urlSetting, "");
    }
    else {
        this.Url = http://localhost:8080/axis/services/GmsWs;
    } */
    //For generating dynamic wrapper classes based on the supplied URL
    
    public WsAPIService(string szURL)   {
        this.Url = szURL; 
    }
  5. Next, sketch the C# library to make calls to the WebService, create instances of the WebService before accessing the methods:
    Library.WebService.WsAPIService myService = 
            new Library.WebService.WsAPIService(URL);

    Library is the name of our C# library and WebService is the name given to the Web Service that we are referencing. WsAPIService is the name given to the WebService at the server end. This can be found in the wsdl:service name =" " tag of the WSDL file generated by the WebService. Also, the URL of the WebService has to be passed to the constructor while instantiating the WebService as we have defined it to be dynamic.

  6. Remember to write interfaces for all the functions that need to be accessed from the C DLL as it would be referencing the C# library�s methods only through the interfaces created:
    //Creating interfaces to the C# Library functions which 
    
    //inturn access the webservice 
    
    public interface IMyInterface 
        {
            int LfindName2(string name, string URL);
            string LgetStandardName(string name,string URL);
        }
  7. Moreover, while doing a .NET Interop we have to supply GUID's for the interfaces and classes created as COM relies on the registry, we have to give a unique GUID for the interface and the classes exposed. This is the equivalent of CLSID's generated while registering a component. Tools->Create GUID->Registry Format, this generates unique GUID's that could be incorporated into the code, now click on Copy and then paste it above the class declaration for interfaces and other classes:
    Guid("876B4EAD-C5C2-4bd2-86C5-E132B1320006")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface IMyInterface 
    {..;
    .. }
  8. Also remember to create data structures that are COM compatible, (refer to Microsoft's Developers Network Library).
  9. Write the functions inside the class which will be used to access the WebService, remember to add GUIDs for the classes:
    [Guid("59B6BA59-A296-4b38-AD6E-D0896C5D3FE8")]
    [ClassInterface(ClassInterfaceType.None)]
    public class LibraryImplementation : IMyInterface
    {    
       public int LfindName2(string name, string URL)
        {
            try
            {
                 //access webservice from here
    
            }
            catch
            { ...
            
            }
        }
    }
  10. Once the C# library is sketched and compiled successfully, register it in the GAC together with converting it into a type library to be imported by the C DLL.
    regasm Library.dll /tlb Library.tlb

    (Library is the name of our C# library; this command can be put in the post-build option of Visual Studio IDE.)

  11. Remember the strong naming of assemblies, yes you have to do it if you need to do an .NET Interop, so do a:
    sn -k Library.snk

    which will generate a key that you have to add to your project.

  12. Go to the assembly.cs file and fill in the title, description, company, products etc., most important of all, give a specific assembly version number instead of the default 1.1.*.* otherwise every time you compile your DLL after making small changes, the revision number would be incremented and registered in your registry as a new library. Hence giving a specific number like 1.1.1.1 would stop populating your registry unnecessarily. Make these changes also in the assembly.cs file, add the generated strong name key in the AssemblyKeyFile.
    [assembly: AssemblyDelaySign(false)]
    [assembly: AssemblyKeyFile("Library.snk")]
    [assembly: AssemblyKeyName("")]
  13. Import the generated Library.tlb file into the C project�s header file and create a reference to the managed interfaces declared in the C# library.
    /////////////////////////////////////////////////////
    
    // .NET Specific Header which is included in the C Project
    
    //////////////////////////////////////////////////////////
    
    #pragma warning (disable: 4278)
    #include <string.h>
    
    #import <mscorlib.tlb> raw_interfaces_only
    
    // Ignoring the server namespace and using named guids:
    
    #if defined (USINGPROJECTSYSTEM)
    #import "..\Library\Library.tlb" no_namespace named_guids
    #else  // Compiling from the command line, all files in the same directory
    
    #import "Library.tlb" no_namespace named_guids
    #endif  
    
    using namespace std;
    
    ///////////////////////////////////////////////////////////////
    
    // End .NET Specific Header, #include this header to the C Main 
    
    ///////////////////////////////////////////////////////////////
  14. The C code can access the C# library only through the managed interface:
    IMyInterface *test = NULL;
    CoInitialize(NULL); //Enter Single Threaded Apartment (STA)-STA Thread
    
    //Instantiate the COM object in the appropriate apartment
    
    HRESULT hr = 
    CoCreateInstance(CLSID_LibraryImplementation,NULL,CLSCTX_INPROC_SERVER, 
      IID_IManagedInterface,reinterpret_cast
    (&test)); 
    if (FAILED(hr))
    {
        MessageBox(NULL,"Couldn't create the instance!","C# Library",MB_OK);
    }
  15. Access the corresponding C# library functions through the created managed pointer:
    string str;
    //getName is a function in the C# Library which inturn calls an exposed 
    
    //function on the web service
    
    str = test->LfindName2(szSearchName,szURL);
  16. After using the managed interface in functions, we have to clean up COM so as to free memory:
    CoUninitialize();
    test->Release();
  17. Well, before that let me tell you how to read the URL of the WebService from the .INI file from C:
    //Reading the .INI file and getting the url of the webservice
    
    //ini file contains URL=http://localhost:8080/axis/service/GmsWs?wsdl
    
    void CheckWebService(LPCSTR szIniFile)
    {
        CHAR wsURL[128];
        GetPrivateProfileString("Central GMS","URL","",
                              wsURL,sizeof(wsURL),szIniFile);
        if(strcmp(wsURL,"") != 0)   
        {    
            szURL = (_bstr_t) wsURL;    
        }  
        else
        {    
           //default webservice running on localhost
    
           szURL = 
             (_bstr_t) "http://localhost:8080/axis/services/GmsWs?wsdl";    
        }
    }
  18. Finally, handle errors and exception thrown or received and the communication between the two libraries.
  19. Converting the C# library to a type library could also be achieved through the command:
    tlbexp Library.dll /out:Library.tlb

Installing the resultant DLL in the clients PCs

The final lap is in installing the resultant DLL to the target PCs. This can be achieved by copying both the resultant C DLL and the C# library (Library.dll) to the application directory of the target PC and then executing:

regasm Library.dll /tlb:Library.tlb
gacutil /I Library.dll

Software required on target PC

The target PC should have the .NET framework re-distributable pack installed. You can download it from the Microsoft website.

Summary

This article gives a way of accessing a WebService from C by creating a C# library to interface between the WebService and the C project. For further clarifications, please email me.

Namespaces used

using System;
using System.Runtime.InteropServices; // for interop services

using System.IO;
using System.Xml; //for parsing the xml

using System.Collections; // in case you are using collection to hold data

using System.ComponentModel;
using System.Text;
using System.Data;

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here