How to get a variable in one DLL assigned a value by calling a function from another DLL file that assigns the value to that variable. RRS feed

  • Question

  • The question is:

        How can I get a variable defined in a function of one DLL, to get assigned its value from a called function of a different DLL file?


    I am working on a project where I am transposing code written for a WindowsCE 3.0 operating system to now run in a WindowsCE 6.0 operating system.

    Since what I have runs ok in WIndwsCE 3.0, I presume that there is some kind of incompatibility or protective mode issues when trying to do the same thing in WindowsCE 6.0

    The problem is that when I execute the following command, the application crashes.

    In a file called Edasapi.DLL I execute the following:

        int    fNumIDs;
        #define MOD_COUNT_IDS    1

        err = (*infoFcn) (MOD_COUNT_IDS, &fNumIDs);

    (where *infoFcn is the pointer to a function called MODULEDESC in a DLL file called anlg_inE.DLL that has already been loaded in.)

    In the file called anlg_inE.DLL I do the following:

        #define NUMBER_OF_IDS 2
        MOD_API long MODULEDESC (int code, void *rtn)
            switch (code)    {
                case MOD_COUNT_IDS:
                    *(int *)rtn = NUMBER_OF_IDS;

    But when *(int *)rtn = NUMBER_OF_IDS executes, the program crashes:

        Exception 'Data Abort' (4): Thread-Id=05840076(pth=831dfc88), Proc-Id=057e0056(p
        prc=854320c0) 'EdasDriver_main.exe', VM-active=057e0056(pprc=854320c0) 'EdasDriver_main.exe'
        PC=40b623e0(anlg_ine.dll+0x000023e0) RA=40b623d4(anlg_ine.dll+0x000023d4) SP=000
        2f83c, BVA=00000000

    The function called MODULEDESC() located in anlg_inE.DLL from the EdasapiE.DLL does execute and run ok.

    But when the command is encountered( *(int *)rtn = NUMBER_OF_IDS;  ), the program crashes.

    The variable 'fNumIDs' in EdasapiE.DLL need to get assigned a value that gets defined when calling a function called MODULEDESC() in the file called anlg_inE.DLL

    There seems to be a conflict with the passed address of '&fNumIDs' from EdasapiE.DLL to the *(int *)rtn variable in the MODULEDESC() function in anlg_inE.DLL.

    I expect  *(int *)rtn is be pointing to the address of 'fNumIDs' and then assigning the value of NUMBER_OF_IDS to 'fNumIDs' variable.  But instead the application crashes at the ' *(int *)rtn = NUMBER_OF_IDS;' command.

    In WindowsCE 6.0, can I, or how can I pass the address of a variable(fNumIDs) in one function(nsSWinit) of a DLL(EdasapiE.DLL) to another function(MODULEDESC) in a different DLL(anlg_inE.DLL) which assigns a value(NUMBER_OF_IDS) to that variable(fNumIDs...i.e... *(int *)rtn)?


    More details.

    I run a program called EdasDriver_main.exe that starts when the OS system first boots up.
    (Even if I run the program manually I get the same results).

    EdasDriver_main.exe executes a LoadLibrary() command which loads in a DLL called EdasapiE.DLL.

    EdasDriver_main.exe then performs a GetProcessAddress() command to run a function located in the  EdasAPI.DLL called nsHWInit().

    nsHWInit() performs some hardware initialzation the runs a function called nsSWInit().

    One of the many things that nsSWInit() performs is to load in all .DLL files it finds in a specific folder.
    In this case, it first finds a file called anlg_inE.DLL.   nsSWInit() then executes a LoadLibrary() command to load in the anlg_inE.DLL file.

        typedef long (*DescFunction) (int code, void *rtn);
        HINSTANCE    fInstanceHandle;
        DescFunction    infoFcn;

        infoFcn = (DescFunction)GetProcAddress (fInstanceHandle, MAKEINTRESOURCE(2));

    (MAKEINTRESOURCE(2) gets decoded to be the function MODULEDESC() in the anlg_inE.DLL file.)

    (I then call the MODULEDESC() function in anlg_inE.DLL from EdasapiE.DLL)

    // MOD_COUNT_IDS ... Number of Module types.
    // fNumIDs is a memory location that gets filled in the MODULEDESC routine.

        err = (*infoFcn) (MOD_COUNT_IDS, &fNumIDs);
        if (err)
            RETAILMSG(1,(TEXT("FAIL ===== FcnDLL::GetModuleInfo...MOD_COUNT_IDS.\r\n")));
            return err;

    (The function located in the anlg_inE.DLL file called MODULEDESC() executes.)

        MOD_API long MODULEDESC (int code, void *rtn)
            switch (code)    {
                case MOD_COUNT_IDS:
                    *(int *)rtn = NUMBER_OF_IDS;

    The function called MODULEDESC receives two input parameters, a parameter called 'code'( MOD_COUNT_IDS), and the address of fNumIDs(void *rtn).  I get no compile errors or warnings.

    So from the EdasAPIE.DLL code I am loading in the file called anlg_INE.DLL and then from EdasaoiE.DLL, running a function called MODULEDESC located in the anlg_inE.FLL file.

    But *(int *)rtn = NUMBER_OF_IDS; crashes when it executes.

    Again, this original code was written and runs fine in WindowsCE 3.0, but I need it to work in WindowsCE 6.0

    Any advice on why this happens and hw to fix it?...Thanks

    Wednesday, July 9, 2014 6:08 PM

All replies

  • You probably need to marshal the pointer, although in this case it's not an embedded pointer so it should have been marshaled already. However, this is only valid in case you are calling from user to kernel mode. Are you calling user to user mode DLLs, is one of them user mode and the other kernel mode, or are they both kernel mode DLLs? Have a look at for more information and understanding.

    If you're writing C++ code you can use the wrapper class MarshalledBuffer_t to make your life easier.

    Good luck,

    Michel Verhagen, eMVP
    Check out my blog:

    Microsoft Embedded Partner
    Consultancy, training and development services.

    Thursday, July 10, 2014 7:46 AM
  • In CE3 everything on the device was running in kernel mode allowing the hardware driver modules to access hardware directly from the same process as the application. I think the CE6 problem is a mixture of DLLs marked for user mode and kernel mode, perhaps, as Michel suggests. Otherwise I think that the pointers should all be in the same process, the EXE, and should not require marshaling (although hardware access will be problematic).

    For CE6, I think that what you really want is to build a pair of components to bridge the user mode/kernel mode interface. The hardware access in anlg_ine.dll will need to run in kernel mode. EDASAPIE.dll will run in user mode where customer applications call it.

    You'd have EDASAPIE.dll still, with the same exported functions for user mode code to call. However, rather than calling the hardware driver modules directly (LoadLibrary, etc.), it would call into a kernel mode driver, EDASAPIE_K.dll, using standard driver I/O like DeviceIoControl, ReadFile, WriteFile.

    EDASAPIE_K.dll will have to marshal various pointers from user mode code passing data back and forth. The user's application doesn't deal with it because of EDASAPIE.dll wrapper. EDASAPIE_K.dll should be able to directly handle loading and calling anlg_ine.dll, since they will be in the same process and all running in kernel mode.

    Paul T.

    Thursday, July 10, 2014 3:08 PM
  • Thanks Michel:

    I'll look into what you descibed.

    I believe the two DLL files(EdasapiE.DLL and anlg_inE.DLL are user mode DLL's.  My dumb question of the day would be how I would know if the drivers are kernel-mode or user-mode? When I build the OS and generate other driver/program  in Subprojects, I define in the .bib file the 'K' parameter which in my mind makes that driver a kernel-mode driver.

    But for the two DLL files in question, EdasapiE.DLL and anlg_inE.DLL, there were just created as SmartDevice DLL projects. They are totally independent files that are not part of the OS at all.

    How do I determine if the two DLL files are marked as Kernel-mode or User-Mode DLL files?


    • Edited by Chulk Ches Thursday, July 10, 2014 5:22 PM Clarify my response better
    Thursday, July 10, 2014 5:20 PM
  • Chulk,

    Paul's insight made it clear to me what you are trying to do. Here are the steps for you to follow:

    1. Any hardware access needs to be moved into a kernel mode driver. Make a stream interface driver (XXX_Init, XXX_Open, XXX_Read, XXX_Write, XXX_IOControl, etc) and handle any special requests in the IoControl handler. Mark this driver as NK SHK (this will run in kernel mode which allows access to hardware) and make sure it's in the MODULES section.
    2. Create an SDK DLL. This DLL takes care of all CreateFile, CloseHandle and DeviceIoControl calls on one side (communication with your driver DLL), and exposes an easy to use API (communication with your application) on the other side. Your DLL will expose functions like AnalogInit(), GetModuleDesc(), AnalogDeinit(), etc. (or keep it at exactly the same API as your current EDASAPIE.dll). Mark this DLL as NK SHQ (this will allow this SDK DLL to be used from your application, but also from another driver if that is required) and make sure it's in the MODULES section.
    3. Change your application (user mode) DLLs (anything NOT marked as K or Q) to talk to the SDK DLL (or, alternatively you can do all the CreateFile, DeviceIoControl, etc from your application directly, but it's much nicer to separate this "difficult" functionality in a separate SDK dll as per item 2).

    You will need to take care of marshaling embedded pointers (as per the links I gave in my previous reply) inside the driver DLL.

    Remember that CE6 changed to a grown-up model where we have a trusted kernel mode where we can access hardware and do all kinds of scary things (things that can make a device hang), and user mode, where we only allow access to certain "safe" APIs and certain "safe" memory areas. The user mode applications run in a fully separate, protected, virtual memory space with very limited or no access to "scary" parts.

    This is good. ;-)

    This all opposed to CE3 where everything was allowed (total anarchy! ;-).

    Good luck,

    Michel Verhagen, eMVP
    Check out my blog:

    Microsoft Embedded Partner
    Consultancy, training and development services.

    Thursday, July 10, 2014 8:49 PM
  • Thanks Paul and Michel:

    Looks like I might have this resolved with your inputs.


    int    fNumIDs;
    infoFcn = (DescFunction)GetProcAddress (fInstanceHandle, MAKEINTRESOURCE(2));
    err = (*infoFcn) (MOD_COUNT_IDS, &fNumIDs);

    I get an exception:

    Exception 'Data Abort' (4): Thread-Id=04930006(pth=854eba8c), Proc-Id=04920006(p
    prc=85401294) 'EdasDriver_main.exe', VM-active=04920006(pprc=85401294) 'EdasDriv
    PC=40b62408(anlg_ine.dll+0x00002408) RA=40b62400(anlg_ine.dll+0x00002400) SP=000
    2f9cc, BVA=00000000

    With your recommendations in using CeOpenCallerBuffer():

    int    fNumIDs;
    int    g_fNumID;

    infoFcn = (DescFunction)GetProcAddress (fInstanceHandle, MAKEINTRESOURCE(2));
    hr = CeOpenCallerBuffer((PVOID*) &g_fNumID, (PVOID)fNumIDs, sizeof(fNumIDs), ARG_I_PTR, FALSE);
    err = (*infoFcn) (MOD_COUNT_IDS, &g_fNumID);
    hr = CeCloseCallerBuffer((PVOID)  &g_fNumID,(PVOID)fNumIDs, sizeof(fNumIDs), ARG_I_PTR);

    I get the correct value returned:



    I was able to put togeather a pair of components to bridge the user mode/kernel mode interface using the standard driver I/O call, DeviceIoControl. In this way any hardware access for of the drivers I end up generating can get accessed in kernel mode.

    Monday, July 14, 2014 6:49 PM