[Q] SeekBar problem in Settings.apk source - Android Software/Hacking General [Developers Only]

I'm stuck. I am building a custom Settings.apk for a new tablet and I've run across an error I can't find.
Any dialog that displays a SeekBar or variation simply FC's with a null pointer exception. I have a reference source tree that is a virgin copy from the android git. If I compile the stock Settings.apk, it does the same thing at the same places as my custom version.
The error occurs in the BrightnessPreference.java and the RingerVolumePreference.java files any time the dialog containing a SeekBar or Volumizer tries to display.
Here's an example of the code:
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
mSeekBar = getSeekBar(view);
mSeekBar.setMax(MAXIMUM_BACKLIGHT - mMinBrightness); <-- errors here
It errors at setMax with a null pointer error, like the setMax method doesn't exist. My build environment seems fine. Everything I've built runs on the target devices, but this one has me stumped. Has anyone else tried building a Settings.apk lately, and does it display the volume (in sound) and brightness (in Display) dialogs?
BTW, the target is Froyo
Thanks,
--- Jem

Are you sure the mMinBrightness variable is defined? BrightnessPreference.java as shown here instead uses MINIMUM_BACKLIGHT and is defined as:
Code:
private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10;
It is then used to set the seekbar max:
Code:
mSeekBar.setMax(MAXIMUM_BACKLIGHT - MINIMUM_BACKLIGHT);
You can also look at the onBindDialogView in RingerVolumePreference.java here. If you want to check whether the java files change from the version I linked(2.2.1 Froyo), just use the arrows on either side of the currently listed version.

I appreciate the response. Yes it is defined and initialized earlier as referenced below.
=== Code ===
private int mMinBrightness;
private boolean mAutomaticAvailable;
// Backlight range is from 0 - 255. Need to make sure that user
// doesn't set the backlight to 0 and get stuck
private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10;
private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
public BrightnessPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mAutomaticAvailable = context.getResources().getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);
setDialogLayoutResource(R.layout.preference_dialog_brightness);
setDialogIcon(R.drawable.ic_settings_display);
mMinBrightness = MINIMUM_BACKLIGHT;
if (Settings.System.getInt(context.getContentResolver(),
Settings.System.LIGHT_SENSOR_CUSTOM, 0) != 0) {
mMinBrightness = Settings.System.getInt(context.getContentResolver(),
Settings.System.LIGHT_SCREEN_DIM, mMinBrightness);
}
}
=== code ===
Thanks,
--- Jem

It might be useful to rule out an issue with mMinBrightness not getting set or getting set incorrectly by either replacing it with MINIMUM_BACKLIGHT in the setMax line or using a toast message to display the value of mMinBrightness before setMax. If that doesn't reveal anything, could you paste the relevant logcat output for the error/fc and also upload your BrightnessPreference.java file somewhere so it could be downloaded.

I saved the dir's with my modified package and blew away the entire repo structure. I then reloaded the official Android tree from kernel.org. I have compiled the Settings.apk app ALL STOCK, and I get the same errors in the brightness and volume areas. A copy of the brightnesspreference.java and a log screenshot of the error tracking can be found at:
mlsoft dot sytes dot net/brightness.zip (I can't post the direct link)
The only change in the error is the line number (same call though).
At this point, I am wondering if anyone can build the Settings.apk from the repo and get it to display a seekbar or variant in brightness or volume settings using 2.2.1 (froyo). I am comfortable at this point thinking that it is in the tree structure and not my build environment. At least I have a place to hunt if you can reproduce the error on your end.
I really appreciate your help.
--- Jem

android:id="@*android
Yeah, same thing happened to me and I found answer.
I hope this might help.
You will find that two xml files using "@*android".
"It gives access to internal resources for platform apps.
It is NOT safe to build apps with such declarations unless you are building a bundled app within a full system image."
- from stackoverflow
"What is the purpose of the star in the ID string?"
Sorry that I can not post URL. I'm new user.

Sorry about not yet having built and tested the Settings.apk on my end but I haven't found the time to do so yet. Your issue could very well be what You Kim posted about and I'm sure I wouldn't have noticed that for awhile if at all. Here's the link to the stack overflow topic You Kim referenced. The two xml files in the Settings package this occurs in are res/layout/preference_dialog_brightness.xml and res/layout/preference_dialog_ringervolume.xml. That part looks like this:
Code:
<!-- In brightness layout -->
<[COLOR="Red"]SeekBar android:id="@*android:id/seekbar"[/COLOR]
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dip" />
<!-- In ringervolume layout -->
<!-- Used for the ring volume. This is what the superclass VolumePreference uses. -->
<[COLOR="red"]SeekBar android:id="@*android:id/seekbar"[/COLOR]
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="2dip"
android:paddingLeft="20dip"
android:paddingRight="20dip" />

Thank you both. I have been through those files more than once and didn't notice the declarations. Outstanding work guys. I'm off to edit/recompile/test.
Thanks again,
--- Jem

Unfortunately, these changes didn't stop the null pounter errors. Something, somewhere HAS to be uninitialized, but damned if I can find it in the absseekbar, progressbar, or any other class in the seekbar hierarchy. I even tried setting a value of 255 in setMax to rule out variable problems, but that produces the same error at the same line.
I know there is a fix, Settings works on millions of Android devices, so it has to be something stupid I'm overlooking.
--- Jem

Well, it's almost 3am, but I fixed the problem in the brightness preference. SeekBarPreference handles the getSeekBar function, but it doesn't work as expected.
It assigns a static system res ID that obviously doesn't work as intended. So, I overrode the routine just above the call to onBindDialogView. Here is the routine in the seekbarpreference file:
(at sign)Override
protected static SeekBar getSeekBar(View dialogView) {
return (SeekBar) dialogView.findViewById(com.android.internal.R.id.seekbar);
}
Overridden in the brightnes preference file to this:
(at sign)Override
protected static SeekBar getSeekBar(View dialogView) {
return (SeekBar) dialogView.findViewById(R.id.seekbar);
}
I'll go back and write the original to accept a res ID as an argument later. For now at least the brightness control works. Tomorrow I'll go after the volume controls.
--- Jem

Related

[Q] Need help to populate a listview from external source

I'm pretty new to java and somewhat new to android and I've been looking everywhere for the past 3 or 4 days but cant seem to find the answer to my question. I'm making an app that is pretty simple and has only one real main function and that is to pull movie names, release dates, and the movie's format (dvd, blu-ray etc) into the app automatically from a very simple file that I will host on my server.
Now I I'm currently able to input the needed information manually via a base-adapter and a strings file within each months activity but I want to have the ability to push the information remotely which will then give my app (and myself) the ability to update the information as well as to place it into the 3 line listview format that I've already established.
Now I've been reading about Simple:XML, gson, SAX, etc but seeing as the information is just 3 small lines of text (again just the movie name, date and format which doesn't use much space and will be using the same string format for all 12 months) my question is what would be the best route as to the format of the file (i.e. xml, json, plain text, etc) and how would I go about getting this information into my app on the fly? I've included some code snippets below in the hopes they will make my question a little more clear.
I've posted this on several other forums and android dev sites and nobody seems to be able to give me some guidance or simply will not reply at all. I cannot continue on my app until I'm able to figure this out and seeing as it is completed aside from this problem it is becoming a very frustrating experience. I'm hopeful that someone here will be able to give my some help out. Thank you in advance.
The string for each result
Code:
public class SearchResults {
private String Name = "";
private String Date = "";
private String Format = "";
public String getName() {
return Name;
}
public void setName(String Name) {
this.Name = Name;
}
public String getDate() {
return Date;
}
public void setDate(String Date) {
this.Date = Date;
}
public String getFormat() {
return Format;
}
public void setFormat(String Format) {
this.Format = Format;
}
This below is where the actual list-view is populated and shown. Each new search result creates a three line text-view.
Code:
private ArrayList<SearchResults> GetSearchResults(){
ArrayList<SearchResults> results = new ArrayList<SearchResults>();
SearchResults sr1 = new SearchResults();
sr1.setName("Movie #1");
sr1.setDate("July 24th");
sr1.setFormat("DVD, Blu-Ray");
results.add(sr1);
sr1 = new SearchResults();
sr1.setName("Movie #2");
sr1.seDate("July 19th");
sr1.setFormat("DVD, Blu-Ray, Digital");
results.add(sr1);
return results;
}
Can anybody help me with this please?

Application crashes when trying to get a list of loaded modules

I have been looking into the C-Sharp__DLLImport project here:
http://forum.xda-developers.com/showthread.php?t=1006331&highlight=dllimport
I am trying to modify the FileSystem project to be able to get a list of the modules that are loaded for each process.
Code:
STDMETHODIMP CFileSystemIO::GetModulesForProcess(DWORD dwPID, BSTR* result)
{
// Get the process snapshot
HANDLE hModuleSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID );
// Initialize the module entry structure
MODULEENTRY32 moduleEntry = { 0 };
moduleEntry.dwSize = sizeof( moduleEntry );
// Get the first module info
BOOL Return = FALSE;
Return = Module32First( hModuleSnapshot, &moduleEntry);
// Getting process info failed.
if( !Return )
{
CloseHandle( hModuleSnapshot );
return S_FALSE;
}
int x = 1;
CString modList(TEXT(""));
do
{
modList.AppendFormat(TEXT("%d-%d-%d-%d-%d-%d-%s-%s\n"),
moduleEntry.dwSize,
moduleEntry.th32ProcessID,
moduleEntry.GlblcntUsage,
moduleEntry.ProccntUsage,
moduleEntry.modBaseAddr,
moduleEntry.modBaseSize,
moduleEntry.szModule,
moduleEntry.szExePath);
}
while( Module32Next( hModuleSnapshot, &moduleEntry ));
// Close the handle
CloseHandle( hModuleSnapshot );
// set the result
*result = (modList.AllocSysString());
return S_OK;
}
The code is based off a similar function that is already in the project (the CFileSystemIO::MessageBoxRunningProc function which works fine).
By putting some MessageBoxes in there for debugging, I can confirm that the Module32First and Module32Next methods are working correctly. When it gets to the line:
*result = (modList.AllocSysString());
The application crashes. I put a try/catch around there and it didn't trigger a CMemoryException or any other exception.
Any idea why this method would be causing the app to crash?
As an update to this, I was able to figure out the problem. I was unaware of this, but closing the handle generated in the method to get the process messes up the generation of the modules. I didn't think this would be the case since when you generate the handle it also takes a pid. I ended up combining the 2 methods.
The final project uses com interop to call the native methods and it builds a tasklist with all the corresponding modules as subclasses. You can find out which libraries and interfaces are in use by which applications, and where those dll files are located on your phone. If anyone wants to see it, I can post it at some point. It's not a very elegant looking interface, but it gets the job done.

[Q] BAD_XML error when trying to use ProvXML to set security settings

I am now trying my hand at provXML to query/change security settings. I realize that most would probably come back with some type of access denied exception, but I am unable to even get that far.
I have an interop unlocked samsung omnia7 with mango. I am trying to use DMProcessConfigXML to send an xml string to set a security policy (in this case SECPOLICY_CFAUTORUN (2) ). I use QueryPolicy to check its value (currently a "0" and send the xm to set it to a 1.
Here is a snippet from the relavent code
Code:
DWORD dwPolicyValue = 0;
HRESULT hr = QueryPolicy(2, &dwPolicyValue);
LPWSTR wszOutput = NULL;
LPCWSTR wszQueryXML =
L"<? xml version=\"1.0\" encoding=\"utf-16\" ?>"
L"<wap-provisioningdoc>"
L"<characteristic type=\"SecurityPolicy\">"
L"<parm name=\"2\" value=\"1\">"
L"</characteristic>"
L"</wap-provisioningdoc>";
MessageBox(NULL,wszQueryXML,L"Now sending the following xml",MB_OK);
HRESULT hr2 = DMProcessConfigXML(wszQueryXML,CFGFLAG_PROCESS,&wszOutput);
delete[] wszOutput;
The first call to QueryPolciy returns a 0. The call to DMProcessConfigXML returns -2147213303 or FFFFFFFF80042009 which I believe is CONFIG_E_BAD_XML. There may be a problem with the xml string, but I can't see it. Any suggestions as to a solution or somewhere else to look?
It seems to only be when I am accessing the security policies. I can send a query for the certificate stores
Code:
LPCWSTR wszOutput =
<wap-provisioningdoc>
<characteristic type="CertificateStore">
<characteristic-query type="Privileged Execution Trust uthorities" />
<characteristic-query type="Unprivileged Execution Trust Authorities" />
<characteristic-query type="SPC" />
<characteristic-query type="CA" />
<characteristic-query type="Root" />
<characteristic-query type="MY" />
</characteristic>
</wap-provisioningdoc>
It returns a 800704EC errorcode, which is a permission problem I think. The security policy queries however give me the bad xml error.
OK I have worked a little more with this and am now successfully able to provision certs in the MY, CA, and ROOT stores with the following code
Code:
void AddCert(const std::wstring& strCertStore, const std::wstring& strCertHash, const std::wstring& strCertEncoded)
{
LPWSTR wszOutput = NULL;
wchar_t wszXML[16384];
swprintf(wszXML,L"<wap-provisioningdoc>"
L"<characteristic type=\"CertificateStore\">"
L"<characteristic type=\"%s\">"
L"<characteristic type=\"%s\">"
L"<parm name=\"EncodedCertificate\" value=\"%s\"/>"
L"</characteristic>"
L"</characteristic>"
L"</characteristic>"
L"</wap-provisioningdoc>", strCertStore.c_str(), strCertHash.c_str(), strCertEncoded.c_str());
HRESULT hr = DMProcessConfigXML(wszXML,CFGFLAG_PROCESS,&wszOutput);
delete[] wszOutput;
}
I am unable however to add a cert to the Code Integrity store, or even query that store with the provisioning xml. The call to DMProcessConfigXML returns 86000011, and I can't find any reference as to what that error code could mean. Any thoughts?
Thanks,
Since you're using native code and messing with pretty serious stuff, you may want to try starting a thread on the Development and Hacking sub-forum (be sure to do a searcch first, of course). I do know that Heathcliff74, an XDA-Devs member, has been able to get full access to the certificate store and exposes this through his WP7 Root Tools app. This app is hosted on that sub-forum.
I know people have tried calling DMProcessConfigXML() before, and been blocked by various security policies. Have you made any changes to your phone that would increase the permissions your app runs at? Being able to make any changes using wap-provisioning straight from a sideloaded app surprises me; I didn't think they had that much permission. Have you tried using registry or filesystem provxml?
My account on these forums is relatively new, and I am unable to post on the dev forums yet. For this reason, I have been posting here and hoping someone would see it and maybe offer some insight. Either that or get enough posts to allow posting in the dev forums. All this code that I have been developing is on a custom HTC image (which is a switch from the original post), so I have TCB access for a native exe that runs on boot.
I follow Heathcliff74's posts as well as yours and a few others on the dev forums and have downloaded quite a few of the apps. I've also been talking with a few of them through private messages. Once I can post on the dev forums, I will probably post some things there, as well as chime in on some of the discussions going on.
Thanks for taking the time to reply though.
I am still working on this but have decided to approach it from a different angle. Instead of using provisioning xml to add the encoded cert to the store, I am going to try using the crypto api functions to do it. I am using the instructions
here to get the strCertEncoded value. Basically just exporting it in base64 encoding and copying the contents (minus the BEGIN CERTIFICATE and END CERTIFICATE). I am trying to add the cert and then list the contents of the store. Here is the code:
Code:
char pszNameString[256];
void* pvData;
DWORD cbData = 0;
PCCERT_CONTEXT pCertContext = NULL;
DWORD dwPropId = 0;
HCERTSTORE hSystemStore = NULL;
std::string strCertEncoded ("INSERT ENCODED VALUE HERE");
DWORD dwEncodedSize = strCertEncoded.size();
if (hSystemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"Code Integrity"))
{
// try to add a cert
if (CertAddEncodedCertificateToStore(hSystemStore,MY_ENCODING_TYPE,(const BYTE*)strCertEncoded.c_str(), dwEncodedSize, CERT_STORE_ADD_REPLACE_EXISTING, NULL))
fprintf(pLog, "Certificate successfully added to Code Integrity store.\n");
else
{
fprintf(pLog, "There was an error adding the certificate to the store. %x\n", GetLastError());
}
fprintf(pLog,"===== Enumerating Code Integrity store =====");
// find certs in this store
while (pCertContext=CertEnumCertificatesInStore(hSystemStore,pCertContext))
{
// get the name
if (CertGetNameString(pCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, (LPWSTR)pszNameString, 256))
{
fprintf(pLog,"\n%S\n",pszNameString);
// get the hash propery
CertGetCertificateContextProperty(pCertContext, CERT_HASH_PROP_ID, NULL, &cbData);
if (cbData == 0)
{
MyHandleError("CertGetCertificateContextProperty 1 failed.");
}
pvData = HeapAlloc(GetProcessHeap(), 0, cbData);
if (!CertGetCertificateContextProperty(pCertContext, CERT_HASH_PROP_ID, pvData, &cbData))
{
MyHandleError("CertGetCertificateContextProperty 2 failed.");
}
for (DWORD i = 0; i < cbData; i++)
{
fprintf(pLog,"%02X", ((byte*)pvData)[i]);
}
fprintf(pLog,"\n");
HeapFree(GetProcessHeap(), 0, pvData);
PCERT_INFO pinfo = pCertContext->pCertInfo;
SYSTEMTIME startDate;
FileTimeToSystemTime(&(pinfo->NotBefore),&startDate);
SYSTEMTIME endDate;
FileTimeToSystemTime(&(pinfo->NotAfter),&endDate);
fprintf(pLog,"Valid from %d/%d/%d to %d/%d/%d\n", startDate.wMonth, startDate.wDay, startDate.wYear, endDate.wMonth, endDate.wDay, endDate.wYear);
}
}
}
else
{
fprintf(pLog,"Error opening Code Integrity store. %d", hSystemStore);
}
The call to CertAddEncodedCertificateToStore returns with the errorcode 8009310B. The message for the error is "ASN1 bad tag value met." I have found some information on this error but have been unable to determine why I am getting it.

Enable GPU 5th Step?

I was looking around in the gpu driver files and I noticed that there was a hidden 5th 533MHz OC in there. I tried to enable it by copying in the "asv_3d_volt_9_prime" table in for the "non-prime" table. I made sure to remove the -1 in the non prime's table so it wouldn't think the extra voltages for 533MHz was out of bounds. I made sure to comment out these crippling lines:
Code:
/*if ((samsung_rev() < EXYNOS4412_REV_2_0) && (maliDvfsStatus.currentStep == 3))
{
level=get_mali_dvfs_status();
}*/ //a crippling check for "prime" note 2?
I also (after looking at siyah's kernel) copied in the entries for the step4 sysfs variables into mali_kernel_linux.c but my cell phone seems to be bent on not giving me this 5th step.
Sorry again for asking a question that's probably obvious to elite developers. Oh and thank you for answering my question on adding frequencies to the cpu. I now have my own (not copy and pasted :cyclops: ) lines in there to give me 2OC frequency and 1 underclock frequency.
Where's that code snippet from? Anyway it's not related to the limiting if the 5th step.
Code:
static mali_bool mali_dvfs_table_update(void)
{
unsigned int i;
unsigned int step_num = MALI_DVFS_STEPS;
//if (samsung_rev() < EXYNOS4412_REV_2_0)
//step_num = MALI_DVFS_STEPS - 1;
Just comment out those two lines in the DVFS decision making and it should scale up to 5 steps.
AndreiLux said:
Where's that code snippet from? Anyway it's not related to the limiting if the 5th step.
Code:
static mali_bool mali_dvfs_table_update(void)
{
unsigned int i;
unsigned int step_num = MALI_DVFS_STEPS;
//if (samsung_rev() < EXYNOS4412_REV_2_0)
//step_num = MALI_DVFS_STEPS - 1;
Just comment out those two lines in the DVFS decision making and it should scale up to 5 steps.
Click to expand...
Click to collapse
I tried that already. It didn't work. The code snippet I pasted comes from the same file.

[GUIDE] Marshalling data in Compact Framework

Author: Apriorit (Device Team)
Permanent link: apriorit(dot)com/our-company/dev-blog/62-marshalling-data-in-cf
In many situations when we create applications for different embedded systems or mobile platforms we can’t develop all parts of the product using managed code only.
For example we need several modules written in native language which perform some low level operations or we already have these libraries written on C++. So we have to use more than one programming language in our product and also use data marshaling in it.
In this article we will review some aspects of using data types and ways of using them during marshalling data to and from unmanaged code.
Making your interop calls more efficient
Marshaling is the act of taking data from one environment to another. In the context of .NET marshalling refers to transferring data from the app-domain you are in to somewhere else, outside.
You should remember that such Platform Invoke calls are slower than direct native calls and than regular managed calls. The speed depends on types marshaled between managed and native code, but nevertheless you should avoid using Platform Invoke calls if you have a chance to do this. Also it is recommended to use calls with some amount of transferred data than several “small” Platform Invoke calls.
Bitable types
It is recommended to use simple data types (int, byte, boolean, characters and strings). It makes the call more efficient and helps to avoid any convertions and copying. These blittable types have identical representation in both managed and unmanaged memory. But you should remember that in Compact Framework during marshaling boolean type is represented as 1-byte integer value (instead of 4-byte integer value in the full .NET Framework), character type (char) is always represented as 2-bytes Unicode character and String type is always treated as a Unicode array (in full .NET Framework it may be treated as a Unicode or ANSI array, or a BSTR).
Method Inlining
The JIT compiler can inline some methods in order to make the calls more efficient. You can not force a method to be inlined by the compiler, but you can make it NOT to be inlined. In order to avoid inlining you can:
• make the method virtual;
• add branching to the method’s body;
• define local variables in the method;
• use 2-bit floating point arguments (or return value).
Disabling method inlining can help to detect a problem during Platform Invoke calls.
Sequential layout
In the Compact Framework all structures and classes always have sequential layout (the managed value type has the same memory layout as the unmanaged structure). This behavior can be specified by setting attribute LayoutKind.Sequential. You don’t need to specify this attribute in Compact Framework, but if you use these pieces of code in both full .NET Framework and Compact Framework you have to set it to avoid different behavior on two platforms.
The following sample shows how to send some pointers from C# code for storing them in the native module.
Code C#:
Code:
[StructLayout(LayoutKind.Sequential)]
public class BasePointers // you can use the struct too
{
public IntPtr pointer1;
public IntPtr pointer2;
}
[DllImport("NativeDLL.dll", CallingConvention = CallingConvention.Winapi)]
// Cdecl
public static extern int TransferStruct(BasePointers pointers);
Code C++:
Code:
struct BasePointers
{
unsigned int pointer1;
unsigned int pointer2;
}
extern "C" __declspec(dllexport) int CDECL TransferArray(BasePointers*
pointers);
One Calling Convention
The Calling Convention determines the order in which parameters are passed to the function, and who is responsible for the stack cleaning. The .NET Compact Framework supports only the Winapi value (Cdecl on this platform) of Calling Convention. It defines the calling convention for C and C++ (instead of the full .NET Framework which supports three different calling conventions). To avoid crashes of your application you should make sure that your calling conventions in both managed and native declarations are same.
If you specify the attribute to preserve signature of functions ([PreserveSig]) then the returned value will contain 32-bit HRESULT value that will give you more data to analyze errors during the native function execution. The Calling Convention can be specified by adding the attribute CallingConvention to the declaration of your function. As it was mentioned the .NET Compact Framework supports only “Winapi” Calling Convention that corresponds to Cdecl:
Code C#:
Code:
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int ProgressEventHandler(int progressValue);
Code C++:
Code:
typedef void (CDECL *ProgressEventHandler)(int progressValue);
Data Alignment
In some situations we need to transfer data between the managed and unmanaged code in the structures. As it’s written above all structures have sequential layout in the Compact Framework, but you should remember about representation of structs in the managed in unmanaged code. The way of packing structures depends on a platform and on a way how the members of structure are aligned. On ARM platform this value for alignment is four (all values in structures are aligned to 4 bytes).
Code:
typedef struct OurStruct
{
unsigned char valueChar;
usingned int valueInt;
} ourStruct_;
This structure could be perfectly acceptable in desktop code, but if you use such structure on the Windows Mobile platform then you might receive valueInt at the offset 4. If you use such structures in both desktop and device's side code you have to use them carefully during marshaling.
During marshaling data you might receive such errors as “Datatype misalignment” (0x80000002) or “Access violation” (0x80000005). It indicates that you are using wrong pointers or try to access to the wrong offset of data. For example, you transfer array of bytes from C# code to the native module and define you functions as:
C# code:
Code:
[DllImport("NativeDLL.dll", CallingConvention = CallingConvention.Winapi)]
// Cdecl
public static extern int TransferArray(IntPtr src, int srcSize);
C++ Native Module code:
extern "C" __declspec(dllexport) int CDECL TransferArray(byte* srcArr,
int srcSize);
If you try to use the pointer “srcArr” as the pointer on integer (int*) and then try to use the corresponding value you will receive an error :
Code:
int value = *(int*)srcArr; // Datatype misalignment
The simple way to avoid this problem is to change declaration of C++ function and change the pointer on array of bytes to the pointer on array of integer and use it without any problems:
Code:
extern "C" __declspec(dllexport) int CDECL TransferArray(int* srcArr,
int srcSize);
Marshal class
You can use methods in the class Marshal to manually convert managed objects and perform conversions between IntPtrs. These methods are PtrToStructure, GetComInterfaceForObject, PtrToStringBSTR, GetFunctionPointerForDelegate and others. It allows you to control marshaling. These methods are also useful for debugging issues with marshaling parameters where the runtime is not able to convert a particular argument.
You cannot pass delegate directly to the native module as parameter of you function because the .NET Compact Framework does not support marshaling of delegates. Instead you should use method Marshal.GetFunctionPointerForDelegate for getting function pointer which you can pass to the native code and call it.
Code:
Code:
class MainClass
{
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int ProgressEventHandler(int progressValue);
...
void OnProgressChanged(int progressValue)
{
...
}
…
…
[DllImport("NativeDLL.dll", CallingConvention = CallingConvention.Winapi)]
// Cdecl
public static extern int SetCallbackFunction(IntPtr functionPointer);
}
// Passing function pointer
Delegate d = new ProgressEventHandler(OnProgressChanged);
IntPtr progressChanged = Marshal.GetFunctionPointerForDelegate(d);
int result = SetCallbackFunction(progressChanged);
But you should be aware of Garbage Collector (GC) in such situation. The GC might collect you delegates and your function pointers will become invalid. It may happen when you passed the function pointer to the native code as callback method in order to call it later - GC might think that there are no references to it in the managed code. To avoid this situation you should keep reference to this delegate. For example, you can store it in the classes variable or create some delegates pool, in which you can keep references to the several delegates.
GCHandle
Since we're passing a pointer to some data we need to allocate memory for that data and make sure that the GC will not remove that memory. One of the possible ways to manage this situation is to use GCHandle.
If you want to pass some class (or array of bytes) to the unmanaged code and you need to pin memory for the proper work with it in the unmanaged code you can write:
Code:
class SampleClass
{
...
}
SampleClass classSample = new SampleClass();
GCHandle classHandle = GCHandle.Alloc(classSample, GCHandleType.Pinned);
IntPtr ptrToClass = classHandle.AddrOfPinnedObject();
int result = PassPtrToUnmanagedCode(ptrToClass); // our function
You can also make an instance of GCHandle as a member of the class to avoid deleting them by GC. Also you should remember that the structure is value-type. And pinning it to the memory will cause a problem, because structure will be copied and GCHandle will handle a reference to created “boxed” copy of the object. It will be hard to find such problem in the future.
Conclusion
During marshaling data you may face with the problems described above. Very often you may get “NotSupportedException” and other exceptions. To find a problem you can enable logging of setting the registry keys. One of the logging components is “Interop ”. The log provides information about Platform Invoke calls and marshaling. You can read MSDN for more information about Creating Log Files.
With the .NET Compact Framework 2.0 you can use Platform Invoke calls in managed application, even though there are a few limitations. You should remember all differences and limitations between full .NET Framework and the Compact Framework to avoid problems in your applications.

Categories

Resources